react-arborist 1.1.0 → 1.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 +18 -17
- package/dist/components/default-drop-cursor.d.ts +3 -0
- package/dist/components/list-outer-element.d.ts +2 -0
- package/dist/components/list.d.ts +4 -0
- package/dist/components/outer-drop.d.ts +4 -0
- package/dist/components/row.d.ts +2 -1
- package/dist/context.d.ts +5 -18
- package/dist/index.d.ts +2 -2
- package/dist/index.js +833 -853
- package/dist/index.js.map +1 -1
- package/dist/module.js +836 -856
- package/dist/module.js.map +1 -1
- package/dist/provider.d.ts +2 -2
- package/dist/selection/selection-hook.d.ts +2 -1
- package/dist/tree-api.d.ts +24 -8
- package/dist/types.d.ts +12 -21
- package/package.json +1 -1
- package/src/components/default-drop-cursor.tsx +41 -0
- package/src/components/drop-cursor.tsx +7 -42
- package/src/components/list-outer-element.tsx +34 -0
- package/src/components/list.tsx +25 -0
- package/src/components/outer-drop.ts +7 -0
- package/src/components/preview.tsx +6 -6
- package/src/components/row.tsx +26 -28
- package/src/components/tree.tsx +4 -85
- package/src/context.tsx +9 -48
- package/src/dnd/drag-hook.ts +5 -6
- package/src/dnd/drop-hook.ts +5 -5
- package/src/dnd/outer-drop-hook.ts +5 -5
- package/src/index.ts +16 -2
- package/src/provider.tsx +29 -49
- package/src/selection/selection-hook.ts +2 -1
- package/src/tree-api.ts +95 -21
- package/src/types.ts +13 -20
- package/dist/tree-api-hook.d.ts +0 -6
- package/src/tree-api-hook.ts +0 -34
package/src/tree-api.ts
CHANGED
|
@@ -1,35 +1,41 @@
|
|
|
1
1
|
import memoizeOne from "memoize-one";
|
|
2
|
-
import { Dispatch } from "react";
|
|
2
|
+
import { Dispatch, MutableRefObject } from "react";
|
|
3
3
|
import { FixedSizeList } from "react-window";
|
|
4
4
|
import { flattenTree } from "./data/flatten-tree";
|
|
5
5
|
import { Cursor } from "./dnd/compute-drop";
|
|
6
6
|
import { Action, actions } from "./reducer";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
Node,
|
|
9
|
+
StateContext,
|
|
10
|
+
TreeProviderProps,
|
|
11
|
+
EditResult,
|
|
12
|
+
IdObj,
|
|
13
|
+
DropCursorProps,
|
|
14
|
+
} from "./types";
|
|
8
15
|
import ReactDOM from "react-dom";
|
|
9
|
-
|
|
10
|
-
|
|
16
|
+
import { noop } from "./utils";
|
|
17
|
+
import { Selection } from "./selection/selection";
|
|
18
|
+
import { defaultDropCursor } from "./components/default-drop-cursor";
|
|
19
|
+
export class TreeApi<T extends IdObj> {
|
|
11
20
|
private edits = new Map<string, (args: EditResult) => void>();
|
|
12
21
|
|
|
13
22
|
constructor(
|
|
14
23
|
public dispatch: Dispatch<Action>,
|
|
15
24
|
public state: StateContext,
|
|
16
25
|
public props: TreeProviderProps<T>,
|
|
17
|
-
public list: FixedSizeList |
|
|
26
|
+
public list: MutableRefObject<FixedSizeList | null>,
|
|
27
|
+
public listEl: MutableRefObject<HTMLDivElement | null>
|
|
18
28
|
) {}
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
dispatch
|
|
22
|
-
state
|
|
23
|
-
props
|
|
24
|
-
list
|
|
25
|
-
|
|
26
|
-
this.dispatch = dispatch;
|
|
27
|
-
this.state = state;
|
|
28
|
-
this.props = props;
|
|
29
|
-
this.list = list;
|
|
30
|
+
sync(other: TreeApi<T>) {
|
|
31
|
+
this.dispatch = other.dispatch;
|
|
32
|
+
this.state = other.state;
|
|
33
|
+
this.props = other.props;
|
|
34
|
+
this.list = other.list;
|
|
35
|
+
this.listEl = other.listEl;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
|
-
getNode(id: string): Node<
|
|
38
|
+
getNode(id: string): Node<T> | null {
|
|
33
39
|
if (id in this.idToIndex)
|
|
34
40
|
return this.visibleNodes[this.idToIndex[id]] || null;
|
|
35
41
|
else return null;
|
|
@@ -49,7 +55,7 @@ export class TreeApi<T = unknown> {
|
|
|
49
55
|
|
|
50
56
|
submit(id: string | number, value: string) {
|
|
51
57
|
const sid = id.toString();
|
|
52
|
-
this.
|
|
58
|
+
this.onEdit(sid, value);
|
|
53
59
|
this.dispatch(actions.edit(null));
|
|
54
60
|
this.resolveEdit(sid, { cancelled: false, value });
|
|
55
61
|
}
|
|
@@ -95,20 +101,20 @@ export class TreeApi<T = unknown> {
|
|
|
95
101
|
if (!this.list) return;
|
|
96
102
|
const index = this.idToIndex[id];
|
|
97
103
|
if (index) {
|
|
98
|
-
this.list.scrollToItem(index);
|
|
104
|
+
this.list.current?.scrollToItem(index);
|
|
99
105
|
} else {
|
|
100
106
|
this.openParents(id);
|
|
101
107
|
ReactDOM.flushSync(() => {
|
|
102
108
|
const index = this.idToIndex[id];
|
|
103
109
|
if (index) {
|
|
104
|
-
this.list?.scrollToItem(index);
|
|
110
|
+
this.list.current?.scrollToItem(index);
|
|
105
111
|
}
|
|
106
112
|
});
|
|
107
113
|
}
|
|
108
114
|
}
|
|
109
115
|
|
|
110
116
|
open(id: string) {
|
|
111
|
-
this.
|
|
117
|
+
this.onToggle(id, true);
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
openParents(id: string) {
|
|
@@ -130,7 +136,75 @@ export class TreeApi<T = unknown> {
|
|
|
130
136
|
}
|
|
131
137
|
|
|
132
138
|
get visibleNodes() {
|
|
133
|
-
return createList(this.props.root);
|
|
139
|
+
return createList(this.props.root) as Node<T>[];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
get width() {
|
|
143
|
+
return this.props.treeProps.width || 300;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get height() {
|
|
147
|
+
return this.props.treeProps.height || 500;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
get indent() {
|
|
151
|
+
return this.props.treeProps.indent || 24;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
get renderer() {
|
|
155
|
+
return this.props.treeProps.children;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
get onToggle() {
|
|
159
|
+
return this.props.treeProps.onToggle || noop;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
get rowHeight() {
|
|
163
|
+
return this.props.treeProps.rowHeight || 24;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
get onClick() {
|
|
167
|
+
return this.props.treeProps.onClick || noop;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get onContextMenu() {
|
|
171
|
+
return this.props.treeProps.onContextMenu || noop;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get onMove() {
|
|
175
|
+
return this.props.treeProps.onMove || noop;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get onEdit() {
|
|
179
|
+
return this.props.treeProps.onEdit || noop;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
get cursorParentId() {
|
|
183
|
+
const { cursor } = this.state;
|
|
184
|
+
switch (cursor.type) {
|
|
185
|
+
case "highlight":
|
|
186
|
+
return cursor.id;
|
|
187
|
+
default:
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
get cursorOverFolder() {
|
|
193
|
+
return this.state.cursor.type === "highlight";
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
get editingId() {
|
|
197
|
+
return this.state.editingId;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
isSelected(index: number | null) {
|
|
201
|
+
const selection = Selection.parse(this.state.selection.data, []);
|
|
202
|
+
return selection.contains(index);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
renderDropCursor(props: DropCursorProps) {
|
|
206
|
+
const render = this.props.treeProps.dropCursor || defaultDropCursor;
|
|
207
|
+
return render(props);
|
|
134
208
|
}
|
|
135
209
|
}
|
|
136
210
|
|
package/src/types.ts
CHANGED
|
@@ -5,6 +5,7 @@ import React, {
|
|
|
5
5
|
MouseEventHandler,
|
|
6
6
|
MutableRefObject,
|
|
7
7
|
ReactElement,
|
|
8
|
+
ReactNode,
|
|
8
9
|
Ref,
|
|
9
10
|
} from "react";
|
|
10
11
|
import { FixedSizeList } from "react-window";
|
|
@@ -38,7 +39,7 @@ export interface IdObj {
|
|
|
38
39
|
id: string;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
|
-
export type NodeRendererProps<T> = {
|
|
42
|
+
export type NodeRendererProps<T extends IdObj> = {
|
|
42
43
|
innerRef: (el: HTMLDivElement | null) => void;
|
|
43
44
|
styles: { row: CSSProperties; indent: CSSProperties };
|
|
44
45
|
data: T;
|
|
@@ -66,7 +67,7 @@ export type NodeHandlers = {
|
|
|
66
67
|
reset: () => void;
|
|
67
68
|
};
|
|
68
69
|
|
|
69
|
-
export type NodeRenderer<T> = ComponentType<NodeRendererProps<T>>;
|
|
70
|
+
export type NodeRenderer<T extends IdObj> = ComponentType<NodeRendererProps<T>>;
|
|
70
71
|
|
|
71
72
|
export type MoveHandler = (
|
|
72
73
|
dragIds: string[],
|
|
@@ -105,7 +106,7 @@ export type StateContext = {
|
|
|
105
106
|
|
|
106
107
|
type BoolFunc<T> = (data: T) => boolean;
|
|
107
108
|
|
|
108
|
-
export interface TreeProps<T> {
|
|
109
|
+
export interface TreeProps<T extends IdObj> {
|
|
109
110
|
children: NodeRenderer<T>;
|
|
110
111
|
className?: string | undefined;
|
|
111
112
|
data: T;
|
|
@@ -126,30 +127,22 @@ export interface TreeProps<T> {
|
|
|
126
127
|
openByDefault?: boolean;
|
|
127
128
|
rowHeight?: number;
|
|
128
129
|
width?: number;
|
|
130
|
+
dropCursor?: (props: DropCursorProps) => ReactElement;
|
|
129
131
|
}
|
|
130
132
|
|
|
131
|
-
export type TreeProviderProps<T> = {
|
|
133
|
+
export type TreeProviderProps<T extends IdObj> = {
|
|
134
|
+
treeProps: TreeProps<T>;
|
|
132
135
|
imperativeHandle: React.Ref<TreeApi<T>> | undefined;
|
|
133
136
|
children: ReactElement;
|
|
134
|
-
height: number;
|
|
135
|
-
indent: number;
|
|
136
|
-
listEl: MutableRefObject<HTMLDivElement | null>;
|
|
137
|
-
onToggle: ToggleHandler;
|
|
138
|
-
onMove: MoveHandler;
|
|
139
|
-
onEdit: EditHandler;
|
|
140
|
-
onClick?: MouseEventHandler;
|
|
141
|
-
onContextMenu?: MouseEventHandler;
|
|
142
|
-
renderer: NodeRenderer<any>;
|
|
143
|
-
rowHeight: number;
|
|
144
137
|
root: Node<T>;
|
|
145
|
-
width: number;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
export type StaticContext<T> = TreeProviderProps<T> & {
|
|
149
|
-
api: TreeApi<T>;
|
|
150
|
-
list: MutableRefObject<FixedSizeList | undefined>;
|
|
151
138
|
};
|
|
152
139
|
|
|
153
140
|
export type EditResult =
|
|
154
141
|
| { cancelled: true }
|
|
155
142
|
| { cancelled: false; value: string };
|
|
143
|
+
|
|
144
|
+
export type DropCursorProps = {
|
|
145
|
+
top: number;
|
|
146
|
+
left: number;
|
|
147
|
+
indent: number;
|
|
148
|
+
};
|
package/dist/tree-api-hook.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { Dispatch } from "react";
|
|
2
|
-
import { FixedSizeList } from "react-window";
|
|
3
|
-
import { Action } from "./reducer";
|
|
4
|
-
import { TreeApi } from "./tree-api";
|
|
5
|
-
import { StateContext, TreeProviderProps } from "./types";
|
|
6
|
-
export declare function useTreeApi<T>(state: StateContext, dispatch: Dispatch<Action>, props: TreeProviderProps<T>, list: FixedSizeList | undefined): TreeApi<T>;
|
package/src/tree-api-hook.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Dispatch, useLayoutEffect, useMemo } from "react";
|
|
2
|
-
import { FixedSizeList } from "react-window";
|
|
3
|
-
import { Action, actions } from "./reducer";
|
|
4
|
-
import { TreeApi } from "./tree-api";
|
|
5
|
-
import { StateContext, TreeProviderProps } from "./types";
|
|
6
|
-
|
|
7
|
-
export function useTreeApi<T>(
|
|
8
|
-
state: StateContext,
|
|
9
|
-
dispatch: Dispatch<Action>,
|
|
10
|
-
props: TreeProviderProps<T>,
|
|
11
|
-
list: FixedSizeList | undefined
|
|
12
|
-
) {
|
|
13
|
-
/**
|
|
14
|
-
* We only ever want one instance of the api object
|
|
15
|
-
* It will get updated as the props change, but the
|
|
16
|
-
* reference will not.
|
|
17
|
-
*/
|
|
18
|
-
const api = useMemo(
|
|
19
|
-
() => new TreeApi<T>(dispatch, state, props, list),
|
|
20
|
-
// eslint-disable-next-line
|
|
21
|
-
[]
|
|
22
|
-
);
|
|
23
|
-
api.assign(dispatch, state, props, list);
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* This ensures that the selection remains correct even
|
|
27
|
-
* after opening and closing a folders
|
|
28
|
-
*/
|
|
29
|
-
useLayoutEffect(() => {
|
|
30
|
-
dispatch(actions.setVisibleIds(api.visibleIds, api.idToIndex));
|
|
31
|
-
}, [dispatch, api, props.root]);
|
|
32
|
-
|
|
33
|
-
return api;
|
|
34
|
-
}
|