react-native-tree-multi-select 3.0.0-beta.3 → 3.0.0-beta.4

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 (96) hide show
  1. package/README.md +84 -24
  2. package/lib/module/TreeView.js +36 -31
  3. package/lib/module/TreeView.js.map +1 -1
  4. package/lib/module/components/CheckboxView.js +8 -4
  5. package/lib/module/components/CheckboxView.js.map +1 -1
  6. package/lib/module/components/CustomExpandCollapseIcon.js +2 -2
  7. package/lib/module/components/CustomExpandCollapseIcon.js.map +1 -1
  8. package/lib/module/components/DragOverlay.js +17 -5
  9. package/lib/module/components/DragOverlay.js.map +1 -1
  10. package/lib/module/components/DropIndicator.js +2 -2
  11. package/lib/module/components/DropIndicator.js.map +1 -1
  12. package/lib/module/components/NodeList.js +74 -57
  13. package/lib/module/components/NodeList.js.map +1 -1
  14. package/lib/module/constants/treeView.constants.js +3 -0
  15. package/lib/module/constants/treeView.constants.js.map +1 -1
  16. package/lib/module/helpers/expandCollapse.helper.js.map +1 -1
  17. package/lib/module/helpers/moveTreeNode.helper.js +30 -0
  18. package/lib/module/helpers/moveTreeNode.helper.js.map +1 -1
  19. package/lib/module/helpers/selectAll.helper.js.map +1 -1
  20. package/lib/module/helpers/toggleCheckbox.helper.js +43 -60
  21. package/lib/module/helpers/toggleCheckbox.helper.js.map +1 -1
  22. package/lib/module/hooks/useDragDrop.js +114 -19
  23. package/lib/module/hooks/useDragDrop.js.map +1 -1
  24. package/lib/module/{handlers/ScrollToNodeHandler.js → hooks/useScrollToNode.js} +27 -26
  25. package/lib/module/hooks/useScrollToNode.js.map +1 -0
  26. package/lib/module/index.js +1 -0
  27. package/lib/module/index.js.map +1 -1
  28. package/lib/module/jest.setup.js +14 -1
  29. package/lib/module/jest.setup.js.map +1 -1
  30. package/lib/module/store/treeView.store.js +3 -0
  31. package/lib/module/store/treeView.store.js.map +1 -1
  32. package/lib/module/utils/typedMemo.js +3 -3
  33. package/lib/module/utils/typedMemo.js.map +1 -1
  34. package/lib/module/utils/useDeepCompareEffect.js +5 -5
  35. package/lib/module/utils/useDeepCompareEffect.js.map +1 -1
  36. package/lib/typescript/src/TreeView.d.ts +3 -3
  37. package/lib/typescript/src/TreeView.d.ts.map +1 -1
  38. package/lib/typescript/src/components/CheckboxView.d.ts +1 -2
  39. package/lib/typescript/src/components/CheckboxView.d.ts.map +1 -1
  40. package/lib/typescript/src/components/CustomExpandCollapseIcon.d.ts +1 -2
  41. package/lib/typescript/src/components/CustomExpandCollapseIcon.d.ts.map +1 -1
  42. package/lib/typescript/src/components/DragOverlay.d.ts +1 -0
  43. package/lib/typescript/src/components/DragOverlay.d.ts.map +1 -1
  44. package/lib/typescript/src/components/DropIndicator.d.ts +1 -2
  45. package/lib/typescript/src/components/DropIndicator.d.ts.map +1 -1
  46. package/lib/typescript/src/components/NodeList.d.ts.map +1 -1
  47. package/lib/typescript/src/constants/treeView.constants.d.ts +2 -0
  48. package/lib/typescript/src/constants/treeView.constants.d.ts.map +1 -1
  49. package/lib/typescript/src/helpers/expandCollapse.helper.d.ts +2 -2
  50. package/lib/typescript/src/helpers/expandCollapse.helper.d.ts.map +1 -1
  51. package/lib/typescript/src/helpers/moveTreeNode.helper.d.ts.map +1 -1
  52. package/lib/typescript/src/helpers/selectAll.helper.d.ts +4 -4
  53. package/lib/typescript/src/helpers/selectAll.helper.d.ts.map +1 -1
  54. package/lib/typescript/src/helpers/toggleCheckbox.helper.d.ts +3 -0
  55. package/lib/typescript/src/helpers/toggleCheckbox.helper.d.ts.map +1 -1
  56. package/lib/typescript/src/hooks/useDragDrop.d.ts +18 -7
  57. package/lib/typescript/src/hooks/useDragDrop.d.ts.map +1 -1
  58. package/lib/typescript/src/{handlers/ScrollToNodeHandler.d.ts → hooks/useScrollToNode.d.ts} +13 -15
  59. package/lib/typescript/src/hooks/useScrollToNode.d.ts.map +1 -0
  60. package/lib/typescript/src/index.d.ts +4 -3
  61. package/lib/typescript/src/index.d.ts.map +1 -1
  62. package/lib/typescript/src/jest.setup.d.ts +1 -1
  63. package/lib/typescript/src/jest.setup.d.ts.map +1 -1
  64. package/lib/typescript/src/store/treeView.store.d.ts +2 -1
  65. package/lib/typescript/src/store/treeView.store.d.ts.map +1 -1
  66. package/lib/typescript/src/types/dragDrop.types.d.ts +10 -0
  67. package/lib/typescript/src/types/dragDrop.types.d.ts.map +1 -1
  68. package/lib/typescript/src/types/treeView.types.d.ts +69 -41
  69. package/lib/typescript/src/types/treeView.types.d.ts.map +1 -1
  70. package/lib/typescript/src/utils/typedMemo.d.ts +1 -1
  71. package/lib/typescript/src/utils/typedMemo.d.ts.map +1 -1
  72. package/lib/typescript/src/utils/useDeepCompareEffect.d.ts +2 -2
  73. package/lib/typescript/src/utils/useDeepCompareEffect.d.ts.map +1 -1
  74. package/package.json +32 -15
  75. package/src/TreeView.tsx +57 -35
  76. package/src/components/CheckboxView.tsx +7 -4
  77. package/src/components/CustomExpandCollapseIcon.tsx +2 -2
  78. package/src/components/DragOverlay.tsx +19 -6
  79. package/src/components/DropIndicator.tsx +2 -2
  80. package/src/components/NodeList.tsx +83 -59
  81. package/src/constants/treeView.constants.ts +4 -1
  82. package/src/helpers/expandCollapse.helper.ts +5 -5
  83. package/src/helpers/moveTreeNode.helper.ts +33 -0
  84. package/src/helpers/selectAll.helper.ts +10 -10
  85. package/src/helpers/toggleCheckbox.helper.ts +56 -68
  86. package/src/hooks/useDragDrop.ts +152 -30
  87. package/src/{handlers/ScrollToNodeHandler.tsx → hooks/useScrollToNode.ts} +48 -45
  88. package/src/index.tsx +9 -0
  89. package/src/jest.setup.ts +14 -1
  90. package/src/store/treeView.store.ts +6 -1
  91. package/src/types/dragDrop.types.ts +12 -0
  92. package/src/types/treeView.types.ts +76 -43
  93. package/src/utils/typedMemo.ts +3 -3
  94. package/src/utils/useDeepCompareEffect.ts +13 -7
  95. package/lib/module/handlers/ScrollToNodeHandler.js.map +0 -1
  96. package/lib/typescript/src/handlers/ScrollToNodeHandler.d.ts.map +0 -1
