120 lines
5.2 KiB
JavaScript
120 lines
5.2 KiB
JavaScript
'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 */ }
|