@senyao-design-system/magic-tree
Version:
A tree component implemented with Konva for handling large datasets.
179 lines (177 loc) • 7.85 kB
TypeScript
import { default as Konva } from 'konva';
/**
* Unique identifier for a tree node.
*/
export type TreeNodeId = string | number;
/**
* Represents a single item in the action menu for a tree node.
*/
export interface NodeActionMenuItem<T = Record<string, unknown>> {
/** Unique key for the menu item. */
readonly key: string;
/** Text label to display for the menu item. */
readonly label: string;
/** Optional Konva shape or ReactNode to display as an icon. */
readonly icon?: React.ReactNode;
/** Callback executed when the menu item is clicked. */
readonly onClick: (node: TreeNodeData<T>) => void;
/** Optional: Indicates if the menu item should be disabled. */
readonly disabled?: boolean;
}
/**
* Defines the structure for a node icon.
*/
export type NodeIconType = 'image' | 'html' | 'konva';
export interface NodeIcon {
readonly type: NodeIconType;
readonly content: any;
readonly width?: number;
readonly height?: number;
}
/**
* Represents the data structure for a single node in the tree.
* @template T - The type of custom data associated with the node.
*/
export interface TreeNodeData<T = Record<string, unknown>> {
/** Unique ID of the node. */
readonly id: TreeNodeId;
/** Label or text to display for the node. */
readonly label: string;
/** Child nodes. */
readonly children?: ReadonlyArray<TreeNodeData<T>>;
/** Custom data associated with the node. */
readonly data?: T;
/** Indicates if this node can load children asynchronously. */
readonly hasAsyncChildren?: boolean;
/** Optional: Icon to display for the node. */
readonly icon?: NodeIcon;
}
/**
* Props for the main MagicTree component.
* @template T - The type of custom data associated with each tree node.
*/
export interface MagicTreeProps<T = Record<string, unknown>> {
/** The array of tree node data to render. */
readonly data: ReadonlyArray<TreeNodeData<T>>;
/** Set of IDs for currently expanded nodes. */
readonly expandedKeys: ReadonlyArray<TreeNodeId>;
/** Callback function invoked when a node's expanded state is toggled. */
readonly onExpand: (nodeIdList: ReadonlyArray<TreeNodeId>) => void;
/** Height of each node in pixels. Essential for virtualization. */
readonly nodeHeight: number;
/** Width of the tree container. */
readonly width?: number;
/** Height of the tree container. */
readonly height?: number;
/**
* Optional custom render function for a node's content (excluding expand/collapse icon and indentation).
* If not provided, a default renderer will be used.
*/
readonly renderNodeContent?: (props: NodeContentRenderProps<T>) => React.ReactNode;
readonly icon?: (props: FlattenedTreeNode<T>) => React.ReactNode;
/** Callback function when a node is clicked. */
readonly onNodeClick?: (node: TreeNodeData<T>, event: Konva.KonvaEventObject<MouseEvent>) => void;
/** Callback function when a node is hovered. */
readonly onNodeHover?: (node: TreeNodeData<T> | null, event: Konva.KonvaEventObject<MouseEvent> | null) => void;
/**
* Callback function to load children for a node asynchronously.
* The parent component is responsible for fetching the data and updating the main 'data' prop.
* The MagicTree component will manage the loading state internally based on this promise.
*/
readonly onLoadData?: (node: TreeNodeData<T>) => Promise<void>;
/**
* Optional function to provide action menu items for a given node.
* Return an array of MenuItem objects or null/undefined to show no menu.
*/
readonly renderRowActionMenu?: (node: TreeNodeData<T>) => ReadonlyArray<NodeActionMenuItem<T>> | null | undefined;
/** Optional: Callback when a drag gesture is initiated on a node. */
readonly onNodeDragStart?: (event: NodeDragStartEvent<T>) => void;
/** Optional: Callback when a node is being dragged. */
readonly onNodeDragMove?: (event: NodeDragMoveEvent<T>) => void;
/** Optional: Callback when a drag gesture ends for a node. */
readonly onNodeDragEnd?: (event: NodeDragEndEvent<T>) => void;
/**
* Optional configuration for displaying tooltips on node hover.
* - If `true`, a default tooltip showing the node label will be used.
* - If a function, it will be called with the node data and should return the tooltip content (string or ReactNode).
* - If `false` or undefined, no tooltip will be shown.
*/
readonly showNodeTooltip?: boolean | ((node: TreeNodeData<T>) => React.ReactNode | string);
readonly iconSize?: number;
}
/** Defines the event payload for onNodeDragStart. */
export interface NodeDragStartEvent<T = Record<string, unknown>> {
/** The node data being dragged. */
readonly node: TreeNodeData<T>;
/** The pointer position relative to the Konva stage at the start of the drag. */
readonly pointerPosition: {
readonly x: number;
readonly y: number;
};
/** The raw mouse event that initiated the drag. */
readonly rawEvent: MouseEvent;
}
/** Defines the event payload for onNodeDragMove. */
export interface NodeDragMoveEvent<T = Record<string, unknown>> {
/** The node data being dragged. */
readonly node: TreeNodeData<T>;
/** The current pointer position relative to the Konva stage. */
readonly pointerPosition: {
readonly x: number;
readonly y: number;
};
/** The change in pointer position from the start of the drag. */
readonly delta: {
readonly x: number;
readonly y: number;
};
/** The raw mouse event. */
readonly rawEvent: MouseEvent;
}
/** Defines the event payload for onNodeDragEnd. */
export interface NodeDragEndEvent<T = Record<string, unknown>> {
/** The node data that was being dragged. */
readonly node: TreeNodeData<T>;
/** The raw mouse event that ended the drag. */
readonly rawEvent: MouseEvent;
}
/**
* Props passed to the custom `renderNodeContent` function.
* @template T - The type of custom data associated with the tree node.
*/
export interface NodeContentRenderProps<T = Record<string, unknown>> {
/** The data for the node being rendered (original TreeNodeData). */
readonly node: TreeNodeData<T>;
/** Current depth of the node in the tree (0-indexed). */
readonly depth: number;
/** Indicates if the node is currently expanded. */
readonly isExpanded: boolean;
/** Indicates if the node has children (either already loaded or loadable). */
readonly hasChildren: boolean;
/** Indicates if the node is currently hovered. */
readonly isHovered: boolean;
/** Indicates if the node is currently loading children (managed by MagicTree). */
readonly isLoading: boolean;
/** Default x offset for the content (after icon and indentation). */
readonly contentX: number;
/** Default y offset for the content. */
readonly contentY: number;
/** Available width for the content area. */
readonly contentWidth: number;
/** Available height for the content area. */
readonly contentHeight: number;
}
/**
* Internal representation of a flattened node, used for rendering and virtualization.
* @template T - The type of custom data associated with the tree node.
*/
export interface FlattenedTreeNode<T = Record<string, unknown>> extends Omit<TreeNodeData<T>, 'isLoading'> {
/** Depth of the node in the tree (0-indexed). */
readonly depth: number;
/** Y position of the node in the virtualized list. */
readonly y: number;
/** Original parent ID (if any) */
readonly parentId?: TreeNodeId;
/** Indicates if the node is currently expanded (if it has children). This is always defined for a flattened node. */
readonly isExpanded: boolean;
}