react-arborist 1.1.0 → 2.0.0-rc

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.
Files changed (122) hide show
  1. package/dist/components/{drop-cursor.d.ts → cursor.d.ts} +0 -0
  2. package/dist/components/default-container.d.ts +2 -0
  3. package/dist/components/default-cursor.d.ts +3 -0
  4. package/dist/components/default-drag-preview.d.ts +3 -0
  5. package/dist/components/default-node.d.ts +4 -0
  6. package/dist/components/default-row.d.ts +4 -0
  7. package/dist/components/drag-preview-container.d.ts +2 -0
  8. package/dist/components/list-inner-element.d.ts +2 -0
  9. package/dist/components/list-outer-element.d.ts +2 -0
  10. package/dist/components/outer-drop.d.ts +4 -0
  11. package/dist/components/provider.d.ts +11 -0
  12. package/dist/components/row-container.d.ts +8 -0
  13. package/dist/components/tree-container.d.ts +2 -0
  14. package/dist/components/tree.d.ts +5 -4
  15. package/dist/context.d.ts +23 -18
  16. package/dist/data/create-index.d.ts +5 -0
  17. package/dist/data/create-list.d.ts +4 -0
  18. package/dist/data/create-root.d.ts +5 -0
  19. package/dist/data/flatten-tree.d.ts +4 -2
  20. package/dist/data/simple-tree.d.ts +43 -0
  21. package/dist/dnd/compute-drop.d.ts +4 -4
  22. package/dist/dnd/drag-hook.d.ts +3 -4
  23. package/dist/dnd/drop-hook.d.ts +2 -3
  24. package/dist/hooks/use-fresh-node.d.ts +2 -0
  25. package/dist/hooks/use-simple-tree.d.ts +13 -0
  26. package/dist/hooks/use-uncontrolled-tree.d.ts +24 -0
  27. package/dist/hooks/use-validated-props.d.ts +3 -0
  28. package/dist/index.d.ts +8 -4
  29. package/dist/index.js +2093 -1184
  30. package/dist/index.js.map +1 -1
  31. package/dist/interfaces/node-api.d.ts +67 -0
  32. package/dist/interfaces/tree-api.d.ts +112 -0
  33. package/dist/module.js +2082 -1192
  34. package/dist/module.js.map +1 -1
  35. package/dist/state/dnd-slice.d.ts +20 -0
  36. package/dist/state/drag-slice.d.ts +7 -0
  37. package/dist/state/edit-slice.d.ts +8 -0
  38. package/dist/state/focus-slice.d.ts +12 -0
  39. package/dist/state/initial.d.ts +3 -0
  40. package/dist/state/open-slice.d.ts +30 -0
  41. package/dist/state/root-reducer.d.ts +13 -0
  42. package/dist/state/selection-slice.d.ts +36 -0
  43. package/dist/types/dnd.d.ts +9 -0
  44. package/dist/types/handlers.d.ts +24 -0
  45. package/dist/types/renderers.d.ts +30 -0
  46. package/dist/types/state.d.ts +2 -0
  47. package/dist/types/tree-props.d.ts +43 -0
  48. package/dist/types/utils.d.ts +21 -0
  49. package/dist/utils/props.d.ts +3 -0
  50. package/dist/utils.d.ts +15 -6
  51. package/package.json +10 -7
  52. package/src/components/cursor.tsx +15 -0
  53. package/src/components/default-container.tsx +229 -0
  54. package/src/components/{drop-cursor.tsx → default-cursor.tsx} +15 -20
  55. package/src/components/default-drag-preview.tsx +92 -0
  56. package/src/components/default-node.tsx +15 -0
  57. package/src/components/default-row.tsx +21 -0
  58. package/src/components/drag-preview-container.tsx +26 -0
  59. package/src/components/list-inner-element.tsx +22 -0
  60. package/src/components/list-outer-element.tsx +45 -0
  61. package/src/components/outer-drop.ts +7 -0
  62. package/src/components/provider.tsx +97 -0
  63. package/src/components/row-container.tsx +82 -0
  64. package/src/components/tree-container.tsx +13 -0
  65. package/src/components/tree.tsx +17 -126
  66. package/src/context.ts +36 -0
  67. package/src/data/create-index.ts +9 -0
  68. package/src/data/create-list.ts +56 -0
  69. package/src/data/create-root.ts +53 -0
  70. package/src/data/simple-tree.ts +103 -0
  71. package/src/dnd/compute-drop.ts +16 -16
  72. package/src/dnd/drag-hook.ts +28 -23
  73. package/src/dnd/drop-hook.ts +35 -21
  74. package/src/dnd/outer-drop-hook.ts +6 -6
  75. package/src/hooks/use-fresh-node.ts +16 -0
  76. package/src/hooks/use-simple-tree.ts +55 -0
  77. package/src/hooks/use-validated-props.ts +35 -0
  78. package/src/index.ts +9 -5
  79. package/src/interfaces/node-api.ts +187 -0
  80. package/src/interfaces/tree-api.ts +552 -0
  81. package/src/state/dnd-slice.ts +36 -0
  82. package/src/state/drag-slice.ts +31 -0
  83. package/src/state/edit-slice.ts +19 -0
  84. package/src/state/focus-slice.ts +28 -0
  85. package/src/state/initial.ts +14 -0
  86. package/src/state/open-slice.ts +53 -0
  87. package/src/state/root-reducer.ts +21 -0
  88. package/src/state/selection-slice.ts +75 -0
  89. package/src/types/dnd.ts +10 -0
  90. package/src/types/handlers.ts +24 -0
  91. package/src/types/renderers.ts +34 -0
  92. package/src/types/state.ts +3 -0
  93. package/src/types/tree-props.ts +63 -0
  94. package/src/types/utils.ts +26 -0
  95. package/src/utils/props.ts +8 -0
  96. package/src/utils.ts +125 -11
  97. package/README.md +0 -220
  98. package/dist/components/preview.d.ts +0 -2
  99. package/dist/components/row.d.ts +0 -7
  100. package/dist/data/enrich-tree.d.ts +0 -2
  101. package/dist/provider.d.ts +0 -3
  102. package/dist/reducer.d.ts +0 -46
  103. package/dist/selection/range.d.ts +0 -13
  104. package/dist/selection/selection-hook.d.ts +0 -3
  105. package/dist/selection/selection.d.ts +0 -33
  106. package/dist/tree-api-hook.d.ts +0 -6
  107. package/dist/tree-api.d.ts +0 -34
  108. package/dist/types.d.ts +0 -131
  109. package/src/components/preview.tsx +0 -108
  110. package/src/components/row.tsx +0 -114
  111. package/src/context.tsx +0 -52
  112. package/src/data/enrich-tree.ts +0 -74
  113. package/src/data/flatten-tree.ts +0 -17
  114. package/src/provider.tsx +0 -61
  115. package/src/reducer.ts +0 -161
  116. package/src/selection/range.ts +0 -41
  117. package/src/selection/selection-hook.ts +0 -24
  118. package/src/selection/selection.test.ts +0 -111
  119. package/src/selection/selection.ts +0 -186
  120. package/src/tree-api-hook.ts +0 -34
  121. package/src/tree-api.ts +0 -156
  122. package/src/types.ts +0 -155
