react-arborist 2.3.0 → 3.0.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.
@@ -3,18 +3,27 @@ import { ActionTypes } from "../types/utils";
3
3
  export declare type DndState = {
4
4
  dragId: null | string;
5
5
  cursor: Cursor;
6
+ dragIds: string[];
7
+ parentId: null | string;
8
+ index: number;
6
9
  };
7
10
  export declare const actions: {
8
11
  cursor(cursor: Cursor): {
9
12
  type: "DND_CURSOR";
10
13
  cursor: Cursor;
11
14
  };
12
- dragStart(id: string): {
15
+ dragStart(id: string, dragIds: string[]): {
13
16
  type: "DND_DRAG_START";
14
17
  id: string;
18
+ dragIds: string[];
15
19
  };
16
20
  dragEnd(): {
17
21
  type: "DND_DRAG_END";
18
22
  };
23
+ hovering(parentId: string | null, index: number): {
24
+ type: "DND_HOVERING";
25
+ parentId: string | null;
26
+ index: number;
27
+ };
19
28
  };
20
29
  export declare function reducer(state: DndState | undefined, action: ActionTypes<typeof actions>): DndState;
@@ -4,6 +4,5 @@ export declare type CursorLocation = {
4
4
  parentId: string | null;
5
5
  };
6
6
  export declare type DragItem = {
7
- dragIds: string[];
8
7
  id: string;
9
8
  };
@@ -25,13 +25,18 @@ export interface TreeProps<T> {
25
25
  paddingTop?: number;
26
26
  paddingBottom?: number;
27
27
  padding?: number;
28
+ childrenAccessor?: string | ((d: T) => T[] | null);
29
+ idAccessor?: string | ((d: T) => string);
28
30
  openByDefault?: boolean;
29
31
  selectionFollowsFocus?: boolean;
30
32
  disableMultiSelection?: boolean;
33
+ disableEdit?: string | boolean | BoolFunc<T>;
31
34
  disableDrag?: string | boolean | BoolFunc<T>;
32
- disableDrop?: string | boolean | BoolFunc<T>;
33
- childrenAccessor?: string | ((d: T) => T[] | null);
34
- idAccessor?: string | ((d: T) => string);
35
+ disableDrop?: string | boolean | ((args: {
36
+ parentNode: NodeApi<T>;
37
+ dragNodes: NodeApi<T>[];
38
+ index: number;
39
+ }) => boolean);
35
40
  onActivate?: (node: NodeApi<T>) => void;
36
41
  onSelect?: (nodes: NodeApi<T>[]) => void;
37
42
  onScroll?: (props: ListOnScrollProps) => void;
@@ -1,13 +1,9 @@
1
- /// <reference types="react" />
2
1
  import { AnyAction } from "redux";
3
2
  import { NodeApi } from "../interfaces/node-api";
4
3
  export interface IdObj {
5
4
  id: string;
6
5
  }
7
6
  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
7
  export declare type BoolFunc<T> = (data: T) => boolean;
12
8
  export declare type ActionTypes<Actions extends {
13
9
  [name: string]: (...args: any[]) => AnyAction;
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "react-arborist",
3
- "version": "2.3.0",
3
+ "version": "3.0.1",
4
4
  "license": "MIT",
5
5
  "source": "src/index.ts",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/module.js",
8
8
  "types": "dist/index.d.ts",
9
- "repository": "github:brimdata/react-arborist",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/brimdata/react-arborist.git"
12
+ },
10
13
  "homepage": "https://react-arborist.netlify.app",
11
14
  "bugs": "https://github.com/brimdata/react-arborist/issues",
12
15
  "keywords": [
@@ -160,9 +160,11 @@ export function DefaultContainer() {
160
160
  return;
161
161
  }
162
162
  if (e.key === "Enter") {
163
- if (!tree.props.onRename) return;
163
+ const node = tree.focusedNode;
164
+ if (!node) return;
165
+ if (!node.isEditable || !tree.props.onRename) return;
164
166
  setTimeout(() => {
165
- if (tree.focusedNode) tree.edit(tree.focusedNode);
167
+ if (node) tree.edit(node);
166
168
  });
167
169
  return;
168
170
  }
@@ -8,7 +8,7 @@ import { TreeProps } from "../types/tree-props";
8
8
  import { IdObj } from "../types/utils";
9
9
  import { useValidatedProps } from "../hooks/use-validated-props";
10
10
 
11
- export const Tree = forwardRef(function Tree<T>(
11
+ function TreeComponent<T>(
12
12
  props: TreeProps<T>,
13
13
  ref: React.Ref<TreeApi<T> | undefined>
14
14
  ) {
@@ -21,4 +21,8 @@ export const Tree = forwardRef(function Tree<T>(
21
21
  <DragPreviewContainer />
22
22
  </TreeProvider>
23
23
  );
24
- });
24
+ }
25
+
26
+ export const Tree = forwardRef(TreeComponent) as <T>(
27
+ props: TreeProps<T> & { ref?: React.ForwardedRef<TreeApi<T> | undefined> }
28
+ ) => ReturnType<typeof TreeComponent>;
@@ -19,7 +19,6 @@ export function createRoot<T>(tree: TreeApi<T>): NodeApi<T> {
19
19
  id,
20
20
  children: null,
21
21
  isDraggable: tree.isDraggable(data),
22
- isDroppable: tree.isDroppable(data),
23
22
  rowIndex: null,
24
23
  });
25
24
  const children = tree.accessChildren(data);
@@ -40,7 +39,6 @@ export function createRoot<T>(tree: TreeApi<T>): NodeApi<T> {
40
39
  parent: null,
41
40
  children: null,
42
41
  isDraggable: true,
43
- isDroppable: true,
44
42
  rowIndex: null,
45
43
  });
46
44
 
@@ -78,21 +78,6 @@ function getDropLevel(
78
78
  return bound(hoverLevel, min, max);
79
79
  }
80
80
 
81
- function canDrop(above: NodeApi | null, below: NodeApi | null) {
82
- if (!above) {
83
- return true;
84
- }
85
-
86
- let n: NodeApi | null = above;
87
- if (isClosed(above) && above !== below) n = above.parent;
88
-
89
- while (n) {
90
- if (!n.isDroppable) return false;
91
- n = n.parent;
92
- }
93
- return true;
94
- }
95
-
96
81
  export type ComputedDrop = {
97
82
  drop: DropResult | null;
98
83
  cursor: Cursor | null;
@@ -147,10 +132,6 @@ export function computeDrop(args: Args): ComputedDrop {
147
132
  const { node, nextNode, prevNode } = args;
148
133
  const [above, below] = getNodesAroundCursor(node, prevNode, nextNode, hover);
149
134
 
150
- if (!canDrop(above, below)) {
151
- return { drop: null, cursor: noCursor() };
152
- }
153
-
154
135
  /* Hovering over the middle of a folder */
155
136
  if (node && node.isInternal && hover.inMiddle) {
156
137
  return {
@@ -4,7 +4,6 @@ import { getEmptyImage } from "react-dnd-html5-backend";
4
4
  import { useTreeApi } from "../context";
5
5
  import { NodeApi } from "../interfaces/node-api";
6
6
  import { DragItem } from "../types/dnd";
7
- import { IdObj } from "../types/utils";
8
7
  import { DropResult } from "./drop-hook";
9
8
  import { actions as dnd } from "../state/dnd-slice";
10
9
  import { safeRun } from "../utils";
@@ -17,30 +16,28 @@ export function useDragHook<T>(node: NodeApi<T>): ConnectDragSource {
17
16
  () => ({
18
17
  canDrag: () => node.isDraggable,
19
18
  type: "NODE",
20
- item: () => ({
21
- id: node.id,
22
- dragIds: tree.isSelected(node.id) ? Array.from(ids) : [node.id],
23
- }),
24
- start: () => {
25
- tree.dispatch(dnd.dragStart(node.id));
19
+ item: () => {
20
+ // This is fired once at the begging of a drag operation
21
+ const dragIds = tree.isSelected(node.id) ? Array.from(ids) : [node.id];
22
+ tree.dispatch(dnd.dragStart(node.id, dragIds));
23
+ return { id: node.id };
26
24
  },
27
- end: (item, monitor) => {
28
- tree.dispatch(dnd.dragEnd());
25
+ end: () => {
29
26
  tree.hideCursor();
30
- const drop = monitor.getDropResult();
27
+ let { parentId, index, dragIds } = tree.state.dnd;
31
28
  // If they held down meta, we need to create a copy
32
29
  // if (drop.dropEffect === "copy")
33
- if (drop && drop.parentId) {
34
- const parentId = drop.parentId === ROOT_ID ? null : drop.parentId;
30
+ if (tree.canDrop()) {
35
31
  safeRun(tree.props.onMove, {
36
- dragIds: item.dragIds,
37
- parentId,
38
- index: drop.index,
39
- dragNodes: item.dragIds.map((id) => tree.get(id)!),
40
- parentNode: tree.get(drop.parentId),
32
+ dragIds,
33
+ parentId: parentId === ROOT_ID ? null : parentId,
34
+ index,
35
+ dragNodes: tree.dragNodes,
36
+ parentNode: tree.get(parentId),
41
37
  });
42
- tree.open(drop.parentId);
38
+ tree.open(parentId);
43
39
  }
40
+ tree.dispatch(dnd.dragEnd());
44
41
  },
45
42
  }),
46
43
  [ids, node]
@@ -3,8 +3,8 @@ import { ConnectDropTarget, useDrop } from "react-dnd";
3
3
  import { useTreeApi } from "../context";
4
4
  import { NodeApi } from "../interfaces/node-api";
5
5
  import { DragItem } from "../types/dnd";
6
- import { isDecendent } from "../utils";
7
6
  import { computeDrop } from "./compute-drop";
7
+ import { actions as dnd } from "../state/dnd-slice";
8
8
 
9
9
  export type DropResult = {
10
10
  parentId: string | null;
@@ -19,11 +19,11 @@ export function useDropHook(
19
19
  const [_, dropRef] = useDrop<DragItem, DropResult | null, void>(
20
20
  () => ({
21
21
  accept: "NODE",
22
- canDrop: (item, m) => {
23
- if (node.tree.isFiltered) return false;
22
+ canDrop: () => tree.canDrop(),
23
+ hover: (_item, m) => {
24
24
  const offset = m.getClientOffset();
25
- if (!el.current || !offset) return false;
26
- const { drop } = computeDrop({
25
+ if (!el.current || !offset) return;
26
+ const { cursor, drop } = computeDrop({
27
27
  element: el.current,
28
28
  offset: offset,
29
29
  indent: tree.indent,
@@ -31,46 +31,16 @@ export function useDropHook(
31
31
  prevNode: node.prev,
32
32
  nextNode: node.next,
33
33
  });
34
- if (!drop) return false;
35
- const dropParent = tree.get(drop.parentId) ?? tree.root;
34
+ if (drop) tree.dispatch(dnd.hovering(drop.parentId, drop.index));
36
35
 
37
- for (let id of item.dragIds) {
38
- const drag = tree.get(id);
39
- if (!drag) return false;
40
- if (!dropParent) return false;
41
- if (drag.isInternal && isDecendent(dropParent, drag)) return false;
42
- }
43
- return true;
44
- },
45
- hover: (item, m) => {
46
36
  if (m.canDrop()) {
47
- const offset = m.getClientOffset();
48
- if (!el.current || !offset) return;
49
- const { cursor } = computeDrop({
50
- element: el.current,
51
- offset: offset,
52
- indent: tree.indent,
53
- node: node,
54
- prevNode: node.prev,
55
- nextNode: node.next,
56
- });
57
37
  if (cursor) tree.showCursor(cursor);
58
38
  } else {
59
39
  tree.hideCursor();
60
40
  }
61
41
  },
62
- drop: (item, m): DropResult | undefined | null => {
63
- const offset = m.getClientOffset();
64
- if (!el.current || !offset) return;
65
- const { drop } = computeDrop({
66
- element: el.current,
67
- offset: offset,
68
- indent: tree.indent,
69
- node: node,
70
- prevNode: node.prev,
71
- nextNode: node.next,
72
- });
73
- return drop;
42
+ drop: (_, m) => {
43
+ if (!m.canDrop()) return null;
74
44
  },
75
45
  }),
76
46
  [node, el.current, tree.props]
@@ -1,9 +1,9 @@
1
1
  import { useDrop } from "react-dnd";
2
2
  import { useTreeApi } from "../context";
3
3
  import { DragItem } from "../types/dnd";
4
- import { isDecendent } from "../utils";
5
4
  import { computeDrop } from "./compute-drop";
6
5
  import { DropResult } from "./drop-hook";
6
+ import { actions as dnd } from "../state/dnd-slice";
7
7
 
8
8
  export function useOuterDrop() {
9
9
  const tree = useTreeApi();
@@ -12,53 +12,15 @@ export function useOuterDrop() {
12
12
  const [, drop] = useDrop<DragItem, DropResult | null, { isOver: boolean }>(
13
13
  () => ({
14
14
  accept: "NODE",
15
- hover: (item, m) => {
16
- if (!m.isOver({ shallow: true })) return;
17
- if (m.canDrop()) {
18
- const offset = m.getClientOffset();
19
- if (!tree.listEl.current || !offset) return;
20
- const { cursor } = computeDrop({
21
- element: tree.listEl.current,
22
- offset: offset,
23
- indent: tree.indent,
24
- node: null,
25
- prevNode: tree.visibleNodes[tree.visibleNodes.length - 1],
26
- nextNode: null,
27
- });
28
- if (cursor) tree.showCursor(cursor);
29
- } else {
30
- tree.hideCursor();
31
- }
32
- },
33
- canDrop: (item, m) => {
15
+ canDrop: (_item, m) => {
34
16
  if (!m.isOver({ shallow: true })) return false;
35
- if (tree.isFiltered) return false;
36
- const offset = m.getClientOffset();
37
- if (!tree.listEl.current || !offset) return false;
38
- const { drop } = computeDrop({
39
- element: tree.listEl.current,
40
- offset: offset,
41
- indent: tree.indent,
42
- node: null,
43
- prevNode: tree.visibleNodes[tree.visibleNodes.length - 1],
44
- nextNode: null,
45
- });
46
- if (!drop) return false;
47
- const dropParent = tree.get(drop.parentId) ?? tree.root;
48
-
49
- for (let id of item.dragIds) {
50
- const drag = tree.get(id);
51
- if (!drag) return false;
52
- if (!dropParent) return false;
53
- if (drag.isInternal && isDecendent(dropParent, drag)) return false;
54
- }
55
- return true;
17
+ return tree.canDrop();
56
18
  },
57
- drop: (item, m) => {
58
- if (m.didDrop()) return;
19
+ hover: (_item, m) => {
20
+ if (!m.isOver({ shallow: true })) return;
59
21
  const offset = m.getClientOffset();
60
22
  if (!tree.listEl.current || !offset) return;
61
- const { drop } = computeDrop({
23
+ const { cursor, drop } = computeDrop({
62
24
  element: tree.listEl.current,
63
25
  offset: offset,
64
26
  indent: tree.indent,
@@ -66,7 +28,13 @@ export function useOuterDrop() {
66
28
  prevNode: tree.visibleNodes[tree.visibleNodes.length - 1],
67
29
  nextNode: null,
68
30
  });
69
- return drop;
31
+ if (drop) tree.dispatch(dnd.hovering(drop.parentId, drop.index));
32
+
33
+ if (m.canDrop()) {
34
+ if (cursor) tree.showCursor(cursor);
35
+ } else {
36
+ tree.hideCursor();
37
+ }
70
38
  },
71
39
  }),
72
40
  [tree]
@@ -10,7 +10,6 @@ type Params<T> = {
10
10
  children: NodeApi<T>[] | null;
11
11
  parent: NodeApi<T> | null;
12
12
  isDraggable: boolean;
13
- isDroppable: boolean;
14
13
  rowIndex: number | null;
15
14
  tree: TreeApi<T>;
16
15
  };
@@ -23,7 +22,6 @@ export class NodeApi<T = any> {
23
22
  children: NodeApi<T>[] | null;
24
23
  parent: NodeApi<T> | null;
25
24
  isDraggable: boolean;
26
- isDroppable: boolean;
27
25
  rowIndex: number | null;
28
26
 
29
27
  constructor(params: Params<T>) {
@@ -34,7 +32,6 @@ export class NodeApi<T = any> {
34
32
  this.children = params.children;
35
33
  this.parent = params.parent;
36
34
  this.isDraggable = params.isDraggable;
37
- this.isDroppable = params.isDroppable;
38
35
  this.rowIndex = params.rowIndex;
39
36
  }
40
37
 
@@ -58,6 +55,10 @@ export class NodeApi<T = any> {
58
55
  return this.isLeaf ? false : !this.tree.isOpen(this.id);
59
56
  }
60
57
 
58
+ get isEditable() {
59
+ return this.tree.isEditable(this.data);
60
+ }
61
+
61
62
  get isEditing() {
62
63
  return this.tree.editingId === this.id;
63
64
  }
@@ -398,6 +398,41 @@ export class TreeApi<T> {
398
398
  return this.state.dnd.cursor.type === "highlight";
399
399
  }
400
400
 
401
+ get dragNodes() {
402
+ return this.state.dnd.dragIds
403
+ .map((id) => this.get(id))
404
+ .filter((n) => !!n) as NodeApi<T>[];
405
+ }
406
+
407
+ canDrop() {
408
+ if (this.isFiltered) return false;
409
+ const parentNode = this.get(this.state.dnd.parentId) ?? this.root;
410
+ const dragNodes = this.dragNodes;
411
+ const check = this.props.disableDrop;
412
+
413
+ for (const drag of dragNodes) {
414
+ if (!drag) return false;
415
+ if (!parentNode) return false;
416
+ if (drag.isInternal && utils.isDecendent(parentNode, drag)) return false;
417
+ }
418
+
419
+ // Allow the user to insert their own logic
420
+ if (typeof check == "function") {
421
+ return check({
422
+ parentNode,
423
+ dragNodes: this.dragNodes,
424
+ index: this.state.dnd.index,
425
+ });
426
+ } else if (typeof check == "string") {
427
+ // @ts-ignore
428
+ return !!parentNode.data[check];
429
+ } else if (typeof check === "boolean") {
430
+ return check;
431
+ } else {
432
+ return true;
433
+ }
434
+ }
435
+
401
436
  hideCursor() {
402
437
  this.dispatch(dnd.cursor({ type: "none" }));
403
438
  }
@@ -529,13 +564,13 @@ export class TreeApi<T> {
529
564
  }
530
565
  }
531
566
 
532
- isDraggable(data: T) {
533
- const check = this.props.disableDrag || (() => false);
567
+ isEditable(data: T) {
568
+ const check = this.props.disableEdit || (() => false);
534
569
  return !utils.access(data, check) ?? true;
535
570
  }
536
571
 
537
- isDroppable(data: T) {
538
- const check = this.props.disableDrop || (() => false);
572
+ isDraggable(data: T) {
573
+ const check = this.props.disableDrag || (() => false);
539
574
  return !utils.access(data, check) ?? true;
540
575
  }
541
576
 
@@ -3,33 +3,44 @@ import { ActionTypes } from "../types/utils";
3
3
  import { initialState } from "./initial";
4
4
 
5
5
  /* Types */
6
- export type DndState = { dragId: null | string; cursor: Cursor };
6
+ export type DndState = {
7
+ dragId: null | string;
8
+ cursor: Cursor;
9
+ dragIds: string[];
10
+ parentId: null | string;
11
+ index: number;
12
+ };
7
13
 
8
14
  /* Actions */
9
15
  export const actions = {
10
16
  cursor(cursor: Cursor) {
11
17
  return { type: "DND_CURSOR" as const, cursor };
12
18
  },
13
- dragStart(id: string) {
14
- return { type: "DND_DRAG_START" as const, id };
19
+ dragStart(id: string, dragIds: string[]) {
20
+ return { type: "DND_DRAG_START" as const, id, dragIds };
15
21
  },
16
22
  dragEnd() {
17
23
  return { type: "DND_DRAG_END" as const };
18
24
  },
25
+ hovering(parentId: string | null, index: number) {
26
+ return { type: "DND_HOVERING" as const, parentId, index };
27
+ },
19
28
  };
20
29
 
21
30
  /* Reducer */
22
31
  export function reducer(
23
32
  state: DndState = initialState()["dnd"],
24
33
  action: ActionTypes<typeof actions>
25
- ) {
34
+ ): DndState {
26
35
  switch (action.type) {
27
36
  case "DND_CURSOR":
28
37
  return { ...state, cursor: action.cursor };
29
38
  case "DND_DRAG_START":
30
- return { ...state, dragId: action.id };
39
+ return { ...state, dragId: action.id, dragIds: action.dragIds };
31
40
  case "DND_DRAG_END":
32
- return { ...state, dragId: null };
41
+ return initialState()["dnd"];
42
+ case "DND_HOVERING":
43
+ return { ...state, parentId: action.parentId, index: action.index };
33
44
  default:
34
45
  return state;
35
46
  }
@@ -10,5 +10,11 @@ export const initialState = (props?: TreeProps<any>): RootState => ({
10
10
  drag: { id: null, idWillReceiveDrop: null },
11
11
  selection: { ids: new Set(), anchor: null, mostRecent: null },
12
12
  },
13
- dnd: { cursor: { type: "none" }, dragId: null },
13
+ dnd: {
14
+ cursor: { type: "none" },
15
+ dragId: null,
16
+ dragIds: [],
17
+ parentId: null,
18
+ index: -1,
19
+ },
14
20
  });
package/src/types/dnd.ts CHANGED
@@ -5,6 +5,5 @@ export type CursorLocation = {
5
5
  };
6
6
 
7
7
  export type DragItem = {
8
- dragIds: string[];
9
8
  id: string;
10
9
  };
@@ -4,7 +4,7 @@ import * as renderers from "./renderers";
4
4
  import { ElementType, MouseEventHandler } from "react";
5
5
  import { ListOnScrollProps } from "react-window";
6
6
  import { NodeApi } from "../interfaces/node-api";
7
- import { OpenMap, OpenSlice } from "../state/open-slice";
7
+ import { OpenMap } from "../state/open-slice";
8
8
 
9
9
  export interface TreeProps<T> {
10
10
  /* Data Options */
@@ -35,13 +35,21 @@ export interface TreeProps<T> {
35
35
  padding?: number;
36
36
 
37
37
  /* Config */
38
+ childrenAccessor?: string | ((d: T) => T[] | null);
39
+ idAccessor?: string | ((d: T) => string);
38
40
  openByDefault?: boolean;
39
41
  selectionFollowsFocus?: boolean;
40
42
  disableMultiSelection?: boolean;
43
+ disableEdit?: string | boolean | BoolFunc<T>;
41
44
  disableDrag?: string | boolean | BoolFunc<T>;
42
- disableDrop?: string | boolean | BoolFunc<T>;
43
- childrenAccessor?: string | ((d: T) => T[] | null);
44
- idAccessor?: string | ((d: T) => string);
45
+ disableDrop?:
46
+ | string
47
+ | boolean
48
+ | ((args: {
49
+ parentNode: NodeApi<T>;
50
+ dragNodes: NodeApi<T>[];
51
+ index: number;
52
+ }) => boolean);
45
53
 
46
54
  /* Event Handlers */
47
55
  onActivate?: (node: NodeApi<T>) => void;
@@ -7,14 +7,6 @@ export interface IdObj {
7
7
 
8
8
  export type Identity = string | IdObj | null;
9
9
 
10
- // Forward ref can't forward generics without this little re-declare
11
- // https://fettblog.eu/typescript-react-generic-forward-refs/
12
- declare module "react" {
13
- function forwardRef<T, P = {}>(
14
- render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
15
- ): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
16
- }
17
-
18
10
  export type BoolFunc<T> = (data: T) => boolean;
19
11
 
20
12
  export type ActionTypes<
package/src/utils.ts CHANGED
@@ -173,6 +173,6 @@ export function getInsertParentId(tree: TreeApi<any>) {
173
173
  const focus = tree.focusedNode;
174
174
  if (!focus) return null;
175
175
  if (focus.isOpen) return focus.id;
176
- if (focus.parent) return focus.parent.id;
176
+ if (focus.parent && !focus.parent.isRoot) return focus.parent.id;
177
177
  return null;
178
178
  }
package/tsconfig.json CHANGED
@@ -1,28 +1,4 @@
1
1
  {
2
+ "extends": "../../tsconfig.json",
2
3
  "include": ["src/index.ts"],
3
- "compilerOptions": {
4
- /* Visit https://aka.ms/tsconfig.json to read more about this file */
5
-
6
- /* Language and Environment */
7
- "target": "ES5", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
8
- "jsx": "react-jsx", /* Specify what JSX code is generated. */
9
-
10
- /* Modules */
11
- "module": "CommonJS", /* Specify what module code is generated. */
12
- "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
13
-
14
- /* Emit */
15
- "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
16
- "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
17
-
18
- /* Interop Constraints */
19
- "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
20
- "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
21
-
22
- /* Type Checking */
23
- "strict": true, /* Enable all strict type-checking options. */
24
-
25
- /* Completeness */
26
- "skipLibCheck": true, /* Skip type checking all .d.ts files. */
27
- }
28
4
  }