react-native-tree-multi-select 0.5.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.
Files changed (108) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +124 -0
  3. package/android/build.gradle +94 -0
  4. package/android/gradle.properties +5 -0
  5. package/android/src/main/AndroidManifest.xml +4 -0
  6. package/android/src/main/java/com/treemultiselect/TreeMultiSelectModule.kt +25 -0
  7. package/android/src/main/java/com/treemultiselect/TreeMultiSelectPackage.kt +17 -0
  8. package/ios/TreeMultiSelect-Bridging-Header.h +2 -0
  9. package/ios/TreeMultiSelect.mm +14 -0
  10. package/ios/TreeMultiSelect.swift +8 -0
  11. package/ios/TreeMultiSelect.xcodeproj/project.pbxproj +283 -0
  12. package/ios/TreeMultiSelect.xcodeproj/project.xcworkspace/contents.xcworkspacedata +4 -0
  13. package/ios/TreeMultiSelect.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  14. package/ios/TreeMultiSelect.xcodeproj/project.xcworkspace/xcuserdata/guest_jj.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  15. package/ios/TreeMultiSelect.xcodeproj/xcuserdata/guest_jj.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
  16. package/lib/commonjs/TreeView.js +99 -0
  17. package/lib/commonjs/TreeView.js.map +1 -0
  18. package/lib/commonjs/components/CheckboxView.js +77 -0
  19. package/lib/commonjs/components/CheckboxView.js.map +1 -0
  20. package/lib/commonjs/components/CustomExpandCollapseIcon.js +20 -0
  21. package/lib/commonjs/components/CustomExpandCollapseIcon.js.map +1 -0
  22. package/lib/commonjs/components/NodeList.js +193 -0
  23. package/lib/commonjs/components/NodeList.js.map +1 -0
  24. package/lib/commonjs/helpers/expandCollapse.helper.js +93 -0
  25. package/lib/commonjs/helpers/expandCollapse.helper.js.map +1 -0
  26. package/lib/commonjs/helpers/index.js +61 -0
  27. package/lib/commonjs/helpers/index.js.map +1 -0
  28. package/lib/commonjs/helpers/initNodeMap.helper.js +44 -0
  29. package/lib/commonjs/helpers/initNodeMap.helper.js.map +1 -0
  30. package/lib/commonjs/helpers/search.helper.js +29 -0
  31. package/lib/commonjs/helpers/search.helper.js.map +1 -0
  32. package/lib/commonjs/helpers/selectAll.helper.js +73 -0
  33. package/lib/commonjs/helpers/selectAll.helper.js.map +1 -0
  34. package/lib/commonjs/helpers/toggleCheckbox.helper.js +153 -0
  35. package/lib/commonjs/helpers/toggleCheckbox.helper.js.map +1 -0
  36. package/lib/commonjs/index.js +28 -0
  37. package/lib/commonjs/index.js.map +1 -0
  38. package/lib/commonjs/signals/global.signals.js +42 -0
  39. package/lib/commonjs/signals/global.signals.js.map +1 -0
  40. package/lib/commonjs/types/treeView.types.js +6 -0
  41. package/lib/commonjs/types/treeView.types.js.map +1 -0
  42. package/lib/module/TreeView.js +89 -0
  43. package/lib/module/TreeView.js.map +1 -0
  44. package/lib/module/components/CheckboxView.js +68 -0
  45. package/lib/module/components/CheckboxView.js.map +1 -0
  46. package/lib/module/components/CustomExpandCollapseIcon.js +13 -0
  47. package/lib/module/components/CustomExpandCollapseIcon.js.map +1 -0
  48. package/lib/module/components/NodeList.js +185 -0
  49. package/lib/module/components/NodeList.js.map +1 -0
  50. package/lib/module/helpers/expandCollapse.helper.js +86 -0
  51. package/lib/module/helpers/expandCollapse.helper.js.map +1 -0
  52. package/lib/module/helpers/index.js +6 -0
  53. package/lib/module/helpers/index.js.map +1 -0
  54. package/lib/module/helpers/initNodeMap.helper.js +39 -0
  55. package/lib/module/helpers/initNodeMap.helper.js.map +1 -0
  56. package/lib/module/helpers/search.helper.js +23 -0
  57. package/lib/module/helpers/search.helper.js.map +1 -0
  58. package/lib/module/helpers/selectAll.helper.js +65 -0
  59. package/lib/module/helpers/selectAll.helper.js.map +1 -0
  60. package/lib/module/helpers/toggleCheckbox.helper.js +148 -0
  61. package/lib/module/helpers/toggleCheckbox.helper.js.map +1 -0
  62. package/lib/module/index.js +4 -0
  63. package/lib/module/index.js.map +1 -0
  64. package/lib/module/signals/global.signals.js +26 -0
  65. package/lib/module/signals/global.signals.js.map +1 -0
  66. package/lib/module/types/treeView.types.js +2 -0
  67. package/lib/module/types/treeView.types.js.map +1 -0
  68. package/lib/typescript/TreeView.d.ts +4 -0
  69. package/lib/typescript/TreeView.d.ts.map +1 -0
  70. package/lib/typescript/components/CheckboxView.d.ts +24 -0
  71. package/lib/typescript/components/CheckboxView.d.ts.map +1 -0
  72. package/lib/typescript/components/CustomExpandCollapseIcon.d.ts +4 -0
  73. package/lib/typescript/components/CustomExpandCollapseIcon.d.ts.map +1 -0
  74. package/lib/typescript/components/NodeList.d.ts +14 -0
  75. package/lib/typescript/components/NodeList.d.ts.map +1 -0
  76. package/lib/typescript/helpers/expandCollapse.helper.d.ts +18 -0
  77. package/lib/typescript/helpers/expandCollapse.helper.d.ts.map +1 -0
  78. package/lib/typescript/helpers/index.d.ts +6 -0
  79. package/lib/typescript/helpers/index.d.ts.map +1 -0
  80. package/lib/typescript/helpers/initNodeMap.helper.d.ts +12 -0
  81. package/lib/typescript/helpers/initNodeMap.helper.d.ts.map +1 -0
  82. package/lib/typescript/helpers/search.helper.d.ts +14 -0
  83. package/lib/typescript/helpers/search.helper.d.ts.map +1 -0
  84. package/lib/typescript/helpers/selectAll.helper.d.ts +25 -0
  85. package/lib/typescript/helpers/selectAll.helper.d.ts.map +1 -0
  86. package/lib/typescript/helpers/toggleCheckbox.helper.d.ts +9 -0
  87. package/lib/typescript/helpers/toggleCheckbox.helper.d.ts.map +1 -0
  88. package/lib/typescript/index.d.ts +5 -0
  89. package/lib/typescript/index.d.ts.map +1 -0
  90. package/lib/typescript/signals/global.signals.d.ts +11 -0
  91. package/lib/typescript/signals/global.signals.d.ts.map +1 -0
  92. package/lib/typescript/types/treeView.types.d.ts +61 -0
  93. package/lib/typescript/types/treeView.types.d.ts.map +1 -0
  94. package/package.json +165 -0
  95. package/react-native-tree-multi-select.podspec +41 -0
  96. package/src/TreeView.tsx +139 -0
  97. package/src/components/CheckboxView.tsx +109 -0
  98. package/src/components/CustomExpandCollapseIcon.tsx +20 -0
  99. package/src/components/NodeList.tsx +278 -0
  100. package/src/helpers/expandCollapse.helper.ts +88 -0
  101. package/src/helpers/index.ts +5 -0
  102. package/src/helpers/initNodeMap.helper.ts +46 -0
  103. package/src/helpers/search.helper.ts +28 -0
  104. package/src/helpers/selectAll.helper.ts +59 -0
  105. package/src/helpers/toggleCheckbox.helper.ts +144 -0
  106. package/src/index.tsx +22 -0
  107. package/src/signals/global.signals.ts +36 -0
  108. package/src/types/treeView.types.ts +86 -0
