import { useControllableState } from '@fluentui/react-utilities'; import { ImmutableMap } from '../../utils/ImmutableMap'; import * as React from 'react'; import { createCheckedItems } from '../../utils/createCheckedItems'; export function useFlatControllableCheckedItems(props, headlessTree) { return useControllableState({ initialState: ImmutableMap.empty, state: React.useMemo(()=>props.selectionMode ? props.checkedItems && createCheckedItems(props.checkedItems) : undefined, [ props.checkedItems, props.selectionMode ]), defaultState: props.defaultCheckedItems ? ()=>initializeCheckedItems(props, headlessTree) : undefined }); } export function createNextFlatCheckedItems(data, previousCheckedItems, headlessTree) { if (data.selectionMode === 'single') { return ImmutableMap.from([ [ data.value, data.checked ] ]); } const treeItem = headlessTree.get(data.value); if (!treeItem) { if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line no-console console.error(`@fluentui/react-tree [useHeadlessFlatTree]: Tree item ${data.value} not found.`); } return previousCheckedItems; } let nextCheckedItems = previousCheckedItems; for (const children of headlessTree.subtree(data.value)){ nextCheckedItems = nextCheckedItems.set(children.value, data.checked); } nextCheckedItems = nextCheckedItems.set(data.value, data.checked); let isAncestorsMixed = false; for (const parent of headlessTree.ancestors(treeItem.value)){ // if one parent is mixed, all ancestors are mixed if (isAncestorsMixed) { nextCheckedItems = nextCheckedItems.set(parent.value, 'mixed'); continue; } let checkedChildrenAmount = 0; for (const child of headlessTree.children(parent.value)){ if ((nextCheckedItems.get(child.value) || false) === data.checked) { checkedChildrenAmount++; } } // if all children are checked, parent is checked if (checkedChildrenAmount === parent.childrenValues.length) { nextCheckedItems = nextCheckedItems.set(parent.value, data.checked); } else { // if one parent is mixed, all ancestors are mixed isAncestorsMixed = true; nextCheckedItems = nextCheckedItems.set(parent.value, 'mixed'); } } return nextCheckedItems; } function initializeCheckedItems(props, headlessTree) { if (!props.selectionMode) { return ImmutableMap.empty; } let state = createCheckedItems(props.defaultCheckedItems); // if selectionMode is multiselect, we need to calculate the checked state of all children // and ancestors of the defaultCheckedItems if (props.selectionMode === 'multiselect') { for (const [value, checked] of state){ state = createNextFlatCheckedItems({ value, checked, selectionMode: props.selectionMode }, state, headlessTree); } } return state; }