react-arborist 1.2.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 (120) 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/provider.d.ts +11 -0
  10. package/dist/components/row-container.d.ts +8 -0
  11. package/dist/components/tree-container.d.ts +2 -0
  12. package/dist/components/tree.d.ts +5 -4
  13. package/dist/context.d.ts +20 -2
  14. package/dist/data/create-index.d.ts +5 -0
  15. package/dist/data/create-list.d.ts +4 -0
  16. package/dist/data/create-root.d.ts +5 -0
  17. package/dist/data/flatten-tree.d.ts +4 -2
  18. package/dist/data/simple-tree.d.ts +43 -0
  19. package/dist/dnd/compute-drop.d.ts +4 -4
  20. package/dist/dnd/drag-hook.d.ts +3 -4
  21. package/dist/dnd/drop-hook.d.ts +2 -3
  22. package/dist/hooks/use-fresh-node.d.ts +2 -0
  23. package/dist/hooks/use-simple-tree.d.ts +13 -0
  24. package/dist/hooks/use-uncontrolled-tree.d.ts +24 -0
  25. package/dist/hooks/use-validated-props.d.ts +3 -0
  26. package/dist/index.d.ts +8 -4
  27. package/dist/index.js +1900 -971
  28. package/dist/index.js.map +1 -1
  29. package/dist/interfaces/node-api.d.ts +67 -0
  30. package/dist/interfaces/tree-api.d.ts +112 -0
  31. package/dist/module.js +1886 -976
  32. package/dist/module.js.map +1 -1
  33. package/dist/state/dnd-slice.d.ts +20 -0
  34. package/dist/state/drag-slice.d.ts +7 -0
  35. package/dist/state/edit-slice.d.ts +8 -0
  36. package/dist/state/focus-slice.d.ts +12 -0
  37. package/dist/state/initial.d.ts +3 -0
  38. package/dist/state/open-slice.d.ts +30 -0
  39. package/dist/state/root-reducer.d.ts +13 -0
  40. package/dist/state/selection-slice.d.ts +36 -0
  41. package/dist/types/dnd.d.ts +9 -0
  42. package/dist/types/handlers.d.ts +24 -0
  43. package/dist/types/renderers.d.ts +30 -0
  44. package/dist/types/state.d.ts +2 -0
  45. package/dist/types/tree-props.d.ts +43 -0
  46. package/dist/types/utils.d.ts +21 -0
  47. package/dist/utils/props.d.ts +3 -0
  48. package/dist/utils.d.ts +15 -6
  49. package/package.json +10 -7
  50. package/src/components/cursor.tsx +15 -0
  51. package/src/components/default-container.tsx +229 -0
  52. package/src/components/{default-drop-cursor.tsx → default-cursor.tsx} +9 -8
  53. package/src/components/{preview.tsx → default-drag-preview.tsx} +25 -41
  54. package/src/components/default-node.tsx +15 -0
  55. package/src/components/default-row.tsx +21 -0
  56. package/src/components/drag-preview-container.tsx +26 -0
  57. package/src/components/list-inner-element.tsx +22 -0
  58. package/src/components/list-outer-element.tsx +26 -15
  59. package/src/components/provider.tsx +97 -0
  60. package/src/components/row-container.tsx +82 -0
  61. package/src/components/tree-container.tsx +13 -0
  62. package/src/components/tree.tsx +16 -44
  63. package/src/context.ts +36 -0
  64. package/src/data/create-index.ts +9 -0
  65. package/src/data/create-list.ts +56 -0
  66. package/src/data/create-root.ts +53 -0
  67. package/src/data/simple-tree.ts +103 -0
  68. package/src/dnd/compute-drop.ts +16 -16
  69. package/src/dnd/drag-hook.ts +25 -19
  70. package/src/dnd/drop-hook.ts +31 -17
  71. package/src/dnd/outer-drop-hook.ts +1 -1
  72. package/src/hooks/use-fresh-node.ts +16 -0
  73. package/src/hooks/use-simple-tree.ts +55 -0
  74. package/src/hooks/use-validated-props.ts +35 -0
  75. package/src/index.ts +9 -19
  76. package/src/interfaces/node-api.ts +187 -0
  77. package/src/interfaces/tree-api.ts +552 -0
  78. package/src/state/dnd-slice.ts +36 -0
  79. package/src/state/drag-slice.ts +31 -0
  80. package/src/state/edit-slice.ts +19 -0
  81. package/src/state/focus-slice.ts +28 -0
  82. package/src/state/initial.ts +14 -0
  83. package/src/state/open-slice.ts +53 -0
  84. package/src/state/root-reducer.ts +21 -0
  85. package/src/state/selection-slice.ts +75 -0
  86. package/src/types/dnd.ts +10 -0
  87. package/src/types/handlers.ts +24 -0
  88. package/src/types/renderers.ts +34 -0
  89. package/src/types/state.ts +3 -0
  90. package/src/types/tree-props.ts +63 -0
  91. package/src/types/utils.ts +26 -0
  92. package/src/utils/props.ts +8 -0
  93. package/src/utils.ts +125 -11
  94. package/README.md +0 -221
  95. package/dist/components/default-drop-cursor.d.ts +0 -3
  96. package/dist/components/list.d.ts +0 -4
  97. package/dist/components/preview.d.ts +0 -2
  98. package/dist/components/row.d.ts +0 -8
  99. package/dist/data/enrich-tree.d.ts +0 -2
  100. package/dist/provider.d.ts +0 -3
  101. package/dist/reducer.d.ts +0 -46
  102. package/dist/selection/range.d.ts +0 -13
  103. package/dist/selection/selection-hook.d.ts +0 -4
  104. package/dist/selection/selection.d.ts +0 -33
  105. package/dist/tree-api.d.ts +0 -50
  106. package/dist/types.d.ts +0 -122
  107. package/src/components/drop-cursor.tsx +0 -12
  108. package/src/components/list.tsx +0 -25
  109. package/src/components/row.tsx +0 -112
  110. package/src/context.tsx +0 -13
  111. package/src/data/enrich-tree.ts +0 -74
  112. package/src/data/flatten-tree.ts +0 -17
  113. package/src/provider.tsx +0 -41
  114. package/src/reducer.ts +0 -161
  115. package/src/selection/range.ts +0 -41
  116. package/src/selection/selection-hook.ts +0 -25
  117. package/src/selection/selection.test.ts +0 -111
  118. package/src/selection/selection.ts +0 -186
  119. package/src/tree-api.ts +0 -230
  120. package/src/types.ts +0 -148
