react-native-tree-multi-select 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -6
- package/lib/commonjs/TreeView.js +6 -4
- package/lib/commonjs/TreeView.js.map +1 -1
- package/lib/commonjs/components/NodeList.js +3 -2
- package/lib/commonjs/components/NodeList.js.map +1 -1
- package/lib/commonjs/helpers/expandCollapse.helper.js.map +1 -1
- package/lib/commonjs/helpers/flattenTree.helper.js +1 -1
- package/lib/commonjs/helpers/flattenTree.helper.js.map +1 -1
- package/lib/commonjs/helpers/search.helper.js +5 -5
- package/lib/commonjs/helpers/search.helper.js.map +1 -1
- package/lib/commonjs/helpers/selectAll.helper.js +5 -5
- package/lib/commonjs/helpers/selectAll.helper.js.map +1 -1
- package/lib/commonjs/helpers/toggleCheckbox.helper.js.map +1 -1
- package/lib/commonjs/helpers/treeNode.helper.js +1 -1
- package/lib/commonjs/helpers/treeNode.helper.js.map +1 -1
- package/lib/commonjs/store/treeView.store.js +5 -3
- package/lib/commonjs/store/treeView.store.js.map +1 -1
- package/lib/commonjs/utils/typedMemo.js +11 -0
- package/lib/commonjs/utils/typedMemo.js.map +1 -0
- package/lib/module/TreeView.js +6 -4
- package/lib/module/TreeView.js.map +1 -1
- package/lib/module/components/NodeList.js +3 -2
- package/lib/module/components/NodeList.js.map +1 -1
- package/lib/module/helpers/expandCollapse.helper.js.map +1 -1
- package/lib/module/helpers/flattenTree.helper.js +1 -1
- package/lib/module/helpers/flattenTree.helper.js.map +1 -1
- package/lib/module/helpers/search.helper.js +5 -5
- package/lib/module/helpers/search.helper.js.map +1 -1
- package/lib/module/helpers/selectAll.helper.js +5 -5
- package/lib/module/helpers/selectAll.helper.js.map +1 -1
- package/lib/module/helpers/toggleCheckbox.helper.js.map +1 -1
- package/lib/module/helpers/treeNode.helper.js +1 -1
- package/lib/module/helpers/treeNode.helper.js.map +1 -1
- package/lib/module/store/treeView.store.js +5 -3
- package/lib/module/store/treeView.store.js.map +1 -1
- package/lib/module/utils/typedMemo.js +7 -0
- package/lib/module/utils/typedMemo.js.map +1 -0
- package/lib/typescript/TreeView.d.ts +5 -1
- package/lib/typescript/TreeView.d.ts.map +1 -1
- package/lib/typescript/components/NodeList.d.ts +2 -2
- package/lib/typescript/components/NodeList.d.ts.map +1 -1
- package/lib/typescript/helpers/expandCollapse.helper.d.ts +3 -3
- package/lib/typescript/helpers/expandCollapse.helper.d.ts.map +1 -1
- package/lib/typescript/helpers/flattenTree.helper.d.ts +1 -1
- package/lib/typescript/helpers/flattenTree.helper.d.ts.map +1 -1
- package/lib/typescript/helpers/search.helper.d.ts +1 -1
- package/lib/typescript/helpers/search.helper.d.ts.map +1 -1
- package/lib/typescript/helpers/selectAll.helper.d.ts +1 -1
- package/lib/typescript/helpers/selectAll.helper.d.ts.map +1 -1
- package/lib/typescript/helpers/toggleCheckbox.helper.d.ts +1 -1
- package/lib/typescript/helpers/toggleCheckbox.helper.d.ts.map +1 -1
- package/lib/typescript/helpers/treeNode.helper.d.ts +1 -1
- package/lib/typescript/helpers/treeNode.helper.d.ts.map +1 -1
- package/lib/typescript/store/treeView.store.d.ts +17 -17
- package/lib/typescript/store/treeView.store.d.ts.map +1 -1
- package/lib/typescript/types/treeView.types.d.ts +22 -22
- package/lib/typescript/types/treeView.types.d.ts.map +1 -1
- package/lib/typescript/utils/typedMemo.d.ts +3 -0
- package/lib/typescript/utils/typedMemo.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/TreeView.tsx +16 -14
- package/src/components/NodeList.tsx +12 -11
- package/src/helpers/expandCollapse.helper.ts +9 -9
- package/src/helpers/flattenTree.helper.ts +7 -7
- package/src/helpers/search.helper.ts +12 -12
- package/src/helpers/selectAll.helper.ts +11 -11
- package/src/helpers/toggleCheckbox.helper.ts +11 -11
- package/src/helpers/treeNode.helper.ts +8 -8
- package/src/store/treeView.store.ts +39 -37
- package/src/types/treeView.types.ts +23 -23
- package/src/utils/typedMemo.ts +4 -0
|
@@ -26,11 +26,12 @@ import { CheckboxView } from "./CheckboxView";
|
|
|
26
26
|
import { CustomExpandCollapseIcon } from "./CustomExpandCollapseIcon";
|
|
27
27
|
import { defaultIndentationMultiplier } from "../constants/treeView.constants";
|
|
28
28
|
import { useShallow } from 'zustand/react/shallow';
|
|
29
|
+
import { typedMemo } from "../utils/typedMemo";
|
|
29
30
|
|
|
30
|
-
const NodeList =
|
|
31
|
+
const NodeList = typedMemo(_NodeList);
|
|
31
32
|
export default NodeList;
|
|
32
33
|
|
|
33
|
-
function _NodeList(props: NodeListProps) {
|
|
34
|
+
function _NodeList<ID>(props: NodeListProps<ID>) {
|
|
34
35
|
const {
|
|
35
36
|
storeId,
|
|
36
37
|
|
|
@@ -50,7 +51,7 @@ function _NodeList(props: NodeListProps) {
|
|
|
50
51
|
updateInnerMostChildrenIds,
|
|
51
52
|
searchKeys,
|
|
52
53
|
searchText
|
|
53
|
-
} = useTreeViewStore(storeId)(useShallow(
|
|
54
|
+
} = useTreeViewStore<ID>(storeId)(useShallow(
|
|
54
55
|
state => ({
|
|
55
56
|
expanded: state.expanded,
|
|
56
57
|
initialTreeViewData: state.initialTreeViewData,
|
|
@@ -61,31 +62,31 @@ function _NodeList(props: NodeListProps) {
|
|
|
61
62
|
));
|
|
62
63
|
|
|
63
64
|
// First we filter the tree as per the search term and keys
|
|
64
|
-
const filteredTree = React.useMemo(() => getFilteredTreeData(
|
|
65
|
+
const filteredTree = React.useMemo(() => getFilteredTreeData<ID>(
|
|
65
66
|
initialTreeViewData,
|
|
66
67
|
searchText.trim().toLowerCase(),
|
|
67
68
|
searchKeys
|
|
68
69
|
), [initialTreeViewData, searchText, searchKeys]);
|
|
69
70
|
|
|
70
71
|
// Then we flatten the tree to make it "render-compatible" in a "flat" list
|
|
71
|
-
const flattenedFilteredNodes = React.useMemo(() => getFlattenedTreeData(
|
|
72
|
+
const flattenedFilteredNodes = React.useMemo(() => getFlattenedTreeData<ID>(
|
|
72
73
|
filteredTree,
|
|
73
74
|
expanded,
|
|
74
75
|
), [filteredTree, expanded]);
|
|
75
76
|
|
|
76
77
|
// And update the innermost children id -> required to un/select filtered tree
|
|
77
78
|
React.useEffect(() => {
|
|
78
|
-
const updatedInnerMostChildrenIds = getInnerMostChildrenIdsInTree(
|
|
79
|
+
const updatedInnerMostChildrenIds = getInnerMostChildrenIdsInTree<ID>(
|
|
79
80
|
filteredTree
|
|
80
81
|
);
|
|
81
82
|
updateInnerMostChildrenIds(updatedInnerMostChildrenIds);
|
|
82
83
|
}, [filteredTree, updateInnerMostChildrenIds]);
|
|
83
84
|
|
|
84
85
|
const nodeRenderer = React.useCallback((
|
|
85
|
-
{ item }: { item: __FlattenedTreeNode__
|
|
86
|
+
{ item }: { item: __FlattenedTreeNode__<ID>; }
|
|
86
87
|
) => {
|
|
87
88
|
return (
|
|
88
|
-
<Node
|
|
89
|
+
<Node<ID>
|
|
89
90
|
storeId={storeId}
|
|
90
91
|
|
|
91
92
|
node={item}
|
|
@@ -144,8 +145,8 @@ function getValue(
|
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
|
|
147
|
-
const Node =
|
|
148
|
-
function _Node(props: NodeProps) {
|
|
148
|
+
const Node = typedMemo(_Node);
|
|
149
|
+
function _Node<ID>(props: NodeProps<ID>) {
|
|
149
150
|
const {
|
|
150
151
|
storeId,
|
|
151
152
|
|
|
@@ -164,7 +165,7 @@ function _Node(props: NodeProps) {
|
|
|
164
165
|
const {
|
|
165
166
|
isExpanded,
|
|
166
167
|
value,
|
|
167
|
-
} = useTreeViewStore(storeId)(useShallow(
|
|
168
|
+
} = useTreeViewStore<ID>(storeId)(useShallow(
|
|
168
169
|
state => ({
|
|
169
170
|
isExpanded: state.expanded.has(node.id),
|
|
170
171
|
value: getValue(
|
|
@@ -8,8 +8,8 @@ import { getTreeViewStore } from "../store/treeView.store";
|
|
|
8
8
|
*
|
|
9
9
|
* @param id - The ID of the tree node to toggle.
|
|
10
10
|
*/
|
|
11
|
-
export function handleToggleExpand(storeId: string, id:
|
|
12
|
-
const treeViewStore = getTreeViewStore(storeId);
|
|
11
|
+
export function handleToggleExpand<ID>(storeId: string, id: ID) {
|
|
12
|
+
const treeViewStore = getTreeViewStore<ID>(storeId);
|
|
13
13
|
const {
|
|
14
14
|
expanded,
|
|
15
15
|
updateExpanded,
|
|
@@ -72,14 +72,14 @@ export function collapseAll(storeId: string) {
|
|
|
72
72
|
* its ancestors up to the root.
|
|
73
73
|
* @param ids - Ids of nodes to expand.
|
|
74
74
|
*/
|
|
75
|
-
export function expandNodes(storeId: string, ids:
|
|
76
|
-
const treeViewStore = getTreeViewStore(storeId);
|
|
75
|
+
export function expandNodes<ID>(storeId: string, ids: ID[]) {
|
|
76
|
+
const treeViewStore = getTreeViewStore<ID>(storeId);
|
|
77
77
|
const { expanded, updateExpanded, childToParentMap } = treeViewStore.getState();
|
|
78
78
|
const newExpanded = new Set(expanded);
|
|
79
|
-
const processedIds = new Set<
|
|
79
|
+
const processedIds = new Set<ID>();
|
|
80
80
|
|
|
81
81
|
ids.forEach((id) => {
|
|
82
|
-
let currentId:
|
|
82
|
+
let currentId: ID | undefined = id;
|
|
83
83
|
while (currentId && !processedIds.has(currentId)) {
|
|
84
84
|
newExpanded.add(currentId);
|
|
85
85
|
processedIds.add(currentId);
|
|
@@ -95,13 +95,13 @@ export function expandNodes(storeId: string, ids: string[]) {
|
|
|
95
95
|
* its descendants.
|
|
96
96
|
* @param ids - Ids of nodes to collapse.
|
|
97
97
|
*/
|
|
98
|
-
export function collapseNodes(storeId: string, ids:
|
|
99
|
-
const treeViewStore = getTreeViewStore(storeId);
|
|
98
|
+
export function collapseNodes<ID>(storeId: string, ids: ID[]) {
|
|
99
|
+
const treeViewStore = getTreeViewStore<ID>(storeId);
|
|
100
100
|
const { expanded, updateExpanded, nodeMap } = treeViewStore.getState();
|
|
101
101
|
const newExpanded = new Set(expanded);
|
|
102
102
|
|
|
103
103
|
// Use an iterative approach to remove all descendants from the expanded set
|
|
104
|
-
const deleteChildrenFromExpanded = (nodeId:
|
|
104
|
+
const deleteChildrenFromExpanded = (nodeId: ID) => {
|
|
105
105
|
const stack = [nodeId];
|
|
106
106
|
|
|
107
107
|
while (stack.length > 0) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TreeNode, __FlattenedTreeNode__ } from "../types/treeView.types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Flatten the tree and attach a "level" key to object to indicate it's depth. This
|
|
4
|
+
* Flatten the tree and attach a "level" key to object to indicate it's depth. This
|
|
5
5
|
* returns the flattened tree data of expanded ids only. We do not prune the tree off the
|
|
6
6
|
* children after the flattening as it would be unnecessary computation.
|
|
7
7
|
*
|
|
@@ -10,12 +10,12 @@ import { TreeNode, __FlattenedTreeNode__ } from "../types/treeView.types";
|
|
|
10
10
|
* @param __level__ - (optional) for internal recursive use only
|
|
11
11
|
* @returns Flattened tree data with expanded ids only
|
|
12
12
|
*/
|
|
13
|
-
export function getFlattenedTreeData(
|
|
14
|
-
nodes: TreeNode[],
|
|
15
|
-
expandedIds: Set<
|
|
16
|
-
): __FlattenedTreeNode__[] {
|
|
17
|
-
const flattened: __FlattenedTreeNode__[] = [];
|
|
18
|
-
const stack: { node: TreeNode
|
|
13
|
+
export function getFlattenedTreeData<ID>(
|
|
14
|
+
nodes: TreeNode<ID>[],
|
|
15
|
+
expandedIds: Set<ID>,
|
|
16
|
+
): __FlattenedTreeNode__<ID>[] {
|
|
17
|
+
const flattened: __FlattenedTreeNode__<ID>[] = [];
|
|
18
|
+
const stack: { node: TreeNode<ID>; level: number; }[] = [];
|
|
19
19
|
|
|
20
20
|
// Initialize stack with the root nodes and level 0
|
|
21
21
|
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
@@ -3,22 +3,22 @@ import { TreeNode } from "../types/treeView.types";
|
|
|
3
3
|
/**
|
|
4
4
|
* Get filtered tree data based on the search term and the search keys
|
|
5
5
|
* If any of the parent contains the search term, the tree will also contain
|
|
6
|
-
* it's children.
|
|
7
|
-
*
|
|
6
|
+
* it's children.
|
|
7
|
+
*
|
|
8
8
|
* If only one of the innermost children contains the search term then it's siblings
|
|
9
9
|
* won't be included in the search. But all it's ancestor nodes will be included
|
|
10
|
-
*
|
|
10
|
+
*
|
|
11
11
|
* @param nodes Input tree data
|
|
12
12
|
* @param trimmedSearchTerm search term
|
|
13
13
|
* @param searchKeys search key
|
|
14
14
|
* @returns filtered tree data
|
|
15
15
|
*/
|
|
16
|
-
export function getFilteredTreeData(
|
|
17
|
-
nodes: TreeNode[],
|
|
16
|
+
export function getFilteredTreeData<ID>(
|
|
17
|
+
nodes: TreeNode<ID>[],
|
|
18
18
|
trimmedSearchTerm: string,
|
|
19
19
|
searchKeys: string[]
|
|
20
|
-
): TreeNode[] {
|
|
21
|
-
let filtered: TreeNode[] = [];
|
|
20
|
+
): TreeNode<ID>[] {
|
|
21
|
+
let filtered: TreeNode<ID>[] = [];
|
|
22
22
|
|
|
23
23
|
for (let node of nodes) {
|
|
24
24
|
const isSearchTermInNode = doesNodeContainSearchTerm(
|
|
@@ -32,7 +32,7 @@ export function getFilteredTreeData(
|
|
|
32
32
|
filtered.push(node);
|
|
33
33
|
} else if (node.children) {
|
|
34
34
|
// If node does not match, check its children and include them if they match
|
|
35
|
-
const childMatches = getFilteredTreeData(
|
|
35
|
+
const childMatches = getFilteredTreeData<ID>(
|
|
36
36
|
node.children,
|
|
37
37
|
trimmedSearchTerm,
|
|
38
38
|
searchKeys
|
|
@@ -51,7 +51,7 @@ export function getFilteredTreeData(
|
|
|
51
51
|
/**
|
|
52
52
|
* Checks if a given tree node contains a specific search term in any of its specified keys.
|
|
53
53
|
*
|
|
54
|
-
* This function will check each of the specified keys in the tree node, convert the key's value to a string,
|
|
54
|
+
* This function will check each of the specified keys in the tree node, convert the key's value to a string,
|
|
55
55
|
* and check if it includes the search term.
|
|
56
56
|
*
|
|
57
57
|
* @param node - The tree node to search through.
|
|
@@ -59,8 +59,8 @@ export function getFilteredTreeData(
|
|
|
59
59
|
* @param searchKeys - The keys in the tree node to search in.
|
|
60
60
|
* @returns True if the search term is found in any of the specified keys, false otherwise.
|
|
61
61
|
*/
|
|
62
|
-
function doesNodeContainSearchTerm(
|
|
63
|
-
node: TreeNode
|
|
62
|
+
function doesNodeContainSearchTerm<ID>(
|
|
63
|
+
node: TreeNode<ID>,
|
|
64
64
|
searchTerm: string,
|
|
65
65
|
searchKeys: string[]
|
|
66
66
|
): boolean {
|
|
@@ -69,7 +69,7 @@ function doesNodeContainSearchTerm(
|
|
|
69
69
|
// Get the value of the key in the tree node
|
|
70
70
|
const nodeValue = node[key];
|
|
71
71
|
// Check if the string representation of the key's value includes the search term
|
|
72
|
-
// If the value is undefined or null, `nodeValue?.toString()` will return undefined,
|
|
72
|
+
// If the value is undefined or null, `nodeValue?.toString()` will return undefined,
|
|
73
73
|
// and the call to `toLowerCase().includes(searchTerm)` will return false.
|
|
74
74
|
return (nodeValue?.toString().toLowerCase().includes(searchTerm));
|
|
75
75
|
});
|
|
@@ -4,7 +4,7 @@ import { toggleCheckboxes } from "./toggleCheckbox.helper";
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Selects all nodes that are currently visible due to the applied filter.
|
|
7
|
-
*
|
|
7
|
+
*
|
|
8
8
|
* If there is no search text, then it selects all nodes; otherwise, it selects all visible nodes.
|
|
9
9
|
*/
|
|
10
10
|
export function selectAllFiltered(storeId: string) {
|
|
@@ -22,7 +22,7 @@ export function selectAllFiltered(storeId: string) {
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Unselects all nodes that are currently visible due to the applied filter.
|
|
25
|
-
*
|
|
25
|
+
*
|
|
26
26
|
* If there is no search text, then it unselects all nodes; otherwise, it unselects all visible nodes.
|
|
27
27
|
*/
|
|
28
28
|
export function unselectAllFiltered(storeId: string) {
|
|
@@ -40,7 +40,7 @@ export function unselectAllFiltered(storeId: string) {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* Selects all nodes in the tree.
|
|
43
|
-
*
|
|
43
|
+
*
|
|
44
44
|
* This function selects all nodes by adding all node ids to the checked set and clearing the indeterminate set.
|
|
45
45
|
*/
|
|
46
46
|
export function selectAll(storeId: string) {
|
|
@@ -61,7 +61,7 @@ export function selectAll(storeId: string) {
|
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Unselects all nodes in the tree.
|
|
64
|
-
*
|
|
64
|
+
*
|
|
65
65
|
* This function unselects all nodes by clearing both the checked and indeterminate sets.
|
|
66
66
|
*/
|
|
67
67
|
export function unselectAll(storeId: string) {
|
|
@@ -75,16 +75,16 @@ export function unselectAll(storeId: string) {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Get the ids of the innermost children in the tree
|
|
78
|
-
*
|
|
78
|
+
*
|
|
79
79
|
* @param filteredTreeNodes - The filtered tree data
|
|
80
80
|
* @returns - array of ids of the inner most children only
|
|
81
81
|
*/
|
|
82
|
-
export function getInnerMostChildrenIdsInTree(
|
|
83
|
-
filteredTreeNodes: TreeNode[]
|
|
84
|
-
):
|
|
85
|
-
const allLeafIds:
|
|
82
|
+
export function getInnerMostChildrenIdsInTree<ID>(
|
|
83
|
+
filteredTreeNodes: TreeNode<ID>[]
|
|
84
|
+
): ID[] {
|
|
85
|
+
const allLeafIds: ID[] = [];
|
|
86
86
|
|
|
87
|
-
const getLeafNodes = (_nodes: TreeNode[]) => {
|
|
87
|
+
const getLeafNodes = (_nodes: TreeNode<ID>[]) => {
|
|
88
88
|
for (let node of _nodes) {
|
|
89
89
|
if (node.children) {
|
|
90
90
|
getLeafNodes(node.children);
|
|
@@ -97,4 +97,4 @@ export function getInnerMostChildrenIdsInTree(
|
|
|
97
97
|
getLeafNodes(filteredTreeNodes);
|
|
98
98
|
|
|
99
99
|
return allLeafIds;
|
|
100
|
-
}
|
|
100
|
+
}
|
|
@@ -7,12 +7,12 @@ import { getTreeViewStore } from "../store/treeView.store";
|
|
|
7
7
|
* @param {boolean} [forceCheck] - Optional. If provided, will force the check state of the nodes to be this value.
|
|
8
8
|
* If not provided, the check state will be toggled based on the current state.
|
|
9
9
|
*/
|
|
10
|
-
export function toggleCheckboxes(
|
|
10
|
+
export function toggleCheckboxes<ID>(
|
|
11
11
|
storeId: string,
|
|
12
|
-
ids:
|
|
12
|
+
ids: ID[],
|
|
13
13
|
forceCheck?: boolean
|
|
14
14
|
) {
|
|
15
|
-
const treeViewStore = getTreeViewStore(storeId);
|
|
15
|
+
const treeViewStore = getTreeViewStore<ID>(storeId);
|
|
16
16
|
const {
|
|
17
17
|
checked,
|
|
18
18
|
updateChecked,
|
|
@@ -32,10 +32,10 @@ export function toggleCheckboxes(
|
|
|
32
32
|
const tempIndeterminate = new Set(indeterminate);
|
|
33
33
|
|
|
34
34
|
// Keep track of nodes that have been toggled or affected.
|
|
35
|
-
const affectedNodes = new Set<
|
|
35
|
+
const affectedNodes = new Set<ID>();
|
|
36
36
|
|
|
37
37
|
// Memoization maps for node depths.
|
|
38
|
-
const nodeDepths = new Map<
|
|
38
|
+
const nodeDepths = new Map<ID, number>();
|
|
39
39
|
|
|
40
40
|
// Step 1: Toggle the clicked nodes and their children without updating parents yet.
|
|
41
41
|
ids.forEach((id) => {
|
|
@@ -66,11 +66,11 @@ export function toggleCheckboxes(
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
// Step 2: Collect all affected parent nodes.
|
|
69
|
-
const nodesToUpdate = new Set<
|
|
69
|
+
const nodesToUpdate = new Set<ID>();
|
|
70
70
|
|
|
71
71
|
if (toParents) {
|
|
72
72
|
affectedNodes.forEach((id) => {
|
|
73
|
-
let currentNodeId:
|
|
73
|
+
let currentNodeId: ID | undefined = id;
|
|
74
74
|
while (currentNodeId) {
|
|
75
75
|
const parentNodeId = childToParentMap.get(currentNodeId);
|
|
76
76
|
if (parentNodeId) {
|
|
@@ -100,7 +100,7 @@ export function toggleCheckboxes(
|
|
|
100
100
|
* @param rootId - The ID of the root node to start updating from.
|
|
101
101
|
* @param childrenChecked - The desired checked state for children.
|
|
102
102
|
*/
|
|
103
|
-
function updateChildrenIteratively(rootId:
|
|
103
|
+
function updateChildrenIteratively(rootId: ID, childrenChecked: boolean) {
|
|
104
104
|
const stack = [rootId];
|
|
105
105
|
|
|
106
106
|
while (stack.length > 0) {
|
|
@@ -130,13 +130,13 @@ export function toggleCheckboxes(
|
|
|
130
130
|
* @param nodeId - The ID of the node to get the depth for.
|
|
131
131
|
* @returns The depth of the node.
|
|
132
132
|
*/
|
|
133
|
-
function getNodeDepth(nodeId:
|
|
133
|
+
function getNodeDepth(nodeId: ID): number {
|
|
134
134
|
if (nodeDepths.has(nodeId)) {
|
|
135
135
|
return nodeDepths.get(nodeId)!;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
let depth = 0;
|
|
139
|
-
let currentNodeId:
|
|
139
|
+
let currentNodeId: ID | undefined = nodeId;
|
|
140
140
|
while (currentNodeId) {
|
|
141
141
|
const parentNodeId = childToParentMap.get(currentNodeId);
|
|
142
142
|
if (parentNodeId) {
|
|
@@ -155,7 +155,7 @@ export function toggleCheckboxes(
|
|
|
155
155
|
* Function to update the state of a node based on its children's states.
|
|
156
156
|
* @param nodeId - The ID of the node to update.
|
|
157
157
|
*/
|
|
158
|
-
function updateNodeState(nodeId:
|
|
158
|
+
function updateNodeState(nodeId: ID) {
|
|
159
159
|
const node = nodeMap.get(nodeId);
|
|
160
160
|
if (!node || !node.children || node.children.length === 0) {
|
|
161
161
|
// Leaf nodes are already updated.
|
|
@@ -6,21 +6,21 @@ import {
|
|
|
6
6
|
/**
|
|
7
7
|
* Initialize the maps for tracking tree nodes and their parent-child relationships.
|
|
8
8
|
*
|
|
9
|
-
* This function is intended to be called once, during component initialization,
|
|
9
|
+
* This function is intended to be called once, during component initialization,
|
|
10
10
|
* with the initial tree data and any preselected node IDs.
|
|
11
11
|
*
|
|
12
12
|
* @param initialData - An array of TreeNode objects that represent the initial tree structure.
|
|
13
13
|
* @param preselectedIds - An optional array of TreeNode IDs that should be preselected.
|
|
14
14
|
*/
|
|
15
|
-
export function initializeNodeMaps(storeId: string, initialData: TreeNode[]) {
|
|
16
|
-
const treeViewStore = getTreeViewStore(storeId);
|
|
15
|
+
export function initializeNodeMaps<ID>(storeId: string, initialData: TreeNode<ID>[]) {
|
|
16
|
+
const treeViewStore = getTreeViewStore<ID>(storeId);
|
|
17
17
|
const {
|
|
18
18
|
updateNodeMap,
|
|
19
19
|
updateChildToParentMap
|
|
20
20
|
} = treeViewStore.getState();
|
|
21
21
|
|
|
22
|
-
const tempNodeMap: Map<
|
|
23
|
-
const tempChildToParentMap: Map<
|
|
22
|
+
const tempNodeMap: Map<ID, TreeNode<ID>> = new Map();
|
|
23
|
+
const tempChildToParentMap: Map<ID, ID> = new Map();
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Recursively processes nodes, adding them to the nodeMap and childToParentMap.
|
|
@@ -29,8 +29,8 @@ export function initializeNodeMaps(storeId: string, initialData: TreeNode[]) {
|
|
|
29
29
|
* @param parentId - The ID of the parent node, if applicable.
|
|
30
30
|
*/
|
|
31
31
|
const processNodes = (
|
|
32
|
-
nodes: TreeNode[],
|
|
33
|
-
parentId:
|
|
32
|
+
nodes: TreeNode<ID>[],
|
|
33
|
+
parentId: ID | null = null
|
|
34
34
|
) => {
|
|
35
35
|
nodes.forEach((node) => {
|
|
36
36
|
// Each node is added to the nodeMap with its ID as the key
|
|
@@ -47,4 +47,4 @@ export function initializeNodeMaps(storeId: string, initialData: TreeNode[]) {
|
|
|
47
47
|
|
|
48
48
|
updateNodeMap(tempNodeMap);
|
|
49
49
|
updateChildToParentMap(tempChildToParentMap);
|
|
50
|
-
}
|
|
50
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
import type { SelectionPropagation, TreeNode } from "src/types/treeView.types";
|
|
2
2
|
import { create, StoreApi, UseBoundStore } from 'zustand';
|
|
3
3
|
|
|
4
|
-
export type TreeViewState = {
|
|
4
|
+
export type TreeViewState<ID> = {
|
|
5
5
|
// Store ids of checked tree nodes
|
|
6
|
-
checked: Set<
|
|
7
|
-
updateChecked: (checked: Set<
|
|
6
|
+
checked: Set<ID>;
|
|
7
|
+
updateChecked: (checked: Set<ID>) => void;
|
|
8
8
|
|
|
9
9
|
// Store ids of indeterminate state nodes
|
|
10
|
-
indeterminate: Set<
|
|
11
|
-
updateIndeterminate: (indeterminate: Set<
|
|
10
|
+
indeterminate: Set<ID>;
|
|
11
|
+
updateIndeterminate: (indeterminate: Set<ID>) => void;
|
|
12
12
|
|
|
13
13
|
// Store ids of expanded parent nodes
|
|
14
|
-
expanded: Set<
|
|
15
|
-
updateExpanded: (expanded: Set<
|
|
14
|
+
expanded: Set<ID>;
|
|
15
|
+
updateExpanded: (expanded: Set<ID>) => void;
|
|
16
16
|
|
|
17
17
|
// Store initial tree view data exactly as passed by the consumer
|
|
18
|
-
initialTreeViewData: TreeNode[];
|
|
19
|
-
updateInitialTreeViewData: (initialTreeViewData: TreeNode[]) => void;
|
|
18
|
+
initialTreeViewData: TreeNode<ID>[];
|
|
19
|
+
updateInitialTreeViewData: (initialTreeViewData: TreeNode<ID>[]) => void;
|
|
20
20
|
|
|
21
21
|
// Map to store the id to the tree node map
|
|
22
|
-
nodeMap: Map<
|
|
23
|
-
updateNodeMap: (nodeMap: Map<
|
|
22
|
+
nodeMap: Map<ID, TreeNode<ID>>;
|
|
23
|
+
updateNodeMap: (nodeMap: Map<ID, TreeNode<ID>>) => void;
|
|
24
24
|
|
|
25
25
|
// Map to store child id to parent id map
|
|
26
|
-
childToParentMap: Map<
|
|
27
|
-
updateChildToParentMap: (childToParentMap: Map<
|
|
26
|
+
childToParentMap: Map<ID, ID>;
|
|
27
|
+
updateChildToParentMap: (childToParentMap: Map<ID, ID>) => void;
|
|
28
28
|
|
|
29
29
|
// Search text state
|
|
30
30
|
searchText: string;
|
|
@@ -35,8 +35,8 @@ export type TreeViewState = {
|
|
|
35
35
|
updateSearchKeys: (searchKeys: string[]) => void;
|
|
36
36
|
|
|
37
37
|
// To store inner most children ids - required to un/select all filtered-only nodes
|
|
38
|
-
innerMostChildrenIds:
|
|
39
|
-
updateInnerMostChildrenIds: (innerMostChildrenIds:
|
|
38
|
+
innerMostChildrenIds: ID[];
|
|
39
|
+
updateInnerMostChildrenIds: (innerMostChildrenIds: ID[]) => void;
|
|
40
40
|
|
|
41
41
|
selectionPropagation: SelectionPropagation;
|
|
42
42
|
setSelectionPropagation: (
|
|
@@ -48,30 +48,32 @@ export type TreeViewState = {
|
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
// Map to store individual tree view stores by id
|
|
51
|
-
const treeViewStores = new Map<string, UseBoundStore<StoreApi<TreeViewState
|
|
51
|
+
const treeViewStores = new Map<string, UseBoundStore<StoreApi<TreeViewState<unknown>>>>();
|
|
52
|
+
// a function that returns a strongly typed version of `treeViewStores`
|
|
53
|
+
const typedStore: <ID>() => Map<string, UseBoundStore<StoreApi<TreeViewState<ID>>>> = <ID>() => treeViewStores as Map<string, UseBoundStore<StoreApi<TreeViewState<ID>>>>;
|
|
52
54
|
|
|
53
|
-
export function getTreeViewStore(id: string): UseBoundStore<StoreApi<TreeViewState
|
|
54
|
-
if (!
|
|
55
|
-
const store = create<TreeViewState
|
|
55
|
+
export function getTreeViewStore<ID>(id: string): UseBoundStore<StoreApi<TreeViewState<ID>>> {
|
|
56
|
+
if (!typedStore<ID>().has(id)) {
|
|
57
|
+
const store = create<TreeViewState<ID>>((set) => ({
|
|
56
58
|
checked: new Set(),
|
|
57
|
-
updateChecked: (checked: Set<
|
|
59
|
+
updateChecked: (checked: Set<ID>) => set({ checked }),
|
|
58
60
|
|
|
59
61
|
indeterminate: new Set(),
|
|
60
|
-
updateIndeterminate: (indeterminate: Set<
|
|
62
|
+
updateIndeterminate: (indeterminate: Set<ID>) => set({ indeterminate }),
|
|
61
63
|
|
|
62
|
-
expanded: new Set<
|
|
63
|
-
updateExpanded: (expanded: Set<
|
|
64
|
+
expanded: new Set<ID>(),
|
|
65
|
+
updateExpanded: (expanded: Set<ID>) => set({ expanded }),
|
|
64
66
|
|
|
65
67
|
initialTreeViewData: [],
|
|
66
|
-
updateInitialTreeViewData: (initialTreeViewData: TreeNode[]) => set({
|
|
68
|
+
updateInitialTreeViewData: (initialTreeViewData: TreeNode<ID>[]) => set({
|
|
67
69
|
initialTreeViewData
|
|
68
70
|
}),
|
|
69
71
|
|
|
70
|
-
nodeMap: new Map<
|
|
71
|
-
updateNodeMap: (nodeMap: Map<
|
|
72
|
+
nodeMap: new Map<ID, TreeNode<ID>>(),
|
|
73
|
+
updateNodeMap: (nodeMap: Map<ID, TreeNode<ID>>) => set({ nodeMap }),
|
|
72
74
|
|
|
73
|
-
childToParentMap: new Map<
|
|
74
|
-
updateChildToParentMap: (childToParentMap: Map<
|
|
75
|
+
childToParentMap: new Map<ID, ID>(),
|
|
76
|
+
updateChildToParentMap: (childToParentMap: Map<ID, ID>) => set({
|
|
75
77
|
childToParentMap
|
|
76
78
|
}),
|
|
77
79
|
|
|
@@ -82,7 +84,7 @@ export function getTreeViewStore(id: string): UseBoundStore<StoreApi<TreeViewSta
|
|
|
82
84
|
updateSearchKeys: (searchKeys: string[]) => set({ searchKeys }),
|
|
83
85
|
|
|
84
86
|
innerMostChildrenIds: [],
|
|
85
|
-
updateInnerMostChildrenIds: (innerMostChildrenIds:
|
|
87
|
+
updateInnerMostChildrenIds: (innerMostChildrenIds: ID[]) => set({
|
|
86
88
|
innerMostChildrenIds
|
|
87
89
|
}),
|
|
88
90
|
|
|
@@ -99,10 +101,10 @@ export function getTreeViewStore(id: string): UseBoundStore<StoreApi<TreeViewSta
|
|
|
99
101
|
set({
|
|
100
102
|
checked: new Set(),
|
|
101
103
|
indeterminate: new Set(),
|
|
102
|
-
expanded: new Set<
|
|
104
|
+
expanded: new Set<ID>(),
|
|
103
105
|
initialTreeViewData: [],
|
|
104
|
-
nodeMap: new Map<
|
|
105
|
-
childToParentMap: new Map<
|
|
106
|
+
nodeMap: new Map<ID, TreeNode<ID>>(),
|
|
107
|
+
childToParentMap: new Map<ID, ID>(),
|
|
106
108
|
searchText: "",
|
|
107
109
|
searchKeys: [""],
|
|
108
110
|
innerMostChildrenIds: [],
|
|
@@ -110,11 +112,11 @@ export function getTreeViewStore(id: string): UseBoundStore<StoreApi<TreeViewSta
|
|
|
110
112
|
}),
|
|
111
113
|
}));
|
|
112
114
|
|
|
113
|
-
|
|
115
|
+
typedStore<ID>().set(id, store);
|
|
114
116
|
}
|
|
115
|
-
return
|
|
117
|
+
return typedStore<ID>().get(id)!;
|
|
116
118
|
}
|
|
117
119
|
|
|
118
|
-
export function useTreeViewStore(id: string) {
|
|
119
|
-
return getTreeViewStore(id);
|
|
120
|
-
}
|
|
120
|
+
export function useTreeViewStore<ID = string>(id: string) {
|
|
121
|
+
return getTreeViewStore<ID>(id);
|
|
122
|
+
}
|
|
@@ -15,14 +15,14 @@ export interface ExpandIconProps {
|
|
|
15
15
|
isExpanded: boolean;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
export interface TreeNode {
|
|
19
|
-
id:
|
|
18
|
+
export interface TreeNode<ID = string> {
|
|
19
|
+
id: ID;
|
|
20
20
|
name: string;
|
|
21
|
-
children?: TreeNode[];
|
|
21
|
+
children?: TreeNode<ID>[];
|
|
22
22
|
[key: string]: any;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export interface __FlattenedTreeNode__ extends TreeNode {
|
|
25
|
+
export interface __FlattenedTreeNode__<ID = string> extends TreeNode<ID> {
|
|
26
26
|
level?: number;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -33,8 +33,8 @@ export type TreeFlatListProps<ItemT = any> = Omit<
|
|
|
33
33
|
| "renderItem"
|
|
34
34
|
>;
|
|
35
35
|
|
|
36
|
-
export interface NodeRowProps {
|
|
37
|
-
node: TreeNode
|
|
36
|
+
export interface NodeRowProps<ID = string> {
|
|
37
|
+
node: TreeNode<ID>;
|
|
38
38
|
level: number;
|
|
39
39
|
|
|
40
40
|
checkedValue: CheckboxValueType;
|
|
@@ -44,7 +44,7 @@ export interface NodeRowProps {
|
|
|
44
44
|
onExpand: () => void;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
export interface TreeItemCustomizations {
|
|
47
|
+
export interface TreeItemCustomizations<ID> {
|
|
48
48
|
checkBoxViewStyleProps?: BuiltInCheckBoxViewStyleProps;
|
|
49
49
|
|
|
50
50
|
indentationMultiplier?: number;
|
|
@@ -53,29 +53,29 @@ export interface TreeItemCustomizations {
|
|
|
53
53
|
ExpandCollapseIconComponent?: React.ComponentType<ExpandIconProps>;
|
|
54
54
|
ExpandCollapseTouchableComponent?: React.ComponentType<TouchableOpacityProps>;
|
|
55
55
|
|
|
56
|
-
CustomNodeRowComponent?: React.ComponentType<NodeRowProps
|
|
56
|
+
CustomNodeRowComponent?: React.ComponentType<NodeRowProps<ID>>;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
export interface NodeProps extends TreeItemCustomizations {
|
|
60
|
-
node: __FlattenedTreeNode__
|
|
59
|
+
export interface NodeProps<ID> extends TreeItemCustomizations<ID> {
|
|
60
|
+
node: __FlattenedTreeNode__<ID>;
|
|
61
61
|
level: number;
|
|
62
62
|
storeId: string;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
export interface NodeListProps extends TreeItemCustomizations {
|
|
65
|
+
export interface NodeListProps<ID> extends TreeItemCustomizations<ID> {
|
|
66
66
|
treeFlashListProps?: TreeFlatListProps;
|
|
67
67
|
storeId: string;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
export interface TreeViewProps extends Omit<NodeListProps
|
|
71
|
-
data: TreeNode[];
|
|
70
|
+
export interface TreeViewProps<ID = string> extends Omit<NodeListProps<ID>, "storeId"> {
|
|
71
|
+
data: TreeNode<ID>[];
|
|
72
72
|
|
|
73
|
-
onCheck?: (checkedIds:
|
|
74
|
-
onExpand?: (expandedIds:
|
|
73
|
+
onCheck?: (checkedIds: ID[], indeterminateIds: ID[]) => void;
|
|
74
|
+
onExpand?: (expandedIds: ID[]) => void;
|
|
75
75
|
|
|
76
|
-
preselectedIds?:
|
|
76
|
+
preselectedIds?: ID[];
|
|
77
77
|
|
|
78
|
-
preExpandedIds?:
|
|
78
|
+
preExpandedIds?: ID[];
|
|
79
79
|
|
|
80
80
|
selectionPropagation?: SelectionPropagation;
|
|
81
81
|
}
|
|
@@ -103,7 +103,7 @@ export type BuiltInCheckBoxViewProps =
|
|
|
103
103
|
CheckBoxViewProps
|
|
104
104
|
& BuiltInCheckBoxViewStyleProps;
|
|
105
105
|
|
|
106
|
-
export interface TreeViewRef {
|
|
106
|
+
export interface TreeViewRef<ID = string> {
|
|
107
107
|
selectAll: () => void;
|
|
108
108
|
unselectAll: () => void;
|
|
109
109
|
|
|
@@ -113,11 +113,11 @@ export interface TreeViewRef {
|
|
|
113
113
|
expandAll: () => void;
|
|
114
114
|
collapseAll: () => void;
|
|
115
115
|
|
|
116
|
-
expandNodes: (ids:
|
|
117
|
-
collapseNodes: (ids:
|
|
116
|
+
expandNodes: (ids: ID[]) => void;
|
|
117
|
+
collapseNodes: (ids: ID[]) => void;
|
|
118
118
|
|
|
119
|
-
selectNodes: (ids:
|
|
120
|
-
unselectNodes: (ids:
|
|
119
|
+
selectNodes: (ids: ID[]) => void;
|
|
120
|
+
unselectNodes: (ids: ID[]) => void;
|
|
121
121
|
|
|
122
122
|
setSearchText: (searchText: string, searchKeys?: string[]) => void;
|
|
123
123
|
}
|
|
@@ -125,4 +125,4 @@ export interface TreeViewRef {
|
|
|
125
125
|
export interface SelectionPropagation {
|
|
126
126
|
toChildren?: boolean;
|
|
127
127
|
toParents?: boolean;
|
|
128
|
-
}
|
|
128
|
+
}
|