@@ -1,7 +1,7 @@
1
1
  /**
2
- * ScrollToNodeHandler Component
2
+ * useScrollToNode Hook
3
3
  *
4
- * This component provides an imperative handle to scroll to a specified node within a tree view.
4
+ * Provides an imperative handle to scroll to a specified node within a tree view.
5
5
  * The scrolling action is orchestrated via a two-step "milestone" mechanism that ensures the target
6
6
  * node is both expanded in the tree and that the rendered list reflects this expansion before the scroll
7
7
  * is performed.
@@ -32,22 +32,23 @@
32
32
  * in the UI, thus preventing issues with attempting to scroll to an element that does not exist yet.
33
33
  */
34
34
 
35
- import React from "react";
35
+ import {
36
+ useEffect,
37
+ useImperativeHandle,
38
+ useLayoutEffect,
39
+ useRef,
40
+ useState,
41
+ type Dispatch,
42
+ type MutableRefObject,
43
+ type RefObject,
44
+ type SetStateAction,
45
+ } from "react";
36
46
  import { expandNodes } from "../helpers/expandCollapse.helper";
37
47
  import { useTreeViewStore } from "../store/treeView.store";
38
48
  import { useShallow } from "zustand/react/shallow";
39
49
  import { type __FlattenedTreeNode__ } from "../types/treeView.types";
40
- import { typedMemo } from "../utils/typedMemo";
41
50
  import { fastIsEqual } from "fast-is-equal";
42
51
 
