'use client'; import { useEventCallback, useMergedRefs } from '@fluentui/react-utilities'; import * as React from 'react'; import { createHeadlessTree } from '../../utils/createHeadlessTree'; import { treeDataTypes } from '../../utils/tokens'; import { useFlatTreeNavigation } from '../../hooks/useFlatTreeNavigation'; import { createNextOpenItems, useControllableOpenItems } from '../../hooks/useControllableOpenItems'; import { dataTreeItemValueAttrName } from '../../utils/getTreeItemValueFromElement'; import { ImmutableSet } from '../../utils/ImmutableSet'; import { createNextFlatCheckedItems, useFlatControllableCheckedItems } from './useFlatControllableCheckedItems'; import { ImmutableMap } from '../../utils/ImmutableMap'; /** * this hook provides FlatTree API to manage all required mechanisms to convert a list of items into renderable TreeItems * in multiple scenarios including virtualization. * * !!A flat tree is an unofficial spec for tree!! * * It should be used on cases where more complex interactions with a Tree is required. * On simple scenarios it is advised to simply use a nested structure instead. * * @param props - a list of tree items * @param options - in case control over the internal openItems is required */ export function useHeadlessFlatTree_unstable(props, options = {}) { 'use no memo'; const headlessTree = React.useMemo(()=>createHeadlessTree(props), [ props ]); const [openItems, setOpenItems] = useControllableOpenItems(options); const [checkedItems, setCheckedItems] = useFlatControllableCheckedItems(options, headlessTree); const navigation = useFlatTreeNavigation(); const treeRef = React.useRef(null); const handleOpenChange = useEventCallback((event, data)=>{ var _options_onOpenChange; const nextOpenItems = createNextOpenItems(data, openItems); (_options_onOpenChange = options.onOpenChange) === null || _options_onOpenChange === void 0 ? void 0 : _options_onOpenChange.call(options, event, { ...data, openItems: ImmutableSet.dangerouslyGetInternalSet(nextOpenItems) }); setOpenItems(nextOpenItems); }); const handleCheckedChange = useEventCallback((event, data)=>{ var _options_onCheckedChange; const nextCheckedItems = createNextFlatCheckedItems(data, checkedItems, headlessTree); (_options_onCheckedChange = options.onCheckedChange) === null || _options_onCheckedChange === void 0 ? void 0 : _options_onCheckedChange.call(options, event, { ...data, checkedItems: ImmutableMap.dangerouslyGetInternalMap(nextCheckedItems) }); setCheckedItems(nextCheckedItems); }); const getNextNavigableItem = useEventCallback((visibleItems, data)=>{ const item = headlessTree.get(data.value); if (item) { switch(data.type){ case treeDataTypes.TypeAhead: return item; case treeDataTypes.ArrowLeft: return headlessTree.get(item.parentValue); case treeDataTypes.ArrowRight: return visibleItems[item.index + 1]; case treeDataTypes.End: return visibleItems[visibleItems.length - 1]; case treeDataTypes.Home: return visibleItems[0]; case treeDataTypes.ArrowDown: return visibleItems[item.index + 1]; case treeDataTypes.ArrowUp: return visibleItems[item.index - 1]; } } }); const getElementFromItem = React.useCallback((item)=>{ var _treeRef_current; return (_treeRef_current = treeRef.current) === null || _treeRef_current === void 0 ? void 0 : _treeRef_current.querySelector(`[${dataTreeItemValueAttrName}="${item.value}"]`); }, []); const ref = useMergedRefs(treeRef, navigation.rootRef); const getTreeProps = React.useCallback(()=>{ var _options_onNavigation; return { ref, openItems, selectionMode: options.selectionMode, checkedItems, onOpenChange: handleOpenChange, onCheckedChange: handleCheckedChange, onNavigation: (_options_onNavigation = options.onNavigation) !== null && _options_onNavigation !== void 0 ? _options_onNavigation : noop }; }, // ref, handleOpenChange - useEventCallback, handleCheckedChange - useEventCallback // eslint-disable-next-line react-hooks/exhaustive-deps [ openItems, checkedItems, options.selectionMode, options.onNavigation ]); const items = React.useCallback(()=>headlessTree.visibleItems(openItems), [ openItems, headlessTree ]); const getItem = React.useCallback((value)=>headlessTree.get(value), [ headlessTree ]); return React.useMemo(()=>({ navigate: navigation.navigate, getTreeProps, getNextNavigableItem, getElementFromItem, items, getItem }), [ navigation.navigate, getTreeProps, getNextNavigableItem, getElementFromItem, items, getItem ]); } /** @internal */ function noop() { /* noop */ }