Linked list box

It is a list-based control used to select linked hierarchical data.

When selecting a higher-level data in the Linked ComboBox, the sub-list of the selected data will be displayed as items in the next list box. When an item is selected, the value of the selected item is added to the value of the higher-level item. If a top-level item is selected, all previously selected sub-item values are removed, and the current selected value is replaced.

By selecting the forward or backward option, the sub-list expands.

The items in the linked list box are composed of "value", "label", and "parentValue".

Key action
Boundary Key Action
List Up Shift upwards
List Down Shift downwards
List Left Shift left
List Right Shift right
Item Enter Select item

Web accessibility

You should provide a label.

Case when you need description for operating a keyboard through screen reader of the linked list box

Control component

Description for the composition of the linked list box

Control component

Component

Component Type Description
headers String[] Header of the linked list box.
Item cpr.controls.TreeItem Item of the linked list box.
listArrowImage String An arrow image of the linked list box.
space String Gap between the lists of linked list box.

Example: Using the components

var linkedlistbox = app.lookup("llb1");
linkedlistbox.headers = ["header1", "header2", "header3"];
linkedlistbox.space = "10px";
linkedlistbox.listArrowImage = "./image/arrow.jpg";

/* Import every item of the lniked list box.*/
var items = itemslinkedlistbox.getItems();
console.log(items[0]);						// {label: "label1", value: "value1", parentValue: "root"}

/* Select item with index'0' from the linked list box.*/
linkedlistbox.selectItem(0);
console.log(linkedlistbox.value);			// value1

Style component

The linked list box expands its sub-list when a parent item is selected. As the sub-list grows, the width of the list is evenly divided across the total width.

The linked list box is composed of lists and items. Separate classes are applied based on the items.

Theme file: linked-listbox.part.less

CSS style composition

Component

Component Description
cl-linkedlistbox Overall style of the linked list box.
cl-linkedlistbox-listbox Style for the header and list section of the linked list box.
cl-linkedlistbox-header Header style of the linked list box.
cl-linkedlistbox-list Style for the item list of the linked list box.
cl-linkedlistbox-item Style for the item of the linked list.
cl-linkedlistbox-arrow Overall style of the linked list box.
cl-text Text style for the item of the linked list box.

State element

Component State element Description
cl-linkelistbox-item cl-selected Style for the item selection of the linked list box.
cl-hover Style for hovering an item of the linked list box.
cl-disabled Style for the disabled item of the linked list box.
cl-folder Style for the item with the sublist of the linked list box.
cl-leaf Style for the item without the sublist of the linked list box.

Changing the style property by using the CSS file

@CHARSET "UTF-8";