43
- interface Props<ID> {
44
- storeId: string;
45
- flashListRef: React.MutableRefObject<any>;
46
- flattenedFilteredNodes: __FlattenedTreeNode__<ID>[];
47
- setInitialScrollIndex: React.Dispatch<React.SetStateAction<number>>;
48
- initialScrollNodeID: ID | undefined;
49
- }
50
-
51
52
  export interface ScrollToNodeParams<ID> {
52
53
  nodeId: ID;
53
54
  expandScrolledNode?: boolean;
@@ -57,27 +58,34 @@ export interface ScrollToNodeParams<ID> {
57
58
  viewPosition?: number;
58
59
  }
59
60
 
61
+ export interface ScrollToNodeHandlerRef<ID> {
62
+ scrollToNodeID: (params: ScrollToNodeParams<ID>) => void;
63
+ }
64
+
60
65
  // Enum representing the two milestones needed before scrolling
61
66
  enum ExpandQueueAction {
62
67
  EXPANDED,
63
68
  RENDERED,
64
69
  }
65
70
 
66
- export interface ScrollToNodeHandlerRef<ID> {
67
- scrollToNodeID: (params: ScrollToNodeParams<ID>) => void;
71
+ interface UseScrollToNodeParams<ID> {
72
+ storeId: string;
73
+ scrollToNodeHandlerRef: RefObject<ScrollToNodeHandlerRef<ID>>;
74
+ flashListRef: MutableRefObject<any>;
75
+ flattenedFilteredNodes: __FlattenedTreeNode__<ID>[];
76
+ setInitialScrollIndex: Dispatch<SetStateAction<number>>;
77
+ initialScrollNodeID: ID | undefined;
68
78
  }
69
79
 
70
- function _innerScrollToNodeHandler<ID>(
71
- props: Props<ID>,
72
- ref: React.ForwardedRef<ScrollToNodeHandlerRef<ID>>
73
- ) {
80
+ export function useScrollToNode<ID>(params: UseScrollToNodeParams<ID>) {
74
81
  const {
75
82
  storeId,
83
+ scrollToNodeHandlerRef,
76
84
  flashListRef,
77
85
  flattenedFilteredNodes,
78
86
  setInitialScrollIndex,
79
87
  initialScrollNodeID
80
- } = props;
88
+ } = params;
81
89
 
82
90
  const { expanded, childToParentMap } = useTreeViewStore<ID>(storeId)(useShallow(
83
91
  state => ({
@@ -86,9 +94,16 @@ function _innerScrollToNodeHandler<ID>(
86
94
  })
87
95
  ));
88
96
 
89
- React.useImperativeHandle(ref, () => ({
90
- scrollToNodeID: (params: ScrollToNodeParams<ID>) => {
91
- queuedScrollToNodeParams.current = params;
97
+ // Ref to store the scroll parameters for the queued action.
98
+ const queuedScrollToNodeParams = useRef<ScrollToNodeParams<ID> | null>(null);
99
+
100
+ // State to track progression: first the expansion is triggered, then the list is rendered.
101
+ const [expandAndScrollToNodeQueue, setExpandAndScrollToNodeQueue]
102
+ = useState<ExpandQueueAction[]>([]);
103
+
104
+ useImperativeHandle(scrollToNodeHandlerRef, () => ({
105
+ scrollToNodeID: (scrollParams: ScrollToNodeParams<ID>) => {
106
+ queuedScrollToNodeParams.current = scrollParams;
92
107
  // Mark that expansion is initiated.
93
108
  setExpandAndScrollToNodeQueue([ExpandQueueAction.EXPANDED]);
94
109
  // Trigger expansion logic (this may update the store and subsequently re-render the list).
@@ -100,18 +115,11 @@ function _innerScrollToNodeHandler<ID>(
100
115
  }
101
116
  }), [storeId]);
102
117
 
103
- // Ref to store the scroll parameters for the queued action.
104
- const queuedScrollToNodeParams = React.useRef<ScrollToNodeParams<ID> | null>(null);
105
-
106
- // State to track progression: first the expansion is triggered, then the list is rendered.
107
- const [expandAndScrollToNodeQueue, setExpandAndScrollToNodeQueue]
108
- = React.useState<ExpandQueueAction[]>([]);
109
-
110
- const latestFlattenedFilteredNodesRef = React.useRef(flattenedFilteredNodes);
118
+ const latestFlattenedFilteredNodesRef = useRef(flattenedFilteredNodes);
111
119
 
112
120
  /* When the rendered node list changes, update the ref.
113
121
  If an expansion was triggered, mark that the list is now rendered. */
114
- React.useEffect(() => {
122
+ useEffect(() => {
115
123
  setExpandAndScrollToNodeQueue(prevQueue => {
116
124
  if (prevQueue.includes(ExpandQueueAction.EXPANDED)) {
117
125
  latestFlattenedFilteredNodesRef.current = flattenedFilteredNodes;
@@ -127,7 +135,7 @@ function _innerScrollToNodeHandler<ID>(
127
135
 
128
136
  /* Once the target node is expanded and the list is updated (milestones reached),
129
137
  perform the scroll using the latest node list. */
130
- React.useLayoutEffect(() => {
138
+ useLayoutEffect(() => {
131
139
  if (queuedScrollToNodeParams.current === null)
132
140
  return;
133
141
 
@@ -146,12 +154,16 @@ function _innerScrollToNodeHandler<ID>(
146
154
  parentId = childToParentMap.get(queuedScrollToNodeParams.current.nodeId) as ID;
147
155
  }
148
156
 
149
- // Ensure if the parent is expanded before proceeding to scroll to the node
157
+ // Ensure if the parent is expanded before proceeding to scroll to the node.
158
+ // This fires transiently during the milestone system — the layout effect runs
159
+ // before the expansion has propagated to the store, then retries on next render.
160
+ /* istanbul ignore next -- async timing guard: expansion not yet propagated to store */
150
161
  if (parentId && !expanded.has(parentId))
151
162
  return;
152
163
  }
153
164
  // If node is set to expand
154
165
  else {
166
+ /* istanbul ignore next -- async timing guard: node expansion not yet propagated */
155
167
  if (!expanded.has(queuedScrollToNodeParams.current.nodeId))
156
168
  return;
157
169
  }
@@ -177,6 +189,7 @@ function _innerScrollToNodeHandler<ID>(
177
189
  viewPosition
178
190
  });
179
191
  } else {
192
+ /* istanbul ignore next -- __DEV__ is false in test/production */
180
193
  if (__DEV__) {
181
194
  console.info("Cannot find the item of the mentioned id to scroll in the rendered tree view list data!");
182
195
  }
@@ -193,8 +206,8 @@ function _innerScrollToNodeHandler<ID>(
193
206
  ////////////////////////////// Handle Initial Scroll /////////////////////////////
194
207
  /* On first render, if an initial scroll target is provided, determine its index.
195
208
  This is done only once. */
196
- const initialScrollDone = React.useRef(false);
197
- React.useLayoutEffect(() => {
209
+ const initialScrollDone = useRef(false);
210
+ useLayoutEffect(() => {
198
211
  if (initialScrollDone.current) return;
199
212
 
200
213
  const index = flattenedFilteredNodes.findIndex(
@@ -209,14 +222,4 @@ function _innerScrollToNodeHandler<ID>(
209
222
  // eslint-disable-next-line react-hooks/exhaustive-deps
210
223
  }, [flattenedFilteredNodes, initialScrollNodeID]);
211
224
  /////////////////////////////////////////////////////////////////////////////////
212
-
213
- return null;
214
225
  }
215
-
216
- const _ScrollToNodeHandler = React.forwardRef(_innerScrollToNodeHandler) as <ID>(
217
- props: Props<ID> & { ref?: React.ForwardedRef<ScrollToNodeHandlerRef<ID>>; }
218
- ) => ReturnType<typeof _innerScrollToNodeHandler>;
219
-
220
- export const ScrollToNodeHandler = typedMemo<
221
- typeof _ScrollToNodeHandler
222
- >(_ScrollToNodeHandler);
package/src/index.tsx CHANGED
@@ -9,20 +9,25 @@ import type {
9
9
  CheckboxValueType,
10
10
  BuiltInCheckBoxViewStyleProps,
11
11
  SelectionPropagation,
12
+ DragAndDropOptions,
12
13
  DragDropCustomizations,
13
14
  DragOverlayStyleProps,
14
15
  DragOverlayComponentProps,
15
16
  DropIndicatorStyleProps,
16
17
  DropIndicatorComponentProps,
18
+ DragHandleProps,
17
19
  } from "./types/treeView.types";
18
20
  import type {
21
+ DragCancelEvent,
19
22
  DragEndEvent,
23
+ DragStartEvent,
20
24
  DropPosition
21
25
  } from "./types/dragDrop.types";
22
26
 
23
27
  export * from "./TreeView";
24
28
  export * from "./components/CheckboxView";
25
29
  export { moveTreeNode } from "./helpers/moveTreeNode.helper";
30
+ export { deleteTreeViewStore } from "./store/treeView.store";
26
31
 
27
32
  export type {
28
33
  TreeNode,
@@ -35,11 +40,15 @@ export type {
35
40
  CheckboxValueType,
36
41
  BuiltInCheckBoxViewStyleProps,
37
42
  SelectionPropagation,
43
+ DragAndDropOptions,
44
+ DragCancelEvent,
38
45
  DragEndEvent,
46
+ DragStartEvent,
39
47
  DropPosition,
40
48
  DragDropCustomizations,
41
49
  DragOverlayStyleProps,
42
50
  DragOverlayComponentProps,
43
51
  DropIndicatorStyleProps,
44
52
  DropIndicatorComponentProps,
53
+ DragHandleProps,
45
54
  };
package/src/jest.setup.ts CHANGED
@@ -1 +1,14 @@
1
- import "@testing-library/jest-native/extend-expect";
1
+ import "@testing-library/react-native/extend-expect";
2
+ import { configure } from "@testing-library/react-native";
3
+
4
+ // Skip host component auto-detection which fails in RN 0.78+ jest environment.
5
+ // The type definition doesn't include hostComponentNames yet, but it works at runtime.
6
+ (configure as any)({
7
+ hostComponentNames: {
8
+ text: "Text",
9
+ textInput: "TextInput",
10
+ switch: "RCTSwitch",
11
+ scrollView: "RCTScrollView",
12
+ modal: "Modal",
13
+ },
14
+ });
@@ -1,6 +1,7 @@
1
+ import { create, type StoreApi, type UseBoundStore } from "zustand";
2
+
1
3
  import type { SelectionPropagation, TreeNode } from "../types/treeView.types";
2
4
  import type { DropPosition } from "../types/dragDrop.types";
3
- import { create, type StoreApi, type UseBoundStore } from "zustand";
4
5
 
5
6
  export type TreeViewState<ID> = {
6
7
  // Store ids of checked tree nodes
@@ -153,6 +154,10 @@ export function getTreeViewStore<ID>(id: string): UseBoundStore<StoreApi<TreeVie
153
154
  return typedStore<ID>().get(id)!;
154
155
  }
155
156
 
157
+ export function deleteTreeViewStore(id: string) {
158
+ treeViewStores.delete(id);
159
+ }
160
+
156
161
  export function useTreeViewStore<ID = string>(id: string) {
157
162
  return getTreeViewStore<ID>(id);
158
163
  }
@@ -3,6 +3,18 @@ import type { TreeNode } from "./treeView.types";
3
3
  /** Where a node is dropped relative to the target: as a sibling above/below, or as a child inside */
4
4
  export type DropPosition = "above" | "below" | "inside";
5
5
 
6
+ /** Event payload passed to the onDragStart callback when a drag begins */
7
+ export interface DragStartEvent<ID = string> {
8
+ /** The id of the node being dragged */
9
+ draggedNodeId: ID;
10
+ }
11
+
12
+ /** Event payload passed to the onDragCancel callback when a drag is cancelled without a drop */
13
+ export interface DragCancelEvent<ID = string> {
14
+ /** The id of the node that was being dragged */
15
+ draggedNodeId: ID;
16
+ }
17
+
6
18
  /** Event payload passed to the onDragEnd callback after a successful drop */
7
19
  export interface DragEndEvent<ID = string> {
8
20
  /** The id of the node that was dragged */
@@ -1,3 +1,4 @@
1
+ import { ComponentType, RefObject } from "react";
1
2
  import type {
2
3
  StyleProp,
3
4
  TextProps,
@@ -8,11 +9,11 @@ import type { FlashListProps } from "@shopify/flash-list";
8
9
  import type {
9
10
  ScrollToNodeHandlerRef,
10
11
  ScrollToNodeParams
11
- } from "../handlers/ScrollToNodeHandler";
12
+ } from "../hooks/useScrollToNode";
12
13
  import type {
13
14
  CheckboxProps as _CheckboxProps
14
15
  } from "@futurejj/react-native-checkbox";
15
- import type { DragEndEvent, DropPosition } from "./dragDrop.types";
16
+ import type { DragCancelEvent, DragEndEvent, DragStartEvent, DropPosition } from "./dragDrop.types";
16
17
 
17
18
  /** The tri-state value of a checkbox: checked, unchecked, or indeterminate */
18
19
  export type CheckboxValueType = boolean | "indeterminate";
@@ -66,11 +67,27 @@ export interface NodeRowProps<ID = string> {
66
67
  onExpand: () => void;
67
68
 
68
69
  /** Whether this node is an invalid drop target during a drag operation */
69
- isDragTarget?: boolean;
70
+ isInvalidDropTarget?: boolean;
71
+ /** Whether this node is the current valid drop target */
72
+ isDropTarget?: boolean;
73
+ /** The drop position if this node is the current drop target */
74
+ dropPosition?: DropPosition;
70
75
  /** Whether a drag operation is currently in progress */
71
76
  isDragging?: boolean;
72
77
  /** Whether this node is the one being dragged */
73
78
  isDraggedNode?: boolean;
79
+
80
+ /** Props to spread on a drag handle element. Attach to a specific View to
81
+ * make only that area initiate drag, or spread on the root for whole-row drag.
82
+ * Only present when drag-and-drop is enabled. */
83
+ dragHandleProps?: DragHandleProps;
84
+ }
85
+
86
+ /** Touch handlers to spread on a drag handle element within a custom node row */
87
+ export interface DragHandleProps {
88
+ onTouchStart: (e: any) => void;
89
+ onTouchEnd: () => void;
90
+ onTouchCancel: () => void;
74
91
  }
75
92
 
76
93
  /** Customization options for tree item appearance and behavior */
@@ -82,14 +99,14 @@ export interface TreeItemCustomizations<ID> {
82
99
  indentationMultiplier?: number;
83
100
 
84
101
  /** Custom checkbox component replacing the built-in checkbox */
85
- CheckboxComponent?: React.ComponentType<CheckBoxViewProps>;
102
+ CheckboxComponent?: ComponentType<CheckBoxViewProps>;
86
103
  /** Custom expand/collapse icon component */
87
- ExpandCollapseIconComponent?: React.ComponentType<ExpandIconProps>;
104
+ ExpandCollapseIconComponent?: ComponentType<ExpandIconProps>;
88
105
  /** Custom touchable component wrapping the expand/collapse icon */
89
- ExpandCollapseTouchableComponent?: React.ComponentType<TouchableOpacityProps>;
106
+ ExpandCollapseTouchableComponent?: ComponentType<TouchableOpacityProps>;
90
107
 
91
108
  /** Fully custom node row component replacing the entire built-in row */
92
- CustomNodeRowComponent?: React.ComponentType<NodeRowProps<ID>>;
109
+ CustomNodeRowComponent?: ComponentType<NodeRowProps<ID>>;
93
110
  }
94
111
 
95
112
  /** Internal props for a single node in the list (extends TreeItemCustomizations) */
@@ -116,43 +133,63 @@ export interface NodeProps<ID> extends TreeItemCustomizations<ID> {
116
133
  ) => void;
117
134
  /** Callback when a touch ends on this node */
118
135
  onNodeTouchEnd?: () => void;
119
- /** Long press duration in ms to start drag */
120
- longPressDuration?: number;
121
136
  /** Callback reporting this node's measured height */
122
137
  onItemLayout?: (height: number) => void;
123
138
  /** Customizations for drag-and-drop visuals */
124
139
  dragDropCustomizations?: DragDropCustomizations<ID>;
125
140
  }
126
141
 
142
+ /** Configuration options for drag-and-drop reordering */
143
+ export interface DragAndDropOptions<ID = string> {
144
+ /** Enable drag-and-drop reordering. Default: true (when dragAndDrop is provided) */
145
+ enabled?: boolean;
146
+ /** Callback fired when a drag operation begins */
147
+ onDragStart?: (event: DragStartEvent<ID>) => void;
148
+ /** Callback fired after a node is successfully dropped at a new position */
149
+ onDragEnd?: (event: DragEndEvent<ID>) => void;
150
+ /** Callback fired when a drag is cancelled without a successful drop */
151
+ onDragCancel?: (event: DragCancelEvent<ID>) => void;
152
+ /** Long press duration in ms to start drag. Default: 400 */
153
+ longPressDuration?: number;
154
+ /** Distance from edge (px) to trigger auto-scroll during drag. Default: 60 */
155
+ autoScrollThreshold?: number;
156
+ /** Speed multiplier for auto-scroll during drag. Default: 1.0 */
157
+ autoScrollSpeed?: number;
158
+ /** Offset of the dragged overlay from the finger, in item-height units. Default: -4 (four items above finger) */
159
+ dragOverlayOffset?: number;
160
+ /** Delay in ms before auto-expanding a collapsed node during drag hover. Default: 800 */
161
+ autoExpandDelay?: number;
162
+ /** Customizations for drag-and-drop visuals (overlay, indicator, opacity) */
163
+ customizations?: DragDropCustomizations<ID>;
164
+
165
+ /** Callback to determine if a node can be dropped on a specific target.
166
+ * Return false to grey out the target and suppress the drop indicator. */
167
+ canDrop?: (draggedNode: TreeNode<ID>, targetNode: TreeNode<ID>, position: DropPosition) => boolean;
168
+ /** Maximum nesting depth allowed. Drops that would exceed this depth are suppressed. */
169
+ maxDepth?: number;
170
+ /** Callback to determine if a node can accept children.
171
+ * Return false to suppress the "inside" drop zone for that node. */
172
+ canNodeHaveChildren?: (node: TreeNode<ID>) => boolean;
173
+ /** Callback to determine if a node can be dragged.
174
+ * Return false to prevent dragging this node. Default: all nodes are draggable. */
175
+ canDrag?: (node: TreeNode<ID>) => boolean;
176
+ }
177
+
127
178
  /** Props for the NodeList component that renders the flattened tree */
128
179
  export interface NodeListProps<ID> extends TreeItemCustomizations<ID> {
129
180
  /** Additional props passed to the underlying FlashList */
130
181
  treeFlashListProps?: TreeFlatListProps;
131
182
 
132
183
  /** Ref for programmatic scroll-to-node functionality */
133
- scrollToNodeHandlerRef: React.RefObject<ScrollToNodeHandlerRef<ID>>;
184
+ scrollToNodeHandlerRef: RefObject<ScrollToNodeHandlerRef<ID>>;
134
185
  /** Node ID to scroll to on initial render */
135
186
  initialScrollNodeID?: ID;
136
187
 
137
188
  /** Internal store identifier */
138
189
  storeId: string;
139
190
 
140
- /** Enable drag-and-drop reordering */
141
- dragEnabled?: boolean;
142
- /** Callback fired after a node is dropped at a new position */
143
- onDragEnd?: (event: DragEndEvent<ID>) => void;
144
- /** Long press duration in ms to start drag. Default: 400 */
145
- longPressDuration?: number;
146
- /** Distance from edge (px) to trigger auto-scroll during drag. Default: 60 */
147
- autoScrollThreshold?: number;
148
- /** Speed multiplier for auto-scroll during drag. Default: 1.0 */
149
- autoScrollSpeed?: number;
150
- /** Offset of the dragged overlay from the finger, in item-height units. Default: -1 (one item above finger) */
151
- dragOverlayOffset?: number;
152
- /** Delay in ms before auto-expanding a collapsed node during drag hover. Default: 800 */
153
- autoExpandDelay?: number;
154
- /** Customizations for drag-and-drop visuals (overlay, indicator, opacity) */
155
- dragDropCustomizations?: DragDropCustomizations<ID>;
191
+ /** Drag-and-drop configuration */
192
+ dragAndDrop?: DragAndDropOptions<ID>;
156
193
  }
157
194
 
158
195
  /** Props for the TreeView component */
@@ -176,20 +213,8 @@ export interface TreeViewProps<ID = string> extends Omit<
176
213
  /** Controls whether checking a node propagates to its children and/or parents */
177
214
  selectionPropagation?: SelectionPropagation;
178
215
 
179
- /** Enable drag-and-drop reordering */
180
- dragEnabled?: boolean;
181
- /** Callback fired after a node is dropped at a new position */
182
- onDragEnd?: (event: DragEndEvent<ID>) => void;
183
- /** Long press duration in ms to start drag. Default: 400 */
184
- longPressDuration?: number;
185
- /** Distance from edge (px) to trigger auto-scroll during drag. Default: 60 */
186
- autoScrollThreshold?: number;
187
- /** Speed multiplier for auto-scroll during drag. Default: 1.0 */
188
- autoScrollSpeed?: number;
189
- /** Offset of the dragged overlay from the finger, in item-height units. Default: -1 (one item above finger) */
190
- dragOverlayOffset?: number;
191
- /** Delay in ms before auto-expanding a collapsed node during drag hover. Default: 800 */
192
- autoExpandDelay?: number;
216
+ /** Drag-and-drop configuration */
217
+ dragAndDrop?: DragAndDropOptions<ID>;
193
218
  }
194
219
 
195
220
  type CheckboxProps = Omit<_CheckboxProps, "onPress" | "status">;
@@ -261,6 +286,10 @@ export interface TreeViewRef<ID = string> {
261
286
 
262
287
  /** Get a map of child node IDs to their parent node IDs */
263
288
  getChildToParentMap: () => Map<ID, ID>;
289
+
290
+ /** Programmatically move a node to a new position in the tree.
291
+ * Works like a drag-and-drop but without user interaction. */
292
+ moveNode: (nodeId: ID, targetNodeId: ID, position: DropPosition) => void;
264
293
  }
265
294
 
266
295
  /** Controls how checkbox selection propagates through the tree hierarchy */
@@ -315,16 +344,18 @@ export interface DragOverlayStyleProps {
315
344
 
316
345
  /** Combined drag-and-drop customization props */
317
346
  export interface DragDropCustomizations<ID = string> {
318
- /** Opacity applied to the dragged node and its invalid drop targets. Default: 0.3 */
347
+ /** Opacity applied to the node being dragged. Default: 0.3 */
319
348
  draggedNodeOpacity?: number;
349
+ /** Opacity applied to invalid drop targets during drag. Default: 0.3 */
350
+ invalidTargetOpacity?: number;
320
351
  /** Style props for the built-in drop indicator */
321
352
  dropIndicatorStyleProps?: DropIndicatorStyleProps;
322
353
  /** Style props for the drag overlay (lifted node ghost) */
323
354
  dragOverlayStyleProps?: DragOverlayStyleProps;
324
355
  /** Fully custom drop indicator component - replaces the built-in line/highlight */
325
- CustomDropIndicatorComponent?: React.ComponentType<DropIndicatorComponentProps>;
356
+ CustomDropIndicatorComponent?: ComponentType<DropIndicatorComponentProps>;
326
357
  /** Fully custom drag overlay component - replaces the built-in ghost node */
327
- CustomDragOverlayComponent?: React.ComponentType<DragOverlayComponentProps<ID>>;
358
+ CustomDragOverlayComponent?: ComponentType<DragOverlayComponentProps<ID>>;
328
359
  }
329
360
 
330
361
  /** Props passed to a custom drag overlay component */
@@ -333,4 +364,6 @@ export interface DragOverlayComponentProps<ID = string> {
333
364
  node: __FlattenedTreeNode__<ID>;
334
365
  /** The nesting level of the dragged node */
335
366
  level: number;
367
+ /** The current checkbox value of the dragged node */
368
+ checkedValue: CheckboxValueType;
336
369
  }
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import { memo } from "react";
2
2
 
3
- /** wrapper for React.memo that works with generic components. */
4
- export const typedMemo: <T>(c: T) => T = React.memo;
3
+ /** wrapper for memo that works with generic components. */
4
+ export const typedMemo: <T>(c: T) => T = memo;
@@ -1,4 +1,10 @@
1
- import React from "react";
1
+ import {
2
+ useEffect,
3
+ useMemo,
4
+ useRef,
5
+ type DependencyList,
6
+ type EffectCallback,
7
+ } from "react";
2
8
  import { fastIsEqual } from "fast-is-equal";
3
9
 
4
10
  /**
@@ -9,17 +15,17 @@ import { fastIsEqual } from "fast-is-equal";
9
15
  * @param deps The dependencies array to compare deeply.
10
16
  */
11
17
  export default function useDeepCompareEffect(
12
- effect: React.EffectCallback,
13
- deps: React.DependencyList
18
+ effect: EffectCallback,
19
+ deps: DependencyList
14
20
  ) {
15
21
  // Ref to track if it's the first render
16
- const firstRenderRef = React.useRef<boolean>(true);
22
+ const firstRenderRef = useRef<boolean>(true);
17
23
 
18
24
  // Memoized dependencies to avoid redundant `isEqual` checks
19
- const memoizedDependencies = React.useMemo(() => deps, [deps]);
25
+ const memoizedDependencies = useMemo(() => deps, [deps]);
20
26
 
21
27
  // Ref to store the previous dependencies
22
- const dependenciesRef = React.useRef<React.DependencyList>(memoizedDependencies);
28
+ const dependenciesRef = useRef<DependencyList>(memoizedDependencies);
23
29
 
24
30
  // Check for dependency changes
25
31
  const dependenciesChanged = !fastIsEqual(
@@ -30,7 +36,7 @@ export default function useDeepCompareEffect(
30
36
  dependenciesRef.current = memoizedDependencies;
31
37
  }
32
38
 
33
- React.useEffect(() => {
39
+ useEffect(() => {
34
40
  if (firstRenderRef.current) {
35
41
  firstRenderRef.current = false;
36
42
  }
@@ -1 +0,0 @@
1
- {"version":3,"names":["React","expandNodes","useTreeViewStore","useShallow","typedMemo","fastIsEqual","ExpandQueueAction","_innerScrollToNodeHandler","props","ref","storeId","flashListRef","flattenedFilteredNodes","setInitialScrollIndex","initialScrollNodeID","expanded","childToParentMap","state","useImperativeHandle","scrollToNodeID","params","queuedScrollToNodeParams","current","setExpandAndScrollToNodeQueue","EXPANDED","nodeId","expandScrolledNode","useRef","expandAndScrollToNodeQueue","useState","latestFlattenedFilteredNodesRef","useEffect","prevQueue","includes","RENDERED","useLayoutEffect","parentId","has","get","animated","viewOffset","viewPosition","scrollToItem","index","findIndex","item","id","scrollToIndex","__DEV__","console","info","initialScrollDone","_ScrollToNodeHandler","forwardRef","ScrollToNodeHandler"],"sourceRoot":"../../../src","sources":["handlers/ScrollToNodeHandler.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAOA,KAAK,MAAM,OAAO;AACzB,SAASC,WAAW,QAAQ,qCAAkC;AAC9D,SAASC,gBAAgB,QAAQ,4BAAyB;AAC1D,SAASC,UAAU,QAAQ,uBAAuB;AAElD,SAASC,SAAS,QAAQ,uBAAoB;AAC9C,SAASC,WAAW,QAAQ,eAAe;AAmB3C;AAAA,IACKC,iBAAiB,0BAAjBA,iBAAiB;EAAjBA,iBAAiB,CAAjBA,iBAAiB;EAAjBA,iBAAiB,CAAjBA,iBAAiB;EAAA,OAAjBA,iBAAiB;AAAA,EAAjBA,iBAAiB;AAStB,SAASC,yBAAyBA,CAChCC,KAAgB,EAChBC,GAAmD,EACnD;EACA,MAAM;IACJC,OAAO;IACPC,YAAY;IACZC,sBAAsB;IACtBC,qBAAqB;IACrBC;EACF,CAAC,GAAGN,KAAK;EAET,MAAM;IAAEO,QAAQ;IAAEC;EAAiB,CAAC,GAAGd,gBAAgB,CAAKQ,OAAO,CAAC,CAACP,UAAU,CAC7Ec,KAAK,KAAK;IACRF,QAAQ,EAAEE,KAAK,CAACF,QAAQ;IACxBC,gBAAgB,EAAEC,KAAK,CAACD;EAC1B,CAAC,CACH,CAAC,CAAC;EAEFhB,KAAK,CAACkB,mBAAmB,CAACT,GAAG,EAAE,OAAO;IACpCU,cAAc,EAAGC,MAA8B,IAAK;MAClDC,wBAAwB,CAACC,OAAO,GAAGF,MAAM;MACzC;MACAG,6BAA6B,CAAC,CAACjB,iBAAiB,CAACkB,QAAQ,CAAC,CAAC;MAC3D;MACAvB,WAAW,CACTS,OAAO,EACP,CAACW,wBAAwB,CAACC,OAAO,CAACG,MAAM,CAAC,EACzC,CAACJ,wBAAwB,CAACC,OAAO,CAACI,kBACpC,CAAC;IACH;EACF,CAAC,CAAC,EAAE,CAAChB,OAAO,CAAC,CAAC;;EAEd;EACA,MAAMW,wBAAwB,GAAGrB,KAAK,CAAC2B,MAAM,CAAgC,IAAI,CAAC;;EAElF;EACA,MAAM,CAACC,0BAA0B,EAAEL,6BAA6B,CAAC,GAC7DvB,KAAK,CAAC6B,QAAQ,CAAsB,EAAE,CAAC;EAE3C,MAAMC,+BAA+B,GAAG9B,KAAK,CAAC2B,MAAM,CAACf,sBAAsB,CAAC;;EAE5E;AACF;EACEZ,KAAK,CAAC+B,SAAS,CAAC,MAAM;IACpBR,6BAA6B,CAACS,SAAS,IAAI;MACzC,IAAIA,SAAS,CAACC,QAAQ,CAAC3B,iBAAiB,CAACkB,QAAQ,CAAC,EAAE;QAClDM,+BAA+B,CAACR,OAAO,GAAGV,sBAAsB;QAChE,OAAO,CACLN,iBAAiB,CAACkB,QAAQ,EAC1BlB,iBAAiB,CAAC4B,QAAQ,CAC3B;MACH,CAAC,MAAM;QACL,OAAOF,SAAS;MAClB;IACF,CAAC,CAAC;EACJ,CAAC,EAAE,CAACpB,sBAAsB,CAAC,CAAC;;EAE5B;AACF;EACEZ,KAAK,CAACmC,eAAe,CAAC,MAAM;IAC1B,IAAId,wBAAwB,CAACC,OAAO,KAAK,IAAI,EAC3C;IAEF,IAAI,CAACjB,WAAW,CACduB,0BAA0B,EAC1B,CAACtB,iBAAiB,CAACkB,QAAQ,EAAElB,iBAAiB,CAAC4B,QAAQ,CACzD,CAAC,EAAE;MACD;IACF;;IAEA;IACA,IAAI,CAACb,wBAAwB,CAACC,OAAO,CAACI,kBAAkB,EAAE;MACxD,IAAIU,QAAwB;MAC5B;MACA,IAAIpB,gBAAgB,CAACqB,GAAG,CAAChB,wBAAwB,CAACC,OAAO,CAACG,MAAM,CAAC,EAAE;QACjEW,QAAQ,GAAGpB,gBAAgB,CAACsB,GAAG,CAACjB,wBAAwB,CAACC,OAAO,CAACG,MAAM,CAAO;MAChF;;MAEA;MACA,IAAIW,QAAQ,IAAI,CAACrB,QAAQ,CAACsB,GAAG,CAACD,QAAQ,CAAC,EACrC;IACJ;IACA;IAAA,KACK;MACH,IAAI,CAACrB,QAAQ,CAACsB,GAAG,CAAChB,wBAAwB,CAACC,OAAO,CAACG,MAAM,CAAC,EACxD;IACJ;IAEA,MAAM;MACJA,MAAM;MACNc,QAAQ;MACRC,UAAU;MACVC;IACF,CAAC,GAAGpB,wBAAwB,CAACC,OAAQ;IAErC,SAASoB,YAAYA,CAAA,EAAG;MACtB,MAAMC,KAAK,GAAGb,+BAA+B,CAACR,OAAO,CAACsB,SAAS,CAC7DC,IAAI,IAAIA,IAAI,CAACC,EAAE,KAAKrB,MACtB,CAAC;MAED,IAAIkB,KAAK,KAAK,CAAC,CAAC,IAAIhC,YAAY,CAACW,OAAO,EAAE;QACxC;QACAX,YAAY,CAACW,OAAO,CAACyB,aAAa,CAAC;UACjCJ,KAAK;UACLJ,QAAQ;UACRC,UAAU;UACVC;QACF,CAAC,CAAC;MACJ,CAAC,MAAM;QACL,IAAIO,OAAO,EAAE;UACXC,OAAO,CAACC,IAAI,CAAC,yFAAyF,CAAC;QACzG;MACF;;MAEA;MACA7B,wBAAwB,CAACC,OAAO,GAAG,IAAI;MACvCC,6BAA6B,CAAC,EAAE,CAAC;IACnC;IAEAmB,YAAY,CAAC,CAAC;EAChB,CAAC,EAAE,CAAC1B,gBAAgB,EAAED,QAAQ,EAAEJ,YAAY,EAAEiB,0BAA0B,CAAC,CAAC;;EAE1E;EACA;AACF;EACE,MAAMuB,iBAAiB,GAAGnD,KAAK,CAAC2B,MAAM,CAAC,KAAK,CAAC;EAC7C3B,KAAK,CAACmC,eAAe,CAAC,MAAM;IAC1B,IAAIgB,iBAAiB,CAAC7B,OAAO,EAAE;IAE/B,MAAMqB,KAAK,GAAG/B,sBAAsB,CAACgC,SAAS,CAC5CC,IAAI,IAAIA,IAAI,CAACC,EAAE,KAAKhC,mBACtB,CAAC;IAEDD,qBAAqB,CAAC8B,KAAK,CAAC;IAE5B,IAAIA,KAAK,KAAK,CAAC,CAAC,EAAE;MAChBQ,iBAAiB,CAAC7B,OAAO,GAAG,IAAI;IAClC;IACA;EACF,CAAC,EAAE,CAACV,sBAAsB,EAAEE,mBAAmB,CAAC,CAAC;EACjD;;EAEA,OAAO,IAAI;AACb;AAEA,MAAMsC,oBAAoB,gBAAGpD,KAAK,CAACqD,UAAU,CAAC9C,yBAAyB,CAEtB;AAEjD,OAAO,MAAM+C,mBAAmB,GAAGlD,SAAS,CAE1CgD,oBAAoB,CAAC","ignoreList":[]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ScrollToNodeHandler.d.ts","sourceRoot":"","sources":["../../../../src/handlers/ScrollToNodeHandler.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAIrE,UAAU,KAAK,CAAC,EAAE;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC1C,sBAAsB,EAAE,qBAAqB,CAAC,EAAE,CAAC,EAAE,CAAC;IACpD,qBAAqB,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IACpE,mBAAmB,EAAE,EAAE,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,kBAAkB,CAAC,EAAE;IACpC,MAAM,EAAE,EAAE,CAAC;IACX,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAE7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAQD,MAAM,WAAW,sBAAsB,CAAC,EAAE;IACxC,cAAc,EAAE,CAAC,MAAM,EAAE,kBAAkB,CAAC,EAAE,CAAC,KAAK,IAAI,CAAC;CAC1D;AAED,iBAAS,yBAAyB,CAAC,EAAE,EACnC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,EAChB,GAAG,EAAE,KAAK,CAAC,YAAY,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,QA8IpD;AAMD,eAAO,MAAM,mBAAmB,GAJ6C,EAAE,SACtE,KAAK,CAAC,EAAE,CAAC,GAAG;IAAE,GAAG,CAAC,EAAE,KAAK,CAAC,YAAY,CAAC,sBAAsB,CAAC,EAAE,CAAC,CAAC,CAAC;CAAE,KACzE,UAAU,CAAC,OAAO,yBAAyB,CAIzB,CAAC"}