react-arborist 1.2.0 → 2.0.0-rc.1

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 (118) hide show
  1. package/README.md +606 -144
  2. package/dist/components/{drop-cursor.d.ts → cursor.d.ts} +0 -0
  3. package/dist/components/default-container.d.ts +7 -0
  4. package/dist/components/default-cursor.d.ts +3 -0
  5. package/dist/components/default-drag-preview.d.ts +3 -0
  6. package/dist/components/default-node.d.ts +4 -0
  7. package/dist/components/default-row.d.ts +4 -0
  8. package/dist/components/drag-preview-container.d.ts +2 -0
  9. package/dist/components/list-inner-element.d.ts +2 -0
  10. package/dist/components/provider.d.ts +11 -0
  11. package/dist/components/row-container.d.ts +8 -0
  12. package/dist/components/tree-container.d.ts +2 -0
  13. package/dist/components/tree.d.ts +5 -4
  14. package/dist/context.d.ts +20 -2
  15. package/dist/data/create-index.d.ts +5 -0
  16. package/dist/data/create-list.d.ts +4 -0
  17. package/dist/data/create-root.d.ts +5 -0
  18. package/dist/data/simple-tree.d.ts +43 -0
  19. package/dist/dnd/compute-drop.d.ts +4 -4
  20. package/dist/dnd/drag-hook.d.ts +3 -4
  21. package/dist/dnd/drop-hook.d.ts +2 -3
  22. package/dist/hooks/use-fresh-node.d.ts +2 -0
  23. package/dist/hooks/use-simple-tree.d.ts +13 -0
  24. package/dist/hooks/use-validated-props.d.ts +3 -0
  25. package/dist/index.d.ts +8 -4
  26. package/dist/index.js +1948 -973
  27. package/dist/index.js.map +1 -1
  28. package/dist/interfaces/node-api.d.ts +67 -0
  29. package/dist/interfaces/tree-api.d.ts +113 -0
  30. package/dist/module.js +1935 -979
  31. package/dist/module.js.map +1 -1
  32. package/dist/state/dnd-slice.d.ts +20 -0
  33. package/dist/state/drag-slice.d.ts +7 -0
  34. package/dist/state/edit-slice.d.ts +8 -0
  35. package/dist/state/focus-slice.d.ts +12 -0
  36. package/dist/state/initial.d.ts +3 -0
  37. package/dist/state/open-slice.d.ts +30 -0
  38. package/dist/state/root-reducer.d.ts +13 -0
  39. package/dist/state/selection-slice.d.ts +36 -0
  40. package/dist/types/dnd.d.ts +9 -0
  41. package/dist/types/handlers.d.ts +24 -0
  42. package/dist/types/renderers.d.ts +30 -0
  43. package/dist/types/state.d.ts +2 -0
  44. package/dist/types/tree-props.d.ts +44 -0
  45. package/dist/types/utils.d.ts +21 -0
  46. package/dist/utils.d.ts +15 -6
  47. package/package.json +11 -7
  48. package/src/components/cursor.tsx +15 -0
  49. package/src/components/default-container.tsx +238 -0
  50. package/src/components/{default-drop-cursor.tsx → default-cursor.tsx} +9 -8
  51. package/src/components/{preview.tsx → default-drag-preview.tsx} +25 -41
  52. package/src/components/default-node.tsx +50 -0
  53. package/src/components/default-row.tsx +21 -0
  54. package/src/components/drag-preview-container.tsx +26 -0
  55. package/src/components/list-inner-element.tsx +22 -0
  56. package/src/components/list-outer-element.tsx +26 -15
  57. package/src/components/provider.tsx +97 -0
  58. package/src/components/row-container.tsx +82 -0
  59. package/src/components/tree-container.tsx +13 -0
  60. package/src/components/tree.tsx +16 -44
  61. package/src/context.ts +36 -0
  62. package/src/data/create-index.ts +9 -0
  63. package/src/data/create-list.ts +56 -0
  64. package/src/data/create-root.ts +54 -0
  65. package/src/data/simple-tree.ts +103 -0
  66. package/src/dnd/compute-drop.ts +16 -16
  67. package/src/dnd/drag-hook.ts +25 -19
  68. package/src/dnd/drop-hook.ts +31 -17
  69. package/src/dnd/outer-drop-hook.ts +1 -1
  70. package/src/hooks/use-fresh-node.ts +16 -0
  71. package/src/hooks/use-simple-tree.ts +55 -0
  72. package/src/hooks/use-validated-props.ts +35 -0
  73. package/src/index.ts +9 -19
  74. package/src/interfaces/node-api.ts +187 -0
  75. package/src/interfaces/tree-api.ts +557 -0
  76. package/src/state/dnd-slice.ts +36 -0
  77. package/src/state/drag-slice.ts +31 -0
  78. package/src/state/edit-slice.ts +19 -0
  79. package/src/state/focus-slice.ts +28 -0
  80. package/src/state/initial.ts +14 -0
  81. package/src/state/open-slice.ts +53 -0
  82. package/src/state/root-reducer.ts +21 -0
  83. package/src/state/selection-slice.ts +75 -0
  84. package/src/types/dnd.ts +10 -0
  85. package/src/types/handlers.ts +24 -0
  86. package/src/types/renderers.ts +34 -0
  87. package/src/types/state.ts +3 -0
  88. package/src/types/tree-props.ts +64 -0
  89. package/src/types/utils.ts +26 -0
  90. package/src/utils.ts +125 -11
  91. package/tsconfig.json +1 -1
  92. package/dist/components/default-drop-cursor.d.ts +0 -3
  93. package/dist/components/list.d.ts +0 -4
  94. package/dist/components/preview.d.ts +0 -2
  95. package/dist/components/row.d.ts +0 -8
  96. package/dist/data/enrich-tree.d.ts +0 -2
  97. package/dist/data/flatten-tree.d.ts +0 -2
  98. package/dist/provider.d.ts +0 -3
  99. package/dist/reducer.d.ts +0 -46
  100. package/dist/selection/range.d.ts +0 -13
  101. package/dist/selection/selection-hook.d.ts +0 -4
  102. package/dist/selection/selection.d.ts +0 -33
  103. package/dist/tree-api.d.ts +0 -50
  104. package/dist/types.d.ts +0 -122
  105. package/src/components/drop-cursor.tsx +0 -12
  106. package/src/components/list.tsx +0 -25
  107. package/src/components/row.tsx +0 -112
  108. package/src/context.tsx +0 -13
  109. package/src/data/enrich-tree.ts +0 -74
  110. package/src/data/flatten-tree.ts +0 -17
  111. package/src/provider.tsx +0 -41
  112. package/src/reducer.ts +0 -161
  113. package/src/selection/range.ts +0 -41
  114. package/src/selection/selection-hook.ts +0 -25
  115. package/src/selection/selection.test.ts +0 -111
  116. package/src/selection/selection.ts +0 -186
  117. package/src/tree-api.ts +0 -230
  118. package/src/types.ts +0 -148
