import React, { useRef, useContext, useEffect } from 'react' import { TreeNode, ClosestPosition, CursorStatus, DragMoveEvent, } from '@designable/core' import { isFn } from '@designable/shared' import { autorun } from '@formily/reactive' import { observer } from '@formily/reactive-react' import { usePrefix, useCursor, useSelection, useOutlineDragon, useDesigner, } from '../../hooks' import { IconWidget } from '../IconWidget' import { NodeTitleWidget } from '../NodeTitleWidget' import { NodeContext } from './context' import cls from 'classnames' import './styles.less' export interface IOutlineTreeNodeProps { node: TreeNode style?: React.CSSProperties className?: string workspaceId?: string } export const OutlineTreeNode: React.FC = observer( ({ node, className, style, workspaceId }) => { const prefix = usePrefix('outline-tree-node') const engine = useDesigner() const ref = useRef() const ctx = useContext(NodeContext) const request = useRef(null) const cursor = useCursor() const selection = useSelection(workspaceId) const outlineDragon = useOutlineDragon(workspaceId) useEffect(() => { return engine.subscribeTo(DragMoveEvent, () => { const closestNodeId = outlineDragon?.closestNode?.id const closestDirection = outlineDragon?.closestDirection const id = node.id if (!ref.current) return if ( closestNodeId === id && closestDirection === ClosestPosition.Inner ) { if (!ref.current.classList.contains('droppable')) { ref.current.classList.add('droppable') } if (!ref.current.classList.contains('expanded')) { if (request.current) { clearTimeout(request.current) request.current = null } request.current = setTimeout(() => { ref.current.classList.add('expanded') }, 600) } } else { if (request.current) { clearTimeout(request.current) request.current = null } if (ref.current.classList.contains('droppable')) { ref.current.classList.remove('droppable') } } }) }, [node, outlineDragon, cursor]) useEffect(() => { return autorun(() => { const selectedIds = selection?.selected || [] const id = node.id if (!ref.current) return if (selectedIds.includes(id)) { if (!ref.current.classList.contains('selected')) { ref.current.classList.add('selected') } } else { if (ref.current.classList.contains('selected')) { ref.current.classList.remove('selected') } } if ( cursor.status === CursorStatus.Dragging && outlineDragon?.dragNodes?.length ) { if (ref.current.classList.contains('selected')) { ref.current.classList.remove('selected') } } }) }, [node, selection, outlineDragon]) if (!node) return null const renderIcon = (node: TreeNode) => { const icon = node.designerProps.icon if (icon) { return } if (node === node?.root) { return } else if (node.designerProps?.droppable) { return } return } const renderTitle = (node: TreeNode) => { if (isFn(ctx.renderTitle)) return ctx.renderTitle(node) return ( ) } const renderActions = (node: TreeNode) => { if (isFn(ctx.renderActions)) return ctx.renderActions(node) } return (
{(node?.children?.length > 0 || node === node.root) && (
{ e.preventDefault() e.stopPropagation() if (ref.current?.classList?.contains('expanded')) { ref.current?.classList.remove('expanded') } else { ref.current?.classList.add('expanded') } }} >
)}
{renderIcon(node)}
{renderTitle(node)}
{renderActions(node)} {node !== node.root && ( { node.hidden = !node.hidden }} /> )}
{node.children?.map((child) => { return ( ) })}
) } )