react-arborist 3.0.2 → 3.2.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.
- package/README.md +1 -0
- package/dist/index.js +52 -23
- package/dist/index.js.map +1 -1
- package/dist/interfaces/tree-api.d.ts +6 -0
- package/dist/module.js +52 -23
- package/dist/module.js.map +1 -1
- package/dist/state/selection-slice.d.ts +8 -2
- package/dist/types/tree-props.d.ts +2 -0
- package/package.json +3 -2
- package/src/components/provider.tsx +2 -2
- package/src/data/create-list.ts +23 -12
- package/src/interfaces/tree-api.ts +24 -9
- package/src/state/selection-slice.ts +12 -3
- package/src/types/tree-props.ts +2 -0
|
@@ -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";
|
|
@@ -5,6 +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
9
|
export interface TreeProps<T> {
|
|
9
10
|
data?: readonly T[];
|
|
10
11
|
initialData?: readonly T[];
|
|
@@ -51,4 +52,5 @@ export interface TreeProps<T> {
|
|
|
51
52
|
dndRootElement?: globalThis.Node | null;
|
|
52
53
|
onClick?: MouseEventHandler;
|
|
53
54
|
onContextMenu?: MouseEventHandler;
|
|
55
|
+
dndManager?: ReturnType<typeof useDragDropManager>;
|
|
54
56
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-arborist",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"source": "src/index.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/module.js",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
9
10
|
"scripts": {
|
|
10
|
-
"start": "run-p '
|
|
11
|
+
"start": "run-p 'start:**'",
|
|
11
12
|
"build": "npm-run-all clean -p 'build:**'",
|
|
12
13
|
"test": "jest",
|
|
13
14
|
"build:js": "parcel build --target main --target module",
|
|
@@ -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";
|
|
@@ -65,7 +64,7 @@ export function TreeProvider<T>({
|
|
|
65
64
|
/* Change selection based on props */
|
|
66
65
|
useEffect(() => {
|
|
67
66
|
if (api.props.selection) {
|
|
68
|
-
api.select(api.props.selection);
|
|
67
|
+
api.select(api.props.selection, { focus: false });
|
|
69
68
|
} else {
|
|
70
69
|
api.deselectAll();
|
|
71
70
|
}
|
|
@@ -86,6 +85,7 @@ export function TreeProvider<T>({
|
|
|
86
85
|
<DndProvider
|
|
87
86
|
backend={HTML5Backend}
|
|
88
87
|
options={{ rootElement: api.props.dndRootElement || undefined }}
|
|
88
|
+
{...(treeProps.dndManager && { manager: treeProps.dndManager })}
|
|
89
89
|
>
|
|
90
90
|
{children}
|
|
91
91
|
</DndProvider>
|
package/src/data/create-list.ts
CHANGED
|
@@ -29,24 +29,35 @@ function flattenAndFilterTree<T>(
|
|
|
29
29
|
root: NodeApi<T>,
|
|
30
30
|
isMatch: (n: NodeApi<T>) => boolean
|
|
31
31
|
): NodeApi<T>[] {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const yes = !node.isRoot && isMatch(node);
|
|
32
|
+
const matches: Record<string, boolean> = {};
|
|
33
|
+
const list: NodeApi<T>[] = [];
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
function markMatch(node: NodeApi<T>) {
|
|
36
|
+
const yes = !node.isRoot && isMatch(node);
|
|
37
|
+
if (yes) {
|
|
38
|
+
matches[node.id] = true;
|
|
39
|
+
let parent = node.parent;
|
|
40
|
+
while (parent) {
|
|
41
|
+
matches[parent.id] = true;
|
|
42
|
+
parent = parent.parent;
|
|
39
43
|
}
|
|
40
44
|
}
|
|
41
|
-
if (
|
|
42
|
-
|
|
43
|
-
return result;
|
|
45
|
+
if (node.children) {
|
|
46
|
+
for (let child of node.children) markMatch(child);
|
|
44
47
|
}
|
|
45
|
-
if (yes) return [node];
|
|
46
|
-
else return [];
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
function collect(node: NodeApi<T>) {
|
|
51
|
+
if (node.level >= 0 && matches[node.id]) {
|
|
52
|
+
list.push(node);
|
|
53
|
+
}
|
|
54
|
+
if (node.isOpen) {
|
|
55
|
+
node.children?.forEach(collect);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
markMatch(root);
|
|
60
|
+
collect(root);
|
|
50
61
|
list.forEach(assignRowIndex);
|
|
51
62
|
return list;
|
|
52
63
|
}
|
|
@@ -323,15 +323,18 @@ export class TreeApi<T> {
|
|
|
323
323
|
this.focus(this.at(index));
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
select(node: Identity, opts: { align?: Align } = {}) {
|
|
326
|
+
select(node: Identity, opts: { align?: Align; focus?: boolean } = {}) {
|
|
327
327
|
if (!node) return;
|
|
328
|
+
const changeFocus = opts.focus !== false;
|
|
328
329
|
const id = identify(node);
|
|
329
|
-
this.dispatch(focus(id));
|
|
330
|
+
if (changeFocus) this.dispatch(focus(id));
|
|
330
331
|
this.dispatch(selection.only(id));
|
|
331
332
|
this.dispatch(selection.anchor(id));
|
|
332
333
|
this.dispatch(selection.mostRecent(id));
|
|
333
334
|
this.scrollTo(id, opts.align);
|
|
334
|
-
if (this.focusedNode
|
|
335
|
+
if (this.focusedNode && changeFocus) {
|
|
336
|
+
safeRun(this.props.onFocus, this.focusedNode);
|
|
337
|
+
}
|
|
335
338
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
336
339
|
}
|
|
337
340
|
|
|
@@ -367,21 +370,33 @@ export class TreeApi<T> {
|
|
|
367
370
|
}
|
|
368
371
|
|
|
369
372
|
deselectAll() {
|
|
370
|
-
this.
|
|
371
|
-
this.dispatch(selection.anchor(null));
|
|
372
|
-
this.dispatch(selection.mostRecent(null));
|
|
373
|
+
this.setSelection({ ids: [], anchor: null, mostRecent: null });
|
|
373
374
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
374
375
|
}
|
|
375
376
|
|
|
376
377
|
selectAll() {
|
|
377
|
-
this.
|
|
378
|
+
this.setSelection({
|
|
379
|
+
ids: Object.keys(this.idToIndex),
|
|
380
|
+
anchor: this.firstNode,
|
|
381
|
+
mostRecent: this.lastNode,
|
|
382
|
+
});
|
|
378
383
|
this.dispatch(focus(this.lastNode?.id));
|
|
379
|
-
this.dispatch(selection.anchor(this.firstNode));
|
|
380
|
-
this.dispatch(selection.mostRecent(this.lastNode));
|
|
381
384
|
if (this.focusedNode) safeRun(this.props.onFocus, this.focusedNode);
|
|
382
385
|
safeRun(this.props.onSelect, this.selectedNodes);
|
|
383
386
|
}
|
|
384
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
|
+
|
|
385
400
|
/* Drag and Drop */
|
|
386
401
|
|
|
387
402
|
get cursorParentId() {
|
|
@@ -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,6 +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
9
|
|
|
9
10
|
export interface TreeProps<T> {
|
|
10
11
|
/* Data Options */
|
|
@@ -75,4 +76,5 @@ export interface TreeProps<T> {
|
|
|
75
76
|
dndRootElement?: globalThis.Node | null;
|
|
76
77
|
onClick?: MouseEventHandler;
|
|
77
78
|
onContextMenu?: MouseEventHandler;
|
|
79
|
+
dndManager?: ReturnType<typeof useDragDropManager>;
|
|
78
80
|
}
|