@@ -0,0 +1,109 @@
1
+ import React from "react";
2
+ import {
3
+ Platform,
4
+ StyleSheet,
5
+ Text,
6
+ TouchableOpacity,
7
+ View
8
+ } from "react-native";
9
+
10
+ import { Checkbox } from 'react-native-paper';
11
+ import type {
12
+ CheckBoxViewProps,
13
+ CheckboxValueType
14
+ } from "../types/treeView.types";
15
+
16
+ function arePropsEqual(
17
+ prevProps: CheckBoxViewProps,
18
+ nextProps: CheckBoxViewProps
19
+ ) {
20
+ return (
21
+ prevProps.value === nextProps.value &&
22
+ prevProps.text === nextProps.text
23
+ );
24
+ }
25
+
26
+ export const CheckboxView = React.memo(_CheckboxView, arePropsEqual);
27
+
28
+ function _CheckboxView(props: CheckBoxViewProps) {
29
+ const {
30
+ value,
31
+ onValueChange,
32
+ text,
33
+
34
+ outermostParentViewStyle = defaultCheckboxViewStyles.mainView,
35
+ checkboxParentViewStyle = defaultCheckboxViewStyles.checkboxView,
36
+ textTouchableStyle,
37
+
38
+ checkboxProps,
39
+ textProps = {
40
+ style: defaultCheckboxViewStyles.checkboxTextStyle,
41
+ numberOfLines: 1,
42
+ ellipsizeMode: "middle",
43
+ },
44
+ } = props;
45
+
46
+ function customCheckboxValueTypeToRNPaperType(
47
+ customCheckboxValueType: CheckboxValueType
48
+ ) {
49
+ return customCheckboxValueType === 'indeterminate'
50
+ ? 'indeterminate'
51
+ : customCheckboxValueType
52
+ ? 'checked'
53
+ : 'unchecked';
54
+ }
55
+
56
+ /**
57
+ * This function modifies the change in value when the previous state was indeterminate.
58
+ * If the prior value is 'indeterminate', it will mark the CheckBox as checked upon click.
59
+ *
60
+ * @param newValue This represents the updated CheckBox value after it's clicked.
61
+ */
62
+ function onValueChangeModifier() {
63
+ // If the previous state was 'indeterminate', set checked to true
64
+ if (value === 'indeterminate') onValueChange(true);
65
+ else onValueChange(!value);
66
+ }
67
+
68
+ return (
69
+ <View
70
+ style={outermostParentViewStyle}>
71
+ <View
72
+ style={checkboxParentViewStyle}>
73
+ <Checkbox.Android
74
+ {...checkboxProps}
75
+ status={customCheckboxValueTypeToRNPaperType(value)}
76
+ onPress={onValueChangeModifier} />
77
+ </View>
78
+
79
+ {text ? (
80
+ <TouchableOpacity
81
+ style={textTouchableStyle}
82
+ onPress={onValueChangeModifier}>
83
+ <Text
84
+ {...textProps}>
85
+ {text}
86
+ </Text>
87
+ </TouchableOpacity>
88
+ ) : null}
89
+ </View>
90
+ );
91
+ }
92
+
93
+ export const defaultCheckboxViewStyles = StyleSheet.create({
94
+ mainView: {
95
+ alignSelf: 'center',
96
+ alignItems: 'center',
97
+ flexDirection: 'row',
98
+
99
+ marginEnd: 10
100
+ },
101
+ checkboxView: {
102
+ marginStart: 5,
103
+ transform: [{ scale: 1.2 }]
104
+ },
105
+ checkboxTextStyle: {
106
+ color: "black",
107
+ marginTop: Platform.OS === 'android' ? 2 : undefined,
108
+ },
109
+ });
@@ -0,0 +1,20 @@
1
+ import React from "react";
2
+ import Icon from 'react-native-vector-icons/FontAwesome';
3
+
4
+ import { ExpandIconProps } from "src/types/treeView.types";
5
+
6
+ export default function CustomExpandCollapseIcon(props: ExpandIconProps) {
7
+ const { isExpanded } = props;
8
+
9
+ return (
10
+ <Icon
11
+ name={
12
+ isExpanded
13
+ ? 'caret-down'
14
+ : 'caret-right'
15
+ }
16
+ size={20}
17
+ color="black"
18
+ />
19
+ );
20
+ }
@@ -0,0 +1,278 @@
1
+ import React from "react";
2
+ import {
3
+ View,
4
+ StyleSheet,
5
+
6
+ TouchableOpacity,
7
+ type TouchableOpacityProps,
8
+ } from "react-native";
9
+ import {
10
+ computed,
11
+ effect,
12
+ Signal,
13
+ useComputed,
14
+ useSignal
15
+ } from "@preact/signals-react";
16
+ import { FlashList } from "@shopify/flash-list";
17
+
18
+ import type {
19
+ TreeFlatListProps,
20
+ TreeNode,
21
+
22
+ CheckboxValueType,
23
+ ExpandIconProps,
24
+ CheckBoxViewStyleProps,
25
+ CheckBoxViewProps,
26
+ __FlattenedTreeNode__,
27
+ } from "../types/treeView.types";
28
+
29
+ import {
30
+ expanded,
31
+ globalData,
32
+ innerMostChildrenIds,
33
+ searchKeys,
34
+ searchText,
35
+ state
36
+ } from "../signals/global.signals";
37
+ import {
38
+ doesNodeContainSearchTerm,
39
+ handleToggleExpand,
40
+ toggleCheckboxes
41
+ } from "../helpers";
42
+ import { CheckboxView } from "./CheckboxView";
43
+ import CustomExpandCollapseIcon from "./CustomExpandCollapseIcon";
44
+
45
+ interface NodeListProps {
46
+ treeFlashListProps?: TreeFlatListProps;
47
+ checkBoxViewStyleProps?: CheckBoxViewStyleProps;
48
+
49
+ CheckboxComponent?: React.ComponentType<CheckBoxViewProps>;
50
+ ExpandCollapseIconComponent?: React.ComponentType<ExpandIconProps>;
51
+ ExpandCollapseTouchableComponent?: React.ComponentType<TouchableOpacityProps>;
52
+ }
53
+
54
+ const NodeList = React.memo(_NodeList);
55
+ export default NodeList;
56
+
57
+ function _NodeList(props: NodeListProps) {
58
+ const {
59
+ treeFlashListProps,
60
+ checkBoxViewStyleProps,
61
+
62
+ CheckboxComponent,
63
+ ExpandCollapseIconComponent,
64
+ ExpandCollapseTouchableComponent,
65
+ } = props;
66
+
67
+ const filteredTree = React.useMemo(() => {
68
+ return computed(() => {
69
+ const searchTrimmed = searchText.value.trim().toLowerCase();
70
+
71
+ const filterTreeData = (_nodes: TreeNode[]): TreeNode[] => {
72
+ let filtered: TreeNode[] = [];
73
+
74
+ for (let node of _nodes) {
75
+ if (!searchTrimmed || doesNodeContainSearchTerm(node, searchTrimmed, searchKeys.value)) {
76
+ // If node itself matches, include it and all its descendants
77
+ filtered.push(node);
78
+ } else if (node.children) {
79
+ // If node does not match, check its children and include them if they match
80
+ const childMatches = filterTreeData(node.children);
81
+ if (childMatches.length > 0) {
82
+ // If any children match, include the node, replacing its children with the matching ones
83
+ filtered.push({ ...node, children: childMatches });
84
+ }
85
+ }
86
+ }
87
+
88
+ return filtered;
89
+ };
90
+
91
+ return filterTreeData(globalData.value);
92
+ });
93
+ }, []);
94
+
95
+ const flattenedFilteredNodes = React.useMemo(() => {
96
+ return computed(() => {
97
+ const flattenTreeData = (
98
+ _nodes: TreeNode[],
99
+ level: number = 0,
100
+ ): __FlattenedTreeNode__[] => {
101
+ let flattened: __FlattenedTreeNode__[] = [];
102
+ for (let node of _nodes) {
103
+ flattened.push({ ...node, level });
104
+ if (node.children && expanded.value.has(node.id)) {
105
+ flattened = [...flattened, ...flattenTreeData(node.children, level + 1)];
106
+ }
107
+ }
108
+ return flattened;
109
+ };
110
+
111
+ return flattenTreeData(filteredTree.value);
112
+ });
113
+ // eslint-disable-next-line react-hooks/exhaustive-deps
114
+ }, []);
115
+
116
+ React.useEffect(() => {
117
+ effect(() => {
118
+ const allLeafIds: string[] = [];
119
+ const getLeafNodes = (_nodes: TreeNode[]) => {
120
+ for (let node of _nodes) {
121
+ if (node.children) {
122
+ getLeafNodes(node.children);
123
+ } else {
124
+ allLeafIds.push(node.id);
125
+ }
126
+ }
127
+ };
128
+
129
+ getLeafNodes(filteredTree.value);
130
+
131
+ innerMostChildrenIds.value = allLeafIds;
132
+ });
133
+ // eslint-disable-next-line react-hooks/exhaustive-deps
134
+ }, []);
135
+
136
+ const nodeRenderer = React.useCallback((
137
+ { item }: { item: __FlattenedTreeNode__; }
138
+ ) => {
139
+ return (
140
+ <Node
141
+ node={item}
142
+ level={item.level || 0}
143
+ checkBoxViewStyleProps={checkBoxViewStyleProps}
144
+
145
+ CheckboxComponent={CheckboxComponent}
146
+ ExpandCollapseIconComponent={ExpandCollapseIconComponent}
147
+ ExpandCollapseTouchableComponent={ExpandCollapseTouchableComponent}
148
+ />
149
+ );
150
+ }, [
151
+ CheckboxComponent,
152
+ ExpandCollapseIconComponent,
153
+ ExpandCollapseTouchableComponent,
154
+ checkBoxViewStyleProps
155
+ ]);
156
+
157
+ const keyExtractor = React.useCallback((item: TreeNode) => item.id, []);
158
+
159
+ return (
160
+ <FlashList
161
+ estimatedItemSize={36}
162
+ removeClippedSubviews={true}
163
+ keyboardShouldPersistTaps="handled"
164
+ drawDistance={50}
165
+ data={flattenedFilteredNodes.value}
166
+ renderItem={nodeRenderer}
167
+ keyExtractor={keyExtractor}
168
+ ListHeaderComponent={<HeaderFooterView />}
169
+ ListFooterComponent={<HeaderFooterView />}
170
+ {...treeFlashListProps}
171
+ />
172
+ );
173
+ };
174
+
175
+ function HeaderFooterView() {
176
+ return (
177
+ <View style={styles.defaultHeaderFooter} />
178
+ );
179
+ }
180
+
181
+ interface NodeProps {
182
+ node: __FlattenedTreeNode__;
183
+ level: number;
184
+
185
+ checkBoxViewStyleProps?: CheckBoxViewStyleProps;
186
+
187
+ ExpandCollapseIconComponent?: React.ComponentType<ExpandIconProps>;
188
+ CheckboxComponent?: React.ComponentType<CheckBoxViewProps>;
189
+ ExpandCollapseTouchableComponent?: React.ComponentType<TouchableOpacityProps>;
190
+ }
191
+
192
+ const Node = React.memo(_Node);
193
+ function _Node(props: NodeProps) {
194
+ const {
195
+ node: _node,
196
+ level,
197
+
198
+ checkBoxViewStyleProps,
199
+
200
+ ExpandCollapseIconComponent = CustomExpandCollapseIcon,
201
+ CheckboxComponent = CheckboxView,
202
+ ExpandCollapseTouchableComponent = TouchableOpacity,
203
+ } = props;
204
+
205
+ const node = useSignal(_node);
206
+ React.useEffect(() => {
207
+ node.value = _node;
208
+ // eslint-disable-next-line react-hooks/exhaustive-deps
209
+ }, [_node]);
210
+
211
+ const isChecked = useComputed(() => state.value.checked.has(node.value.id));
212
+ const isIndeterminate = useComputed(() => state.value.indeterminate.has(
213
+ node.value.id
214
+ ));
215
+ const value: Signal<CheckboxValueType> = useComputed(() => {
216
+ if (isIndeterminate.value) {
217
+ return 'indeterminate';
218
+ } else if (isChecked.value) {
219
+ return true;
220
+ } else {
221
+ return false;
222
+ }
223
+ });
224
+ const isExpanded = useComputed(() => expanded.value.has(node.value.id));
225
+
226
+ const _onToggleExpand = React.useCallback(() => {
227
+ handleToggleExpand(node.value.id);
228
+ // eslint-disable-next-line react-hooks/exhaustive-deps
229
+ }, []);
230
+
231
+ const _onCheck = React.useCallback(() => {
232
+ toggleCheckboxes([node.value.id]);
233
+ // eslint-disable-next-line react-hooks/exhaustive-deps
234
+ }, []);
235
+
236
+ return (
237
+ <View style={[
238
+ styles.nodeParentView,
239
+ { marginLeft: level * 15, }
240
+ ]}>
241
+ <View style={styles.nodeCheckboxAndArrowRow}>
242
+ <CheckboxComponent
243
+ text={node.value.name}
244
+ onValueChange={_onCheck}
245
+ value={value.value}
246
+ {...checkBoxViewStyleProps} />
247
+
248
+ {node.value.children?.length ? (
249
+ <ExpandCollapseTouchableComponent
250
+ style={styles.nodeExpandableArrowTouchable}
251
+ onPress={_onToggleExpand}>
252
+ <ExpandCollapseIconComponent
253
+ isExpanded={isExpanded.value}
254
+ />
255
+ </ExpandCollapseTouchableComponent>
256
+ ) : null}
257
+ </View>
258
+ </View>
259
+ );
260
+ };
261
+
262
+ const styles = StyleSheet.create({
263
+ defaultHeaderFooter: {
264
+ padding: 5
265
+ },
266
+ nodeParentView: {
267
+ flex: 1
268
+ },
269
+ nodeExpandableArrowTouchable: {
270
+ flex: 1
271
+ },
272
+ nodeCheckboxAndArrowRow: {
273
+ flexDirection: 'row',
274
+ alignItems: 'center',
275
+ minWidth: "100%",
276
+ }
277
+ });
278
+
@@ -0,0 +1,88 @@
1
+ import { TreeNode } from "../types/treeView.types";
2
+ import {
3
+ expanded,
4
+ globalData,
5
+ nodeMap
6
+ } from "../signals/global.signals";
7
+
8
+ /**
9
+ * Toggle the expanded state of a tree node by its ID.
10
+ *
11
+ * If the node is currently expanded, it and its descendants will be collapsed.
12
+ * If it is currently collapsed, it will be expanded.
13
+ *
14
+ * @param id - The ID of the tree node to toggle.
15
+ */
16
+ export function handleToggleExpand(id: string) {
17
+ // Create a new Set based on the current expanded state
18
+ const newExpanded = new Set(expanded.value);
19
+
20
+ /**
21
+ * Recursively deletes a node and its descendants from the expanded set.
22
+ *
23
+ * @param node - The tree node to start deleting from.
24
+ */
25
+ function deleteChildrenFromExpanded(node: TreeNode) {
26
+ if (node.children) {
27
+ for (let child of node.children) {
28
+ newExpanded.delete(child.id);
29
+ deleteChildrenFromExpanded(child);
30
+ }
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Finds a node in the tree by its ID.
36
+ *
37
+ * @param nodes - The array of tree nodes to search through.
38
+ * @returns The found tree node, or undefined if not found.
39
+ */
40
+ function findNode(nodes: TreeNode[]): TreeNode | undefined {
41
+ for (let node of nodes) {
42
+ if (node.id === id) {
43
+ return node;
44
+ } else if (node.children) {
45
+ const found = findNode(node.children);
46
+ if (found) {
47
+ return found;
48
+ }
49
+ }
50
+ }
51
+ return undefined;
52
+ }
53
+
54
+ // Find the node to expand or collapse
55
+ const node = findNode(globalData.value);
56
+
57
+ if (expanded.value.has(id)) {
58
+ // If the node is currently expanded, collapse it and its descendants
59
+ newExpanded.delete(id);
60
+ if (node) {
61
+ deleteChildrenFromExpanded(node);
62
+ }
63
+ } else {
64
+ // If the node is currently collapsed, expand it
65
+ newExpanded.add(id);
66
+ }
67
+
68
+ // Set the new expanded state
69
+ expanded.value = newExpanded;
70
+ };
71
+
72
+ /**
73
+ * Expand all nodes in the tree.
74
+ */
75
+ export function expandAll() {
76
+ // Create a new Set containing the IDs of all nodes
77
+ const newExpanded = new Set(nodeMap.value.keys());
78
+ expanded.value = newExpanded;
79
+ };
80
+
81
+ /**
82
+ * Collapse all nodes in the tree.
83
+ */
84
+ export function collapseAll() {
85
+ // Create an empty Set
86
+ const newExpanded = new Set<string>();
87
+ expanded.value = newExpanded;
88
+ };
@@ -0,0 +1,5 @@
1
+ export * from "./expandCollapse.helper";
2
+ export * from "./initNodeMap.helper";
3
+ export * from "./selectAll.helper";
4
+ export * from "./toggleCheckbox.helper";
5
+ export * from "./search.helper";
@@ -0,0 +1,46 @@
1
+ import type { TreeNode } from "../types/treeView.types";
2
+ import {
3
+ childToParentMap,
4
+ nodeMap,
5
+ } from "../signals/global.signals";
6
+ import { toggleCheckboxes } from "./toggleCheckbox.helper";
7
+
8
+ /**
9
+ * Initialize the maps for tracking tree nodes and their parent-child relationships.
10
+ *
11
+ * This function is intended to be called once, during component initialization,
12
+ * with the initial tree data and any preselected node IDs.
13
+ *
14
+ * @param initialData - An array of TreeNode objects that represent the initial tree structure.
15
+ * @param preselectedIds - An optional array of TreeNode IDs that should be preselected.
16
+ */
17
+ export function initializeNodeMaps(
18
+ initialData: TreeNode[],
19
+ preselectedIds: string[] = [],
20
+ ) {
21
+ /**
22
+ * Recursively processes nodes, adding them to the nodeMap and childToParentMap.
23
+ *
24
+ * @param nodes - An array of TreeNode objects to be processed.
25
+ * @param parentId - The ID of the parent node, if applicable.
26
+ */
27
+ const processNodes = (
28
+ nodes: TreeNode[],
29
+ parentId: string | null = null
30
+ ) => {
31
+ nodes.forEach((node) => {
32
+ // Each node is added to the nodeMap with its ID as the key
33
+ nodeMap.value.set(node.id, node);
34
+ // If the node has a parent, its ID is mapped to the parent's ID in the childToParentMap
35
+ if (parentId) childToParentMap.value.set(node.id, parentId);
36
+ // If the node has children, recursively process them
37
+ if (node.children) processNodes(node.children, node.id);
38
+ });
39
+ };
40
+
41
+ // Begin processing with the initial tree data
42
+ processNodes(initialData);
43
+
44
+ // Check any preselected nodes
45
+ toggleCheckboxes(preselectedIds, true);
46
+ }
@@ -0,0 +1,28 @@
1
+ import { TreeNode } from "../types/treeView.types";
2
+
3
+ /**
4
+ * Checks if a given tree node contains a specific search term in any of its specified keys.
5
+ *
6
+ * This function will check each of the specified keys in the tree node, convert the key's value to a string,
7
+ * and check if it includes the search term.
8
+ *
9
+ * @param node - The tree node to search through.
10
+ * @param searchTerm - The term to search for.
11
+ * @param searchKeys - The keys in the tree node to search in.
12
+ * @returns True if the search term is found in any of the specified keys, false otherwise.
13
+ */
14
+ export function doesNodeContainSearchTerm(
15
+ node: TreeNode,
16
+ searchTerm: string,
17
+ searchKeys: string[]
18
+ ): boolean {
19
+ // We're using the `some` method on the array of keys to check each one
20
+ return searchKeys.some(key => {
21
+ // Get the value of the key in the tree node
22
+ const nodeValue = node[key];
23
+ // Check if the string representation of the key's value includes the search term
24
+ // If the value is undefined or null, `nodeValue?.toString()` will return undefined,
25
+ // and the call to `toLowerCase().includes(searchTerm)` will return false.
26
+ return (nodeValue?.toString().toLowerCase().includes(searchTerm));
27
+ });
28
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ innerMostChildrenIds,
3
+ nodeMap,
4
+ searchText,
5
+ state
6
+ } from "../signals/global.signals";
7
+ import { toggleCheckboxes } from "./toggleCheckbox.helper";
8
+
9
+ /**
10
+ * Selects all nodes that are currently visible due to the applied filter.
11
+ *
12
+ * If there is no search text, then it selects all nodes; otherwise, it selects all visible nodes.
13
+ */
14
+ export function selectAllFiltered() {
15
+ // If there's no search text, select all nodes
16
+ if (!searchText.value) {
17
+ selectAll();
18
+ } else {
19
+ // If there's search text, only select the visible nodes
20
+ toggleCheckboxes(innerMostChildrenIds.value, true);
21
+ }
22
+ };
23
+
24
+ /**
25
+ * Unselects all nodes that are currently visible due to the applied filter.
26
+ *
27
+ * If there is no search text, then it unselects all nodes; otherwise, it unselects all visible nodes.
28
+ */
29
+ export function unselectAllFiltered() {
30
+ // If there's no search text, unselect all nodes
31
+ if (!searchText.value) {
32
+ unselectAll();
33
+ } else {
34
+ // If there's search text, only unselect the visible nodes
35
+ toggleCheckboxes(innerMostChildrenIds.value, false);
36
+ }
37
+ };
38
+
39
+ /**
40
+ * Selects all nodes in the tree.
41
+ *
42
+ * This function selects all nodes by adding all node ids to the checked set and clearing the indeterminate set.
43
+ */
44
+ export function selectAll() {
45
+ // Create a new set containing the ids of all nodes
46
+ const newChecked = new Set(nodeMap.value.keys());
47
+ // Update the state to mark all nodes as checked
48
+ state.value = ({ checked: newChecked, indeterminate: new Set() });
49
+ };
50
+
51
+ /**
52
+ * Unselects all nodes in the tree.
53
+ *
54
+ * This function unselects all nodes by clearing both the checked and indeterminate sets.
55
+ */
56
+ export function unselectAll() {
57
+ // Update the state to mark all nodes as unchecked
58
+ state.value = ({ checked: new Set(), indeterminate: new Set() });
59
+ };