diff --git a/src/Node/index.tsx b/src/Node/index.tsx index 11be7955..226fd6df 100644 --- a/src/Node/index.tsx +++ b/src/Node/index.tsx @@ -7,7 +7,6 @@ import { TreeNodeDatum, RawNodeDatum, RenderCustomNodeElementFn, - AddChildrenFunction, } from '../types/common.js'; import DefaultNodeElement from './DefaultNodeElement.js'; @@ -36,7 +35,18 @@ type NodeProps = { onNodeMouseOut: NodeEventHandler; subscriptions: object; centerNode: (hierarchyPointNode: HierarchyPointNode) => void; - handleAddChildrenToNode: (nodeId: string, children: RawNodeDatum[]) => void; + handleAddChildrenToNode: ( + nodeId: string, + children: RawNodeDatum[], + replace?: boolean, + callback?: () => void + ) => void; + handleRemoveNode: (nodeId: string, parentNodeId: string, callback?: () => void) => void; + handleUpdateNodeAttributes: ( + nodeId: string, + attributes: Omit, + callback?: () => void + ) => void; }; type NodeState = { @@ -149,6 +159,9 @@ export default class Node extends React.Component { onNodeMouseOver: this.handleOnMouseOver, onNodeMouseOut: this.handleOnMouseOut, addChildren: this.handleAddChildren, + removeNode: this.handleRemoveNode, + replaceChildren: this.handleReplaceChildren, + updateNodeAttributes: this.handleUpdateNodeAttributes, }; return renderNode(nodeProps); @@ -172,8 +185,24 @@ export default class Node extends React.Component { this.props.onNodeMouseOut(this.props.hierarchyPointNode, evt); }; - handleAddChildren: AddChildrenFunction = childrenData => { - this.props.handleAddChildrenToNode(this.props.data.__rd3t.id, childrenData); + handleAddChildren = (childrenData, callback?: () => void) => { + this.props.handleAddChildrenToNode(this.props.data.__rd3t.id, childrenData, false, callback); + }; + + handleReplaceChildren = (childrenData, callback?: () => void) => { + this.props.handleAddChildrenToNode(this.props.data.__rd3t.id, childrenData, true, callback); + }; + + handleUpdateNodeAttributes = (attributes, callback?: () => void) => { + this.props.handleUpdateNodeAttributes(this.props.data.__rd3t.id, attributes, callback); + }; + + handleRemoveNode = (callback?: () => void) => { + this.props.handleRemoveNode( + this.props.data.__rd3t.id, + this.props.parent.data.__rd3t.id, + callback + ); }; componentWillLeave(done) { diff --git a/src/Tree/index.tsx b/src/Tree/index.tsx index cc36898a..4a6542b2 100644 --- a/src/Tree/index.tsx +++ b/src/Tree/index.tsx @@ -328,7 +328,42 @@ class Tree extends React.Component { } }; - handleAddChildrenToNode = (nodeId: string, childrenData: RawNodeDatum[]) => { + handleRemoveNode = (nodeId: string, parentNodeId: string, callback?: () => void) => { + const data = clone(this.state.data); + const parentMatches = this.findNodesById(parentNodeId, data, []); + if (parentMatches.length > 0) { + const targetNodeDatum = parentMatches[0]; + if (targetNodeDatum.children && targetNodeDatum.children.length > 0) { + const removeNodeIndex = targetNodeDatum.children.findIndex(c => c.__rd3t.id === nodeId); + if (removeNodeIndex > -1) { + targetNodeDatum.children.splice(removeNodeIndex, 1); + this.setState({ data }, callback); + } + } + } + }; + + handleUpdateNodeAttributes = ( + nodeId: string, + node: Omit, + callback?: () => void + ) => { + const data = clone(this.state.data); + const matches = this.findNodesById(nodeId, data, []); + if (matches.length > 0) { + const targetNodeDatum = matches[0]; + targetNodeDatum.name = node.name; + targetNodeDatum.attributes = node.attributes; + this.setState({ data }, callback); + } + }; + + handleAddChildrenToNode = ( + nodeId: string, + childrenData: RawNodeDatum[], + replace?: boolean, + callback?: () => void + ) => { const data = clone(this.state.data); const matches = this.findNodesById(nodeId, data, []); @@ -339,9 +374,13 @@ class Tree extends React.Component { const formattedChildren = clone(childrenData).map((node: RawNodeDatum) => Tree.assignInternalProperties([node], depth + 1) ); - targetNodeDatum.children.push(...formattedChildren.flat()); - this.setState({ data }); + if (replace) { + targetNodeDatum.children = formattedChildren.flat(); + } else { + targetNodeDatum.children.push(...formattedChildren.flat()); + } + this.setState({ data }, callback); } }; @@ -584,7 +623,7 @@ class Tree extends React.Component { const { data, x, y, parent } = hierarchyPointNode; return ( { handleAddChildrenToNode={this.handleAddChildrenToNode} subscriptions={subscriptions} centerNode={this.centerNode} + handleRemoveNode={this.handleRemoveNode} + handleUpdateNodeAttributes={this.handleUpdateNodeAttributes} /> ); })} diff --git a/src/types/common.ts b/src/types/common.ts index 3e77dec7..bb09becf 100644 --- a/src/types/common.ts +++ b/src/types/common.ts @@ -33,7 +33,11 @@ export type PathFunction = (link: TreeLinkDatum, orientation: Orientation) => st export type PathClassFunction = PathFunction; export type SyntheticEventHandler = (evt: SyntheticEvent) => void; -export type AddChildrenFunction = (children: RawNodeDatum[]) => void; +export type UpdateChildrenFunction = (children: RawNodeDatum[], callback?: () => void) => void; +export type UpdateNodeAttributesFunction = ( + attributes: Omit, + callback?: () => void +) => void; /** * The properties that are passed to the user-defined `renderCustomNodeElement` render function. @@ -70,7 +74,15 @@ export interface CustomNodeElementProps { /** * The `Node` class's internal `addChildren` handler. */ - addChildren: AddChildrenFunction; + addChildren: UpdateChildrenFunction; + /** + * The `Node` class's internal `replaceChildren` handler. + */ + replaceChildren: UpdateChildrenFunction; + /** + * The `Node` class's internal `updateNodeAttributes` handler. + */ + updateNodeAttributes: UpdateNodeAttributesFunction; } export type RenderCustomNodeElementFn = (rd3tNodeProps: CustomNodeElementProps) => JSX.Element;