@@ -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,3 @@
1
+ import { TreeProps } from "../types/tree-props";
2
+ import { RootState } from "./root-reducer";
3
+ export declare const initialState: (props?: TreeProps<any>) => RootState;
@@ -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,9 @@
1
+ export declare type CursorLocation = {
2
+ index: number | null;
3
+ level: number | null;
4
+ parentId: string | null;
5
+ };
6
+ export declare type DragItem = {
7
+ dragIds: string[];
8
+ id: string;
9
+ };
@@ -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,2 @@
1
+ import { NodeApi } from "../interfaces/node-api";
2
+ export declare type NodeState = typeof NodeApi.prototype["state"];
@@ -0,0 +1,43 @@
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
+ getChildren?: string | ((d: T) => T[]);
32
+ onActivate?: (node: NodeApi<T>) => void;
33
+ onSelect?: (nodes: NodeApi<T>[]) => void;
34
+ onScroll?: (props: ListOnScrollProps) => void;
35
+ selection?: string;
36
+ initialOpenState?: OpenMap;
37
+ searchTerm?: string;
38
+ searchMatch?: (node: NodeApi<T>, searchTerm: string) => boolean;
39
+ className?: string | undefined;
40
+ dndRootElement?: globalThis.Node | null;
41
+ onClick?: MouseEventHandler;
42
+ onContextMenu?: MouseEventHandler;
43
+ }
@@ -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
+ };
@@ -0,0 +1,3 @@
1
+ import { TreeProps } from "../types/tree-props";
2
+ import { IdObj } from "../types/utils";
3
+ export declare function validateProps<T extends IdObj>(props: TreeProps<T>): TreeProps<T>;
package/dist/utils.d.ts CHANGED
@@ -1,11 +1,20 @@
1
- import { Node } from "./types";
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 const isFolder: (node: Node<any>) => boolean;
4
- export declare function isItem(node: Node | null): boolean | null;
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: Node, b: Node) => boolean;
10
- export declare const indexOf: (node: Node) => number;
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": "1.1.0",
3
+ "version": "2.0.0-rc",
4
4
  "license": "MIT",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
