react-arborist 1.2.0 → 2.0.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/README.md +606 -144
- package/dist/components/{drop-cursor.d.ts → cursor.d.ts} +0 -0
- package/dist/components/default-container.d.ts +7 -0
- package/dist/components/default-cursor.d.ts +3 -0
- package/dist/components/default-drag-preview.d.ts +3 -0
- package/dist/components/default-node.d.ts +4 -0
- package/dist/components/default-row.d.ts +4 -0
- package/dist/components/drag-preview-container.d.ts +2 -0
- package/dist/components/list-inner-element.d.ts +2 -0
- package/dist/components/provider.d.ts +11 -0
- package/dist/components/row-container.d.ts +8 -0
- package/dist/components/tree-container.d.ts +2 -0
- package/dist/components/tree.d.ts +5 -4
- package/dist/context.d.ts +20 -2
- package/dist/data/create-index.d.ts +5 -0
- package/dist/data/create-list.d.ts +4 -0
- package/dist/data/create-root.d.ts +5 -0
- package/dist/data/simple-tree.d.ts +43 -0
- package/dist/dnd/compute-drop.d.ts +4 -4
- package/dist/dnd/drag-hook.d.ts +3 -4
- package/dist/dnd/drop-hook.d.ts +2 -3
- package/dist/hooks/use-fresh-node.d.ts +2 -0
- package/dist/hooks/use-simple-tree.d.ts +13 -0
- package/dist/hooks/use-validated-props.d.ts +3 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.js +1948 -973
- package/dist/index.js.map +1 -1
- package/dist/interfaces/node-api.d.ts +67 -0
- package/dist/interfaces/tree-api.d.ts +113 -0
- package/dist/module.js +1935 -979
- package/dist/module.js.map +1 -1
- package/dist/state/dnd-slice.d.ts +20 -0
- package/dist/state/drag-slice.d.ts +7 -0
- package/dist/state/edit-slice.d.ts +8 -0
- package/dist/state/focus-slice.d.ts +12 -0
- package/dist/state/initial.d.ts +3 -0
- package/dist/state/open-slice.d.ts +30 -0
- package/dist/state/root-reducer.d.ts +13 -0
- package/dist/state/selection-slice.d.ts +36 -0
- package/dist/types/dnd.d.ts +9 -0
- package/dist/types/handlers.d.ts +24 -0
- package/dist/types/renderers.d.ts +30 -0
- package/dist/types/state.d.ts +2 -0
- package/dist/types/tree-props.d.ts +44 -0
- package/dist/types/utils.d.ts +21 -0
- package/dist/utils.d.ts +15 -6
- package/package.json +11 -7
- package/src/components/cursor.tsx +15 -0
- package/src/components/default-container.tsx +238 -0
- package/src/components/{default-drop-cursor.tsx → default-cursor.tsx} +9 -8
- package/src/components/{preview.tsx → default-drag-preview.tsx} +25 -41
- package/src/components/default-node.tsx +50 -0
- package/src/components/default-row.tsx +21 -0
- package/src/components/drag-preview-container.tsx +26 -0
- package/src/components/list-inner-element.tsx +22 -0
- package/src/components/list-outer-element.tsx +26 -15
- package/src/components/provider.tsx +97 -0
- package/src/components/row-container.tsx +82 -0
- package/src/components/tree-container.tsx +13 -0
- package/src/components/tree.tsx +16 -44
- package/src/context.ts +36 -0
- package/src/data/create-index.ts +9 -0
- package/src/data/create-list.ts +56 -0
- package/src/data/create-root.ts +54 -0
- package/src/data/simple-tree.ts +103 -0
- package/src/dnd/compute-drop.ts +16 -16
- package/src/dnd/drag-hook.ts +25 -19
- package/src/dnd/drop-hook.ts +31 -17
- package/src/dnd/outer-drop-hook.ts +1 -1
- package/src/hooks/use-fresh-node.ts +16 -0
- package/src/hooks/use-simple-tree.ts +55 -0
- package/src/hooks/use-validated-props.ts +35 -0
- package/src/index.ts +9 -19
- package/src/interfaces/node-api.ts +187 -0
- package/src/interfaces/tree-api.ts +557 -0
- package/src/state/dnd-slice.ts +36 -0
- package/src/state/drag-slice.ts +31 -0
- package/src/state/edit-slice.ts +19 -0
- package/src/state/focus-slice.ts +28 -0
- package/src/state/initial.ts +14 -0
- package/src/state/open-slice.ts +53 -0
- package/src/state/root-reducer.ts +21 -0
- package/src/state/selection-slice.ts +75 -0
- package/src/types/dnd.ts +10 -0
- package/src/types/handlers.ts +24 -0
- package/src/types/renderers.ts +34 -0
- package/src/types/state.ts +3 -0
- package/src/types/tree-props.ts +64 -0
- package/src/types/utils.ts +26 -0
- package/src/utils.ts +125 -11
- package/tsconfig.json +1 -1
- package/dist/components/default-drop-cursor.d.ts +0 -3
- package/dist/components/list.d.ts +0 -4
- package/dist/components/preview.d.ts +0 -2
- package/dist/components/row.d.ts +0 -8
- package/dist/data/enrich-tree.d.ts +0 -2
- package/dist/data/flatten-tree.d.ts +0 -2
- package/dist/provider.d.ts +0 -3
- package/dist/reducer.d.ts +0 -46
- package/dist/selection/range.d.ts +0 -13
- package/dist/selection/selection-hook.d.ts +0 -4
- package/dist/selection/selection.d.ts +0 -33
- package/dist/tree-api.d.ts +0 -50
- package/dist/types.d.ts +0 -122
- package/src/components/drop-cursor.tsx +0 -12
- package/src/components/list.tsx +0 -25
- package/src/components/row.tsx +0 -112
- package/src/context.tsx +0 -13
- package/src/data/enrich-tree.ts +0 -74
- package/src/data/flatten-tree.ts +0 -17
- package/src/provider.tsx +0 -41
- package/src/reducer.ts +0 -161
- package/src/selection/range.ts +0 -41
- package/src/selection/selection-hook.ts +0 -25
- package/src/selection/selection.test.ts +0 -111
- package/src/selection/selection.ts +0 -186
- package/src/tree-api.ts +0 -230
- package/src/types.ts +0 -148
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare type FocusState = {
|
|
2
|
+
id: string | null;
|
|
3
|
+
treeFocused: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare function focus(id: string | null): {
|
|
6
|
+
type: "FOCUS";
|
|
7
|
+
id: string | null;
|
|
8
|
+
};
|
|
9
|
+
export declare function treeBlur(): {
|
|
10
|
+
readonly type: "TREE_BLUR";
|
|
11
|
+
};
|
|
12
|
+
export declare function reducer(state: FocusState | undefined, action: ReturnType<typeof focus> | ReturnType<typeof treeBlur>): FocusState;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ActionTypes } from "../types/utils";
|
|
2
|
+
export declare type OpenMap = {
|
|
3
|
+
[id: string]: boolean;
|
|
4
|
+
};
|
|
5
|
+
export declare type OpenSlice = {
|
|
6
|
+
unfiltered: OpenMap;
|
|
7
|
+
filtered: OpenMap;
|
|
8
|
+
};
|
|
9
|
+
export declare const actions: {
|
|
10
|
+
open(id: string, filtered: boolean): {
|
|
11
|
+
type: "VISIBILITY_OPEN";
|
|
12
|
+
id: string;
|
|
13
|
+
filtered: boolean;
|
|
14
|
+
};
|
|
15
|
+
close(id: string, filtered: boolean): {
|
|
16
|
+
type: "VISIBILITY_CLOSE";
|
|
17
|
+
id: string;
|
|
18
|
+
filtered: boolean;
|
|
19
|
+
};
|
|
20
|
+
toggle(id: string, filtered: boolean): {
|
|
21
|
+
type: "VISIBILITY_TOGGLE";
|
|
22
|
+
id: string;
|
|
23
|
+
filtered: boolean;
|
|
24
|
+
};
|
|
25
|
+
clear(filtered: boolean): {
|
|
26
|
+
type: "VISIBILITY_CLEAR";
|
|
27
|
+
filtered: boolean;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export declare function reducer(state: OpenSlice | undefined, action: ActionTypes<typeof actions>): OpenSlice;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ActionFromReducer } from "redux";
|
|
2
|
+
export declare const rootReducer: import("redux").Reducer<import("redux").CombinedState<{
|
|
3
|
+
nodes: import("redux").CombinedState<{
|
|
4
|
+
focus: import("./focus-slice").FocusState;
|
|
5
|
+
edit: import("./edit-slice").EditState;
|
|
6
|
+
open: import("./open-slice").OpenSlice;
|
|
7
|
+
selection: import("./selection-slice").SelectionState;
|
|
8
|
+
drag: import("./drag-slice").DragSlice;
|
|
9
|
+
}>;
|
|
10
|
+
dnd: import("./dnd-slice").DndState;
|
|
11
|
+
}>, import("redux").AnyAction>;
|
|
12
|
+
export declare type RootState = ReturnType<typeof rootReducer>;
|
|
13
|
+
export declare type Actions = ActionFromReducer<typeof rootReducer>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ActionTypes, IdObj } from "../types/utils";
|
|
2
|
+
export declare type SelectionState = {
|
|
3
|
+
ids: Set<string>;
|
|
4
|
+
anchor: string | null;
|
|
5
|
+
mostRecent: string | null;
|
|
6
|
+
};
|
|
7
|
+
export declare const actions: {
|
|
8
|
+
clear: () => {
|
|
9
|
+
type: "SELECTION_CLEAR";
|
|
10
|
+
};
|
|
11
|
+
only: (id: string | IdObj) => {
|
|
12
|
+
type: "SELECTION_ONLY";
|
|
13
|
+
id: string;
|
|
14
|
+
};
|
|
15
|
+
add: (id: string | string[] | IdObj | IdObj[]) => {
|
|
16
|
+
type: "SELECTION_ADD";
|
|
17
|
+
ids: string[];
|
|
18
|
+
};
|
|
19
|
+
remove: (id: string | string[] | IdObj | IdObj[]) => {
|
|
20
|
+
type: "SELECTION_REMOVE";
|
|
21
|
+
ids: string[];
|
|
22
|
+
};
|
|
23
|
+
set: (ids: Set<string>) => {
|
|
24
|
+
type: "SELECTION_SET";
|
|
25
|
+
ids: Set<string>;
|
|
26
|
+
};
|
|
27
|
+
mostRecent: (id: string | null | IdObj) => {
|
|
28
|
+
type: "SELECTION_MOST_RECENT";
|
|
29
|
+
id: string | null;
|
|
30
|
+
};
|
|
31
|
+
anchor: (id: string | null | IdObj) => {
|
|
32
|
+
type: "SELECTION_ANCHOR";
|
|
33
|
+
id: string | null;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export declare function reducer(state: SelectionState | undefined, action: ActionTypes<typeof actions>): SelectionState;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { IdObj } from "./utils";
|
|
2
|
+
export declare type CreateHandler = (args: {
|
|
3
|
+
parentId: string | null;
|
|
4
|
+
index: number;
|
|
5
|
+
type: "internal" | "leaf";
|
|
6
|
+
}) => (IdObj | null) | Promise<IdObj | null>;
|
|
7
|
+
export declare type MoveHandler = (args: {
|
|
8
|
+
dragIds: string[];
|
|
9
|
+
parentId: string | null;
|
|
10
|
+
index: number;
|
|
11
|
+
}) => void | Promise<void>;
|
|
12
|
+
export declare type RenameHandler = (args: {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
}) => void | Promise<void>;
|
|
16
|
+
export declare type DeleteHandler = (args: {
|
|
17
|
+
ids: string[];
|
|
18
|
+
}) => void | Promise<void>;
|
|
19
|
+
export declare type EditResult = {
|
|
20
|
+
cancelled: true;
|
|
21
|
+
} | {
|
|
22
|
+
cancelled: false;
|
|
23
|
+
value: string;
|
|
24
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { CSSProperties, HTMLAttributes, ReactElement } from "react";
|
|
2
|
+
import { IdObj } from "./utils";
|
|
3
|
+
import { NodeApi } from "../interfaces/node-api";
|
|
4
|
+
import { TreeApi } from "../interfaces/tree-api";
|
|
5
|
+
import { XYCoord } from "react-dnd";
|
|
6
|
+
export declare type NodeRendererProps<T extends IdObj> = {
|
|
7
|
+
style: CSSProperties;
|
|
8
|
+
node: NodeApi<T>;
|
|
9
|
+
tree: TreeApi<T>;
|
|
10
|
+
dragHandle?: (el: HTMLDivElement | null) => void;
|
|
11
|
+
preview?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare type RowRendererProps<T extends IdObj> = {
|
|
14
|
+
node: NodeApi<T>;
|
|
15
|
+
innerRef: (el: HTMLDivElement | null) => void;
|
|
16
|
+
attrs: HTMLAttributes<any>;
|
|
17
|
+
children: ReactElement;
|
|
18
|
+
};
|
|
19
|
+
export declare type DragPreviewProps = {
|
|
20
|
+
offset: XYCoord | null;
|
|
21
|
+
mouse: XYCoord | null;
|
|
22
|
+
id: string | null;
|
|
23
|
+
dragIds: string[];
|
|
24
|
+
isDragging: boolean;
|
|
25
|
+
};
|
|
26
|
+
export declare type DropCursorProps = {
|
|
27
|
+
top: number;
|
|
28
|
+
left: number;
|
|
29
|
+
indent: number;
|
|
30
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BoolFunc, IdObj } from "./utils";
|
|
2
|
+
import * as handlers from "./handlers";
|
|
3
|
+
import * as renderers from "./renderers";
|
|
4
|
+
import { ElementType, MouseEventHandler } from "react";
|
|
5
|
+
import { ListOnScrollProps } from "react-window";
|
|
6
|
+
import { NodeApi } from "../interfaces/node-api";
|
|
7
|
+
import { OpenMap } from "../state/open-slice";
|
|
8
|
+
export interface TreeProps<T extends IdObj> {
|
|
9
|
+
data?: T[];
|
|
10
|
+
initialData?: T[];
|
|
11
|
+
onCreate?: handlers.CreateHandler;
|
|
12
|
+
onMove?: handlers.MoveHandler;
|
|
13
|
+
onRename?: handlers.RenameHandler;
|
|
14
|
+
onDelete?: handlers.DeleteHandler;
|
|
15
|
+
children?: ElementType<renderers.NodeRendererProps<T>>;
|
|
16
|
+
renderRow?: ElementType<renderers.RowRendererProps<T>>;
|
|
17
|
+
renderDragPreview?: ElementType<renderers.DragPreviewProps>;
|
|
18
|
+
renderCursor?: ElementType<renderers.DropCursorProps>;
|
|
19
|
+
renderContainer?: ElementType<{}>;
|
|
20
|
+
rowHeight?: number;
|
|
21
|
+
width?: number;
|
|
22
|
+
height?: number;
|
|
23
|
+
indent?: number;
|
|
24
|
+
paddingTop?: number;
|
|
25
|
+
paddingBottom?: number;
|
|
26
|
+
padding?: number;
|
|
27
|
+
openByDefault?: boolean;
|
|
28
|
+
selectionFollowsFocus?: boolean;
|
|
29
|
+
disableDrag?: string | boolean | BoolFunc<T>;
|
|
30
|
+
disableDrop?: string | boolean | BoolFunc<T>;
|
|
31
|
+
childrenAccessor?: string | ((d: T) => T[]);
|
|
32
|
+
idAccessor?: string | ((d: T) => string);
|
|
33
|
+
onActivate?: (node: NodeApi<T>) => void;
|
|
34
|
+
onSelect?: (nodes: NodeApi<T>[]) => void;
|
|
35
|
+
onScroll?: (props: ListOnScrollProps) => void;
|
|
36
|
+
selection?: string;
|
|
37
|
+
initialOpenState?: OpenMap;
|
|
38
|
+
searchTerm?: string;
|
|
39
|
+
searchMatch?: (node: NodeApi<T>, searchTerm: string) => boolean;
|
|
40
|
+
className?: string | undefined;
|
|
41
|
+
dndRootElement?: globalThis.Node | null;
|
|
42
|
+
onClick?: MouseEventHandler;
|
|
43
|
+
onContextMenu?: MouseEventHandler;
|
|
44
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { AnyAction } from "redux";
|
|
3
|
+
import { NodeApi } from "../interfaces/node-api";
|
|
4
|
+
export interface IdObj {
|
|
5
|
+
id: string;
|
|
6
|
+
}
|
|
7
|
+
export declare type Identity = string | IdObj | null;
|
|
8
|
+
declare module "react" {
|
|
9
|
+
function forwardRef<T, P = {}>(render: (props: P, ref: React.Ref<T>) => React.ReactElement | null): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
|
|
10
|
+
}
|
|
11
|
+
export declare type BoolFunc<T> = (data: T) => boolean;
|
|
12
|
+
export declare type ActionTypes<Actions extends {
|
|
13
|
+
[name: string]: (...args: any[]) => AnyAction;
|
|
14
|
+
}> = ReturnType<Actions[keyof Actions]>;
|
|
15
|
+
export declare type SelectOptions = {
|
|
16
|
+
multi?: boolean;
|
|
17
|
+
contiguous?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare type NodesById<T extends IdObj> = {
|
|
20
|
+
[id: string]: NodeApi<T>;
|
|
21
|
+
};
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NodeApi } from "./interfaces/node-api";
|
|
2
|
+
import { IdObj } from "./types/utils";
|
|
2
3
|
export declare function bound(n: number, min: number, max: number): number;
|
|
3
|
-
export declare
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function isClosed(node: Node | null): boolean | null;
|
|
4
|
+
export declare function isItem(node: NodeApi<any> | null): boolean | null;
|
|
5
|
+
export declare function isClosed(node: NodeApi<any> | null): boolean | null;
|
|
6
6
|
/**
|
|
7
7
|
* Is first param a decendent of the second param
|
|
8
8
|
*/
|
|
9
|
-
export declare const isDecendent: (a:
|
|
10
|
-
export declare const indexOf: (node:
|
|
9
|
+
export declare const isDecendent: (a: NodeApi<any>, b: NodeApi<any>) => boolean;
|
|
10
|
+
export declare const indexOf: (node: NodeApi<any>) => number;
|
|
11
11
|
export declare function noop(): void;
|
|
12
|
+
export declare function dfs(node: NodeApi<any>, id: string): NodeApi<any> | null;
|
|
13
|
+
export declare function focusNextElement(target: HTMLElement): void;
|
|
14
|
+
export declare function focusPrevElement(target: HTMLElement): void;
|
|
15
|
+
export declare function access<T = boolean>(obj: any, accessor: string | boolean | Function): T;
|
|
16
|
+
export declare function identifyNull(obj: string | IdObj | null): string | null;
|
|
17
|
+
export declare function identify(obj: string | IdObj): string;
|
|
18
|
+
export declare function mergeRefs(...refs: any): (instance: any) => void;
|
|
19
|
+
export declare function safeRun<T extends (...args: any[]) => any>(fn: T | undefined, ...args: Parameters<T>): any;
|
|
20
|
+
export declare function waitFor(fn: () => boolean): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-arborist",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-rc.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"source": "src/index.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,30 +9,34 @@
|
|
|
9
9
|
"repository": "github:brimdata/react-arborist",
|
|
10
10
|
"homepage": "https://react-arborist.netlify.app",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"memoize-one": "^6.0.0",
|
|
13
12
|
"react-dnd": "^14.0.3",
|
|
14
13
|
"react-dnd-html5-backend": "^14.0.1",
|
|
15
|
-
"react-window": "^1.8.6"
|
|
14
|
+
"react-window": "^1.8.6",
|
|
15
|
+
"redux": "^4.1.1",
|
|
16
|
+
"use-sync-external-store": "^1.2.0"
|
|
16
17
|
},
|
|
17
18
|
"scripts": {
|
|
18
19
|
"build": "run-p 'build:**'",
|
|
19
20
|
"build:js": "parcel build --target main --target module",
|
|
20
21
|
"build:types": "tsc --outDir dist",
|
|
21
|
-
"watch": "
|
|
22
|
+
"watch:js": "parcel watch --target main --target module --no-hmr --no-cache",
|
|
23
|
+
"watch:types": "tsc --outDir dist --watch",
|
|
24
|
+
"watch": "run-p 'watch:**'",
|
|
25
|
+
"prepack": "yarn build"
|
|
22
26
|
},
|
|
23
27
|
"peerDependencies": {
|
|
24
28
|
"react": ">= 16.14",
|
|
25
29
|
"react-dom": ">= 16.14"
|
|
26
30
|
},
|
|
27
31
|
"devDependencies": {
|
|
32
|
+
"@parcel/core": "^2.3.2",
|
|
28
33
|
"@types/jest": "^27.4.1",
|
|
29
|
-
"@types/react": "^
|
|
34
|
+
"@types/react": "^18.0.0",
|
|
30
35
|
"@types/react-window": "^1.8.5",
|
|
36
|
+
"@types/use-sync-external-store": "^0.0.3",
|
|
31
37
|
"jest": "^27.5.1",
|
|
32
38
|
"npm-run-all": "^4.1.5",
|
|
33
39
|
"parcel": "^2.3.2",
|
|
34
|
-
"react": "^17.0.2",
|
|
35
|
-
"react-dom": "^17.0.2",
|
|
36
40
|
"typescript": "^4.6.2"
|
|
37
41
|
}
|
|
38
42
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useDndContext, useTreeApi } from "../context";
|
|
2
|
+
|
|
3
|
+
export function DropCursor() {
|
|
4
|
+
const tree = useTreeApi();
|
|
5
|
+
const state = useDndContext();
|
|
6
|
+
const cursor = state.cursor;
|
|
7
|
+
if (!cursor || cursor.type !== "line") return null;
|
|
8
|
+
const indent = tree.indent;
|
|
9
|
+
const top =
|
|
10
|
+
tree.rowHeight * cursor.index +
|
|
11
|
+
(tree.props.padding ?? tree.props.paddingTop ?? 0);
|
|
12
|
+
const left = indent * cursor.level;
|
|
13
|
+
const Cursor = tree.renderCursor;
|
|
14
|
+
return <Cursor {...{ top, left, indent }} />;
|
|
15
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { FixedSizeList } from "react-window";
|
|
2
|
+
import { useDataUpdates, useTreeApi } from "../context";
|
|
3
|
+
import { focusNextElement, focusPrevElement } from "../utils";
|
|
4
|
+
import { ListOuterElement } from "./list-outer-element";
|
|
5
|
+
import { ListInnerElement } from "./list-inner-element";
|
|
6
|
+
import { RowContainer } from "./row-container";
|
|
7
|
+
|
|
8
|
+
let focusSearchTerm = "";
|
|
9
|
+
let timeoutId: any = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* All these keyboard shortcuts seem like they should be configurable.
|
|
13
|
+
* Each operation should be a given a name and separated from
|
|
14
|
+
* the event handler. Future clean up welcome.
|
|
15
|
+
*/
|
|
16
|
+
export function DefaultContainer() {
|
|
17
|
+
useDataUpdates();
|
|
18
|
+
const tree = useTreeApi();
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
style={{
|
|
22
|
+
height: tree.height,
|
|
23
|
+
width: tree.width,
|
|
24
|
+
minHeight: 0,
|
|
25
|
+
minWidth: 0,
|
|
26
|
+
}}
|
|
27
|
+
onContextMenu={tree.props.onContextMenu}
|
|
28
|
+
onClick={tree.props.onClick}
|
|
29
|
+
tabIndex={0}
|
|
30
|
+
onFocus={(e) => {
|
|
31
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
32
|
+
tree.onFocus();
|
|
33
|
+
}
|
|
34
|
+
}}
|
|
35
|
+
onBlur={(e) => {
|
|
36
|
+
if (!e.currentTarget.contains(e.relatedTarget)) {
|
|
37
|
+
tree.onBlur();
|
|
38
|
+
}
|
|
39
|
+
}}
|
|
40
|
+
onKeyDown={(e) => {
|
|
41
|
+
if (tree.isEditing) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (e.key === "Backspace") {
|
|
45
|
+
if (!tree.props.onDelete) return;
|
|
46
|
+
const ids = Array.from(tree.selectedIds);
|
|
47
|
+
if (ids.length > 1) {
|
|
48
|
+
let nextFocus = tree.mostRecentNode;
|
|
49
|
+
while (nextFocus && nextFocus.isSelected) {
|
|
50
|
+
nextFocus = nextFocus.nextSibling;
|
|
51
|
+
}
|
|
52
|
+
if (!nextFocus) nextFocus = tree.lastNode;
|
|
53
|
+
tree.focus(nextFocus, { scroll: false });
|
|
54
|
+
tree.delete(Array.from(ids));
|
|
55
|
+
} else {
|
|
56
|
+
const node = tree.focusedNode;
|
|
57
|
+
if (node) {
|
|
58
|
+
const sib = node.nextSibling;
|
|
59
|
+
const parent = node.parent;
|
|
60
|
+
tree.focus(sib || parent, { scroll: false });
|
|
61
|
+
tree.delete(node);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (e.key === "Tab" && !e.shiftKey) {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
focusNextElement(e.currentTarget);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (e.key === "Tab" && e.shiftKey) {
|
|
72
|
+
e.preventDefault();
|
|
73
|
+
focusPrevElement(e.currentTarget);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (e.key === "ArrowDown" && !e.shiftKey && !e.metaKey) {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
const next = tree.nextNode;
|
|
79
|
+
tree.focus(next);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (e.key === "ArrowDown" && e.shiftKey) {
|
|
83
|
+
e.preventDefault();
|
|
84
|
+
const next = tree.nextNode;
|
|
85
|
+
if (!next) return;
|
|
86
|
+
const current = tree.focusedNode;
|
|
87
|
+
if (!current) {
|
|
88
|
+
tree.focus(tree.firstNode);
|
|
89
|
+
} else if (current.isSelected) {
|
|
90
|
+
tree.selectContiguous(next);
|
|
91
|
+
} else {
|
|
92
|
+
tree.selectMulti(next);
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (e.key === "ArrowUp" && !e.shiftKey) {
|
|
97
|
+
e.preventDefault();
|
|
98
|
+
tree.focus(tree.prevNode);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (e.key === "ArrowUp" && e.shiftKey) {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
const prev = tree.prevNode;
|
|
104
|
+
const current = tree.focusedNode;
|
|
105
|
+
if (!prev) return;
|
|
106
|
+
if (!current) {
|
|
107
|
+
tree.focus(tree.lastNode); // ?
|
|
108
|
+
} else if (current.isSelected) {
|
|
109
|
+
tree.selectContiguous(prev);
|
|
110
|
+
} else {
|
|
111
|
+
tree.selectMulti(prev);
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (e.key === "ArrowRight") {
|
|
116
|
+
const node = tree.focusedNode;
|
|
117
|
+
if (!node) return;
|
|
118
|
+
if (node.isInternal && node.isOpen) {
|
|
119
|
+
tree.focus(tree.nextNode);
|
|
120
|
+
} else if (node.isInternal) tree.open(node.id);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (e.key === "ArrowLeft") {
|
|
124
|
+
const node = tree.focusedNode;
|
|
125
|
+
if (!node || node.isRoot) return;
|
|
126
|
+
if (node.isInternal && node.isOpen) tree.close(node.id);
|
|
127
|
+
else if (!node.parent?.isRoot) {
|
|
128
|
+
tree.focus(node.parent);
|
|
129
|
+
}
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (e.key === "a" && e.metaKey) {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
tree.selectAll();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (e.key === "a" && !e.metaKey) {
|
|
138
|
+
if (!tree.props.onCreate) return;
|
|
139
|
+
tree.createLeaf();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (e.key === "A" && !e.metaKey) {
|
|
143
|
+
if (!tree.props.onCreate) return;
|
|
144
|
+
tree.createInternal();
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (e.key === "Home") {
|
|
149
|
+
// add shift keys
|
|
150
|
+
e.preventDefault();
|
|
151
|
+
tree.focus(tree.firstNode);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (e.key === "End") {
|
|
155
|
+
// add shift keys
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
tree.focus(tree.lastNode);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (e.key === "Enter") {
|
|
161
|
+
if (!tree.props.onRename) return;
|
|
162
|
+
setTimeout(() => {
|
|
163
|
+
if (tree.focusedNode) tree.edit(tree.focusedNode);
|
|
164
|
+
});
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (e.key === " ") {
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
const node = tree.focusedNode;
|
|
170
|
+
if (!node) return;
|
|
171
|
+
if (node.isLeaf) {
|
|
172
|
+
node.select();
|
|
173
|
+
node.activate();
|
|
174
|
+
} else {
|
|
175
|
+
node.toggle();
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (e.key === "*") {
|
|
180
|
+
const node = tree.focusedNode;
|
|
181
|
+
if (!node) return;
|
|
182
|
+
tree.openSiblings(node);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (e.key === "ArrowDown" && e.metaKey) {
|
|
186
|
+
e.preventDefault();
|
|
187
|
+
tree.select(tree.focusedNode);
|
|
188
|
+
tree.activate(tree.focusedNode);
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (e.key === "PageUp") {
|
|
192
|
+
e.preventDefault();
|
|
193
|
+
tree.pageUp();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (e.key === "PageDown") {
|
|
197
|
+
e.preventDefault();
|
|
198
|
+
tree.pageDown();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// If they type a sequence of characters
|
|
202
|
+
// collect them. Reset them after a timeout.
|
|
203
|
+
// Use it to search the tree for a node, then focus it.
|
|
204
|
+
// Clean this up a bit later
|
|
205
|
+
clearTimeout(timeoutId);
|
|
206
|
+
focusSearchTerm += e.key;
|
|
207
|
+
timeoutId = setTimeout(() => {
|
|
208
|
+
focusSearchTerm = "";
|
|
209
|
+
}, 600);
|
|
210
|
+
const node = tree.visibleNodes.find((n) => {
|
|
211
|
+
// @ts-ignore
|
|
212
|
+
const name = n.data.name;
|
|
213
|
+
if (typeof name === "string") {
|
|
214
|
+
return name.toLowerCase().startsWith(focusSearchTerm);
|
|
215
|
+
} else return false;
|
|
216
|
+
});
|
|
217
|
+
if (node) tree.focus(node.id);
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
<FixedSizeList
|
|
221
|
+
className={tree.props.className}
|
|
222
|
+
outerRef={tree.listEl}
|
|
223
|
+
itemCount={tree.visibleNodes.length}
|
|
224
|
+
height={tree.height}
|
|
225
|
+
width={tree.width}
|
|
226
|
+
itemSize={tree.rowHeight}
|
|
227
|
+
itemKey={(index) => tree.visibleNodes[index]?.id || index}
|
|
228
|
+
outerElementType={ListOuterElement}
|
|
229
|
+
innerElementType={ListInnerElement}
|
|
230
|
+
onScroll={tree.props.onScroll}
|
|
231
|
+
onItemsRendered={tree.onItemsRendered.bind(tree)}
|
|
232
|
+
ref={tree.list}
|
|
233
|
+
>
|
|
234
|
+
{RowContainer}
|
|
235
|
+
</FixedSizeList>
|
|
236
|
+
</div>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import React, { CSSProperties } from "react";
|
|
2
|
-
import { DropCursorProps } from "../types";
|
|
2
|
+
import { DropCursorProps } from "../types/renderers";
|
|
3
3
|
|
|
4
4
|
const placeholderStyle = {
|
|
5
5
|
display: "flex",
|
|
6
6
|
alignItems: "center",
|
|
7
|
+
zIndex: 1,
|
|
7
8
|
};
|
|
8
9
|
|
|
9
10
|
const lineStyle = {
|
|
@@ -20,12 +21,16 @@ const circleStyle = {
|
|
|
20
21
|
borderRadius: "50%",
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
export const DefaultCursor = React.memo(function DefaultCursor({
|
|
25
|
+
top,
|
|
26
|
+
left,
|
|
27
|
+
indent,
|
|
28
|
+
}: DropCursorProps) {
|
|
24
29
|
const style: CSSProperties = {
|
|
25
30
|
position: "absolute",
|
|
26
31
|
pointerEvents: "none",
|
|
27
32
|
top: top - 2 + "px",
|
|
28
|
-
left:
|
|
33
|
+
left: left + "px",
|
|
29
34
|
right: indent + "px",
|
|
30
35
|
};
|
|
31
36
|
return (
|
|
@@ -34,8 +39,4 @@ function DefaultCursor({ top, left, indent }: DropCursorProps) {
|
|
|
34
39
|
<div style={{ ...lineStyle }}></div>
|
|
35
40
|
</div>
|
|
36
41
|
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function defaultDropCursor(props: DropCursorProps) {
|
|
40
|
-
return <DefaultCursor {...props} />;
|
|
41
|
-
}
|
|
42
|
+
});
|