react-native-tree-multi-select
Version:
A super-fast, customizable tree view component for React Native with multi-selection, checkboxes, and search filtering capabilities.
149 lines (146 loc) • 5.48 kB
JavaScript
import React from "react";
import { InteractionManager } from "react-native";
import NodeList from "./components/NodeList";
import { selectAll, selectAllFiltered, unselectAll, unselectAllFiltered, initializeNodeMaps, expandAll, collapseAll, toggleCheckboxes, expandNodes, collapseNodes } from "./helpers";
import { getTreeViewStore, useTreeViewStore } from "./store/treeView.store";
import usePreviousState from "./utils/usePreviousState";
import { useShallow } from "zustand/react/shallow";
import uuid from "react-native-uuid";
import useDeepCompareEffect from "./utils/useDeepCompareEffect";
import { typedMemo } from "./utils/typedMemo";
import { jsx as _jsx } from "react/jsx-runtime";
function _innerTreeView(props, ref) {
const {
data,
onCheck,
onExpand,
selectionPropagation,
preselectedIds = [],
preExpandedIds = [],
initialScrollNodeID,
treeFlashListProps,
checkBoxViewStyleProps,
indentationMultiplier,
CheckboxComponent,
ExpandCollapseIconComponent,
ExpandCollapseTouchableComponent,
CustomNodeRowComponent
} = props;
const storeId = React.useMemo(() => uuid.v4(), []);
const {
expanded,
updateExpanded,
initialTreeViewData,
updateInitialTreeViewData,
searchText,
updateSearchText,
updateSearchKeys,
checked,
indeterminate,
setSelectionPropagation,
cleanUpTreeViewStore
} = useTreeViewStore(storeId)(useShallow(state => ({
expanded: state.expanded,
updateExpanded: state.updateExpanded,
initialTreeViewData: state.initialTreeViewData,
updateInitialTreeViewData: state.updateInitialTreeViewData,
searchText: state.searchText,
updateSearchText: state.updateSearchText,
updateSearchKeys: state.updateSearchKeys,
checked: state.checked,
indeterminate: state.indeterminate,
setSelectionPropagation: state.setSelectionPropagation,
cleanUpTreeViewStore: state.cleanUpTreeViewStore
})));
React.useImperativeHandle(ref, () => ({
selectAll: () => selectAll(storeId),
unselectAll: () => unselectAll(storeId),
selectAllFiltered: () => selectAllFiltered(storeId),
unselectAllFiltered: () => unselectAllFiltered(storeId),
expandAll: () => expandAll(storeId),
collapseAll: () => collapseAll(storeId),
expandNodes: ids => expandNodes(storeId, ids),
collapseNodes: ids => collapseNodes(storeId, ids),
selectNodes: ids => selectNodes(ids),
unselectNodes: ids => unselectNodes(ids),
setSearchText,
scrollToNodeID,
getChildToParentMap
}));
const scrollToNodeHandlerRef = React.useRef(null);
const prevSearchText = usePreviousState(searchText);
useDeepCompareEffect(() => {
cleanUpTreeViewStore();
updateInitialTreeViewData(data);
if (selectionPropagation) setSelectionPropagation(selectionPropagation);
initializeNodeMaps(storeId, data);
// Check any pre-selected nodes
toggleCheckboxes(storeId, preselectedIds, true);
// Expand pre-expanded nodes
expandNodes(storeId, [...preExpandedIds, ...(initialScrollNodeID ? [initialScrollNodeID] : [])]);
}, [data]);
function selectNodes(ids) {
toggleCheckboxes(storeId, ids, true);
}
function unselectNodes(ids) {
toggleCheckboxes(storeId, ids, false);
}
function setSearchText(text, keys = ["name"]) {
updateSearchText(text);
updateSearchKeys(keys);
}
function scrollToNodeID(params) {
scrollToNodeHandlerRef.current?.scrollToNodeID(params);
}
function getChildToParentMap() {
const treeViewStore = getTreeViewStore(storeId);
return treeViewStore.getState().childToParentMap;
}
const getIds = React.useCallback(node => {
if (!node.children || node.children.length === 0) {
return [node.id];
} else {
return [node.id, ...node.children.flatMap(item => getIds(item))];
}
}, []);
React.useEffect(() => {
onCheck?.(Array.from(checked), Array.from(indeterminate));
}, [onCheck, checked, indeterminate]);
React.useEffect(() => {
onExpand?.(Array.from(expanded));
}, [onExpand, expanded]);
React.useEffect(() => {
if (searchText) {
InteractionManager.runAfterInteractions(() => {
updateExpanded(new Set(initialTreeViewData.flatMap(item => getIds(item))));
});
} else if (prevSearchText && prevSearchText !== "") {
/* Collapse all nodes only if previous search query was non-empty: this is
done to prevent node collapse on first render if preExpandedIds is provided */
InteractionManager.runAfterInteractions(() => {
updateExpanded(new Set());
});
}
}, [getIds, initialTreeViewData, prevSearchText, searchText, updateExpanded]);
React.useEffect(() => {
return () => {
cleanUpTreeViewStore();
};
}, [cleanUpTreeViewStore]);
return /*#__PURE__*/_jsx(NodeList, {
storeId: storeId,
scrollToNodeHandlerRef: scrollToNodeHandlerRef,
initialScrollNodeID: initialScrollNodeID,
treeFlashListProps: treeFlashListProps,
checkBoxViewStyleProps: checkBoxViewStyleProps,
indentationMultiplier: indentationMultiplier,
CheckboxComponent: CheckboxComponent,
ExpandCollapseIconComponent: ExpandCollapseIconComponent,
ExpandCollapseTouchableComponent: ExpandCollapseTouchableComponent,
CustomNodeRowComponent: CustomNodeRowComponent
});
}
const _TreeView = /*#__PURE__*/React.forwardRef(_innerTreeView);
export const TreeView = typedMemo(_TreeView);
//# sourceMappingURL=TreeView.js.map
;