@@ -0,0 +1,54 @@
1
+ import { IdObj } from "../types/utils";
2
+ import { NodeApi } from "../interfaces/node-api";
3
+ import { TreeApi } from "../interfaces/tree-api";
4
+
5
+ export const ROOT_ID = "__REACT_ARBORIST_INTERNAL_ROOT__";
6
+
7
+ export function createRoot<T extends IdObj>(tree: TreeApi<T>): NodeApi<T> {
8
+ function visitSelfAndChildren(
9
+ data: T,
10
+ level: number,
11
+ parent: NodeApi<T> | null
12
+ ) {
13
+ const id = tree.accessId(data);
14
+ const node = new NodeApi<T>({
15
+ tree,
16
+ data,
17
+ level,
18
+ parent,
19
+ id,
20
+ children: null,
21
+ isDraggable: tree.isDraggable(data),
22
+ isDroppable: tree.isDroppable(data),
23
+ rowIndex: null,
24
+ });
25
+ const children = tree.accessChildren(data);
26
+ if (children) {
27
+ node.children = children.map((child: T) =>
28
+ visitSelfAndChildren(child, level + 1, node)
29
+ );
30
+ }
31
+ return node;
32
+ }
33
+
34
+ const root = new NodeApi<T>({
35
+ tree,
36
+ id: ROOT_ID,
37
+ // @ts-ignore
38
+ data: { id: ROOT_ID },
39
+ level: -1,
40
+ parent: null,
41
+ children: null,
42
+ isDraggable: true,
43
+ isDroppable: true,
44
+ rowIndex: null,
45
+ });
46
+
47
+ const data: T[] = tree.props.data ?? [];
48
+
49
+ root.children = data.map((child) => {
50
+ return visitSelfAndChildren(child, 0, root);
51
+ });
52
+
53
+ return root;
54
+ }
@@ -0,0 +1,103 @@
1
+ type IdObj = { id: string; children?: IdObj[] };
2
+
3
+ export class SimpleTree<T extends IdObj> {
4
+ root: SimpleNode<T>;
5
+ constructor(data: T[]) {
6
+ this.root = createRoot<T>(data);
7
+ }
8
+
9
+ get data() {
10
+ return this.root.children?.map((node) => node.data) ?? [];
11
+ }
12
+
13
+ create(args: { parentId: string | null; index: number; data: T }) {
14
+ const parent = args.parentId ? this.find(args.parentId) : this.root;
15
+ if (!parent) return null;
16
+ parent.addChild(args.data, args.index);
17
+ }
18
+
19
+ move(args: { id: string; parentId: string | null; index: number }) {
20
+ const src = this.find(args.id);
21
+ const parent = args.parentId ? this.find(args.parentId) : this.root;
22
+ if (!src || !parent) return;
23
+ parent.addChild(src.data, args.index);
24
+ src.drop();
25
+ }
26
+
27
+ update(args: { id: string; changes: Partial<T> }) {
28
+ const node = this.find(args.id);
29
+ if (node) node.update(args.changes);
30
+ }
31
+
32
+ drop(args: { id: string }) {
33
+ const node = this.find(args.id);
34
+ if (node) node.drop();
35
+ }
36
+
37
+ find(id: string, node: SimpleNode<T> = this.root): SimpleNode<T> | null {
38
+ if (!node) return null;
39
+ if (node.id === id) return node as SimpleNode<T>;
40
+ if (node.children) {
41
+ for (let child of node.children) {
42
+ const found = this.find(id, child);
43
+ if (found) return found;
44
+ }
45
+ return null;
46
+ }
47
+ return null;
48
+ }
49
+ }
50
+
51
+ function createRoot<T extends IdObj>(data: T[]) {
52
+ const root = new SimpleNode<T>({ id: "ROOT" } as T, null);
53
+ root.children = data.map((d) => createNode(d as T, root));
54
+ return root;
55
+ }
56
+
57
+ function createNode<T extends IdObj>(data: T, parent: SimpleNode<T>) {
58
+ const node = new SimpleNode<T>(data, parent);
59
+ if (data.children)
60
+ node.children = data.children.map((d) => createNode<T>(d as T, node));
61
+ return node;
62
+ }
63
+
64
+ class SimpleNode<T extends IdObj> {
65
+ id: string;
66
+ children?: SimpleNode<T>[];
67
+ constructor(public data: T, public parent: SimpleNode<T> | null) {
68
+ this.id = data.id;
69
+ }
70
+
71
+ hasParent(): this is this & { parent: SimpleNode<T> } {
72
+ return !!this.parent;
73
+ }
74
+
75
+ get childIndex(): number {
76
+ return this.hasParent() ? this.parent.children!.indexOf(this) : -1;
77
+ }
78
+
79
+ addChild(data: T, index: number) {
80
+ const node = createNode(data, this);
81
+ this.children = this.children ?? [];
82
+ this.children.splice(index, 0, node);
83
+ this.data.children = this.data.children ?? [];
84
+ this.data.children.splice(index, 0, data);
85
+ }
86
+
87
+ removeChild(index: number) {
88
+ this.children?.splice(index, 1);
89
+ this.data.children?.splice(index, 1);
90
+ }
91
+
92
+ update(changes: Partial<T>) {
93
+ if (this.hasParent()) {
94
+ const i = this.childIndex;
95
+ this.parent.addChild({ ...this.data, ...changes }, i);
96
+ this.drop();
97
+ }
98
+ }
99
+
100
+ drop() {
101
+ if (this.hasParent()) this.parent.removeChild(this.childIndex);
102
+ }
103
+ }
@@ -1,6 +1,6 @@
1
1
  import { XYCoord } from "react-dnd";