@@ -9,16 +9,20 @@
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": "yarn build:types --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",
@@ -26,13 +30,12 @@
26
30
  },
27
31
  "devDependencies": {
28
32
  "@types/jest": "^27.4.1",
29
- "@types/react": "^17.0.40",
33
+ "@types/react": "^18.0.0",
30
34
  "@types/react-window": "^1.8.5",
35
+ "@types/use-sync-external-store": "^0.0.3",
31
36
  "jest": "^27.5.1",
32
37
  "npm-run-all": "^4.1.5",
33
38
  "parcel": "^2.3.2",
34
- "react": "^17.0.2",
35
- "react-dom": "^17.0.2",
36
39
  "typescript": "^4.6.2"
37
40
  }
38
41
  }
@@ -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,229 @@
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
+ export function DefaultContainer() {
12
+ useDataUpdates();
13
+ const tree = useTreeApi();
14
+ return (
15
+ <div
16
+ style={{
17
+ height: tree.height,
18
+ width: tree.width,
19
+ minHeight: 0,
20
+ minWidth: 0,
21
+ }}
22
+ onContextMenu={tree.props.onContextMenu}
23
+ onClick={tree.props.onClick}
24
+ tabIndex={0}
25
+ onFocus={(e) => {
26
+ if (!e.currentTarget.contains(e.relatedTarget)) {
27
+ tree.onFocus();
28
+ }
29
+ }}
30
+ onBlur={(e) => {
31
+ if (!e.currentTarget.contains(e.relatedTarget)) {
32
+ tree.onBlur();
33
+ }
34
+ }}
35
+ onKeyDown={(e) => {
36
+ if (tree.isEditing) {
37
+ return;
38
+ }
39
+ if (e.key === "Backspace") {
40
+ const ids = Array.from(tree.selectedIds);
41
+ if (ids.length > 1) {
42
+ let nextFocus = tree.mostRecentNode;
43
+ while (nextFocus && nextFocus.isSelected) {
44
+ nextFocus = nextFocus.nextSibling;
45
+ }
46
+ if (!nextFocus) nextFocus = tree.lastNode;
47
+ tree.focus(nextFocus, { scroll: false });
48
+ tree.delete(Array.from(ids));
49
+ } else {
50
+ const node = tree.focusedNode;
51
+ if (node) {
52
+ const sib = node.nextSibling;
53
+ const parent = node.parent;
54
+ tree.focus(sib || parent, { scroll: false });
55
+ tree.delete(node);
56
+ }
57
+ }
58
+ return;
59
+ }
60
+ if (e.key === "Tab" && !e.shiftKey) {
61
+ e.preventDefault();
62
+ focusNextElement(e.currentTarget);
63
+ return;
64
+ }
65
+ if (e.key === "Tab" && e.shiftKey) {
66
+ e.preventDefault();
67
+ focusPrevElement(e.currentTarget);
68
+ return;
69
+ }
70
+ if (e.key === "ArrowDown" && !e.shiftKey && !e.metaKey) {
71
+ e.preventDefault();
72
+ const next = tree.nextNode;
73
+ tree.focus(next);
74
+ return;
75
+ }
76
+ if (e.key === "ArrowDown" && e.shiftKey) {
77
+ e.preventDefault();
78
+ const next = tree.nextNode;
79
+ if (!next) return;
80
+ const current = tree.focusedNode;
81
+ if (!current) {
82
+ tree.focus(tree.firstNode);
83
+ } else if (current.isSelected) {
84
+ tree.selectContiguous(next);
85
+ } else {
86
+ tree.selectMulti(next);
87
+ }
88
+ return;
89
+ }
90
+ if (e.key === "ArrowUp" && !e.shiftKey) {
91
+ e.preventDefault();
92
+ tree.focus(tree.prevNode);
93
+ return;
94
+ }
95
+ if (e.key === "ArrowUp" && e.shiftKey) {
96
+ e.preventDefault();
97
+ const prev = tree.prevNode;
98
+ const current = tree.focusedNode;
99
+ if (!prev) return;
100
+ if (!current) {
101
+ tree.focus(tree.lastNode); // ?
102
+ } else if (current.isSelected) {
103
+ tree.selectContiguous(prev);
104
+ } else {
105
+ tree.selectMulti(prev);
106
+ }
107
+ return;
108
+ }
109
+ if (e.key === "ArrowRight") {
110
+ const node = tree.focusedNode;
111
+ if (!node) return;
112
+ if (node.isInternal && node.isOpen) {
113
+ tree.focus(tree.nextNode);
114
+ } else if (node.isInternal) tree.open(node.id);
115
+ return;
116
+ }
117
+ if (e.key === "ArrowLeft") {
118
+ const node = tree.focusedNode;
119
+ if (!node || node.isRoot) return;
120
+ if (node.isInternal && node.isOpen) tree.close(node.id);
121
+ else if (!node.parent?.isRoot) {
122
+ tree.focus(node.parent);
123
+ }
124
+ return;
125
+ }
126
+ if (e.key === "a" && e.metaKey) {
127
+ e.preventDefault();
128
+ tree.selectAll();
129
+ return;
130
+ }
131
+ if (e.key === "a" && !e.metaKey) {
132
+ tree.createLeaf();
133
+ return;
134
+ }
135
+ if (e.key === "A" && !e.metaKey) {
136
+ tree.createInternal();
137
+ return;
138
+ }
139
+
140
+ if (e.key === "Home") {
141
+ // add shift keys
142
+ e.preventDefault();
143
+ tree.focus(tree.firstNode);
144
+ return;
145
+ }
146
+ if (e.key === "End") {
147
+ // add shift keys
148
+ e.preventDefault();
149
+ tree.focus(tree.lastNode);
150
+ return;
151
+ }
152
+ if (e.key === "Enter") {
153
+ setTimeout(() => {
154
+ if (tree.focusedNode) tree.edit(tree.focusedNode);
155
+ });
156
+ return;
157
+ }
158
+ if (e.key === " ") {
159
+ e.preventDefault();
160
+ const node = tree.focusedNode;
161
+ if (!node) return;
162
+ if (node.isLeaf) {
163
+ node.select();
164
+ node.activate();
165
+ } else {
166
+ node.toggle();
167
+ }
168
+ return;
169
+ }
170
+ if (e.key === "*") {
171
+ const node = tree.focusedNode;
172
+ if (!node) return;
173
+ tree.openSiblings(node);
174
+ return;
175
+ }
176
+ if (e.key === "ArrowDown" && e.metaKey) {
177
+ e.preventDefault();
178
+ tree.select(tree.focusedNode);
179
+ tree.activate(tree.focusedNode);
180
+ return;
181
+ }
182
+ if (e.key === "PageUp") {
183
+ e.preventDefault();
184
+ tree.pageUp();
185
+ return;
186
+ }
187
+ if (e.key === "PageDown") {
188
+ e.preventDefault();
189
+ tree.pageDown();
190
+ }
191
+
192
+ // If they type a sequence of characters
193
+ // collect them. Reset them after a timeout.
194
+ // Use it to search the tree for a node, then focus it.
195
+ // Clean this up a bit later
196
+ clearTimeout(timeoutId);
197
+ focusSearchTerm += e.key;
198
+ timeoutId = setTimeout(() => {
199
+ focusSearchTerm = "";
200
+ }, 600);
201
+ const node = tree.visibleNodes.find((n) => {
202
+ // @ts-ignore
203
+ const name = n.data.name;
204
+ if (typeof name === "string") {
205
+ return name.toLowerCase().startsWith(focusSearchTerm);
206
+ } else return false;
207
+ });
208
+ if (node) tree.focus(node.id);
209
+ }}
210
+ >
211
+ <FixedSizeList
212
+ className={tree.props.className}
213
+ outerRef={tree.listEl}
214
+ itemCount={tree.visibleNodes.length}
215
+ height={tree.height}
216
+ width={tree.width}
217
+ itemSize={tree.rowHeight}
218
+ itemKey={(index) => tree.visibleNodes[index]?.id || index}
219
+ outerElementType={ListOuterElement}
220
+ innerElementType={ListInnerElement}
221
+ onScroll={tree.props.onScroll}
222
+ onItemsRendered={tree.onItemsRendered.bind(tree)}
223
+ ref={tree.list}
224
+ >
225
+ {RowContainer}
226
+ </FixedSizeList>
227
+ </div>
228
+ );
229
+ }
@@ -1,26 +1,10 @@
1
1
  import React, { CSSProperties } from "react";
