TreeSelect
TreeSelect component for selecting values from hierarchical tree data structures. It supports single and multiple selection, cascading, filtering, and asynchronous data loading.
Default TreeSelect
Basic tree select with hierarchical options.
<template>
<HLTreeSelect id="tree-select-default" :options="options" :value="selectedValue" @update:value="handleChange" />
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { ref } from 'vue'
const selectedValue = ref(null)
const options = [
{
label: 'Rubber Soul',
key: 'Rubber Soul',
children: [
{
label: 'Drive My Car',
key: 'Drive My Car',
},
{
label: 'Norwegian Wood',
key: 'Norwegian Wood',
},
],
},
{
label: 'Let It Be',
key: 'Let It Be Album',
children: [
{
label: 'Two Of Us',
key: 'Two Of Us',
},
{
label: 'Let It Be',
key: 'Let It Be',
},
],
},
]
const handleChange = value => {
selectedValue.value = value
}
</script>Multiple Selection
Enable multiple selection with the multiple prop.
<template>
<HLTreeSelect id="tree-select-multiple" :options="options" :value="multipleValue" @update:value="(val) => multipleValue = val" multiple />
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { ref } from 'vue'
const multipleValue = ref([])
const options = [
// ... options data
]
</script>Filterable and Clearable
Add search functionality and clear button.
<template>
<HLTreeSelect id="tree-select-filterable" :options="options" :value="selectedValue" @update:value="handleChange" filterable clearable />
</template>Max Tag Count
Limit the number of visible tags in multiple selection mode.
<template>
<HLTreeSelect
id="tree-select-max-tags"
:options="options"
:value="multipleValue"
@update:value="(val) => multipleValue = val"
multiple
:max-tag-count="4"
/>
</template>Cascade Selection
Enable cascade mode where selecting a parent automatically selects all children. This will support with checkable prop and multiple selection.
<template>
<HLTreeSelect
id="tree-select-cascade"
:options="options"
:value="cascadeValue"
@update:value="(val) => cascadeValue = val"
multiple
cascade
checkable
/>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { ref } from 'vue'
const cascadeValue = ref([])
</script>Default Expand All
Expand all tree nodes by default.
<template>
<HLTreeSelect id="tree-select-expand-all" :options="options" :value="selectedValue" @update:value="handleChange" default-expand-all />
</template>Default Expanded Keys
Specify which nodes should be expanded by default using their keys.
<template>
<HLTreeSelect
id="tree-select-expanded-keys"
:options="options"
:value="selectedValue"
@update:value="handleChange"
:default-expanded-keys="['Let It Be Album']"
/>
</template>Override Default Node Click Behavior
Customize what happens when a node is clicked.Here is the example to toggle expand when a node is clicked.
<template>
<HLTreeSelect
id="tree-select-click-behavior"
:options="options"
:value="selectedValue"
@update:value="handleChange"
:override-default-node-click-behavior="overrideDefaultNodeClickBehavior"
/>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { ref } from 'vue'
const selectedValue = ref(null)
const overrideDefaultNodeClickBehavior = info => {
// If the node has children, toggle expand instead of selecting
if (info?.option?.children?.length) {
return 'toggleExpand'
}
return 'default'
}
const handleChange = value => {
selectedValue.value = value
}
</script>Custom Slots
Customize various parts of the tree select using slots.
<template>
<HLTreeSelect id="tree-select-slots" :options="options" :value="selectedValue" @update:value="handleChange" multiple filterable>
<template #action>
<div class="p-2 text-sm text-gray-600">If you click this demo, you may need it.</div>
</template>
<template #empty>
<div class="p-4 text-center text-gray-500">Empty handler when options are empty</div>
</template>
<template #arrow>
<Star01Icon class="w-5 h-5" />
</template>
</HLTreeSelect>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { Star01Icon } from '@gohighlevel/ghl-icons/24/outline'
import { ref } from 'vue'
const selectedValue = ref(null)
</script>Render Label
Customize how labels are rendered in the tree.
<template>
<HLTreeSelect
id="tree-select-render-label"
:options="options"
:value="renderLabelValue"
@update:value="(val) => renderLabelValue = val"
:render-label="renderLabel"
/>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { ref, h } from 'vue'
const renderLabelValue = ref(null)
const renderLabel = info => {
return h(
'div',
{
style: {
display: 'flex',
alignItems: 'center',
},
},
[
h(
'div',
{
style: {
fontWeight: 400,
fontSize: '14px',
lineHeight: '20px',
padding: '12px',
},
class: info.selected ? 'bg-primary' : '',
},
[
h(
'p',
{
style: { margin: 0 },
class: info.selected ? 'bg-primary' : '',
},
[info.option.label]
),
]
),
]
)
}
</script>Render Tag
Customize how selected tags are rendered in multiple selection mode.
<template>
<HLTreeSelect
id="tree-select-render-tag"
:options="options"
:value="renderTagValue"
@update:value="(val) => renderTagValue = val"
:render-tag="renderTag"
multiple
/>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { ref, h } from 'vue'
const renderTagValue = ref(['Let It Be Album'])
const renderTag = info => {
return h('div', { class: 'bg-primary border border-solid border-black px-2' }, [
info.option.label,
h('button', { class: 'ml-2', onClick: () => info.handleClose() }, 'X'),
])
}
</script>Render Prefix
Add custom prefix content to tree nodes.
<template>
<HLTreeSelect
id="tree-select-render-prefix"
:options="options"
:value="renderPrefixValue"
@update:value="(val) => renderPrefixValue = val"
:render-prefix="renderPrefix"
/>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { Star01Icon } from '@gohighlevel/ghl-icons/24/outline'
import { ref, h } from 'vue'
const renderPrefixValue = ref(null)
const renderPrefix = info => {
return h(Star01Icon, {
style: {
width: '16px',
height: '16px',
fill: info.checked ? 'blue' : 'none',
},
})
}
</script>Render Suffix
Add custom suffix content to tree nodes.
<template>
<HLTreeSelect
id="tree-select-render-suffix"
:options="options"
:value="renderSuffixValue"
@update:value="(val) => renderSuffixValue = val"
:render-suffix="renderSuffix"
/>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { Star01Icon } from '@gohighlevel/ghl-icons/24/outline'
import { ref, h } from 'vue'
const renderSuffixValue = ref(null)
const renderSuffix = info => {
return h(Star01Icon, {
style: {
width: '16px',
height: '16px',
fill: info.checked ? 'blue' : 'none',
},
})
}
</script>Async Loading
Load child nodes asynchronously when expanding parent nodes.
<template>
<HLTreeSelect
id="tree-select-async"
:options="asyncOptions"
:value="asyncValue"
@update:value="(val) => asyncValue = val"
:on-load="handleLoad"
allow-checking-not-loaded
multiple
checkable
cascade
/>
</template>
<script setup>
import { HLTreeSelect } from '@platform-ui/highrise'
import { ref } from 'vue'
const asyncValue = ref(null)
const asyncOptions = ref([
{
label: 'Rubber Soul',
key: 'Rubber Soul',
depth: 1,
isLeaf: false,
},
])
const getChildren = option => {
const children = []
for (let i = 0; i <= option.depth; ++i) {
children.push({
label: option.label + '-' + i,
key: option.label + '-' + i,
depth: option.depth + 1,
isLeaf: option.depth === 3,
})
}
return children
}
const handleLoad = option => {
return new Promise(resolve => {
window.setTimeout(() => {
option.children = getChildren(option)
resolve()
}, 1000)
})
}
</script>Imports
import { HLTreeSelect } from '@platform-ui/highrise'Props
| Prop | Type | Default | Description |
|---|---|---|---|
| id * | string | undefined | Unique identifier for the tree select |
| allowCheckingNotLoaded | boolean | false | Allow checking nodes that haven't been loaded yet (for async loading) |
| cascade | boolean | false | Whether to cascade selection to child nodes |
| checkable | boolean | false | Show checkboxes for nodes |
| checkStrategy | 'all' | 'parent' | 'child' | 'all' | Strategy for what values are shown when cascading: 'all' shows all checked nodes, 'parent' shows only parent nodes, 'child' shows only leaf nodes |
| clearable | boolean | false | Show clear button |
| consistentMenuWidth | boolean | true | Whether dropdown menu width matches the select width |
| defaultExpandAll | boolean | false | Expand all nodes by default |
| defaultExpandedKeys | Array<string | number> | [] | Keys of nodes that should be expanded by default |
| disabled | boolean | false | Disable the tree select |
| expandedKeys | Array<string | number> | undefined | Controlled expanded keys (requires @update:expandedKeys handler) |
| filterable | boolean | false | Enable search/filter functionality |
| loading | boolean | false | Show loading state |
| maxTagCount | number | 'responsive' | 'responsive' | Maximum number of visible tags in multiple mode |
| multiple | boolean | false | Enable multiple selection |
| onLoad | (node: TreeSelectOption) => Promise<void> | undefined | Async function to load child nodes |
| options | TreeSelectOption[] | [] | Tree data options |
| overrideDefaultNodeClickBehavior | (info: { option: TreeSelectOption }) => ClickBehaviourType | undefined | Override default click behavior for nodes |
| placeholder | string | 'Please Select' | Placeholder text |
| placement | FollowerPlacement | 'bottom-start' | Dropdown placement |
| renderLabel | (info: { option: TreeSelectOption; checked: boolean; selected: boolean }) => VNodeChild | undefined | Custom label render function |
| renderPrefix | (info: { option: TreeSelectOption; checked: boolean; selected: boolean }) => VNodeChild | undefined | Custom prefix render function |
| renderSuffix | (info: { option: TreeSelectOption; checked: boolean; selected: boolean }) => VNodeChild | undefined | Custom suffix render function |
| renderTag | (info: { option: TreeSelectOption; handleClose: () => void }) => VNodeChild | undefined | Custom tag render function |
| show | boolean | undefined | Control dropdown visibility |
| showAvatarInTags | boolean | true | Show avatar in tags for multiple selection |
| size | 'sm' | 'md' | 'lg' | 'md' | Size of the tree select |
| status | 'success' | 'warning' | 'error' | undefined | Validation status |
| value | string | number | Array<string | number> | null | undefined | Selected value(s) |
TreeSelectOption
- Always provide unique
keyvalues for each node to ensure proper tracking and updates.
interface HLTreeSelectOption {
key: string | number // Unique identifier for the node
label: string // Display text for the node
description?: string // Optional description text
src?: string // Optional image source (e.g., for avatars)
children?: TreeSelectOption[] // Child nodes
disabled?: boolean // Whether the node is disabled
isLeaf?: boolean // Indicates if the node is a leaf (no children, used in async loading)
depth?: number // Depth level (used in async loading)
renderOption?: () => VNodeChild // Custom render function for the option
[key: string]: any // Additional custom properties
}Emits
| Event | Parameters | Description |
|---|---|---|
@update:value | (value: string | number | Array<string | number>, option: TreeSelectOption | TreeSelectOption[], meta: { node: TreeSelectOption, action: string }) | Emitted when selection changes |
@update:expandedKeys | (keys: Array<string | number>, meta: { node: TreeSelectOption, action: string }) | Emitted when expanded keys change |
Slots
| Name | Parameters | Description |
|---|---|---|
| action | () | Footer content in the dropdown |
| arrow | () | Custom arrow icon |
| empty | () | Content shown when there are no options |
| header | () | Header content in the dropdown |