react-arborist 3.0.1 → 3.1.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 +31 -19
- package/dist/index.js.map +1 -1
- package/dist/interfaces/tree-api.d.ts +1 -0
- package/dist/module.js +31 -19
- package/dist/module.js.map +1 -1
- package/dist/types/tree-props.d.ts +2 -0
- package/jest.config.js +5 -0
- package/package.json +16 -11
- package/src/components/provider.tsx +2 -1
- package/src/data/create-list.ts +23 -12
- package/src/interfaces/tree-api.test.ts +15 -0
- package/src/interfaces/tree-api.ts +13 -10
- package/src/types/tree-props.ts +2 -0
|
@@ -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/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-arborist",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.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,
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "run-p 'start:**'",
|
|
12
|
+
"build": "npm-run-all clean -p 'build:**'",
|
|
13
|
+
"test": "jest",
|
|
14
|
+
"build:js": "parcel build --target main --target module",
|
|
15
|
+
"build:types": "tsc --outDir dist",
|
|
16
|
+
"clean": "rimraf dist",
|
|
17
|
+
"start:js": "parcel watch --target main --target module --no-hmr --no-cache",
|
|
18
|
+
"start:types": "tsc --outDir dist --watch",
|
|
19
|
+
"prepack": "yarn build"
|
|
20
|
+
},
|
|
9
21
|
"repository": {
|
|
10
22
|
"type": "git",
|
|
11
23
|
"url": "https://github.com/brimdata/react-arborist.git"
|
|
@@ -30,15 +42,6 @@
|
|
|
30
42
|
"redux": "^4.1.1",
|
|
31
43
|
"use-sync-external-store": "^1.2.0"
|
|
32
44
|
},
|
|
33
|
-
"scripts": {
|
|
34
|
-
"build": "run-p 'build:**'",
|
|
35
|
-
"build:js": "parcel build --target main --target module",
|
|
36
|
-
"build:types": "tsc --outDir dist",
|
|
37
|
-
"watch:js": "parcel watch --target main --target module --no-hmr --no-cache",
|
|
38
|
-
"watch:types": "tsc --outDir dist --watch",
|
|
39
|
-
"watch": "run-p 'watch:**'",
|
|
40
|
-
"prepack": "yarn build"
|
|
41
|
-
},
|
|
42
45
|
"peerDependencies": {
|
|
43
46
|
"react": ">= 16.14",
|
|
44
47
|
"react-dom": ">= 16.14"
|
|
@@ -49,9 +52,11 @@
|
|
|
49
52
|
"@types/react": "^18.0.0",
|
|
50
53
|
"@types/react-window": "^1.8.5",
|
|
51
54
|
"@types/use-sync-external-store": "^0.0.3",
|
|
52
|
-
"jest": "^
|
|
55
|
+
"jest": "^29.4.1",
|
|
53
56
|
"npm-run-all": "^4.1.5",
|
|
54
57
|
"parcel": "^2.3.2",
|
|
58
|
+
"rimraf": "^4.1.2",
|
|
59
|
+
"ts-jest": "^29.0.5",
|
|
55
60
|
"typescript": "^4.6.2"
|
|
56
61
|
}
|
|
57
62
|
}
|
|
@@ -65,7 +65,7 @@ export function TreeProvider<T>({
|
|
|
65
65
|
/* Change selection based on props */
|
|
66
66
|
useEffect(() => {
|
|
67
67
|
if (api.props.selection) {
|
|
68
|
-
api.select(api.props.selection);
|
|
68
|
+
api.select(api.props.selection, { focus: false });
|
|
69
69
|
} else {
|
|
70
70
|
api.deselectAll();
|
|
71
71
|
}
|
|
@@ -86,6 +86,7 @@ export function TreeProvider<T>({
|
|
|
86
86
|
<DndProvider
|
|
87
87
|
backend={HTML5Backend}
|
|
88
88
|
options={{ rootElement: api.props.dndRootElement || undefined }}
|
|
89
|
+
{...(treeProps.dndManager && { manager: treeProps.dndManager })}
|
|
89
90
|
>
|
|
90
91
|
{children}
|
|
91
92
|
</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
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createStore } from "redux";
|
|
2
|
+
import { rootReducer } from "../state/root-reducer";
|
|
3
|
+
import { TreeProps } from "../types/tree-props";
|
|
4
|
+
import { TreeApi } from "./tree-api";
|
|
5
|
+
|
|
6
|
+
function setupApi(props: TreeProps<any>) {
|
|
7
|
+
const store = createStore(rootReducer);
|
|
8
|
+
return new TreeApi(store, props, { current: null }, { current: null });
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
test("tree.canDrop()", () => {
|
|
12
|
+
expect(setupApi({ disableDrop: true }).canDrop()).toBe(false);
|
|
13
|
+
expect(setupApi({ disableDrop: () => false }).canDrop()).toBe(true);
|
|
14
|
+
expect(setupApi({ disableDrop: false }).canDrop()).toBe(true);
|
|
15
|
+
});
|
|
@@ -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
|
|
|
@@ -408,7 +411,7 @@ export class TreeApi<T> {
|
|
|
408
411
|
if (this.isFiltered) return false;
|
|
409
412
|
const parentNode = this.get(this.state.dnd.parentId) ?? this.root;
|
|
410
413
|
const dragNodes = this.dragNodes;
|
|
411
|
-
const
|
|
414
|
+
const isDisabled = this.props.disableDrop;
|
|
412
415
|
|
|
413
416
|
for (const drag of dragNodes) {
|
|
414
417
|
if (!drag) return false;
|
|
@@ -417,17 +420,17 @@ export class TreeApi<T> {
|
|
|
417
420
|
}
|
|
418
421
|
|
|
419
422
|
// Allow the user to insert their own logic
|
|
420
|
-
if (typeof
|
|
421
|
-
return
|
|
423
|
+
if (typeof isDisabled == "function") {
|
|
424
|
+
return !isDisabled({
|
|
422
425
|
parentNode,
|
|
423
426
|
dragNodes: this.dragNodes,
|
|
424
427
|
index: this.state.dnd.index,
|
|
425
428
|
});
|
|
426
|
-
} else if (typeof
|
|
429
|
+
} else if (typeof isDisabled == "string") {
|
|
427
430
|
// @ts-ignore
|
|
428
|
-
return
|
|
429
|
-
} else if (typeof
|
|
430
|
-
return
|
|
431
|
+
return !parentNode.data[isDisabled];
|
|
432
|
+
} else if (typeof isDisabled === "boolean") {
|
|
433
|
+
return !isDisabled;
|
|
431
434
|
} else {
|
|
432
435
|
return true;
|
|
433
436
|
}
|
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
|
}
|