/* Style for the header of the linked list box */
.cl-linkedlistbox .cl-linkedlistbox-listbox .cl-linkedlistbox-header {
  	border: 1px solid green;
  	background-image: linear-gradient(to bottom, #FEFFFF, #EFF0F1);
}

/* Style for the sublist of the linked list box */
.cl-linkedlistbox .cl-linkedlistbox-listbox .cl-linkedlistbox-list {
  	border: 1px solid red;
}

/* Background color for the item selected from the linked list box */
.cl-linkedlistbox-list .cl-linkedlistbox-item.cl-selected {
	background-color: yellowgreen;
}

/* Image for the arrow folder of the linked list box */
.cl-linkedlistbox .cl-linkedlistbox-listbox .cl-linkedlistbox-list .cl-linkedlistbox-item .cl-linkedlistbox-arrow.cl-folder {
  	width: 16px;
  	height: 16px;
  	background-image: url(icons/drill-down.png);
}
Styler composition

Component

Component Description
listbox Styler for the header and list of the linked list box.
header Styler for the header of the linked list box.
list Styler for the item list of the linked list box.
item Styler for the item of the linked list box.

Changing the style property by using the styler

// UI Configuration
var linkedListBox_1 = new cpr.controls.LinkedListBox("llb1");

/* Default style of the linked list box */
/* (Multiple settings can be configured using JSON data.) */
linkedListBox_1.style.css({
	"background-color": "green",
	"border": "2px solid red"
});

/* Default style of the linked list box(For single selection) */
linkedListBox_1.style.css("background-color": "green");

/* Style for the box of the linked list box */
linkedListBox_1.style.listbox.css({
	"border": "2px solid blue"
});

/* Style for the header of the linked list box */
linkedListBox_1.style.header.css({
	"font-weight": "bold"
});

/* Style for the item list of the linked list box */
linkedListBox_1.style.list.css({
	"color" : "yellow"
});

/* Style for the item of the linked list box */
linkedListBox_1.style.item.css({
	"background-color": "red"
});

Example

A linked list box is composed of connected list-type data where selecting a parent item expands the corresponding child list. The data for the linked list box is set through creating precursor data or binding to a data set.

var linkedListBox_1 = new cpr.controls.ComboBox("llb1");

/* Configure preceding data */
var item_1 = new cpr.controls.TreeItem("pre_label1", "pre_value1", "root");
linkedListBox_1.addItem(item_1);
linkedListBox_1.addItem(new cpr.controls.TreeItem("pre_label2", "pre_value2", "root"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("pre_label3", "pre_value3", "pre_value1"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("pre_label4", "pre_value4", "pre_value1"));

/* Configure binding for the DataSet */
var dataset = new cpr.data.DataSet("ds1");
dataset.parseData({
	columns: [
		{name: "label"},
		{name: "value"},
		{name: "parent"}
	],
	rows: [
		{label: "label1", value: "value1", parent: "root"},
		{label: "label2", value: "value2", parent: "root"},
		{label: "label3", value: "value3", parent: "value1"},
		{label: "label4", value: "value4", parent: "value3"}
	]
});

linkedListBox_1.setItemSet(dataset, {label: "label", value: "value", parentValue: "parent"});
linkedListBox_1.redraw(); //Renew control

console.log(linkedListBox_1.getItems());	
//[pre_value1, pre_value2, pre_value3, pre_value4, value1, value2, value3, value4]; Array of items

Related API: value , cpr.controls.TreeItem , addItem , setItemSet , redraw , getItems , LinkedListBox API overview

Select direction for the expansion

A linked list box allows you to set the direction in which the child list expands using the 'direction' property. The forward direction expands from left to right, while the reverse direction expands from right to left.

var linkedListBox_1 = new cpr.controls.LinkedListBox("llb1");
linkedListBox_1.addItem(new cpr.controls.TreeItem("label1", "value1", "root"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("label2", "value2", "root"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("label3", "value3", "value1"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("label4", "value4", "value1"));

/* Sublist expands to the left. */
linkedListBox_1.direction = left;

/* Configure the value of the linked list box*/
linkedListBox_1.values = ["value1", "value3"];

Related API: direction , values , cpr.controls.TreeItem , addItem , LinkedListBox API overview

Drag and drop event

A linked list box provides drag and drop events to allow moving items from one list box to the same or different list box. It includes the 'allowDrop' and 'draggableItem' properties as related attributes.

var linkedListBox_1 = new cpr.controls.LinkedListBox("llb1");
var linkedListBox_2 = new cpr.controls.LinkedListBox("llb2");

linkedListBox_1.addItem(new cpr.controls.TreeItem("label1", "value1", "root"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("label2", "value2", "root"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("label3", "value3", "value1"));
linkedListBox_1.addItem(new cpr.controls.TreeItem("label4", "value4", "value1"));

linkedListBox_2.addItem(new cpr.controls.TreeItem("label5", "value5", "root"));
linkedListBox_2.addItem(new cpr.controls.TreeItem("label6", "value6", "root"));
linkedListBox_2.addItem(new cpr.controls.TreeItem("label7", "value7", "value5"));
linkedListBox_2.addItem(new cpr.controls.TreeItem("label8", "value8", "value5"));

/* Configure to allow item drag */
linkedListBox_1.draggableItem = true;
/* Configure to allow item drop */
linkedListBox_2.allowDrop = true

Related API: cpr.controls.TreeItem , addItem , draggableItem , allowDrop , LinkedListBox API overview

User definition of drag and drop(DataSet)

The drop event or dragover event provides default behavior. To ignore this behavior, please use event.preventDefault(). The following code customizes the drop action in a tree connected to a dataset.

var linkedListBox_1 = new cpr.controls.LinkedListBox("llbx1");
/* Configure to allow item drag */
linkedListBox_1.draggableItem = true;
/* Configure to allow item drop */
linkedListBox_1.allowDrop = true
/* Connect the ItemSet to the linked list box */
linkedListBox_1.setItemSet(app.lookup("ds1"),{label:"label",value:"value",icon:null,parentValue:"parentValue"});

/* Definition of drop event */
linkedListBox_1.addEventListener("drop",function(e){
  var linkedlistbox = e.control;
  e.preventDefault();// Prevent the default action of the linked list's drop.
  var sourceData = null;
  try{
    sourceData = JSON.parse(e.dataTransfer.getData("text")); // The dragged data can be imported through e.dataTransfer.getData("text").
  }catch(exception){
  }
  var canAction = sourceData && sourceData['from']; // Verify if it is a proper drag data.
  if(!canAction){
    return;
  }
  
  var targetElement;
  if(!e.target.closest){ // Does not support closest IE(additional implementation is required)
    return;
  }
  targetElement = e.target.closest("[role='option']");
  var after = true; // Determine the position of the additional item above or below the target item.
  if(targetElement != null){ 
    var rect = targetElement.getBoundingClientRect();
    var top = e.pageY - rect.top;
    var bottom = rect.top + rect.height - e.pageY;
    if(top < bottom){
      after = false;
    }
  }else if(targetElement == null){
    targetElement = e.target.closest("[role='listbox']");
    if(targetElement == null){
      return;
    }
  }
  
  var targetItem = e.targetObject.item;
  var isSelf = sourceData.from.uuid == linkedlistbox.uuid; // Verify whether the data has been dragged within the same control.
  var items = []; // Use dragged data to create an item.
  sourceData.content.forEach(function(each){
    var item = new cpr.controls.TreeItem(each.label,each.value,each.parentValue);
    if(each['icon']){
      item.icon = each['icon'];
    }
    items.push(item);
  });
  
  for(var i=0;i < items.length;i++){
    var each = items[i];
    //When an item is brought from another control, it cannot be added if the value is the same.
    if(isSelf == false && linkedlistbox.getItemByValue(each.value) != null){
      continue;
    }
    
    if(targetItem){
       each.parentValue = targetItem.parentValue; // Changed to be the same as the parent target of the item.
       if(isSelf == false){ // When dropping an item dragged from another control.
         if(after){
           var targetIndex = linkedlistbox.getIndex(targetItem);
           var row = linkedlistbox.dataSet.insertRow(targetIndex,true);
           row.setValue(linkedlistbox.itemSetConfig.label,each.label);
           row.setValue(linkedlistbox.itemSetConfig.value,each.value);
           row.setValue(linkedlistbox.itemSetConfig.parentValue,each.parentValue);

           targetItem = each;
         }else{
           var targetIndex = linkedlistbox.getIndex(targetItem);
           var row = linkedlistbox.dataSet.insertRow(targetIndex,false);
           row.setValue(linkedlistbox.itemSetConfig.label,each.label);
           row.setValue(linkedlistbox.itemSetConfig.value,each.value);
           row.setValue(linkedlistbox.itemSetConfig.parentValue,each.parentValue);
         }
       }else{ // When dropping an item dragged from the same control.
         if(after){
           var sourceIndex = linkedlistbox.getIndex(each);
           var targetIndex = linkedlistbox.getIndex(targetItem);
           var row = linkedlistbox.dataSet.getRow(sourceIndex);
           row.setValue(linkedlistbox.itemSetConfig.label,each.label);
           row.setValue(linkedlistbox.itemSetConfig.value,each.value);
           row.setValue(linkedlistbox.itemSetConfig.parentValue,each.parentValue);
           linkedlistbox.dataSet.moveRowIndex(sourceIndex,targetIndex,true);
           targetItem = each; // If multiple items are being moved, the reference item needs to be changed.
         }else{
           var sourceIndex = linkedlistbox.getIndex(each);
           var targetIndex = linkedlistbox.getIndex(targetItem);
           var row = linkedlistbox.dataSet.getRow(sourceIndex);
           row.setValue(linkedlistbox.itemSetConfig.label,each.label);
           row.setValue(linkedlistbox.itemSetConfig.value,each.value);
           row.setValue(linkedlistbox.itemSetConfig.parentValue,each.parentValue);
           linkedlistbox.dataSet.moveRowIndex(sourceIndex,targetIndex,false);
         }
      }
    }else{ 
      if(isSelf == false){
        each.parentValue = targetElement.getAttribute("data-id"); //If the target item is not present, set the parent value of the list.
        linkedlistbox.addItem(each);
      }
    }
  }
  linkedlistbox.redraw();
});

Related API: cpr.controls.TreeItem , itemSetConfig , dataSet , getItemByValue , LinkedListBox API overview