react-arborist 3.1.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.
- package/dist/dnd/drop-hook.d.ts +1 -1
- package/dist/index.js +68 -32
- package/dist/index.js.map +1 -1
- package/dist/interfaces/node-api.d.ts +1 -0
- package/dist/interfaces/tree-api.d.ts +8 -0
- package/dist/module.js +68 -32
- package/dist/module.js.map +1 -1
- package/dist/state/dnd-slice.d.ts +3 -3
- package/dist/state/drag-slice.d.ts +3 -1
- package/dist/state/selection-slice.d.ts +8 -2
- package/dist/utils.d.ts +2 -2
- package/package.json +1 -1
- package/src/components/provider.tsx +0 -1
- package/src/dnd/compute-drop.ts +6 -3
- package/src/dnd/drag-hook.ts +1 -1
- package/src/dnd/drop-hook.ts +1 -1
- package/src/interfaces/node-api.ts +10 -0
- package/src/interfaces/tree-api.ts +34 -9
- package/src/state/dnd-slice.ts +2 -2
- package/src/state/drag-slice.ts +27 -11
- package/src/state/initial.ts +6 -1
- package/src/state/selection-slice.ts +12 -3
- package/src/types/tree-props.ts +2 -2
- package/src/utils.ts +2 -2
|
@@ -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
|
-
|
|
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;
|
|
@@ -20,9 +20,15 @@ export declare const actions: {
|
|
|
20
20
|
type: "SELECTION_REMOVE";
|
|
21
21
|
ids: string[];
|
|
22
22
|
};
|
|
23
|
-
set: (
|
|
24
|
-
|
|
23
|
+
set: (args: {
|
|
24
|
+
ids: Set<string>;
|
|
25
|
+
anchor: string | null;
|
|
26
|
+
mostRecent: string | null;
|
|
27
|
+
}) => {
|
|
25
28
|
ids: Set<string>;
|
|
29
|
+
anchor: string | null;
|
|
30
|
+
mostRecent: string | null;
|
|
31
|
+
type: "SELECTION_SET";
|
|
26
32
|
};
|
|
27
33
|
mostRecent: (id: string | null | IdObj) => {
|
|
28
34
|
type: "SELECTION_MOST_RECENT";
|
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
|
|
8
|
+
* Is first param a descendant of the second param
|
|
9
9
|
*/
|
|
10
|
-
export declare const
|
|
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
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
TreeApiContext,
|
|
15
15
|
} from "../context";
|
|
16
16
|
import { TreeApi } from "../interfaces/tree-api";
|
|
17
|
-
import { IdObj } from "../types/utils";
|
|
18
17
|
import { initialState } from "../state/initial";
|
|
19
18
|
import { rootReducer, RootState } from "../state/root-reducer";
|
|
20
19
|
import { HTML5Backend } from "react-dnd-html5-backend";
|
package/src/dnd/compute-drop.ts
CHANGED
|
@@ -26,7 +26,7 @@ function getNodesAroundCursor(
|
|
|
26
26
|
hover: HoverData
|
|
27
27
|
): [NodeApi | null, NodeApi | null] {
|
|
28
28
|
if (!node) {
|
|
29
|
-
// We're
|
|
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(
|
|
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,
|
|
141
|
+
drop: dropAt(node.id, null),
|
|
139
142
|
cursor: highlightCursor(node.id),
|
|
140
143
|
};
|
|
141
144
|
}
|
package/src/dnd/drag-hook.ts
CHANGED
|
@@ -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
|
});
|
package/src/dnd/drop-hook.ts
CHANGED
|
@@ -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
|
}
|
|
@@ -370,21 +370,33 @@ export class TreeApi<T> {
|
|
|
370
370
|
}
|
|
371
371
|
|
|
372
372
|
deselectAll() {
|
|
373
|
-
this.
|
|
374
|
-
this.dispatch(selection.anchor(null));
|
|
375
|
-
this.dispatch(selection.mostRecent(null));
|
|
373
|
+
this.setSelection({ ids: [], anchor: null, mostRecent: null });
|
|
376
374
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
377
375
|
}
|
|
378
376
|
|
|
379
377
|
selectAll() {
|
|
380
|
-
this.
|
|
378
|
+
this.setSelection({
|
|
379
|
+
ids: Object.keys(this.idToIndex),
|
|
380
|
+
anchor: this.firstNode,
|
|
381
|
+
mostRecent: this.lastNode,
|
|
382
|
+
});
|
|
381
383
|
this.dispatch(focus(this.lastNode?.id));
|
|
382
|
-
this.dispatch(selection.anchor(this.firstNode));
|
|
383
|
-
this.dispatch(selection.mostRecent(this.lastNode));
|
|
384
384
|
if (this.focusedNode) safeRun(this.props.onFocus, this.focusedNode);
|
|
385
385
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
setSelection(args: {
|
|
389
|
+
ids: (IdObj | string)[] | null;
|
|
390
|
+
anchor: IdObj | string | null;
|
|
391
|
+
mostRecent: IdObj | string | null;
|
|
392
|
+
}) {
|
|
393
|
+
const ids = new Set(args.ids?.map(identify));
|
|
394
|
+
const anchor = identifyNull(args.anchor);
|
|
395
|
+
const mostRecent = identifyNull(args.mostRecent);
|
|
396
|
+
this.dispatch(selection.set({ ids, anchor, mostRecent }));
|
|
397
|
+
safeRun(this.props.onSelect, this.selectedNodes);
|
|
398
|
+
}
|
|
399
|
+
|
|
388
400
|
/* Drag and Drop */
|
|
389
401
|
|
|
390
402
|
get cursorParentId() {
|
|
@@ -407,6 +419,18 @@ export class TreeApi<T> {
|
|
|
407
419
|
.filter((n) => !!n) as NodeApi<T>[];
|
|
408
420
|
}
|
|
409
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
|
+
|
|
410
434
|
canDrop() {
|
|
411
435
|
if (this.isFiltered) return false;
|
|
412
436
|
const parentNode = this.get(this.state.dnd.parentId) ?? this.root;
|
|
@@ -416,7 +440,7 @@ export class TreeApi<T> {
|
|
|
416
440
|
for (const drag of dragNodes) {
|
|
417
441
|
if (!drag) return false;
|
|
418
442
|
if (!parentNode) return false;
|
|
419
|
-
if (drag.isInternal && utils.
|
|
443
|
+
if (drag.isInternal && utils.isDescendant(parentNode, drag)) return false;
|
|
420
444
|
}
|
|
421
445
|
|
|
422
446
|
// Allow the user to insert their own logic
|
|
@@ -424,7 +448,7 @@ export class TreeApi<T> {
|
|
|
424
448
|
return !isDisabled({
|
|
425
449
|
parentNode,
|
|
426
450
|
dragNodes: this.dragNodes,
|
|
427
|
-
index: this.state.dnd.index,
|
|
451
|
+
index: this.state.dnd.index || 0,
|
|
428
452
|
});
|
|
429
453
|
} else if (typeof isDisabled == "string") {
|
|
430
454
|
// @ts-ignore
|
|
@@ -594,7 +618,8 @@ export class TreeApi<T> {
|
|
|
594
618
|
willReceiveDrop(node: string | IdObj | null) {
|
|
595
619
|
const id = identifyNull(node);
|
|
596
620
|
if (!id) return false;
|
|
597
|
-
|
|
621
|
+
const { destinationParentId, destinationIndex } = this.state.nodes.drag;
|
|
622
|
+
return id === destinationParentId && destinationIndex === null;
|
|
598
623
|
}
|
|
599
624
|
|
|
600
625
|
/* Tree Event Handlers */
|
package/src/state/dnd-slice.ts
CHANGED
|
@@ -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
|
};
|
package/src/state/drag-slice.ts
CHANGED
|
@@ -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 = {
|
|
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 =
|
|
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 {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
}
|
package/src/state/initial.ts
CHANGED
|
@@ -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: {
|
|
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: {
|
|
@@ -28,9 +28,13 @@ export const actions = {
|
|
|
28
28
|
ids: (Array.isArray(id) ? id : [id]).map(identify),
|
|
29
29
|
}),
|
|
30
30
|
|
|
31
|
-
set: (
|
|
31
|
+
set: (args: {
|
|
32
|
+
ids: Set<string>;
|
|
33
|
+
anchor: string | null;
|
|
34
|
+
mostRecent: string | null;
|
|
35
|
+
}) => ({
|
|
32
36
|
type: "SELECTION_SET" as const,
|
|
33
|
-
|
|
37
|
+
...args,
|
|
34
38
|
}),
|
|
35
39
|
|
|
36
40
|
mostRecent: (id: string | null | IdObj) => ({
|
|
@@ -64,7 +68,12 @@ export function reducer(
|
|
|
64
68
|
action.ids.forEach((id) => ids.delete(id));
|
|
65
69
|
return { ...state, ids: new Set(ids) };
|
|
66
70
|
case "SELECTION_SET":
|
|
67
|
-
return {
|
|
71
|
+
return {
|
|
72
|
+
...state,
|
|
73
|
+
ids: action.ids,
|
|
74
|
+
mostRecent: action.mostRecent,
|
|
75
|
+
anchor: action.anchor,
|
|
76
|
+
};
|
|
68
77
|
case "SELECTION_MOST_RECENT":
|
|
69
78
|
return { ...state, mostRecent: action.id };
|
|
70
79
|
case "SELECTION_ANCHOR":
|
package/src/types/tree-props.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { ElementType, MouseEventHandler } from "react";
|
|
|
5
5
|
import { ListOnScrollProps } from "react-window";
|
|
6
6
|
import { NodeApi } from "../interfaces/node-api";
|
|
7
7
|
import { OpenMap } from "../state/open-slice";
|
|
8
|
-
import { useDragDropManager } from "react-dnd"
|
|
8
|
+
import { useDragDropManager } from "react-dnd";
|
|
9
9
|
|
|
10
10
|
export interface TreeProps<T> {
|
|
11
11
|
/* Data Options */
|
|
@@ -76,5 +76,5 @@ export interface TreeProps<T> {
|
|
|
76
76
|
dndRootElement?: globalThis.Node | null;
|
|
77
77
|
onClick?: MouseEventHandler;
|
|
78
78
|
onContextMenu?: MouseEventHandler;
|
|
79
|
-
dndManager?: ReturnType<typeof useDragDropManager
|
|
79
|
+
dndManager?: ReturnType<typeof useDragDropManager>;
|
|
80
80
|
}
|
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
|
|
18
|
+
* Is first param a descendant of the second param
|
|
19
19
|
*/
|
|
20
|
-
export const
|
|
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;
|