import classNames from "classnames" ; import * as React from "react" ; import * as Classes from "../../common/classes" ; import { DISPLAYNAME_PREFIX , Props } from "../../common/props" ; import { isFunction } from "../../common/utils" ; import { TreeNodeInfo , TreeNode } from "./treeNode" ; export type TreeEventHandler < T = { } > = ( node : TreeNodeInfo < T > , nodePath : number [ ] , e : React . MouseEvent < HTMLElement > ) => void ; export type TreeProps < T = { } > = ITreeProps < T > ; deprecated export interface ITreeProps < T = { } > extends Props { /**
     * The data specifying the contents and appearance of the tree.
     */ contents : Array < TreeNodeInfo < T > > ; /**
     * Invoked when a node is clicked anywhere other than the caret for expanding/collapsing the node.
     */ onNodeClick ? : TreeEventHandler < T > ; /**
     * Invoked when caret of an expanded node is clicked.
     */ onNodeCollapse ? : TreeEventHandler < T > ; /**
     * Invoked when a node is right-clicked or the context menu button is pressed on a focused node.
     */ onNodeContextMenu ? : TreeEventHandler < T > ; /**
     * Invoked when a node is double-clicked. Be careful when using this in combination with
     * an `onNodeClick` (single-click) handler, as the way this behaves can vary between browsers.
     * See http://stackoverflow.com/q/5497073/3124288
     */ onNodeDoubleClick ? : TreeEventHandler < T > ; /**
     * Invoked when the caret of a collapsed node is clicked.
     */ onNodeExpand ? : TreeEventHandler < T > ; /**
     * Invoked when the mouse is moved over a node.
     */ onNodeMouseEnter ? : TreeEventHandler < T > ; /**
     * Invoked when the mouse is moved out of a node.
     */ onNodeMouseLeave ? : TreeEventHandler < T > ; } export class Tree < T = { } > extends React . Component < TreeProps < T > > { public static displayName = `${ DISPLAYNAME_PREFIX }.Tree` ; public static ofType < U > ( ) { return Tree as new ( props ) => Tree < U > ; } public static nodeFromPath < U > ( path , treeNodes ? ) { if ( path . length === 1 ) { return treeNodes ! [ path [ 0 ] ] ; } else { return Tree . nodeFromPath ( path . slice ( 1 ) , treeNodes ! [ path [ 0 ] ] . childNodes ) ; } } private nodeRefs : { [ nodeId ] : HTMLElement ; } = { } ; public render ( ) { return ( < div className = { classNames ( Classes . TREE , this . props . className ) } > { this . renderNodes ( this . props . contents , [ ] , Classes . TREE_ROOT ) } < /div>); } /**
     * Returns the underlying HTML element of the `Tree` node with an id of `nodeId`.
     * This element does not contain the children of the node, only its label and controls.
     * If the node is not currently mounted, `undefined` is returned.
     */ public getNodeContentElement ( nodeId ) { return this . nodeRefs [ nodeId ] ; } private renderNodes ( treeNodes , currentPath ? , className ? ) { if ( treeNodes == null ) { return null ; } const nodeItems = treeNodes . map ( ( node , i ) => { const elementPath = currentPath ! . concat ( i ) ; const TypedTreeNode = TreeNode . ofType < T > ( ) ; return ( < TypedTreeNode { ... node } key = { node . id } contentRef = { this . handleContentRef } depth = { elementPath . length - 1 } onClick = { this . handleNodeClick } onContextMenu = { this . handleNodeContextMenu } onCollapse = { this . handleNodeCollapse } onDoubleClick = { this . handleNodeDoubleClick } onExpand = { this . handleNodeExpand } onMouseEnter = { this . handleNodeMouseEnter } onMouseLeave = { this . handleNodeMouseLeave } path = { elementPath } > { this . renderNodes ( node . childNodes , elementPath ) } < /TypedTreeNode>); } ) ; return < ul className = { classNames ( Classes . TREE_NODE_LIST , className ) } > { nodeItems } < /ul>; } private handleNodeCollapse = ( node , e ) => { this . handlerHelper ( this . props . onNodeCollapse , node , e ) ; } ; private handleNodeClick = ( node , e ) => { this . handlerHelper ( this . props . onNodeClick , node , e ) ; } ; private handleContentRef = ( node , element ) => { if ( element != null ) { this . nodeRefs [ node . props . id ] = element ; } else { delete this . nodeRefs [ node . props . id ] ; } } ; private handleNodeContextMenu = ( node , e ) => { this . handlerHelper ( this . props . onNodeContextMenu , node , e ) ; } ; private handleNodeDoubleClick = ( node , e ) => { this . handlerHelper ( this . props . onNodeDoubleClick , node , e ) ; } ; private handleNodeExpand = ( node , e ) => { this . handlerHelper ( this . props . onNodeExpand , node , e ) ; } ; private handleNodeMouseEnter = ( node , e ) => { this . handlerHelper ( this . props . onNodeMouseEnter , node , e ) ; } ; private handleNodeMouseLeave = ( node , e ) => { this . handlerHelper ( this . props . onNodeMouseLeave , node , e ) ; } ; private handlerHelper ( handlerFromProps , node , e ) { if ( isFunction ( handlerFromProps ) ) { const nodeData = Tree . nodeFromPath ( node . props . path , this . props . contents ) ; handlerFromProps ( nodeData , node . props . path , e ) ; } } }