mirror of
https://github.com/shoelace-style/webawesome.git
synced 2026-01-12 04:09:12 +00:00
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
344 lines
9.9 KiB
Markdown
344 lines
9.9 KiB
Markdown
# 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.
|
|
|
|
```html preview
|
|
<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>
|
|
```
|
|
|
|
```jsx react
|
|
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 `single` to allow the selection of a single item.
|
|
- Set `leaf` to allow the selection of a single leaf node. Clicking on a parent node will expand/collapse the node.
|
|
- Set `multiple` to allow the selection of multiple items.
|
|
|
|
```html preview
|
|
<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>
|
|
```
|
|
|
|
```jsx react
|
|
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`.
|
|
|
|
```html preview
|
|
<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>
|
|
```
|
|
|
|
```jsx react
|
|
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.
|
|
|
|
```html preview
|
|
<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
|
|
|
|
```html preview
|
|
<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
|
|
|
|
```html preview
|
|
<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]
|