feat: add indeterminate state fix: rename item role fix: aria attributes fix: restore hover effect fix: indeterminate state chore: fix lint issue fix: address (partially) review comment fix: minor fix feat: add keyboard navigation fix: dependency name fix: keyboard selection does not update all items chore: minor changes chore: remove leftover chore: update documentation feat: add lazy loading + several fixes fix: add aria-busy attribute fix: improve keyboard navigation and lazy loading chore: fix linting issue chore: minor fixes fix: update component styling and slot chore: remove exportparts attribute fix: remove button margin for safari fix: set correct part name feat: implement selection modes and fix accessibility issues chore: fix linting issues chore: update docs fix: minor fix fix: treeitem height style chore: update documentation chore: refactor implementation chore: implement unit tests chore: update Enter key behavior chore: update doc chore: update doc
9.9 KiB
Tree
[component-header:sl-tree]
A tree component allow the user to display a hierarchical list of items, expanding and collapsing the nodes that have nested items. The user can select one or more items from the list.
<sl-tree>
<sl-tree-item expanded>
Getting Started
<sl-tree-item>
Overview
<sl-tree-item>Quick Start</sl-tree-item>
<sl-tree-item>New to Web Components?</sl-tree-item>
<sl-tree-item>What Problem Does This Solve?</sl-tree-item>
<sl-tree-item>Browser Support</sl-tree-item>
<sl-tree-item>License</sl-tree-item>
<sl-tree-item>Attribution</sl-tree-item>
</sl-tree-item>
<sl-tree-item> Installation </sl-tree-item>
<sl-tree-item> Usage </sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Frameworks
<sl-tree-item> React</sl-tree-item>
<sl-tree-item> Vue</sl-tree-item>
<sl-tree-item> Angular</sl-tree-item>
</sl-tree-item>
<sl-tree-item disabled> Resources </sl-tree-item>
</sl-tree>
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => (
<SlTree>
<SlTreeItem expanded>
Getting Started
<SlTreeItem> Overview </SlTreeItem>
<SlTreeItem> Installation </SlTreeItem>
<SlTreeItem> Usage </SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Frameworks
<SlTreeItem> React</SlTreeItem>
<SlTreeItem> Vue</SlTreeItem>
<SlTreeItem> Angular</SlTreeItem>
</SlTreeItem>
<SlTreeItem disabled> Resources </SlTreeItem>
</SlTree>
);
Examples
Selection modes
Use the selection attribute to specify the selection behavior of the tree
- Set
none(default) to disable the selection. - Set
singleto allow the selection of a single item. - Set
leafto allow the selection of a single leaf node. Clicking on a parent node will expand/collapse the node. - Set
multipleto allow the selection of multiple items.
<sl-select id="selection-mode" value="none" label="Selection">
<sl-menu-item value="none">none</sl-menu-item>
<sl-menu-item value="single">single</sl-menu-item>
<sl-menu-item value="leaf">leaf</sl-menu-item>
<sl-menu-item value="multiple">multiple</sl-menu-item>
</sl-select>
<br />
<sl-tree class="selectable">
<sl-tree-item expanded>
Parent
<sl-tree-item expanded>
Parent 1
<sl-tree-item> Child 1 </sl-tree-item>
<sl-tree-item> Child 2 </sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Parent 2
<sl-tree-item> Child 1</sl-tree-item>
<sl-tree-item> Child 2</sl-tree-item>
<sl-tree-item> Child 3</sl-tree-item>
</sl-tree-item>
</sl-tree-item>
</sl-tree>
<style>
.selectable sl-tree-item::part(item--selected) {
color: var(--sl-color-primary-600);
}
</style>
<script>
(() => {
const selectionMode = document.querySelector('#selection-mode');
const treeItem = document.querySelector('.selectable');
selectionMode.addEventListener('sl-change', () => {
treeItem.selection = selectionMode.value;
});
})();
</script>
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [selection, setSelection] = useState('none');
return (
<>
<SlSelect label="Selection" value={value} onSlChange={event => setSelection(event.target.value)}>
<SlMenuItem value="none">none</SlMenuItem>
<SlMenuItem value="single">single</SlMenuItem>
<SlMenuItem value="leaf">leaf</SlMenuItem>
<SlMenuItem value="multiple">multiple</SlMenuItem>
</SlSelect>
<br />
<SlTree selection={selection} class="selectable">
<SlTreeItem expanded>
Parent
<SlTreeItem expanded>
Parent 1<SlTreeItem> Child 1 </SlTreeItem>
<SlTreeItem> Child 2 </SlTreeItem>
</SlTreeItem>
<SlTreeItem>
Parent 2<SlTreeItem> Child 1</SlTreeItem>
<SlTreeItem> Child 2</SlTreeItem>
<SlTreeItem> Child 3</SlTreeItem>
</SlTreeItem>
</SlTreeItem>
</SlTree>
</>
);
};
Lazy loading
Use the lazy attribute on a item to indicate that the content is not yet present and will be loaded later.
When the user tries to expand the node, the loading state is set to true and a special event named
sl-lazy-load is emitted to let the loading of the content. The item will remain in a loading state until its content
is changed.
If you want to disable this behavior, for example after the content has been loaded, it will be sufficient to set
lazy to false.
<sl-tree>
<sl-tree-item lazy> Getting Started </sl-tree-item>
</sl-tree>
<script type="module">
const lazyItem = document.querySelector('sl-tree-item[lazy]');
lazyItem.addEventListener('sl-lazy-load', () => {
// Simulate an asynchronous loading
setTimeout(() => {
const subItems = ['Overview', 'Installation', 'Usage'];
const fragment = document.createDocumentFragment();
for (const item of subItems) {
const treeItem = document.createElement('sl-tree-item');
treeItem.innerText = item;
fragment.appendChild(treeItem);
}
lazyItem.appendChild(fragment);
// Disable lazy mode since the content has been loaded
lazyItem.lazy = false;
}, 2000);
});
</script>
import { SlTree, SlTreeItem } from '@shoelace-style/shoelace/dist/react';
const App = () => {
const [childItems, setChildItems] = useState([]);
const [lazy, setLazy] = useState(true);
const handleLazyLoad = () => {
// Simulate asynchronous loading
setTimeout(() => {
setChildItems(['Overview', 'Installation', 'Usage']);
// Disable lazy mode since the content has been loaded
setLazy(false);
}, 2000);
};
return (
<SlTree>
<SlTreeItem lazy={lazy} onSlLazyLoad={handleLazyLoad}>
Getting Started
{childItems.map(item => (
<SlTreeItem>{item}</SlTreeItem>
))}
</SlTreeItem>
</SlTree>
);
};
Styling trees
Using CSS parts is possible to apply custom styles to the tree. For example, it is possible to change the hover effect and to highlight the selected item.
<style>
.with-custom-style sl-tree-item::part(item) {
border-left: 2px solid transparent;
}
.with-custom-style sl-tree-item:not([disabled])::part(item):hover,
.with-custom-style sl-tree-item:focus-visible::part(item) {
color: var(--sl-color-primary-1000);
background-color: var(--sl-color-neutral-200);
}
.with-custom-style sl-tree-item::part(item--selected),
.with-custom-style sl-tree-item::part(item--selected):hover,
.with-custom-style sl-tree-item:focus-visible::part(item--selected) {
color: var(--sl-color-neutral-1000);
background-color: var(--sl-color-neutral-100);
border-left-color: var(--sl-color-primary-600);
}
</style>
<sl-tree selection="leaf" class="with-custom-style">
<sl-tree-item expanded>
Getting Started
<sl-tree-item>
Overview
<sl-tree-item>Quick Start</sl-tree-item>
<sl-tree-item>New to Web Components?</sl-tree-item>
<sl-tree-item>What Problem Does This Solve?</sl-tree-item>
<sl-tree-item>Browser Support</sl-tree-item>
<sl-tree-item>License</sl-tree-item>
<sl-tree-item>Attribution</sl-tree-item>
</sl-tree-item>
<sl-tree-item selected> Installation </sl-tree-item>
<sl-tree-item> Usage </sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Frameworks
<sl-tree-item> React</sl-tree-item>
<sl-tree-item> Vue</sl-tree-item>
<sl-tree-item> Angular</sl-tree-item>
</sl-tree-item>
<sl-tree-item disabled> Resources </sl-tree-item>
</sl-tree>
With indentation lines
<style>
.with-indentation-lines sl-tree-item[expanded]::part(children) {
position: relative;
}
.with-indentation-lines sl-tree-item[expanded]::part(children)::before {
content: '';
position: absolute;
left: 1em;
top: var(--sl-spacing-2x-small);
bottom: var(--sl-spacing-2x-small);
border-right: 1px solid var(--sl-color-neutral-100);
transition: 0.2s border-right ease-in-out;
}
.with-indentation-lines sl-tree-item[expanded]::part(children):hover::before {
border-right: 1px solid var(--sl-color-neutral-600);
}
</style>
<sl-tree class="with-indentation-lines">
<sl-tree-item expanded>
Getting Started
<sl-tree-item>
Overview
<sl-tree-item>Quick Start</sl-tree-item>
<sl-tree-item>New to Web Components?</sl-tree-item>
<sl-tree-item>What Problem Does This Solve?</sl-tree-item>
<sl-tree-item>Browser Support</sl-tree-item>
<sl-tree-item>License</sl-tree-item>
<sl-tree-item>Attribution</sl-tree-item>
</sl-tree-item>
<sl-tree-item> Installation </sl-tree-item>
<sl-tree-item> Usage </sl-tree-item>
</sl-tree-item>
<sl-tree-item>
Frameworks
<sl-tree-item> React</sl-tree-item>
<sl-tree-item> Vue</sl-tree-item>
<sl-tree-item> Angular</sl-tree-item>
</sl-tree-item>
<sl-tree-item disabled> Resources </sl-tree-item>
</sl-tree>
With icons
<style>
sl-icon {
margin-right: var(--sl-spacing-x-small);
}
</style>
<sl-tree>
<sl-tree-item expanded>
<sl-icon name="folder"></sl-icon>Root
<sl-tree-item>
<sl-icon name="folder"> </sl-icon>Folder 1
<sl-tree-item> <sl-icon name="files"></sl-icon>File 1 - 1 </sl-tree-item>
<sl-tree-item disabled> <sl-icon name="files"></sl-icon>File 1 - 2 </sl-tree-item>
<sl-tree-item> <sl-icon name="files"></sl-icon>File 1 - 3 </sl-tree-item>
</sl-tree-item>
<sl-tree-item>
<sl-icon name="files"></sl-icon>
Folder 2
<sl-tree-item selected> <sl-icon name="files"></sl-icon>File 2 - 1 </sl-tree-item>
<sl-tree-item> <sl-icon name="files"></sl-icon>File 2 - 2 </sl-tree-item>
</sl-tree-item>
<sl-tree-item> <sl-icon name="files"></sl-icon>File 1 </sl-tree-item>
</sl-tree-item>
</sl-tree>
[component-metadata:sl-tree]