react-native-tree-multi-select
Version:
A super-fast, customizable tree view component for React Native with multi-selection, checkboxes, and search filtering capabilities.
183 lines (179 loc) • 6.34 kB
JavaScript
"use strict";
import React from "react";
import { View, StyleSheet, TouchableOpacity } from "react-native";
import { FlashList } from "@shopify/flash-list";
import { useTreeViewStore } from "../store/treeView.store";
import { getFilteredTreeData, getFlattenedTreeData, getInnerMostChildrenIdsInTree, handleToggleExpand, toggleCheckboxes } from "../helpers";
import { CheckboxView } from "./CheckboxView";
import { CustomExpandCollapseIcon } from "./CustomExpandCollapseIcon";
import { defaultIndentationMultiplier } from "../constants/treeView.constants";
import { useShallow } from "zustand/react/shallow";
import { typedMemo } from "../utils/typedMemo";
import { ScrollToNodeHandler } from "../handlers/ScrollToNodeHandler";
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
const NodeList = typedMemo(_NodeList);
export default NodeList;
function _NodeList(props) {
const {
storeId,
scrollToNodeHandlerRef,
initialScrollNodeID,
treeFlashListProps,
checkBoxViewStyleProps,
indentationMultiplier,
CheckboxComponent,
ExpandCollapseIconComponent,
ExpandCollapseTouchableComponent,
CustomNodeRowComponent
} = props;
const {
expanded,
initialTreeViewData,
updateInnerMostChildrenIds,
searchKeys,
searchText
} = useTreeViewStore(storeId)(useShallow(state => ({
expanded: state.expanded,
initialTreeViewData: state.initialTreeViewData,
updateInnerMostChildrenIds: state.updateInnerMostChildrenIds,
searchKeys: state.searchKeys,
searchText: state.searchText
})));
const flashListRef = React.useRef(null);
const [initialScrollIndex, setInitialScrollIndex] = React.useState(-1);
// First we filter the tree as per the search term and keys
const filteredTree = React.useMemo(() => getFilteredTreeData(initialTreeViewData, searchText.trim().toLowerCase(), searchKeys), [initialTreeViewData, searchText, searchKeys]);
// Then we flatten the tree to make it "render-compatible" in a "flat" list
const flattenedFilteredNodes = React.useMemo(() => getFlattenedTreeData(filteredTree, expanded), [filteredTree, expanded]);
// And update the innermost children id -> required to un/select filtered tree
React.useEffect(() => {
const updatedInnerMostChildrenIds = getInnerMostChildrenIdsInTree(filteredTree);
updateInnerMostChildrenIds(updatedInnerMostChildrenIds);
}, [filteredTree, updateInnerMostChildrenIds]);
const nodeRenderer = React.useCallback(({
item
}) => {
return /*#__PURE__*/_jsx(Node, {
storeId: storeId,
node: item,
level: item.level || 0,
checkBoxViewStyleProps: checkBoxViewStyleProps,
indentationMultiplier: indentationMultiplier,
CheckboxComponent: CheckboxComponent,
ExpandCollapseIconComponent: ExpandCollapseIconComponent,
ExpandCollapseTouchableComponent: ExpandCollapseTouchableComponent,
CustomNodeRowComponent: CustomNodeRowComponent
});
}, [storeId, CheckboxComponent, ExpandCollapseIconComponent, ExpandCollapseTouchableComponent, CustomNodeRowComponent, checkBoxViewStyleProps, indentationMultiplier]);
return /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx(ScrollToNodeHandler, {
ref: scrollToNodeHandlerRef,
storeId: storeId,
flashListRef: flashListRef,
flattenedFilteredNodes: flattenedFilteredNodes,
setInitialScrollIndex: setInitialScrollIndex,
initialScrollNodeID: initialScrollNodeID
}), /*#__PURE__*/_jsx(FlashList, {
ref: flashListRef,
estimatedItemSize: 36,
initialScrollIndex: initialScrollIndex,
removeClippedSubviews: true,
keyboardShouldPersistTaps: "handled",
drawDistance: 50,
ListHeaderComponent: /*#__PURE__*/_jsx(HeaderFooterView, {}),
ListFooterComponent: /*#__PURE__*/_jsx(HeaderFooterView, {}),
...treeFlashListProps,
data: flattenedFilteredNodes,
renderItem: nodeRenderer
})]
});
}
;
function HeaderFooterView() {
return /*#__PURE__*/_jsx(View, {
style: styles.defaultHeaderFooter
});
}
function getValue(isChecked, isIndeterminate) {
if (isIndeterminate) {
return "indeterminate";
} else if (isChecked) {
return true;
} else {
return false;
}
}
const Node = typedMemo(_Node);
function _Node(props) {
const {
storeId,
node,
level,
checkBoxViewStyleProps,
indentationMultiplier = defaultIndentationMultiplier,
ExpandCollapseIconComponent = CustomExpandCollapseIcon,
CheckboxComponent = CheckboxView,
ExpandCollapseTouchableComponent = TouchableOpacity,
CustomNodeRowComponent
} = props;
const {
isExpanded,
value
} = useTreeViewStore(storeId)(useShallow(state => ({
isExpanded: state.expanded.has(node.id),
value: getValue(state.checked.has(node.id),
// isChecked
state.indeterminate.has(node.id) // isIndeterminate
)
})));
const _onToggleExpand = React.useCallback(() => {
handleToggleExpand(storeId, node.id);
}, [storeId, node.id]);
const _onCheck = React.useCallback(() => {
toggleCheckboxes(storeId, [node.id]);
}, [storeId, node.id]);
if (!CustomNodeRowComponent) {
return /*#__PURE__*/_jsxs(View, {
style: [styles.nodeCheckboxAndArrowRow, {
paddingStart: level * indentationMultiplier
}],
children: [/*#__PURE__*/_jsx(CheckboxComponent, {
text: node.name,
onValueChange: _onCheck,
value: value,
...checkBoxViewStyleProps
}), node.children?.length ? /*#__PURE__*/_jsx(ExpandCollapseTouchableComponent, {
style: styles.nodeExpandableArrowTouchable,
onPress: _onToggleExpand,
children: /*#__PURE__*/_jsx(ExpandCollapseIconComponent, {
isExpanded: isExpanded
})
}) : null]
});
} else {
return /*#__PURE__*/_jsx(CustomNodeRowComponent, {
node: node,
level: level,
checkedValue: value,
isExpanded: isExpanded,
onCheck: _onCheck,
onExpand: _onToggleExpand
});
}
}
;
const styles = StyleSheet.create({
defaultHeaderFooter: {
padding: 5
},
nodeExpandableArrowTouchable: {
flex: 1
},
nodeCheckboxAndArrowRow: {
flex: 1,
flexDirection: "row",
alignItems: "center",
minWidth: "100%"
}
});
//# sourceMappingURL=NodeList.js.map