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
@@ -1,114 +0,0 @@
1
- import React, { useCallback, useMemo, useRef } from "react";
2
- import {
3
- useCursorParentId,
4
- useEditingId,
5
- useIsCursorOverFolder,
6
- useIsSelected,
7
- useStaticContext,
8
- } from "../context";
9
- import { useDragHook } from "../dnd/drag-hook";
10
- import { useDropHook } from "../dnd/drop-hook";
11
-
12
- type Props = {
13
- style: React.CSSProperties;
14
- index: number;
15
- };
16
-
17
- export const Row = React.memo(function Row({ index, style }: Props) {
18
- const tree = useStaticContext();
19
- const selected = useIsSelected();
20
- const node = tree.api.visibleNodes[index];
21
- const next = tree.api.visibleNodes[index + 1] || null;
22
- const prev = tree.api.visibleNodes[index - 1] || null;
23
- const cursorParentId = useCursorParentId();
24
- const cursorOverFolder = useIsCursorOverFolder();
25
- const el = useRef<HTMLDivElement | null>(null);
26
- const [{ isDragging }, dragRef] = useDragHook(node);
27
- const [, dropRef] = useDropHook(el, node, prev, next);
28
- const isEditing = node.id === useEditingId();
29
- const isSelected = selected(index);
30
- const nextSelected = next && selected(index + 1);
31
- const prevSelected = prev && selected(index - 1);
32
- const isHoveringOverChild = node.id === cursorParentId;
33
- const isOverFolder = node.id === cursorParentId && cursorOverFolder;
34
- const isOpen = node.isOpen;
35
- const indent = tree.indent * node.level;
36
- const state = useMemo(() => {
37
- return {
38
- isEditing,
39
- isDragging,
40
- isSelectedStart: isSelected && !prevSelected,
41
- isSelectedEnd: isSelected && !nextSelected,
42
- isSelected,
43
- isHoveringOverChild,
44
- isOpen,
45
- isOverFolder,
46
- };
47
- }, [
48
- isEditing,
49
- isSelected,
50
- prevSelected,
51
- nextSelected,
52
- isHoveringOverChild,
53
- isOpen,
54
- isDragging,
55
- isOverFolder,
56
- ]);
57
-
58
- const ref = useCallback(
59
- (n: HTMLDivElement | null) => {
60
- el.current = n;
61
- dragRef(dropRef(n));
62
- },
63
- [dragRef, dropRef]
64
- );
65
-
66
- const styles = useMemo(
67
- () => ({
68
- row: { ...style },
69
- indent: { paddingLeft: indent },
70
- }),
71
- [indent, style]
72
- );
73
-
74
- const handlers = useMemo(() => {
75
- return {
76
- select: (
77
- e: React.MouseEvent,
78
- args: { selectOnClick: boolean } = { selectOnClick: true }
79
- ) => {
80
- if (node.rowIndex === null) return;
81
- if (args.selectOnClick || e.metaKey || e.shiftKey) {
82
- tree.api.select(node.rowIndex, e.metaKey, e.shiftKey);
83
- } else {
84
- tree.api.select(null, false, false);
85
- }
86
- },
87
- toggle: (e: React.MouseEvent) => {
88
- e.stopPropagation();
89
- tree.onToggle(node.id, !node.isOpen);
90
- },
91
- edit: () => tree.api.edit(node.id),
92
- submit: (name: string) => {
93
- name.trim() ? tree.api.submit(node.id, name) : tree.api.reset(node.id);
94
- },
95
- reset: () => tree.api.reset(node.id),
96
- };
97
- }, [tree, node]);
98
-
99
- const Renderer = useMemo(() => {
100
- return React.memo(tree.renderer);
101
- }, [tree.renderer]);
102
-
103
- return (
104
- <Renderer
105
- innerRef={ref}
106
- data={node.model}
107
- styles={styles}
108
- state={state}
109
- handlers={handlers}
110
- preview={false}
111
- tree={tree.api}
112
- />
113
- );
114
- });
package/src/context.tsx DELETED
@@ -1,52 +0,0 @@
1
- import { createContext, useContext, useMemo } from "react";
2
- import { Cursor } from "./dnd/compute-drop";
3
- import { Selection } from "./selection/selection";
4
- import { IdObj, SelectionState, StaticContext } from "./types";
5
-
6
- export const CursorParentId = createContext<string | null>(null);
7
- export function useCursorParentId() {
8
- return useContext(CursorParentId);
9
- }
10
-
11
- export const IsCursorOverFolder = createContext<boolean>(false);
12
- export function useIsCursorOverFolder() {
13
- return useContext(IsCursorOverFolder);
14
- }
15
-
16
- export const CursorLocationContext = createContext<Cursor | null>(null);
17
- export function useCursorLocation() {
18
- return useContext(CursorLocationContext);
19
- }
20
-
21
- export const Static = createContext<StaticContext<IdObj> | null>(null);
22
- export function useStaticContext() {
23
- const value = useContext(Static);
24
- if (!value) throw new Error("Context must be in a provider");
25
- return value;
26
- }
27
-
28
- export const DispatchContext = createContext(null);
29
- export function useDispatch() {
30
- const dispatch = useContext(DispatchContext);
31
- if (!dispatch) throw new Error("No dispatch provided");
32
- return dispatch;
33
- }
34
-
35
- export const SelectionContext = createContext<SelectionState | null>(null);
36
- export function useSelectedIds(): string[] {
37
- const value = useContext(SelectionContext);
38
- if (!value) throw new Error("Must provide selection context");
39
- return value.ids;
40
- }
41
-
42
- export function useIsSelected(): (index: number | null) => boolean {
43
- const value = useContext(SelectionContext);
44
- if (!value) throw new Error("Must provide selection context");
45
- const s = useMemo(() => Selection.parse(value.data, []), [value.data]);
46
- return (i) => s.contains(i);
47
- }
48
-
49
- export const EditingIdContext = createContext<string | null>(null);
50
- export function useEditingId(): string | null {
51
- return useContext(EditingIdContext);
52
- }
@@ -1,74 +0,0 @@
1
- import { TreeProps, IdObj, Node } from "../types";
2
-
3
- function createNode<T extends IdObj>(
4
- model: T,
5
- level: number,
6
- parent: Node<T> | null,
7
- children: Node<T>[] | null,
8
- isOpen: boolean,
9
- isDraggable: boolean,
10
- isDroppable: boolean
11
- ): Node<T> {
12
- return {
13
- id: model.id,
14
- level,
15
- parent,
16
- children,
17
- isOpen,
18
- isDraggable,
19
- isDroppable,
20
- model,
21
- rowIndex: null,
22
- };
23
- }
24
-
25
- function access(obj: any, accessor: string | boolean | Function) {
26
- if (typeof accessor === "boolean") {
27
- return accessor;
28
- }
29
-
30
- if (typeof accessor === "string") {
31
- return obj[accessor];
32
- }
33
-
34
- return accessor(obj);
35
- }
36
-
37
- export function enrichTree<T extends IdObj>(
38
- model: T,
39
- hideRoot: boolean = false,
40
- getChildren: TreeProps<T>["getChildren"] = "children",
41
- isOpen: TreeProps<T>["isOpen"] = "isOpen",
42
- disableDrag: TreeProps<T>["disableDrag"] = false,
43
- disableDrop: TreeProps<T>["disableDrop"] = false,
44
- openByDefault: boolean = true
45
- ): Node<T> {
46
- function visitSelfAndChildren(
47
- model: T,
48
- level: number,
49
- parent: Node<T> | null
50
- ) {
51
- const open = access(model, isOpen) as boolean;
52
- const draggable = !access(model, disableDrag) as boolean;
53
- const droppable = !access(model, disableDrop) as boolean;
54
- const node = createNode<T>(
55
- model,
56
- level,
57
- parent,
58
- null,
59
- open === undefined ? openByDefault : open,
60
- draggable,
61
- droppable
62
- );
63
- const children = access(model, getChildren) as T[];
64
-
65
- if (children) {
66
- node.children = children.map((child: T) =>
67
- visitSelfAndChildren(child, level + 1, node)
68
- );
69
- }
70
- return node;
71
- }
72
-
73
- return visitSelfAndChildren(model, hideRoot ? -1 : 0, null);
74
- }
@@ -1,17 +0,0 @@
1
- import { Node } from "../types";
2
-
3
- export function flattenTree<T>(root: Node<T>): Node<T>[] {
4
- const list: Node<T>[] = [];
5
- let index = 0;
6
- function collect(node: Node<T>) {
7
- if (node.level >= 0) {
8
- node.rowIndex = index++;
9
- list.push(node);
10
- }
11
- if (node.isOpen) {
12
- node.children?.forEach(collect);
13
- }
14
- }
15
- collect(root);
16
- return list;
17
- }
package/src/provider.tsx DELETED
@@ -1,61 +0,0 @@
1
- import { useImperativeHandle, useMemo, useReducer, useRef } from "react";
2
- import { FixedSizeList } from "react-window";
3
- import {
4
- CursorLocationContext,
5
- CursorParentId,
6
- EditingIdContext,
7
- IsCursorOverFolder,
8
- SelectionContext,
9
- Static,
10
- } from "./context";
11
- import { Cursor } from "./dnd/compute-drop";
12
- import { initState, reducer } from "./reducer";
13
- import { useSelectionKeys } from "./selection/selection-hook";
14
- import { useTreeApi } from "./tree-api-hook";
15
- import { StateContext, StaticContext, TreeProviderProps } from "./types";
16
-
17
- export function TreeViewProvider<T>(props: TreeProviderProps<T>) {
18
- const [state, dispatch] = useReducer(reducer, initState());
19
- const list = useRef<FixedSizeList>();
20
- const api = useTreeApi<T>(state, dispatch, props, list.current);
21
-
22
- useImperativeHandle(props.imperativeHandle, () => api);
23
- useSelectionKeys(props.listEl, api);
24
- const staticValue = useMemo<StaticContext<T>>(
25
- () => ({ ...props, api, list }),
26
- [props, api, list]
27
- );
28
-
29
- /**
30
- * This context pattern is ridiculous, next time use redux.
31
- */
32
- return (
33
- // @ts-ignore
34
- <Static.Provider value={staticValue}>
35
- <EditingIdContext.Provider value={state.editingId}>
36
- <SelectionContext.Provider value={state.selection}>
37
- <CursorParentId.Provider value={getParentId(state.cursor)}>
38
- <IsCursorOverFolder.Provider value={isOverFolder(state)}>
39
- <CursorLocationContext.Provider value={state.cursor}>
40
- {props.children}
41
- </CursorLocationContext.Provider>
42
- </IsCursorOverFolder.Provider>
43
- </CursorParentId.Provider>
44
- </SelectionContext.Provider>
45
- </EditingIdContext.Provider>
46
- </Static.Provider>
47
- );
48
- }
49
-
50
- function getParentId(cursor: Cursor) {
51
- switch (cursor.type) {
52
- case "highlight":
53
- return cursor.id;
54
- default:
55
- return null;
56
- }
57
- }
58
-
59
- function isOverFolder(state: StateContext) {
60
- return state.cursor.type === "highlight";
61
- }
package/src/reducer.ts DELETED
@@ -1,161 +0,0 @@
1
- import { Cursor } from "./dnd/compute-drop";
2
- import { Selection } from "./selection/selection";
3
- import { StateContext } from "./types";
4
-
5
- export const initState = (): StateContext => ({
6
- visibleIds: [],
7
- cursor: { type: "none" } as Cursor,
8
- editingId: null,
9
- selection: {
10
- data: null,
11
- ids: [],
12
- },
13
- });
14
-
15
- export const actions = {
16
- setCursorLocation: (cursor: Cursor) => ({
17
- type: "SET_CURSOR_LOCATION" as "SET_CURSOR_LOCATION",
18
- cursor,
19
- }),
20
-
21
- setVisibleIds: (
22
- ids: string[], // index to id
23
- idMap: { [id: string]: number } // id to index
24
- ) => ({
25
- type: "SET_VISIBLE_IDS" as "SET_VISIBLE_IDS",
26
- ids,
27
- idMap,
28
- }),
29
-
30
- select: (index: number | null, meta: boolean, shift: boolean) => ({
31
- type: "SELECT" as "SELECT",
32
- index,
33
- meta,
34
- shift,
35
- }),
36
-
37
- selectId: (id: string) => ({
38
- type: "SELECT_ID" as "SELECT_ID",
39
- id,
40
- }),
41
-
42
- edit: (id: string | null) => ({
43
- type: "EDIT" as "EDIT",
44
- id,
45
- }),
46
-
47
- stepUp: (shift: boolean, ids: string[]) => ({
48
- type: "STEP_UP" as "STEP_UP",
49
- shift,
50
- }),
51
-
52
- stepDown: (shift: boolean, ids: string[]) => ({
53
- type: "STEP_DOWN" as "STEP_DOWN",
54
- shift,
55
- }),
56
- };
57
-
58
- type ActionObj = {
59
- [Prop in keyof typeof actions]: ReturnType<typeof actions[Prop]>;
60
- };
61
- export type Action = ActionObj[keyof ActionObj];
62
-
63
- export function reducer(state: StateContext, action: Action): StateContext {
64
- switch (action.type) {
65
- case "EDIT":
66
- return {
67
- ...state,
68
- editingId: action.id,
69
- };
70
- case "SET_CURSOR_LOCATION":
71
- if (equal(state.cursor, action.cursor)) {
72
- return state;
73
- } else {
74
- return { ...state, cursor: action.cursor };
75
- }
76
- case "SELECT":
77
- var s = Selection.parse(state.selection.data, state.visibleIds);
78
- if (action.index === null) {
79
- s.clear();
80
- } else if (action.meta) {
81
- if (s.contains(action.index)) {
82
- s.deselect(action.index);
83
- } else {
84
- s.multiSelect(action.index);
85
- }
86
- } else if (action.shift) {
87
- s.extend(action.index);
88
- } else {
89
- s.select(action.index);
90
- }
91
- return {
92
- ...state,
93
- selection: {
94
- data: s.serialize(),
95
- ids: s.getSelectedItems(),
96
- },
97
- };
98
- case "SELECT_ID":
99
- return {
100
- ...state,
101
- selection: {
102
- ...state.selection,
103
- ids: [action.id],
104
- },
105
- };
106
- case "STEP_UP":
107
- var s3 = Selection.parse(state.selection.data, state.visibleIds);
108
- var f = s3.getFocus();
109
- if (action.shift) {
110
- s3.extend(f - 1);
111
- } else {
112
- s3.select(f - 1);
113
- }
114
- return {
115
- ...state,
116
- selection: {
117
- data: s3.serialize(),
118
- ids: s3.getSelectedItems(),
119
- },
120
- };
121
- case "STEP_DOWN":
122
- var s6 = Selection.parse(state.selection.data, state.visibleIds);
123
- var f2 = s6.getFocus();
124
- if (action.shift) {
125
- s6.extend(f2 + 1);
126
- } else {
127
- s6.select(f2 + 1);
128
- }
129
- return {
130
- ...state,
131
- selection: {
132
- data: s6.serialize(),
133
- ids: s6.getSelectedItems(),
134
- },
135
- };
136
- case "SET_VISIBLE_IDS":
137
- // The visible ids changed
138
- var ids = state.selection.ids;
139
- // Start with a blank selection
140
- var s2 = new Selection([], null, "none", state.visibleIds);
141
- // Add each of the old selected ids to this new selection
142
- for (let id of ids) {
143
- if (id in action.idMap) s2.multiSelect(action.idMap[id]);
144
- }
145
- return {
146
- ...state,
147
- visibleIds: action.ids,
148
- selection: {
149
- ids,
150
- data: s2.serialize(),
151
- },
152
- };
153
- default:
154
- return state;
155
- }
156
- }
157
-
158
- function equal(a: Cursor | null, b: Cursor | null) {
159
- if (a === null || b === null) return false;
160
- return JSON.stringify(a) === JSON.stringify(b);
161
- }
@@ -1,41 +0,0 @@
1
- export class Range {
2
- constructor(public start: number, public end: number) {
3
- if (this.start > this.end)
4
- throw new Error("Invalid range: start larger than end");
5
- }
6
-
7
- serialize(): [number, number] {
8
- return [this.start, this.end];
9
- }
10
-
11
- contains(n: number) {
12
- return n >= this.start && n <= this.end;
13
- }
14
-
15
- overlaps(r: Range) {
16
- return this.contains(r.start - 1) || this.contains(r.end + 1);
17
- }
18
-
19
- combine(r: Range) {
20
- this.start = Math.min(r.start, this.start);
21
- this.end = Math.max(r.end, this.end);
22
- }
23
-
24
- get size() {
25
- return this.end - this.start + 1;
26
- }
27
-
28
- clone() {
29
- return new Range(this.start, this.end);
30
- }
31
-
32
- map(fn: (index: any) => string): any {
33
- let returns = [];
34
- for (let i = this.start; i <= this.end; i++) returns.push(fn(i));
35
- return returns;
36
- }
37
-
38
- isEqual(other: Range) {
39
- return this.start === other.start && this.end === other.end;
40
- }
41
- }
@@ -1,24 +0,0 @@
1
- import { MutableRefObject, useEffect } from "react";
2
- import { TreeApi } from "../tree-api";
3
-
4
- export function useSelectionKeys<T>(
5
- ref: MutableRefObject<HTMLDivElement | null>,
6
- api: TreeApi<T>
7
- ) {
8
- useEffect(() => {
9
- const el = ref.current;
10
- const cb = (e: KeyboardEvent) => {
11
- if (e.code === "ArrowDown") {
12
- e.preventDefault();
13
- api.selectDownwards(e.shiftKey);
14
- } else if (e.code === "ArrowUp") {
15
- e.preventDefault();
16
- api.selectUpwards(e.shiftKey);
17
- }
18
- };
19
- el?.addEventListener("keydown", cb);
20
- return () => {
21
- el?.removeEventListener("keydown", cb);
22
- };
23
- }, [ref, api]);
24
- }
@@ -1,111 +0,0 @@
1
- import { Selection } from "./selection";
2
-
3
- const createSelection = (...ranges: [number, number][]) => {
4
- return new Selection(ranges);
5
- };
6
-
7
- describe("select", () => {
8
- test("select one after end", () => {
9
- const s = createSelection([0, 0]);
10
- s.multiSelect(1);
11
- expect(s.getRanges()).toEqual([[0, 1]]);
12
- expect(s.direction).toEqual("forward");
13
- });
14
-
15
- test("select one before start", () => {
16
- const s = createSelection([1, 1]);
17
- s.multiSelect(0);
18
- expect(s.getRanges()).toEqual([[0, 1]]);
19
- expect(s.direction).toEqual("backward");
20
- });
21
-
22
- test("select between two ranges", () => {
23
- const s = createSelection([0, 0], [2, 2]);
24
- s.multiSelect(1);
25
- expect(s.getRanges()).toEqual([[0, 2]]);
26
- expect(s.direction).toEqual("forward");
27
- });
28
-
29
- test("select new spot", () => {
30
- const s = createSelection([0, 0]);
31
- s.multiSelect(5);
32
- expect(s.getRanges()).toEqual([
33
- [0, 0],
34
- [5, 5],
35
- ]);
36
- expect(s.direction).toEqual("none");
37
- });
38
- });
39
-
40
- describe("deselect", () => {
41
- test("one", () => {
42
- const s = createSelection([0, 0]);
43
- s.deselect(0);
44
- expect(s.getRanges()).toEqual([]);
45
- });
46
-
47
- test("start of a range", () => {
48
- const s = createSelection([0, 5]);
49
- s.deselect(0);
50
- expect(s.getRanges()).toEqual([[1, 5]]);
51
- });
52
-
53
- test("end of a range", () => {
54
- const s = createSelection([0, 5]);
55
- s.deselect(5);
56
- expect(s.getRanges()).toEqual([[0, 4]]);
57
- });
58
-
59
- test("between a range", () => {
60
- const s = createSelection([0, 5]);
61
- s.deselect(3);
62
- expect(s.getRanges()).toEqual([
63
- [0, 2],
64
- [4, 5],
65
- ]);
66
- });
67
- });
68
-
69
- describe("extend", () => {
70
- test("up", () => {
71
- const s = createSelection();
72
- s.multiSelect(5);
73
- s.extend(6);
74
- expect(s.getRanges()).toEqual([[5, 6]]);
75
- });
76
-
77
- test("down", () => {
78
- const s = createSelection();
79
- s.multiSelect(5);
80
- s.extend(4);
81
- expect(s.getRanges()).toEqual([[4, 5]]);
82
- });
83
-
84
- test("around anchor", () => {
85
- const s = createSelection([5, 10]);
86
- s.extend(1);
87
- expect(s.getRanges()).toEqual([[1, 5]]);
88
- });
89
-
90
- test("through other ranges", () => {
91
- const s = createSelection([0, 0], [5, 5], [9, 10]);
92
- s.multiSelect(2);
93
- s.extend(20);
94
- expect(s.getRanges()).toEqual([
95
- [0, 0],
96
- [2, 20],
97
- ]);
98
- });
99
-
100
- test("clicking backward", () => {
101
- const s = createSelection([15, 15]);
102
- s.extend(3);
103
- expect(s.getRanges()).toEqual([[3, 15]]);
104
- });
105
-
106
- test("split range then extend", () => {
107
- const s = createSelection([5, 10]);
108
- s.deselect(8);
109
- expect(s.currentIndex).toEqual(1);
110
- });
111
- });