@@ -1,112 +0,0 @@
1
- import React, { useCallback, useMemo, useRef } from "react";
2
- import { useTreeApi } from "../context";
3
- import { useDragHook } from "../dnd/drag-hook";
4
- import { useDropHook } from "../dnd/drop-hook";
5
- import { IdObj } from "../types";
6
-
7
- type Props = {
8
- style: React.CSSProperties;
9
- index: number;
10
- };
11
-
12
- export const Row = React.memo(function Row<T extends IdObj>({
13
- index,
14
- style,
15
- }: Props) {
16
- const realTree = useTreeApi<T>();
17
- const tree = useMemo(() => realTree, []);
18
- tree.sync(realTree);
19
-
20
- const node = tree.visibleNodes[index];
21
- const next = tree.visibleNodes[index + 1] || null;
22
- const prev = tree.visibleNodes[index - 1] || null;
23
- const el = useRef<HTMLDivElement | null>(null);
24
- const [{ isDragging }, dragRef] = useDragHook(node);
25
- const [, dropRef] = useDropHook(el, node, prev, next);
26
- const isEditing = node.id === tree.editingId;
27
- const isSelected = tree.isSelected(index);
28
- const nextSelected = next && tree.isSelected(index + 1);
29
- const prevSelected = prev && tree.isSelected(index - 1);
30
- const isHoveringOverChild = node.id === tree.cursorParentId;
31
- const isOverFolder = node.id === tree.cursorParentId && tree.cursorOverFolder;
32
- const isOpen = node.isOpen;
33
- const indent = tree.indent * node.level;
34
- const state = useMemo(() => {
35
- return {
36
- isEditing,
37
- isDragging,
38
- isSelectedStart: isSelected && !prevSelected,
39
- isSelectedEnd: isSelected && !nextSelected,
40
- isSelected,
41
- isHoveringOverChild,
42
- isOpen,
43
- isOverFolder,
44
- };
45
- }, [
46
- isEditing,
47
- isSelected,
48
- prevSelected,
49
- nextSelected,
50
- isHoveringOverChild,
51
- isOpen,
52
- isDragging,
53
- isOverFolder,
54
- ]);
55
-
56
- const ref = useCallback(
57
- (n: HTMLDivElement | null) => {
58
- el.current = n;
59
- dragRef(dropRef(n));
60
- },
61
- [dragRef, dropRef]
62
- );
63
-
64
- const styles = useMemo(
65
- () => ({
66
- row: { ...style },
67
- indent: { paddingLeft: indent },
68
- }),
69
- [indent, style]
70
- );
71
-
72
- const handlers = useMemo(() => {
73
- return {
74
- select: (
75
- e: React.MouseEvent,
76
- args: { selectOnClick: boolean } = { selectOnClick: true }
77
- ) => {
78
- if (node.rowIndex === null) return;
79
- if (args.selectOnClick || e.metaKey || e.shiftKey) {
80
- tree.select(node.rowIndex, e.metaKey, e.shiftKey);
81
- } else {
82
- tree.select(null, false, false);
83
- }
84
- },
85
- toggle: (e: React.MouseEvent) => {
86
- e.stopPropagation();
87
- tree.onToggle(node.id, !node.isOpen);
88
- },
89
- edit: () => tree.edit(node.id),
90
- submit: (name: string) => {
91
- name.trim() ? tree.submit(node.id, name) : tree.reset(node.id);
92
- },
93
- reset: () => tree.reset(node.id),
94
- };
95
- }, [node, tree]);
96
-
97
- const Renderer = useMemo(() => {
98
- return React.memo(tree.renderer);
99
- }, [tree.renderer]);
100
-
101
- return (
102
- <Renderer
103
- innerRef={ref}
104
- data={node.model}
105
- styles={styles}
106
- state={state}
107
- handlers={handlers}
108
- preview={false}
109
- tree={tree}
110
- />
111
- );
112
- });
package/src/context.tsx DELETED
@@ -1,13 +0,0 @@
1
- import React, { createContext, useContext, useMemo } from "react";
2
- import { TreeApi } from "./tree-api";
3
- import { IdObj } from "./types";
4
-
5
- export const TreeApiContext = createContext<TreeApi<any> | null>(null);
6
-
7
- export function useTreeApi<T extends IdObj>() {
8
- const value = useContext<TreeApi<T> | null>(
9
- TreeApiContext as unknown as React.Context<TreeApi<T> | null>
10
- );
11
- if (value === null) throw new Error("No Tree Api Provided");
12
- return value;
13
- }
@@ -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,41 +0,0 @@
1
- import {
2
- useImperativeHandle,
3
- useLayoutEffect,
4
- useMemo,
5
- useReducer,
6
- useRef,
7
- } from "react";
8
- import { FixedSizeList } from "react-window";
9
- import { TreeApiContext } from "./context";
10
- import { actions, initState, reducer } from "./reducer";
11
- import { useSelectionKeys } from "./selection/selection-hook";
12
- import { TreeApi } from "./tree-api";
13
- import { IdObj, TreeProviderProps } from "./types";
14
-
15
- export function TreeViewProvider<T extends IdObj>(props: TreeProviderProps<T>) {
16
- const [state, dispatch] = useReducer(reducer, initState());
17
- const list = useRef<FixedSizeList | null>(null);
18
- const listEl = useRef<HTMLDivElement | null>(null);
19
-
20
- const api = useMemo(
21
- () => new TreeApi<T>(dispatch, state, props, list, listEl),
22
- [dispatch, state, props, list, listEl]
23
- );
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.visibleIds, api.idToIndex, props.root]);
32
-
33
- useImperativeHandle(props.imperativeHandle, () => api);
34
- useSelectionKeys(listEl, api);
35
-
36
- return (
37
- <TreeApiContext.Provider value={api}>
38
- {props.children}
39
- </TreeApiContext.Provider>
40
- );
41
- }
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,25 +0,0 @@
1
- import { MutableRefObject, useEffect } from "react";
2
- import { TreeApi } from "../tree-api";
3
- import { IdObj } from "../types";
4
-
5
- export function useSelectionKeys<T extends IdObj>(
6
- ref: MutableRefObject<HTMLDivElement | null>,
7
- api: TreeApi<T>
8
- ) {
9
- useEffect(() => {
10
- const el = ref.current;
11
- const cb = (e: KeyboardEvent) => {
12
- if (e.code === "ArrowDown") {
13
- e.preventDefault();
14
- api.selectDownwards(e.shiftKey);
15
- } else if (e.code === "ArrowUp") {
16
- e.preventDefault();
17
- api.selectUpwards(e.shiftKey);
18
- }
19
- };
20
- el?.addEventListener("keydown", cb);
21
- return () => {
22
- el?.removeEventListener("keydown", cb);
23
- };
24
- }, [ref, api]);
25
- }
@@ -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
- });