A lightweight (~3kb gzipped), zero-dependency UI control that transforms <select>
or <input>
elements into interactive hierarchical trees with checkboxes.
- Async data fetching
- Customizable depth for collapsing, checkboxes and values
- Parent/child selection propagation
- Supports both input and select HTML elements
- Built in functionalities like search, shift+select, clear
- Tailwind support by injectable classNames
- TypeScript support
npm install @wesleydebruijn/tree-select
<input type="text" id="tree-select" value="1,2" />
<!-- or -->
<select id="tree-select" multiple>
<option value="1" selected>1</option>
<option value="2" selected>2</option>
<option value="3">3</option>
</select>
import { TreeSelect } from 'tree-select';
const settings = {
data: [
{
id: '1',
name: 'Parent 1',
children: [
{ id: '1-1', name: 'Child 1-1' },
{ id: '1-2', name: 'Child 1-2' },
],
},
{
id: '2',
name: 'Parent 2',
children: [
{ id: '2-1', name: 'Child 2-1' },
{ id: '2-2', name: 'Child 2-2' },
],
},
],
text: {
search: 'Search items...',
selected: 'items selected',
loading: 'Loading...',
},
};
const element = document.getElementByid('tree-select');
const treeSelect = new TreeSelect(element, settings);
// or
const treeSelect = new TreeSelect('#tree-select', settings);
const treeSelect = new TreeSelect('#tree-select', {
// UI state
mode: 'vertical',
open: false,
collapsible: true,
searchable: true,
clearable: true,
results: false,
// Data configuration
data: [...], // Tree data
dataSrc: 'api/tree-data.json', // Or load from URL
// Depth settings
depthCollapsible: 0, // Depth at which items become collapsible
depthCollapsed: 0, // Depth at which items are collapsed by default
depthCheckboxes: 0, // Depth at which checkboxes appear
depthValues: 'last', // Use 'last' or number to specify which depth values are used
// Text customization
text: {
search: 'Search...',
selected: 'selected',
loading: 'Loading...'
},
// HTML customization
focus: 'focus',
disabled: 'disabled',
html: {
wrapper: { className: 'custom-wrapper', data: { value: "custom" } },
control: { className: 'custom-control' },
// ... customize other elements
},
// Event handlers
onOpen: () => console.log('Opened'),
onClose: () => console.log('Closed'),
onSelect: (selected) => console.log('Selected:', selected),
onSearch: (search) => console.log('Searching:', search),
onLoad: (data) => console.log('Data loaded:', data),
onClear: () => console.log('Cleared')
});
// Open the dropdown
treeSelect.open();
// Close the dropdown
treeSelect.close();
// Load data & HTML tree to the DOM
treeSelect.load();
// Disable the input & control
treeSelect.disable();
// Enable the input & control
treeSelect.enable();
// Destroy the instance and return to original input/select
treeSelect.destroy();
interface Data {
id: string | number;
name: string;
children?: Data[];
}
Setting | Type | Default | Description |
---|---|---|---|
mode | 'vertical' | 'horizontal' | 'vertical' | Display tree vertically or horizontally |
open | boolean | false | Whether dropdown should be open initially |
clearable | boolean | true | Whether to show clear button |
searchable | boolean | true | Whether to show search input |
collapsible | boolean | true | Whether nodes can be collapsed |
results | boolean | false | Whether to show selected items on side |
delimiter | string | ',' | Delimiter for input values |
depthCollapsible | number | 0 | Depth at which nodes become collapsible |
depthCollapsed | number | 0 | Depth at which nodes start collapsed |
depthCheckboxes | number | 0 | Depth at which checkboxes start appearing |
depthValues | number | 'last' | 'last' | Depth at which values start being collected |
data | Data[] | undefined | Initial data array |
dataSrc | string | undefined | URL to fetch data from |
focus | string | 'focus' | Class name applied when dropdown is focused |
text | object | {} | Text customization options |
text.heading | string | undefined | Text shown in dropdown heading |
text.selected | string | 'selected' | Text shown after selected count |
text.loading | string | 'Loading...' | Text shown while loading |
text.search | string | 'Search...' | Placeholder text for search input |
html | object | {} | HTML elements customization |
onLoad | function | undefined | Callback when data is loaded |
onOpen | function | undefined | Callback when dropdown opens |
onClose | function | undefined | Callback when dropdown closes |
onClear | function | undefined | Callback when selection is cleared |
onSearch | function | undefined | Callback when search is performed |
onSelect | function | undefined | Callback when items are selected/deselected |
Element | Class Name | Description |
---|---|---|
wrapper | tree-select-wrapper | Container element wrapping the entire component |
control | tree-select-control | Main control element showing selected count |
dropdown | tree-select-dropdown | Dropdown container that appears when opened |
clear | tree-select-clear | Clear button to remove all selections |
search | tree-select-search | Search input field for filtering items |
loading | tree-select-loading | Loading indicator shown while data loads |
listContainer | tree-select-list-container | Container for all lists of the tree |
list | tree-select-list | Container for a list of the three |
item | tree-select-item | Individual item in the tree |
checkbox | tree-select-checkbox | Checkbox input for selecting items |
collapse | tree-select-collapse | Toggle button for expanding/collapsing nodes |
label | tree-select-label | Label container for item text and controls |
labelSpan | tree-select-label-span | Text element within item label |
children | tree-select-children | Container for child items |
result | tree-select-result | Individual item of selected item in the tree |
To set up the development environment:
-
Clone the repository:
git clone https://github.com/@wesleydebruijn/tree-select.git cd tree-select
-
Start the development server:
cd demo npm install npm run dev
This will start a Vite development server with hot module reloading. The demo page will be available at http://localhost:5173/tree-select/
.