Basic
Basic tree select with hierarchical options.
Select a region
Code
<% options = [
{
title: "Asia",
key: "asia",
children: [
{ title: "Japan", key: "japan" },
{ title: "Thailand", key: "thailand" }
]
},
{
title: "Europe",
key: "europe",
children: [
{ title: "France", key: "france" },
{ title: "Italy", key: "italy" }
]
}
] %>
<%= render Hakumi::TreeSelect::Component.new(
name: "tree_select_basic",
options: options,
placeholder: "Select a region"
) %>
Multi-Level Nested
Tree with multiple levels of nesting (continent > country > state > city).
Select a location
Code
<%
tree_options = [
{
title: "North America",
key: "na",
children: [
{
title: "United States",
key: "us",
children: [
{
title: "California",
key: "ca",
children: [
{ title: "Los Angeles", key: "la" },
{ title: "San Francisco", key: "sf" },
{ title: "San Diego", key: "sd" }
]
},
{
title: "New York",
key: "ny",
children: [
{ title: "New York City", key: "nyc" },
{ title: "Buffalo", key: "buf" }
]
},
{
title: "Texas",
key: "tx",
children: [
{ title: "Houston", key: "hou" },
{ title: "Austin", key: "aus" },
{ title: "Dallas", key: "dal" }
]
}
]
},
{
title: "Canada",
key: "canada",
children: [
{ title: "Toronto", key: "tor" },
{ title: "Vancouver", key: "van" },
{ title: "Montreal", key: "mon" }
]
}
]
},
{
title: "Europe",
key: "eu",
children: [
{
title: "United Kingdom",
key: "uk",
children: [
{ title: "London", key: "lon" },
{ title: "Manchester", key: "man" }
]
},
{
title: "Germany",
key: "de",
children: [
{ title: "Berlin", key: "ber" },
{ title: "Munich", key: "mun" }
]
}
]
}
]
%>
<%= render Hakumi::TreeSelect::Component.new(
name: "location",
placeholder: "Select a location",
tree_default_expand_all: true,
allow_clear: true,
options: tree_options
) %>
Multiple Selection
Allow selecting multiple nodes from the tree.
Select multiple countries
Code
<% options = [
{
title: "North America",
key: "north-america",
children: [
{ title: "USA", key: "usa" },
{ title: "Canada", key: "canada" }
]
},
{
title: "South America",
key: "south-america",
children: [
{ title: "Brazil", key: "brazil" },
{ title: "Chile", key: "chile" }
]
}
] %>
<%= render Hakumi::TreeSelect::Component.new(
name: "tree_select_multiple",
options: options,
multiple: true,
allow_clear: true,
placeholder: "Select multiple countries"
) %>
Checkable
Tree with checkboxes for parent-child selection.
Check departments
Code
<% options = [
{
title: "Engineering",
key: "engineering",
children: [
{ title: "Frontend", key: "frontend" },
{ title: "Backend", key: "backend" }
]
},
{
title: "Design",
key: "design",
children: [
{ title: "UX", key: "ux" },
{ title: "Visual", key: "visual" }
]
}
] %>
<%= render Hakumi::TreeSelect::Component.new(
name: "tree_select_checkable",
options: options,
tree_checkable: true,
tree_default_expand_all: true,
allow_clear: true,
placeholder: "Check departments"
) %>
Searchable
Enable search to filter tree nodes.
Search and select
Code
<%= render Hakumi::TreeSelect::Component.new(
name: "category",
placeholder: "Search and select",
show_search: true,
tree_default_expand_all: true,
options: [
{
title: "Electronics",
key: "electronics",
children: [
{ title: "Phones", key: "phones" },
{ title: "Laptops", key: "laptops" },
{ title: "Tablets", key: "tablets" }
]
},
{
title: "Clothing",
key: "clothing",
children: [
{ title: "Shirts", key: "shirts" },
{ title: "Pants", key: "pants" },
{ title: "Shoes", key: "shoes" }
]
}
]
) %>
Sizes
Three sizes: small, middle (default), and large.
Small
Middle (default)
Large
Code
<%
tree_options = [
{
title: "Asia",
key: "asia",
children: [
{ title: "China", key: "china" },
{ title: "Japan", key: "japan" }
]
},
{
title: "Europe",
key: "europe",
children: [
{ title: "Germany", key: "germany" },
{ title: "France", key: "france" }
]
}
]
%>
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :middle) do %>
<%= render Hakumi::TreeSelect::Component.new(
name: "small_select",
placeholder: "Small",
size: :small,
options: tree_options
) %>
<%= render Hakumi::TreeSelect::Component.new(
name: "middle_select",
placeholder: "Middle (default)",
size: :middle,
options: tree_options
) %>
<%= render Hakumi::TreeSelect::Component.new(
name: "large_select",
placeholder: "Large",
size: :large,
options: tree_options
) %>
<% end %>
Leaf Only Selection
Only allow selecting leaf nodes (nodes without children).
Select product (leaf nodes only)
Code
<%
tree_options = [
{
title: "Electronics",
key: "electronics",
children: [
{ title: "Phones", key: "phones" },
{ title: "Laptops", key: "laptops" },
{ title: "Tablets", key: "tablets" }
]
},
{
title: "Clothing",
key: "clothing",
children: [
{ title: "Shirts", key: "shirts" },
{ title: "Pants", key: "pants" },
{ title: "Shoes", key: "shoes" }
]
}
]
%>
<%= render Hakumi::TreeSelect::Component.new(
name: "product",
placeholder: "Select product (leaf nodes only)",
tree_selectable_leaf_only: true,
tree_default_expand_all: true,
options: tree_options
) %>
Status
Error and warning status states.
Error status
Warning status
Code
<%
tree_options = [
{
title: "Departments",
key: "departments",
children: [
{ title: "Engineering", key: "eng" },
{ title: "Marketing", key: "mkt" },
{ title: "Sales", key: "sales" }
]
},
{
title: "Locations",
key: "locations",
children: [
{ title: "New York", key: "ny" },
{ title: "San Francisco", key: "sf" }
]
}
]
%>
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :middle) do %>
<%= render Hakumi::TreeSelect::Component.new(
name: "error_select",
placeholder: "Error status",
status: :error,
options: tree_options
) %>
<%= render Hakumi::TreeSelect::Component.new(
name: "warning_select",
placeholder: "Warning status",
status: :warning,
options: tree_options
) %>
<% end %>
Prefix
Add prefix icon or text to the selector.
Select with icon prefix
Region:
Select with text prefix
Code
<%
tree_options = [
{ title: "Asia", key: "asia", children: [
{ title: "Japan", key: "japan" },
{ title: "Thailand", key: "thailand" }
]},
{ title: "Europe", key: "europe", children: [
{ title: "France", key: "france" },
{ title: "Italy", key: "italy" }
]}
]
%>
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :middle) do %>
<%= render Hakumi::TreeSelect::Component.new(
name: "location_icon",
placeholder: "Select with icon prefix",
prefix_icon: :environment,
options: tree_options
) %>
<%= render Hakumi::TreeSelect::Component.new(
name: "location_text",
placeholder: "Select with text prefix",
prefix: "Region:",
options: tree_options
) %>
<% end %>
Max Count
Limit the maximum number of selections.
Select up to 3 technologies
Code
<%
tree_options = [
{
title: "Frontend",
key: "frontend",
children: [
{ title: "React", key: "react" },
{ title: "Vue", key: "vue" },
{ title: "Angular", key: "angular" }
]
},
{
title: "Backend",
key: "backend",
children: [
{ title: "Node.js", key: "node" },
{ title: "Ruby", key: "ruby" },
{ title: "Python", key: "python" }
]
},
{
title: "Mobile",
key: "mobile",
children: [
{ title: "React Native", key: "rn" },
{ title: "Flutter", key: "flutter" }
]
}
]
%>
<%= render Hakumi::TreeSelect::Component.new(
name: "frameworks",
placeholder: "Select up to 3 technologies",
tree_checkable: true,
max_count: 3,
tree_default_expand_all: true,
options: tree_options
) %>
Borderless
Borderless variant for minimal styling.
Select file
Code
<%
tree_options = [
{
title: "Documents",
key: "docs",
children: [
{ title: "Resume.pdf", key: "resume" },
{ title: "Cover Letter.pdf", key: "cover" }
]
},
{
title: "Images",
key: "images",
children: [
{ title: "Photo.jpg", key: "photo" },
{ title: "Logo.png", key: "logo" }
]
}
]
%>
<%= render Hakumi::TreeSelect::Component.new(
name: "borderless_select",
placeholder: "Select file",
variant: :borderless,
tree_default_expand_all: true,
options: tree_options
) %>
Empty / No Data
Shows empty state when options is nil or empty array.
Options is nil
No Data
Options is empty array
No Data
Code
<%= render Hakumi::Space::Component.new(direction: :vertical, size: :middle) do %>
<%= render Hakumi::TreeSelect::Component.new(
name: "empty_nil",
placeholder: "Options is nil",
options: nil
) %>
<%= render Hakumi::TreeSelect::Component.new(
name: "empty_array",
placeholder: "Options is empty array",
options: []
) %>
<% end %>
Async Loading
Load children dynamically when expanding a node. Uses addNodes() and setLoading() API methods.
Expand nodes to load data
Code
<%
# Initial options with async nodes (children will be loaded dynamically)
tree_options = [
{
title: "Load Users",
key: "users",
async: true # Mark as async - children will be loaded on expand
},
{
title: "Load Products",
key: "products",
async: true
},
{
title: "Load Empty (becomes leaf)",
key: "empty",
async: true # Will receive empty array, becomes leaf
},
{
title: "Load Error",
key: "error",
async: true # Will simulate an error
}
]
%>
<%= render Hakumi::TreeSelect::Component.new(
name: "async_select",
placeholder: "Expand nodes to load data",
id: "async-tree-select",
options: tree_options
) %>
<script>
document.addEventListener('DOMContentLoaded', function() {
const treeSelect = document.getElementById('async-tree-select')
// Simulated data that would come from an API
const asyncData = {
users: [
{ title: "John Doe", key: "user-1" },
{ title: "Jane Smith", key: "user-2" },
{ title: "Bob Johnson", key: "user-3" }
],
products: [
{ title: "Laptop Pro", key: "prod-1" },
{ title: "Wireless Mouse", key: "prod-2" },
{ title: "USB-C Hub", key: "prod-3" }
],
empty: [], // Empty array - node will become a leaf
error: null // Null simulates an error
}
// Listen for the load event from the Tree component inside TreeSelect
treeSelect.addEventListener('hakumi--tree:load', function(event) {
const { key } = event.detail
const api = treeSelect.hakumiComponent?.api
if (!api) return
// Show loading state
api.setLoading(key, true)
// Simulate API call with timeout
setTimeout(function() {
const data = asyncData[key]
if (data === null) {
// Simulate error
api.setError(key, true)
} else {
// Add nodes (if empty array, node becomes leaf automatically)
api.addNodes(key, data)
api.setLoading(key, false)
}
}, 1500)
})
})
</script>
TreeSelect API
| Prop | Type | Default | Description |
|---|---|---|---|
name |
String |
auto |
Name attribute for the hidden input. |
options |
Array[Hash] |
[] |
Tree data with keys: key, title, children, disabled, selectable, checkable. |
value |
String | Array |
- |
Selected value(s). |
placeholder |
String |
"Select" |
Placeholder text. |
multiple |
Boolean |
false |
Allow multiple selection. |
tree_checkable |
Boolean |
false |
Show checkboxes for tree nodes. |
show_search |
Boolean |
false |
Show search input in dropdown. |
tree_default_expand_all |
Boolean |
false |
Expand all nodes by default. |
tree_selectable_leaf_only |
Boolean |
false |
Only allow selecting leaf nodes. |
disabled |
Boolean |
false |
Disable the select. |
allow_clear |
Boolean |
false |
Show clear button. |
size |
Symbol |
:middle |
Size: :small, :middle, :large. |
variant |
Symbol |
:default |
Variant: :default, :borderless. |
status |
Symbol |
- |
Status: :error, :warning. |
max_count |
Integer |
- |
Maximum number of selections (multiple/checkable mode). |
prefix |
String |
- |
Prefix text content. |
prefix_icon |
Symbol |
- |
Prefix icon name. |
required |
Boolean |
false |
Mark as required (shows asterisk). |
label |
String |
- |
Form field label (when standalone: false). |
caption |
String |
- |
Help text (when standalone: false). |
errors |
Array |
[] |
Validation errors (when standalone: false). |
standalone |
Boolean |
true |
Render without form item wrapper. |
TreeSelect JavaScript API (element.hakumiComponent.api)
| Prop | Type | Default | Description |
|---|---|---|---|
getValue() |
Function |
- |
Get selected value(s). |
setValue(value) |
Function |
- |
Set selected value(s). |
getLabel() |
Function |
- |
Get displayed label text. |
open() |
Function |
- |
Open the dropdown. |
close() |
Function |
- |
Close the dropdown. |
toggle() |
Function |
- |
Toggle the dropdown. |
isOpen() |
Function |
- |
Check if dropdown is open. |
clear() |
Function |
- |
Clear selection. |
focus() |
Function |
- |
Focus the selector. |
blur() |
Function |
- |
Remove focus. |
isDisabled() |
Function |
- |
Check if disabled. |
getMaxCount() |
Function |
- |
Get max selection count. |
isMaxReached() |
Function |
- |
Check if max selection count reached. |
expandAll() |
Function |
- |
Expand all tree nodes. |
collapseAll() |
Function |
- |
Collapse all tree nodes. |
getExpandedKeys() |
Function |
- |
Get expanded node keys. |
setExpandedKeys(keys) |
Function |
- |
Set expanded node keys. |
addNodes(parentKey, nodes) |
Function |
- |
Add child nodes to a parent. If empty array, node becomes leaf. |
setLoading(key, loading) |
Function |
- |
Set loading state on a node (shows spinner icon). |
setError(key, error) |
Function |
- |
Set error state on a node (shows error styling). |
Events
| Prop | Type | Default | Description |
|---|---|---|---|
hakumi:tree-select:select |
CustomEvent |
- |
Triggered when selection changes. Detail: { value, label, values, labels, key }. |
hakumi:tree-select:change |
CustomEvent |
- |
Triggered when value changes. Detail: { value, label, values, labels, key }. |