2
- import { useCursorLocation, useStaticContext } from "../context";
3
-
4
- export function DropCursor() {
5
- const treeView = useStaticContext();
6
- const cursor = useCursorLocation();
7
- if (!cursor || cursor.type !== "line") return null;
8
- const top = treeView.rowHeight * cursor.index;
9
- const left = treeView.indent * cursor.level;
10
- const style: CSSProperties = {
11
- position: "absolute",
12
- pointerEvents: "none",
13
- top: top - 2 + "px",
14
- left: treeView.indent + left + "px",
15
- right: treeView.indent + "px",
16
- };
17
-
18
- return <DefaultCursor style={style} />;
19
- }
2
+ import { DropCursorProps } from "../types/renderers";
20
3
 
21
4
  const placeholderStyle = {
22
5
  display: "flex",
23
6
  alignItems: "center",
7
+ zIndex: 1,
24
8
  };
25
9
 
26
10
  const lineStyle = {
@@ -37,11 +21,22 @@ const circleStyle = {
37
21
  borderRadius: "50%",
38
22
  };
39
23
 
40
- function DefaultCursor({ style }: { style: CSSProperties }) {
24
+ export const DefaultCursor = React.memo(function DefaultCursor({
25
+ top,
26
+ left,
27
+ indent,
28
+ }: DropCursorProps) {
29
+ const style: CSSProperties = {
30
+ position: "absolute",
31
+ pointerEvents: "none",
32
+ top: top - 2 + "px",
33
+ left: left + "px",
34
+ right: indent + "px",
35
+ };
41
36
  return (
42
37
  <div style={{ ...placeholderStyle, ...style }}>
43
38
  <div style={{ ...circleStyle }}></div>
44
39
  <div style={{ ...lineStyle }}></div>
45
40
  </div>
46
41
  );
47
- }
42
+ });