react-arborist
Version:
210 lines (171 loc) • 4.17 kB
text/typescript
import React from "react";
import { TreeApi } from "./tree-api";
import { IdObj } from "../types/utils";
import { ROOT_ID } from "../data/create-root";
type Params<T> = {
id: string;
data: T;
level: number;
children: NodeApi<T>[] | null;
parent: NodeApi<T> | null;
isDraggable: boolean;
rowIndex: number | null;
tree: TreeApi<T>;
};
export class NodeApi<T = any> {
tree: TreeApi<T>;
id: string;
data: T;
level: number;
children: NodeApi<T>[] | null;
parent: NodeApi<T> | null;
isDraggable: boolean;
rowIndex: number | null;
constructor(params: Params<T>) {
this.tree = params.tree;
this.id = params.id;
this.data = params.data;
this.level = params.level;
this.children = params.children;
this.parent = params.parent;
this.isDraggable = params.isDraggable;
this.rowIndex = params.rowIndex;
}
get isRoot() {
return this.id === ROOT_ID;
}
get isLeaf() {
return !Array.isArray(this.children);
}
get isInternal() {
return !this.isLeaf;
}
get isOpen() {
return this.isLeaf ? false : this.tree.isOpen(this.id);
}
get isClosed() {
return this.isLeaf ? false : !this.tree.isOpen(this.id);
}
get isEditable() {
return this.tree.isEditable(this.data);
}
get isEditing() {
return this.tree.editingId === this.id;
}
get isSelected() {
return this.tree.isSelected(this.id);
}
get isOnlySelection() {
return this.isSelected && this.tree.hasOneSelection;
}
get isSelectedStart() {
return this.isSelected && !this.prev?.isSelected;
}
get isSelectedEnd() {
return this.isSelected && !this.next?.isSelected;
}
get isFocused() {
return this.tree.isFocused(this.id);
}
get isDragging() {
return this.tree.isDragging(this.id);
}
get willReceiveDrop() {
return this.tree.willReceiveDrop(this.id);
}
get state() {
return {
isClosed: this.isClosed,
isDragging: this.isDragging,
isEditing: this.isEditing,
isFocused: this.isFocused,
isInternal: this.isInternal,
isLeaf: this.isLeaf,
isOpen: this.isOpen,
isSelected: this.isSelected,
isSelectedEnd: this.isSelectedEnd,
isSelectedStart: this.isSelectedStart,
willReceiveDrop: this.willReceiveDrop,
};
}
get childIndex() {
if (this.parent && this.parent.children) {
return this.parent.children.findIndex((child) => child.id === this.id);
} else {
return -1;
}
}
get next(): NodeApi<T> | null {
if (this.rowIndex === null) return null;
return this.tree.at(this.rowIndex + 1);
}
get prev(): NodeApi<T> | null {
if (this.rowIndex === null) return null;
return this.tree.at(this.rowIndex - 1);
}
get nextSibling(): NodeApi<T> | null {
const i = this.childIndex;
return this.parent?.children![i + 1] ?? null;
}
isAncestorOf(node: NodeApi<T> | null) {
if (!node) return false;
let ancestor: NodeApi<T> | null = node;
while (ancestor) {
if (ancestor.id === this.id) return true;
ancestor = ancestor.parent;
}
return false;
}
select() {
this.tree.select(this);
}
deselect() {
this.tree.deselect(this);
}
selectMulti() {
this.tree.selectMulti(this);
}
selectContiguous() {
this.tree.selectContiguous(this);
}
activate() {
this.tree.activate(this);
}
focus() {
this.tree.focus(this);
}
toggle() {
this.tree.toggle(this);
}
open() {
this.tree.open(this);
}
openParents() {
this.tree.openParents(this);
}
close() {
this.tree.close(this);
}
submit(value: string) {
this.tree.submit(this, value);
}
reset() {
this.tree.reset();
}
clone() {
return new NodeApi<T>({ ...this });
}
edit() {
return this.tree.edit(this);
}
handleClick = (e: React.MouseEvent) => {
if (e.metaKey && !this.tree.props.disableMultiSelection) {
this.isSelected ? this.deselect() : this.selectMulti();
} else if (e.shiftKey && !this.tree.props.disableMultiSelection) {
this.selectContiguous();
} else {
this.select();
this.activate();
}
};
}