2
- import { Node } from "../types";
3
- import { bound, indexOf, isClosed, isFolder, isItem } from "../utils";
2
+ import { NodeApi } from "../interfaces/node-api";
3
+ import { bound, indexOf, isClosed, isItem } from "../utils";
4
4
  import { DropResult } from "./drop-hook";
5
5
 
6
6
  function measureHover(el: HTMLElement, offset: XYCoord) {
@@ -20,17 +20,17 @@ function measureHover(el: HTMLElement, offset: XYCoord) {
20
20
  type HoverData = ReturnType<typeof measureHover>;
21
21
 
22
22
  function getNodesAroundCursor(
23
- node: Node | null,
24
- prev: Node | null,
25
- next: Node | null,
23
+ node: NodeApi | null,
24
+ prev: NodeApi | null,
25
+ next: NodeApi | null,
26
26
  hover: HoverData
27
- ): [Node | null, Node | null] {
27
+ ): [NodeApi | null, NodeApi | null] {
28
28
  if (!node) {
29
29
  // We're hoving over the empty part of the list, not over an item,
30
30
  // Put the cursor below the last item which is "prev"
31
31
  return [prev, null];
32
32
  }
33
- if (isFolder(node)) {
33
+ if (node.isInternal) {
34
34
  if (hover.atTop) {
35
35
  return [prev, node];
36
36
  } else if (hover.inMiddle) {
@@ -51,15 +51,15 @@ type Args = {
51
51
  element: HTMLElement;
52
52
  offset: XYCoord;
53
53
  indent: number;
54
- node: Node | null;
55
- prevNode: Node | null;
56
- nextNode: Node | null;
54
+ node: NodeApi | null;
55
+ prevNode: NodeApi | null;
56
+ nextNode: NodeApi | null;
57
57
  };
58
58
 
59
59
  function getDropLevel(
60
60
  hovering: HoverData,
61
- aboveCursor: Node | null,
62
- belowCursor: Node | null,
61
+ aboveCursor: NodeApi | null,
62
+ belowCursor: NodeApi | null,
63
63
  indent: number
64
64
  ) {
65
65
  const hoverLevel = Math.round(Math.max(0, hovering.x - indent) / indent);
@@ -78,12 +78,12 @@ function getDropLevel(
78
78
  return bound(hoverLevel, min, max);
79
79
  }
80
80
 
81
- function canDrop(above: Node | null, below: Node | null) {
81
+ function canDrop(above: NodeApi | null, below: NodeApi | null) {
82
82
  if (!above) {
83
83
  return true;
84
84
  }
85
85
 
86
- let n: Node | null = above;
86
+ let n: NodeApi | null = above;
87
87
  if (isClosed(above) && above !== below) n = above.parent;
88
88
 
89
89
  while (n) {
@@ -123,7 +123,7 @@ function highlightCursor(id: string) {
123
123
  };
124
124
  }
125
125
 
126
- function walkUpFrom(node: Node, level: number) {
126
+ function walkUpFrom(node: NodeApi, level: number) {
127
127
  let drop = node;
128
128
  while (drop.parent && drop.level > level) {
129
129
  drop = drop.parent;
@@ -152,7 +152,7 @@ export function computeDrop(args: Args): ComputedDrop {
152
152
  }
153
153
 
154
154
  /* Hovering over the middle of a folder */
155
- if (node && isFolder(node) && hover.inMiddle) {
155
+ if (node && node.isInternal && hover.inMiddle) {
156
156
  return {
157
157
  drop: dropAt(node.id, 0),
158
158
  cursor: highlightCursor(node.id),
@@ -2,37 +2,43 @@ import { useEffect } from "react";
2
2
  import { ConnectDragSource, useDrag } from "react-dnd";
3
3
  import { getEmptyImage } from "react-dnd-html5-backend";
4
4
  import { useTreeApi } from "../context";
5
- import { DragItem, Node } from "../types";
5
+ import { NodeApi } from "../interfaces/node-api";
6
+ import { DragItem } from "../types/dnd";
7
+ import { IdObj } from "../types/utils";
6
8
  import { DropResult } from "./drop-hook";
9
+ import { actions as dnd } from "../state/dnd-slice";
10
+ import { safeRun } from "../utils";
11
+ import { ROOT_ID } from "../data/create-root";
7
12
 
8
- type CollectedProps = { isDragging: boolean };
9
-
10
- export function useDragHook(
11
- node: Node
12
- ): [{ isDragging: boolean }, ConnectDragSource] {
13
+ export function useDragHook<T extends IdObj>(
14
+ node: NodeApi<T>
15
+ ): ConnectDragSource {
13
16
  const tree = useTreeApi();
14
- const ids = tree.getSelectedIds();
15
- const [{ isDragging }, ref, preview] = useDrag<
16
- DragItem,
17
- DropResult,
18
- CollectedProps
19
- >(
17
+ const ids = tree.selectedIds;
18
+ const [_, ref, preview] = useDrag<DragItem, DropResult, void>(
20
19
  () => ({
21
20
  canDrag: () => node.isDraggable,
22
21
  type: "NODE",
23
22
  item: () => ({
24
23
  id: node.id,
25
- dragIds: tree.isSelected(node.rowIndex) ? ids : [node.id],
26
- }),
27
- collect: (m) => ({
28
- isDragging: m.isDragging(),
24
+ dragIds: tree.isSelected(node.id) ? Array.from(ids) : [node.id],
29
25
  }),
26
+ start: () => {
27
+ tree.dispatch(dnd.dragStart(node.id));
28
+ },
30
29
  end: (item, monitor) => {
30
+ tree.dispatch(dnd.dragEnd());
31
31
  tree.hideCursor();
32
32
  const drop = monitor.getDropResult();
33
+ // If they held down meta, we need to create a copy
34
+ // if (drop.dropEffect === "copy")
33
35
  if (drop && drop.parentId) {
34
- tree.onMove(item.dragIds, drop.parentId, drop.index);
35
- tree.onToggle(drop.parentId, true);
36
+ safeRun(tree.props.onMove, {
37
+ dragIds: item.dragIds,
38
+ parentId: drop.parentId === ROOT_ID ? null : drop.parentId,
39
+ index: drop.index,
40
+ });
41
+ tree.open(drop.parentId);
36
42
  }
37
43
  },
38
44
  }),
@@ -43,5 +49,5 @@ export function useDragHook(
43
49
  preview(getEmptyImage());
44
50
  }, [preview]);
45
51
 
46
- return [{ isDragging }, ref];
52
+ return ref;
47
53
  }
@@ -1,8 +1,9 @@
1
1
  import { RefObject } from "react";
2
2
  import { ConnectDropTarget, useDrop } from "react-dnd";
3
3
  import { useTreeApi } from "../context";
4
- import { DragItem, Node } from "../types";
5
- import { isDecendent, isFolder } from "../utils";
4
+ import { NodeApi } from "../interfaces/node-api";
5
+ import { DragItem } from "../types/dnd";
6
+ import { isDecendent } from "../utils";
6
7
  import { computeDrop } from "./compute-drop";
7
8
 
8
9
  export type DropResult = {
@@ -10,23 +11,34 @@ export type DropResult = {
10
11
  index: number;
11
12
  };
12
13
 
13
- export type CollectedProps = undefined;
14
-
15
14
  export function useDropHook(
16
15
  el: RefObject<HTMLElement | null>,
17
- node: Node,
18
- prev: Node | null,
19
- next: Node | null
20
- ): [CollectedProps, ConnectDropTarget] {
16
+ node: NodeApi<any>
17
+ ): ConnectDropTarget {
21
18
  const tree = useTreeApi();
22
- return useDrop<DragItem, DropResult | null, CollectedProps>(
19
+ const [_, dropRef] = useDrop<DragItem, DropResult | null, void>(
23
20
  () => ({
24
21
  accept: "NODE",
25
- canDrop: (item) => {
22
+ canDrop: (item, m) => {
23
+ if (node.tree.isFiltered) return false;
24
+ const offset = m.getClientOffset();
25
+ if (!el.current || !offset) return false;
26
+ const { drop } = computeDrop({
27
+ element: el.current,
28
+ offset: offset,
29
+ indent: tree.indent,
30
+ node: node,
31
+ prevNode: node.prev,
32
+ nextNode: node.next,
33
+ });
34
+ if (!drop) return false;
35
+ const dropParent = tree.get(drop.parentId) ?? tree.root;
36
+
26
37
  for (let id of item.dragIds) {
27
- const drag = tree.getNode(id);
38
+ const drag = tree.get(id);
28
39
  if (!drag) return false;
29
- if (isFolder(drag) && isDecendent(node, drag)) return false;
40
+ if (!dropParent) return false;
41
+ if (drag.isInternal && isDecendent(dropParent, drag)) return false;
30
42
  }
31
43
  return true;
32
44
  },
@@ -39,8 +51,8 @@ export function useDropHook(
39
51
  offset: offset,
40
52
  indent: tree.indent,
41
53
  node: node,
42
- prevNode: prev,
43
- nextNode: next,
54
+ prevNode: node.prev,
55
+ nextNode: node.next,
44
56
  });
45
57
  if (cursor) tree.showCursor(cursor);
46
58
  } else {
@@ -55,12 +67,14 @@ export function useDropHook(
55
67
  offset: offset,
56
68
  indent: tree.indent,
57
69
  node: node,
58
- prevNode: prev,
59
- nextNode: next,
70
+ prevNode: node.prev,
71
+ nextNode: node.next,
60
72
  });
61
73
  return drop;
62
74
  },
63
75
  }),
64
- [node, prev, el, tree]
76
+ [node, el.current, tree.props]
65
77
  );
78
+
79
+ return dropRef;
66
80
  }
@@ -1,6 +1,6 @@
1
1
  import { useDrop } from "react-dnd";
2
2
  import { useTreeApi } from "../context";
3
- import { DragItem } from "../types";
3
+ import { DragItem } from "../types/dnd";
4
4
  import { computeDrop } from "./compute-drop";
5
5
  import { DropResult } from "./drop-hook";
6
6
 
@@ -0,0 +1,16 @@
1
+ import { useMemo } from "react";
2
+ import { useTreeApi } from "../context";
3
+ import { IdObj } from "../types/utils";
4
+
5
+ export function useFreshNode<T extends IdObj>(index: number) {
6
+ const tree = useTreeApi<T>();
7
+ const original = tree.at(index);
8
+ if (!original) throw new Error(`Could not find node for index: ${index}`);
9
+
10
+ return useMemo(() => {
11
+ const fresh = original.clone();
12
+ tree.visibleNodes[index] = fresh; // sneaky
13
+ return fresh;
14
+ // Return a fresh instance if the state values change
15
+ }, [...Object.values(original.state), original]);
16
+ }
@@ -0,0 +1,55 @@
1
+ import { useMemo, useState } from "react";
2
+ import { SimpleTree } from "../data/simple-tree";
3
+ import {
4
+ CreateHandler,
5
+ DeleteHandler,
6
+ MoveHandler,
7
+ RenameHandler,
8
+ } from "../types/handlers";
9
+ import { IdObj } from "../types/utils";
10
+
11
+ export type SimpleTreeData = {
12
+ id: string;
13
+ name: string;
14
+ children?: SimpleTreeData[];
15
+ };
16
+
17
+ let nextId = 0;
18
+
19
+ export function useSimpleTree<T extends IdObj>(initialData: T[]) {
20
+ const [data, setData] = useState(initialData);
21
+ const tree = useMemo(() => new SimpleTree<T>(data), [data]);
22
+
23
+ const onMove: MoveHandler = (args: {
24
+ dragIds: string[];
25
+ parentId: null | string;
26
+ index: number;
27
+ }) => {
28
+ for (const id of args.dragIds) {
29
+ tree.move({ id, parentId: args.parentId, index: args.index });
30
+ }
31
+ setData(tree.data);
32
+ };
33
+
34
+ const onRename: RenameHandler = ({ name, id }) => {
35
+ tree.update({ id, changes: { name } as any });
36
+ setData(tree.data);
37
+ };
38
+
39
+ const onCreate: CreateHandler = ({ parentId, index, type }) => {
40
+ const data = { id: `simple-tree-id-${nextId++}`, name: "" } as any;
41
+ if (type === "internal") data.children = [];
42
+ tree.create({ parentId, index, data });
43
+ setData(tree.data);
44
+ return data;
45
+ };
46
+
47
+ const onDelete: DeleteHandler = (args: { ids: string[] }) => {
48
+ args.ids.forEach((id) => tree.drop({ id }));
49
+ setData(tree.data);
50
+ };
51
+
52
+ const controller = { onMove, onRename, onCreate, onDelete };
53
+
54
+ return [data, controller] as const;
55
+ }
@@ -0,0 +1,35 @@
1
+ import { TreeProps } from "../types/tree-props";
2
+ import { IdObj } from "../types/utils";
3
+ import { SimpleTreeData, useSimpleTree } from "./use-simple-tree";
4
+
5
+ export function useValidatedProps<T extends IdObj>(
6
+ props: TreeProps<T>
7
+ ): TreeProps<T> {
8
+ if (props.initialData && props.data) {
9
+ throw new Error(
10
+ `React Arborist Tree => Provide either a data or initialData prop, but not both.`
11
+ );
12
+ }
13
+ if (
14
+ props.initialData &&
15
+ (props.onCreate || props.onDelete || props.onMove || props.onRename)
16
+ ) {
17
+ throw new Error(
18
+ `React Arborist Tree => You passed the initialData prop along with a data handler.
19
+ Use the data prop if you want to provide your own handlers.`
20
+ );
21
+ }
22
+ if (props.initialData) {
23
+ /**
24
+ * Let's break the rules of hooks here. If the initialData prop
25
+ * is provided, we will assume it will not change for the life of
26
+ * the component.
27
+ *
28
+ * We will provide the real data and the handlers to update it.
29
+ * */
30
+ const [data, controller] = useSimpleTree<T>(props.initialData);
31
+ return { ...props, ...controller, data };
32
+ } else {
33
+ return props;
34
+ }
35
+ }
package/src/index.ts CHANGED
@@ -1,19 +1,9 @@
1
- import { Tree } from "./components/tree";
2
- import { TreeApi } from "./tree-api";
3
- import type {
4
- NodeRenderer,
5
- NodeState,
6
- NodeHandlers,
7
- NodeRendererProps,
8
- DropCursorProps,
9
- } from "./types";
10
-
11
- export {
12
- Tree,
13
- TreeApi,
14
- NodeRenderer,
15
- NodeState,
16
- NodeHandlers,
17
- NodeRendererProps,
18
- DropCursorProps,
19
- };
1
+ /* The Public Api */
2
+ export { Tree } from "./components/tree";
3
+ export * from "./types/handlers";
4
+ export * from "./types/renderers";
5
+ export * from "./types/state";
6
+ export * from "./interfaces/node-api";
7
+ export * from "./interfaces/tree-api";
8
+ export * from "./data/simple-tree";
9
+ export * from "./hooks/use-simple-tree";