diff --git a/packages/@react-spectrum/listbox/stories/ListBox.stories.tsx b/packages/@react-spectrum/listbox/stories/ListBox.stories.tsx index 1727cf665a0..7a0fb34df33 100644 --- a/packages/@react-spectrum/listbox/stories/ListBox.stories.tsx +++ b/packages/@react-spectrum/listbox/stories/ListBox.stories.tsx @@ -22,6 +22,7 @@ import Cut from '@spectrum-icons/workflow/Cut'; import Delete from '@spectrum-icons/workflow/Delete'; import {FocusScope} from '@react-aria/focus'; import {Item, ListBox, Section} from '../'; +import {Key} from '@react-types/shared'; import {Label} from '@react-spectrum/label'; import Paste from '@spectrum-icons/workflow/Paste'; import React, {useRef, useState} from 'react'; @@ -943,11 +944,12 @@ export function FocusExample(args = {}) { let [dialog, setDialog] = useState(null); let ref = useRef(null); + return ( setDialog({action})}> - {tree.selectedKeys.size > 0 && + {(tree.selectedKeys.size > 0) && } @@ -961,7 +963,11 @@ export function FocusExample(args = {}) { aria-labelledby="label" items={tree.items} selectedKeys={tree.selectedKeys} - onSelectionChange={tree.setSelectedKeys} + onSelectionChange={(keys) => { + if (keys !== 'all') { + tree.setSelectedKeys(keys); + } + }} selectionMode="multiple" {...args}> {item => item.children.length && ( @@ -1043,3 +1049,68 @@ export const WithAvatars = { )] }; + +interface Iitem { + name: string, + items?: Array | null +} + +export function WithTreeData() { + let tree = useTreeData({ + initialItems: [ + { + name: 'People', + items: [ + {name: 'David'}, + {name: 'Sam'}, + {name: 'Jane'} + ] + }, + { + name: 'Animals', + items: [ + {name: 'Aardvark'}, + {name: 'Kangaroo'}, + {name: 'Snake', items: null} + ] + } + ], + initialSelectedKeys: ['Sam', 'Kangaroo'], + getKey: item => item.name, + getChildren: item => item.items || [] + }); + return ( + { + if (keys === 'all') { + tree.setSelectedKeys(new Set(tree.items.reduce((acc, item) => { + acc.push(item.key); + function traverse(children) { + if (children) { + children.forEach(child => { + acc.push(child.key); + traverse(child.children); + }); + } + } + traverse(item.children); + return acc; + }, [] as Key[]))); + } else { + tree.setSelectedKeys(keys); + } + }}> + {node => ( +
+ {node => {node.value.name}} +
+ )} +
+ ); +} diff --git a/packages/@react-stately/data/docs/useTreeData.mdx b/packages/@react-stately/data/docs/useTreeData.mdx index 2f8596a30d8..292a2118b72 100644 --- a/packages/@react-stately/data/docs/useTreeData.mdx +++ b/packages/@react-stately/data/docs/useTreeData.mdx @@ -62,7 +62,12 @@ as the unique key for that item, and the `items` property as the children. In ad for the listbox, which will automatically be updated when items are removed from the tree. ```tsx -let tree = useTreeData({ +interface Iitem { + name: string; + items?: Array; +} + +let tree = useTreeData({ initialItems: [ { name: 'People', @@ -83,13 +88,19 @@ let tree = useTreeData({ ], initialSelectedKeys: ['Sam', 'Kangaroo'], getKey: item => item.name, - getChildren: item => item.items + getChildren: item => item.items || [] }); + onSelectionChange={(keys) => { + if (keys !== 'all') { + tree.setSelectedKeys(keys); + } + }}> {node =>
{node => {node.value.name}} diff --git a/packages/@react-stately/data/src/useTreeData.ts b/packages/@react-stately/data/src/useTreeData.ts index 06e2a1001cf..7f01bc3d5d4 100644 --- a/packages/@react-stately/data/src/useTreeData.ts +++ b/packages/@react-stately/data/src/useTreeData.ts @@ -123,7 +123,7 @@ export function useTreeData(options: TreeOptions): TreeData let { initialItems = [], initialSelectedKeys, - getKey = (item: any) => item.id || item.key, + getKey = (item: any) => item.id ?? item.key, getChildren = (item: any) => item.children } = options; @@ -133,7 +133,10 @@ export function useTreeData(options: TreeOptions): TreeData let [selectedKeys, setSelectedKeys] = useState(new Set(initialSelectedKeys || [])); - function buildTree(initialItems: T[] = [], map: Map>, parentKey?: Key | null) { + function buildTree(initialItems: T[] | null = [], map: Map>, parentKey?: Key | null) { + if (initialItems == null) { + initialItems = []; + } return { items: initialItems.map(item => { let node: TreeNode = {