react-arborist 3.2.0 → 3.3.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.
@@ -5,7 +5,7 @@ export declare type DndState = {
5
5
  cursor: Cursor;
6
6
  dragIds: string[];
7
7
  parentId: null | string;
8
- index: number;
8
+ index: number | null;
9
9
  };
10
10
  export declare const actions: {
11
11
  cursor(cursor: Cursor): {
@@ -20,10 +20,10 @@ export declare const actions: {
20
20
  dragEnd(): {
21
21
  type: "DND_DRAG_END";
22
22
  };
23
- hovering(parentId: string | null, index: number): {
23
+ hovering(parentId: string | null, index: number | null): {
24
24
  type: "DND_HOVERING";
25
25
  parentId: string | null;
26
- index: number;
26
+ index: number | null;
27
27
  };
28
28
  };
29
29
  export declare function reducer(state: DndState | undefined, action: ActionTypes<typeof actions>): DndState;
@@ -2,6 +2,8 @@ import { ActionTypes } from "../types/utils";
2
2
  import { actions as dnd } from "./dnd-slice";
3
3
  export declare type DragSlice = {
4
4
  id: string | null;
5
- idWillReceiveDrop: string | null;
5
+ selectedIds: string[];
6
+ destinationParentId: string | null;
7
+ destinationIndex: number | null;
6
8
  };
7
9
  export declare function reducer(state: DragSlice | undefined, action: ActionTypes<typeof dnd>): DragSlice;
package/dist/utils.d.ts CHANGED
@@ -5,9 +5,9 @@ export declare function bound(n: number, min: number, max: number): number;
5
5
  export declare function isItem(node: NodeApi<any> | null): boolean | null;
6
6
  export declare function isClosed(node: NodeApi<any> | null): boolean | null;
7
7
  /**
8
- * Is first param a decendent of the second param
8
+ * Is first param a descendant of the second param
9
9
  */
10
- export declare const isDecendent: (a: NodeApi<any>, b: NodeApi<any>) => boolean;
10
+ export declare const isDescendant: (a: NodeApi<any>, b: NodeApi<any>) => boolean;
11
11
  export declare const indexOf: (node: NodeApi<any>) => number;
12
12
  export declare function noop(): void;
13
13
  export declare function dfs(node: NodeApi<any>, id: string): NodeApi<any> | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-arborist",
3
- "version": "3.2.0",
3
+ "version": "3.3.0-rc.1",
4
4
  "license": "MIT",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -26,7 +26,7 @@ function getNodesAroundCursor(
26
26
  hover: HoverData
27
27
  ): [NodeApi | null, NodeApi | null] {
28
28
  if (!node) {
29
- // We're hoving over the empty part of the list, not over an item,
29
+ // We're hovering 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
  }
@@ -83,7 +83,10 @@ export type ComputedDrop = {
83
83
  cursor: Cursor | null;
84
84
  };
85
85
 
86
- function dropAt(parentId: string | undefined, index: number): DropResult {
86
+ function dropAt(
87
+ parentId: string | undefined,
88
+ index: number | null
89
+ ): DropResult {
87
90
  return { parentId: parentId || null, index };
88
91
  }
89
92
 
@@ -135,7 +138,7 @@ export function computeDrop(args: Args): ComputedDrop {
135
138
  /* Hovering over the middle of a folder */
136
139
  if (node && node.isInternal && hover.inMiddle) {
137
140
  return {
138
- drop: dropAt(node.id, 0),
141
+ drop: dropAt(node.id, null),
139
142
  cursor: highlightCursor(node.id),
140
143
  };
141
144
  }
@@ -31,7 +31,7 @@ export function useDragHook<T>(node: NodeApi<T>): ConnectDragSource {
31
31
  safeRun(tree.props.onMove, {
32
32
  dragIds,
33
33
  parentId: parentId === ROOT_ID ? null : parentId,
34
- index,
34
+ index: index === null ? 0 : index, // When it's null it was dropped over a folder
35
35
  dragNodes: tree.dragNodes,
36
36
  parentNode: tree.get(parentId),
37
37
  });
@@ -8,7 +8,7 @@ import { actions as dnd } from "../state/dnd-slice";
8
8
 
9
9
  export type DropResult = {
10
10
  parentId: string | null;
11
- index: number;
11
+ index: number | null;
12
12
  };
13
13
 
14
14
  export function useDropHook(
@@ -130,6 +130,16 @@ export class NodeApi<T = any> {
130
130
  return this.parent?.children![i + 1] ?? null;
131
131
  }
132
132
 
133
+ isAncestorOf(node: NodeApi<T> | null) {
134
+ if (!node) return false;
135
+ let ancestor: NodeApi<T> | null = node;
136
+ while (ancestor) {
137
+ if (ancestor.id === this.id) return true;
138
+ ancestor = ancestor.parent;
139
+ }
140
+ return false;
141
+ }
142
+
133
143
  select() {
134
144
  this.tree.select(this);
135
145
  }
@@ -419,6 +419,18 @@ export class TreeApi<T> {
419
419
  .filter((n) => !!n) as NodeApi<T>[];
420
420
  }
421
421
 
422
+ get dragNode() {
423
+ return this.get(this.state.nodes.drag.id);
424
+ }
425
+
426
+ get dragDestinationParent() {
427
+ return this.get(this.state.nodes.drag.destinationParentId);
428
+ }
429
+
430
+ get dragDestinationIndex() {
431
+ return this.state.nodes.drag.destinationIndex;
432
+ }
433
+
422
434
  canDrop() {
423
435
  if (this.isFiltered) return false;
424
436
  const parentNode = this.get(this.state.dnd.parentId) ?? this.root;
@@ -428,7 +440,7 @@ export class TreeApi<T> {
428
440
  for (const drag of dragNodes) {
429
441
  if (!drag) return false;
430
442
  if (!parentNode) return false;
431
- if (drag.isInternal && utils.isDecendent(parentNode, drag)) return false;
443
+ if (drag.isInternal && utils.isDescendant(parentNode, drag)) return false;
432
444
  }
433
445
 
434
446
  // Allow the user to insert their own logic
@@ -436,7 +448,7 @@ export class TreeApi<T> {
436
448
  return !isDisabled({
437
449
  parentNode,
438
450
  dragNodes: this.dragNodes,
439
- index: this.state.dnd.index,
451
+ index: this.state.dnd.index || 0,
440
452
  });
441
453
  } else if (typeof isDisabled == "string") {
442
454
  // @ts-ignore
@@ -606,7 +618,8 @@ export class TreeApi<T> {
606
618
  willReceiveDrop(node: string | IdObj | null) {
607
619
  const id = identifyNull(node);
608
620
  if (!id) return false;
609
- return id === this.state.nodes.drag.idWillReceiveDrop;
621
+ const { destinationParentId, destinationIndex } = this.state.nodes.drag;
622
+ return id === destinationParentId && destinationIndex === null;
610
623
  }
611
624
 
612
625
  /* Tree Event Handlers */
@@ -8,7 +8,7 @@ export type DndState = {
8
8
  cursor: Cursor;
9
9
  dragIds: string[];
10
10
  parentId: null | string;
11
- index: number;
11
+ index: number | null;
12
12
  };
13
13
 
14
14
  /* Actions */
@@ -22,7 +22,7 @@ export const actions = {
22
22
  dragEnd() {
23
23
  return { type: "DND_DRAG_END" as const };
24
24
  },
25
- hovering(parentId: string | null, index: number) {
25
+ hovering(parentId: string | null, index: number | null) {
26
26
  return { type: "DND_HOVERING" as const, parentId, index };
27
27
  },
28
28
  };
@@ -1,27 +1,43 @@
1
1
  import { ActionTypes } from "../types/utils";
2
2
  import { actions as dnd } from "./dnd-slice";
3
+ import { initialState } from "./initial";
3
4
 
4
5
  /* Types */
5
6
 
6
- export type DragSlice = { id: string | null; idWillReceiveDrop: string | null };
7
+ export type DragSlice = {
8
+ id: string | null;
9
+ selectedIds: string[];
10
+ destinationParentId: string | null;
11
+ destinationIndex: number | null;
12
+ };
7
13
 
8
14
  /* Reducer */
9
15
 
10
16
  export function reducer(
11
- state: DragSlice = { id: null, idWillReceiveDrop: null },
17
+ state: DragSlice = initialState().nodes.drag,
12
18
  action: ActionTypes<typeof dnd>
13
- ) {
19
+ ): DragSlice {
14
20
  switch (action.type) {
15
21
  case "DND_DRAG_START":
16
- return { ...state, id: action.id };
22
+ return { ...state, id: action.id, selectedIds: action.dragIds };
17
23
  case "DND_DRAG_END":
18
- return { ...state, id: null };
19
- case "DND_CURSOR":
20
- const c = action.cursor;
21
- if (c.type === "highlight" && c.id !== state.idWillReceiveDrop) {
22
- return { ...state, idWillReceiveDrop: c.id };
23
- } else if (c.type !== "highlight" && state.idWillReceiveDrop !== null) {
24
- return { ...state, idWillReceiveDrop: null };
24
+ return {
25
+ ...state,
26
+ id: null,
27
+ destinationParentId: null,
28
+ destinationIndex: null,
29
+ selectedIds: [],
30
+ };
31
+ case "DND_HOVERING":
32
+ if (
33
+ action.parentId !== state.destinationParentId ||
34
+ action.index != state.destinationIndex
35
+ ) {
36
+ return {
37
+ ...state,
38
+ destinationParentId: action.parentId,
39
+ destinationIndex: action.index,
40
+ };
25
41
  } else {
26
42
  return state;
27
43
  }
@@ -7,7 +7,12 @@ export const initialState = (props?: TreeProps<any>): RootState => ({
7
7
  open: { filtered: {}, unfiltered: props?.initialOpenState ?? {} },
8
8
  focus: { id: null, treeFocused: false },
9
9
  edit: { id: null },
10
- drag: { id: null, idWillReceiveDrop: null },
10
+ drag: {
11
+ id: null,
12
+ selectedIds: [],
13
+ destinationParentId: null,
14
+ destinationIndex: null,
15
+ },
11
16
  selection: { ids: new Set(), anchor: null, mostRecent: null },
12
17
  },
13
18
  dnd: {
package/src/utils.ts CHANGED
@@ -15,9 +15,9 @@ export function isClosed(node: NodeApi<any> | null) {
15
15
  }
16
16
 
17
17
  /**
18
- * Is first param a decendent of the second param
18
+ * Is first param a descendant of the second param
19
19
  */
20
- export const isDecendent = (a: NodeApi<any>, b: NodeApi<any>) => {
20
+ export const isDescendant = (a: NodeApi<any>, b: NodeApi<any>) => {
21
21
  let n: NodeApi<any> | null = a;
22
22
  while (n) {
23
23
  if (n.id === b.id) return true;