,
- ref
+ ref,
) {
const { children, ...rest } = props;
const tree = useTreeApi();
@@ -29,7 +29,9 @@ const DropContainer = () => {
return (
({
imperativeHandle,
children,
}: Props) {
- const list = useRef(null);
+ const list = useRef(null);
const listEl = useRef(null);
const store = useRef>(
// @ts-ignore
diff --git a/modules/react-arborist/src/dnd/drag-hook.ts b/modules/react-arborist/src/dnd/drag-hook.ts
index 22ac564e..9388e233 100644
--- a/modules/react-arborist/src/dnd/drag-hook.ts
+++ b/modules/react-arborist/src/dnd/drag-hook.ts
@@ -37,6 +37,7 @@ export function useDragHook(node: NodeApi): ConnectDragSource {
});
tree.open(parentId);
}
+ tree.list?.current?.resetAfterIndex(0);
tree.dispatch(dnd.dragEnd());
},
}),
diff --git a/modules/react-arborist/src/interfaces/tree-api.ts b/modules/react-arborist/src/interfaces/tree-api.ts
index 50e03a84..f516a789 100644
--- a/modules/react-arborist/src/interfaces/tree-api.ts
+++ b/modules/react-arborist/src/interfaces/tree-api.ts
@@ -2,7 +2,11 @@ import { EditResult } from "../types/handlers";
import { Identity, IdObj } from "../types/utils";
import { TreeProps } from "../types/tree-props";
import { MutableRefObject } from "react";
-import { Align, FixedSizeList, ListOnItemsRenderedProps } from "react-window";
+import {
+ Align,
+ ListOnItemsRenderedProps,
+ VariableSizeList,
+} from "react-window";
import * as utils from "../utils";
import { DefaultCursor } from "../components/default-cursor";
import { DefaultRow } from "../components/default-row";
@@ -34,8 +38,8 @@ export class TreeApi {
constructor(
public store: Store,
public props: TreeProps,
- public list: MutableRefObject,
- public listEl: MutableRefObject
+ public list: MutableRefObject,
+ public listEl: MutableRefObject,
) {
/* Changes here must also be made in update() */
this.root = createRoot(this);
@@ -79,10 +83,6 @@ export class TreeApi {
return this.props.indent ?? 24;
}
- get rowHeight() {
- return this.props.rowHeight ?? 24;
- }
-
get overscanCount() {
return this.props.overscanCount ?? 1;
}
@@ -91,6 +91,33 @@ export class TreeApi {
return (this.props.searchTerm || "").trim();
}
+ rowHeight = (index: number): number => {
+ if (!this.props.rowHeight) {
+ return 24;
+ }
+
+ const node = this.at(index);
+ if (!node) {
+ return 0;
+ }
+
+ return typeof this.props.rowHeight === "function"
+ ? this.props.rowHeight(node)
+ : this.props.rowHeight;
+ };
+
+ rowTopPosition = (index: number): number => {
+ let position = 0;
+ for (let i = 0; i < index; i++) {
+ position += this.rowHeight(i);
+ }
+ return position;
+ };
+
+ redrawList = (afterIndex?: number | undefined | null) => {
+ this.list.current?.resetAfterIndex(afterIndex ?? 0);
+ };
+
get matchFn() {
const match =
this.props.searchMatch ??
@@ -194,7 +221,7 @@ export class TreeApi {
type?: "internal" | "leaf";
parentId?: null | string;
index?: null | number;
- } = {}
+ } = {},
) {
const parentId =
opts.parentId === undefined
@@ -224,6 +251,9 @@ export class TreeApi {
const idents = Array.isArray(node) ? node : [node];
const ids = idents.map(identify);
const nodes = ids.map((id) => this.get(id)!).filter((n) => !!n);
+
+ this.redrawList(Math.min(...nodes.map((node) => node.rowIndex ?? 0)));
+
await safeRun(this.props.onDelete, { nodes, ids });
}
@@ -232,6 +262,7 @@ export class TreeApi {
this.resolveEdit({ cancelled: true });
this.scrollTo(id);
this.dispatch(edit(id));
+ this.redrawList(this.get(id)?.rowIndex);
return new Promise((resolve) => {
TreeApi.editPromise = resolve;
});
@@ -247,12 +278,14 @@ export class TreeApi {
});
this.dispatch(edit(null));
this.resolveEdit({ cancelled: false, value });
+ this.redrawList(this.get(id)?.rowIndex);
setTimeout(() => this.onFocus()); // Return focus to element;
}
reset() {
this.dispatch(edit(null));
this.resolveEdit({ cancelled: true });
+ this.redrawList();
setTimeout(() => this.onFocus()); // Return focus to element;
}
@@ -470,19 +503,21 @@ export class TreeApi {
/* Visibility */
- open(identity: Identity) {
+ open(identity: Identity, redraw: boolean = true) {
const id = identifyNull(identity);
if (!id) return;
if (this.isOpen(id)) return;
this.dispatch(visibility.open(id, this.isFiltered));
+ redraw && this.redrawList(this.get(id)?.rowIndex);
safeRun(this.props.onToggle, id);
}
- close(identity: Identity) {
+ close(identity: Identity, redraw: boolean = true) {
const id = identifyNull(identity);
if (!id) return;
if (!this.isOpen(id)) return;
this.dispatch(visibility.close(id, this.isFiltered));
+ redraw && this.redrawList(this.get(id)?.rowIndex);
safeRun(this.props.onToggle, id);
}
@@ -499,9 +534,10 @@ export class TreeApi {
let parent = node?.parent;
while (parent) {
- this.open(parent.id);
+ this.open(parent.id, false);
parent = parent.parent;
}
+ this.redrawList();
}
openSiblings(node: NodeApi) {
@@ -512,23 +548,26 @@ export class TreeApi {
const isOpen = node.isOpen;
for (let sibling of parent.children) {
if (sibling.isInternal) {
- isOpen ? this.close(sibling.id) : this.open(sibling.id);
+ isOpen ? this.close(sibling.id, false) : this.open(sibling.id, false);
}
}
+ this.redrawList();
this.scrollTo(this.focusedNode);
}
}
openAll() {
utils.walk(this.root, (node) => {
- if (node.isInternal) node.open();
+ if (node.isInternal) this.open(node, false);
});
+ this.redrawList();
}
closeAll() {
utils.walk(this.root, (node) => {
- if (node.isInternal) node.close();
+ if (node.isInternal) this.close(node, false);
});
+ this.redrawList();
}
/* Scrolling */
diff --git a/modules/react-arborist/src/types/tree-props.ts b/modules/react-arborist/src/types/tree-props.ts
index 440bcc40..2ada067b 100644
--- a/modules/react-arborist/src/types/tree-props.ts
+++ b/modules/react-arborist/src/types/tree-props.ts
@@ -7,6 +7,27 @@ import { NodeApi } from "../interfaces/node-api";
import { OpenMap } from "../state/open-slice";
import { useDragDropManager } from "react-dnd";
+export type RowHeightCalculatorParams = Pick<
+ NodeApi,
+ | "childIndex"
+ | "children"
+ | "data"
+ | "parent"
+ | "id"
+ | "rowIndex"
+ | "tree"
+ | "isRoot"
+ | "isLeaf"
+ | "isClosed"
+ | "isOpen"
+ | "isAncestorOf"
+ | "level"
+>;
+
+export type RowHeightCalculator = (
+ params: RowHeightCalculatorParams,
+) => number;
+
export interface TreeProps {
/* Data Options */
data?: readonly T[];
@@ -26,7 +47,7 @@ export interface TreeProps {
renderContainer?: ElementType<{}>;
/* Sizes */
- rowHeight?: number;
+ rowHeight?: number | RowHeightCalculator;
overscanCount?: number;
width?: number | string;
height?: number;