rsuite
Version:
A suite of react components
166 lines • 5.8 kB
JavaScript
'use client';
import isNil from 'lodash/isNil';
import { useCallback, useEffect, useRef, useState } from 'react';
import { KEY_VALUES } from "../../internals/constants/index.js";
import { useEventCallback, useCustom } from "../../internals/hooks/index.js";
import { onMenuKeyDown } from "../../internals/Picker/index.js";
import { useItemDataKeys, useRegisterTreeMethods } from "../../internals/Tree/TreeProvider.js";
import { isSearching, focusNextItem, getFocusableItems, getActiveItem, focusPreviousItem, focusFirstItem, focusLastItem, focusCurrentItem, focusTreeNode, handleLeftArrow, handleRightArrow } from "../utils/index.js";
import useTreeNodeRefs from "./useTreeNodeRefs.js";
/**
* Custom hook that manages the focus behavior of a tree component.
*/
function useFocusTree(props) {
const {
filteredData,
searchKeyword,
flattenedNodes,
expandItemValues,
disabledItemValues,
onExpand,
onFocused
} = props;
const {
rtl
} = useCustom();
const {
valueKey,
childrenKey
} = useItemDataKeys();
const {
treeNodesRefs,
saveTreeNodeRef
} = useTreeNodeRefs();
const treeViewRef = useRef(null);
const [focusItemValue, setFocusItemValue] = useState(null);
const register = useRegisterTreeMethods();
const flattenedNodesRef = useRef(flattenedNodes);
const getFocusProps = value => {
const options = {
disabledItemValues,
valueKey,
childrenKey,
expandItemValues
};
const focusableItems = getFocusableItems(filteredData, options, isSearching(searchKeyword));
return {
focusItemValue: value || focusItemValue,
valueKey,
focusableItems,
treeNodesRefs
};
};
const handleFocusItem = useEventCallback(key => {
const focusProps = getFocusProps();
let focusedValue = null;
if (key === KEY_VALUES.DOWN) {
focusedValue = focusNextItem(focusProps);
} else if (key === KEY_VALUES.UP) {
focusedValue = focusPreviousItem(focusProps);
}
if (focusedValue) {
setFocusItemValue(focusedValue);
onFocused === null || onFocused === void 0 || onFocused(focusedValue);
}
});
const handleLeftArrowEvent = useEventCallback(() => {
if (isNil(focusItemValue)) {
return;
}
const focusItem = getActiveItem(focusItemValue, flattenedNodes, valueKey);
const expand = expandItemValues.includes(focusItem === null || focusItem === void 0 ? void 0 : focusItem[valueKey]);
const onFocusItem = () => {
var _focusItem$parent, _focusItem$parent2;
const focusedValue = focusItem === null || focusItem === void 0 || (_focusItem$parent = focusItem.parent) === null || _focusItem$parent === void 0 ? void 0 : _focusItem$parent[valueKey];
setFocusItemValue(focusedValue);
onFocused === null || onFocused === void 0 || onFocused(focusedValue);
focusTreeNode(focusItem === null || focusItem === void 0 || (_focusItem$parent2 = focusItem.parent) === null || _focusItem$parent2 === void 0 ? void 0 : _focusItem$parent2.refKey, treeNodesRefs);
};
handleLeftArrow({
focusItem,
expand,
onExpand,
childrenKey,
onFocusItem
});
});
const handleRightArrowEvent = useEventCallback(() => {
if (isNil(focusItemValue)) {
return;
}
const focusItem = getActiveItem(focusItemValue, flattenedNodes, valueKey);
const expand = expandItemValues.includes(focusItem === null || focusItem === void 0 ? void 0 : focusItem[valueKey]);
const onFocusItem = () => handleFocusItem(KEY_VALUES.DOWN);
handleRightArrow({
focusItem,
expand,
childrenKey,
onExpand,
onFocusItem
});
});
const handleHomeKey = useEventCallback(() => {
const focusProps = getFocusProps();
const focusedValue = focusFirstItem(focusProps);
if (focusedValue) {
setFocusItemValue(focusedValue);
onFocused === null || onFocused === void 0 || onFocused(focusedValue);
}
});
const handleEndKey = useEventCallback(() => {
const focusProps = getFocusProps();
const focusedValue = focusLastItem(focusProps);
if (focusedValue) {
setFocusItemValue(focusedValue);
onFocused === null || onFocused === void 0 || onFocused(focusedValue);
}
});
const onTreeKeydown = useEventCallback(event => {
onMenuKeyDown(event, {
down: () => handleFocusItem(KEY_VALUES.DOWN),
up: () => handleFocusItem(KEY_VALUES.UP),
left: rtl ? handleRightArrowEvent : handleLeftArrowEvent,
right: rtl ? handleLeftArrowEvent : handleRightArrowEvent,
home: handleHomeKey,
end: handleEndKey
});
});
const focusTreeFirstNode = useEventCallback(() => {
handleFocusItem(KEY_VALUES.DOWN);
});
const focusTreeActiveNode = useCallback(() => {
const refKey = focusCurrentItem({
container: treeViewRef.current
});
if (refKey) {
var _flattenedNodesRef$cu;
const node = (_flattenedNodesRef$cu = flattenedNodesRef.current) === null || _flattenedNodesRef$cu === void 0 ? void 0 : _flattenedNodesRef$cu[refKey];
if (node) {
setFocusItemValue(node[valueKey]);
onFocused === null || onFocused === void 0 || onFocused(node[valueKey]);
}
}
}, [onFocused, valueKey]);
useEffect(() => {
const unregister = register === null || register === void 0 ? void 0 : register({
focusTreeFirstNode,
focusTreeActiveNode
});
return () => {
unregister === null || unregister === void 0 || unregister();
};
}, []);
useEffect(() => {
flattenedNodesRef.current = flattenedNodes;
}, [flattenedNodes]);
return {
treeViewRef,
focusTreeFirstNode,
focusItemValue,
treeNodesRefs,
saveTreeNodeRef,
setFocusItemValue,
onTreeKeydown
};
}
export default useFocusTree;