/* eslint-disable max-len */ import { Component, KeyboardEvent, FocusEvent, Fragment } from 'react'; import classNames from 'classnames'; import { createIcon } from '@alilc/lowcode-utils'; import { IPublicModelPluginContext, IPublicApiEvent } from '@alilc/lowcode-types'; import TreeNode from '../controllers/tree-node'; import { IconLock, IconUnlock, IconArrowRight, IconEyeClose, IconEye, IconCond, IconLoop, IconRadioActive, IconRadio, IconSetting } from '../icons'; function emitOutlineEvent(event: IPublicApiEvent, type: string, treeNode: TreeNode, rest?: Record) { const node = treeNode?.node; const npm = node?.componentMeta?.npm; const selected = [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || node?.componentMeta?.componentName || ''; event.emit(`outlinePane.${type}`, { selected, ...rest, }); } export default class TreeTitle extends Component<{ treeNode: TreeNode; isModal?: boolean; pluginContext: IPublicModelPluginContext; }> { state: { editing: boolean; title: string; } = { editing: false, title: '', }; private enableEdit = (e) => { e.preventDefault(); this.setState({ editing: true, }); }; private cancelEdit() { this.setState({ editing: false, }); this.lastInput = undefined; } private saveEdit = (e: FocusEvent | KeyboardEvent) => { const { treeNode } = this.props; const value = (e.target as HTMLInputElement).value || ''; treeNode.setTitleLabel(value); emitOutlineEvent(this.props.pluginContext.event, 'rename', treeNode, { value }); this.cancelEdit(); }; private handleKeyUp = (e: KeyboardEvent) => { if (e.keyCode === 13) { this.saveEdit(e); } if (e.keyCode === 27) { this.cancelEdit(); } }; private lastInput?: HTMLInputElement; private setCaret = (input: HTMLInputElement | null) => { if (!input || this.lastInput === input) { return; } input.focus(); input.select(); // 光标定位最后一个 // input.selectionStart = input.selectionEnd; }; offTitleLabelChanged: (() => void) | undefined; componentDidMount() { const { treeNode, pluginContext } = this.props; const { id } = treeNode; const { pluginEvent } = pluginContext; this.setState({ editing: false, title: treeNode.titleLabel, }); this.offTitleLabelChanged = pluginEvent.on('tree-node.titleLabelChanged', (payload: any) => { const { nodeId } = payload; if (nodeId === id) { this.setState({ title: treeNode.titleLabel, }); } }); } componentWillUnmount(): void { if (this.offTitleLabelChanged) { this.offTitleLabelChanged(); } } render() { const { treeNode, isModal, pluginContext } = this.props; const { editing } = this.state; const isCNode = !treeNode.isRoot(); const { node } = treeNode; const isNodeParent = node.isParentalNode; const isContainer = node.isContainerNode; let style: any; if (isCNode) { const { depth } = treeNode; const indent = depth * 12; style = { paddingLeft: indent + (isModal ? 12 : 0), marginLeft: -indent, }; } const { filterWorking, matchSelf, keywords } = treeNode.filterReult; const Extra = pluginContext.extraTitle; const { intlNode, common, config } = pluginContext; const Tip = common.editorCabin.Tip; const Title = common.editorCabin.Title; const shouldShowHideBtn = isCNode && isNodeParent && !isModal; const shouldShowLockBtn = config.get('enableCanvasLock', false) && isContainer && isCNode && isNodeParent; const shouldEditBtn = isCNode && isNodeParent; return (
{ if (isModal) { if (node.getVisible()) { node.document?.modalNodesManager?.setInvisible(node); } else { node.document?.modalNodesManager?.setVisible(node); } return; } if (node.conditionGroup) { node.setConditionalVisible(); } }} > {isModal && node.getVisible() && (
{ node.document?.modalNodesManager?.setInvisible(node); }} >
)} {isModal && !node.getVisible() && (
{ node.document?.modalNodesManager?.setVisible(node); }} >
)} {isCNode && }
{createIcon(treeNode.icon)}
{editing ? ( ) : ( {Extra && <Extra node={treeNode?.node} />} {node.slotFor && ( <a className="tree-node-tag slot"> {/* todo: click redirect to prop */} <Tip>{intlNode('Slot for {prop}', { prop: node.slotFor.key })}</Tip> </a> )} {node.hasLoop() && ( <a className="tree-node-tag loop"> {/* todo: click todo something */} <IconLoop /> <Tip>{intlNode('Loop')}</Tip> </a> )} {node.hasCondition() && !node.conditionGroup && ( <a className="tree-node-tag cond"> {/* todo: click todo something */} <IconCond /> <Tip>{intlNode('Conditional')}</Tip> </a> )} </Fragment> )} </div> {shouldShowHideBtn && <HideBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />} {shouldShowLockBtn && <LockBtn treeNode={treeNode} pluginContext={this.props.pluginContext} />} {shouldEditBtn && <RenameBtn treeNode={treeNode} pluginContext={this.props.pluginContext} onClick={this.enableEdit} /> } </div> ); } } class RenameBtn extends Component<{ treeNode: TreeNode; pluginContext: IPublicModelPluginContext; onClick: (e: any) => void; }> { render() { const { intl, common } = this.props.pluginContext; const Tip = common.editorCabin.Tip; return ( <div className="tree-node-rename-btn" onClick={this.props.onClick} > <IconSetting /> <Tip>{intl('Rename')}</Tip> </div> ); } } class LockBtn extends Component<{ treeNode: TreeNode; pluginContext: IPublicModelPluginContext; }, { locked: boolean; }> { state = { locked: false, }; offLockedChanged: () => void; componentDidMount(): void { const { treeNode, pluginContext } = this.props; const { id } = treeNode; const { pluginEvent } = pluginContext; this.setState({ locked: treeNode.locked, }); this.offLockedChanged = pluginEvent.on('tree-node.lockedChanged', (payload: any) => { const { locked, nodeId } = payload; if (nodeId === id) { this.setState({ locked }); } }); } componentWillUnmount() { if (this.offLockedChanged) { this.offLockedChanged(); } } render() { const { treeNode } = this.props; const { intl, common } = this.props.pluginContext; const Tip = common.editorCabin.Tip; return ( <div className="tree-node-lock-btn" onClick={(e) => { e.stopPropagation(); treeNode.setLocked(!this.state.locked); }} > {this.state.locked ? <IconUnlock /> : <IconLock /> } <Tip>{this.state.locked ? intl('Unlock') : intl('Lock')}</Tip> </div> ); } } class HideBtn extends Component<{ treeNode: TreeNode; pluginContext: IPublicModelPluginContext; }, { hidden: boolean; }> { state = { hidden: false, }; offHiddenChanged: () => void; componentDidMount(): void { const { treeNode, pluginContext } = this.props; const { pluginEvent } = pluginContext; const { id } = treeNode; this.setState({ hidden: treeNode.hidden, }); this.offHiddenChanged = pluginEvent.on('tree-node.hiddenChanged', (payload: any) => { const { hidden, nodeId } = payload; if (nodeId === id) { this.setState({ hidden }); } }); } componentWillUnmount(): void { if (this.offHiddenChanged) { this.offHiddenChanged(); } } render() { const { treeNode } = this.props; const { intl, common } = this.props.pluginContext; const Tip = common.editorCabin.Tip; return ( <div className="tree-node-hide-btn" onClick={(e) => { e.stopPropagation(); emitOutlineEvent(this.props.pluginContext.event, this.state.hidden ? 'show' : 'hide', treeNode); treeNode.setHidden(!this.state.hidden); }} > {this.state.hidden ? <IconEye /> : <IconEyeClose />} <Tip>{this.state.hidden ? intl('Show') : intl('Hide')}</Tip> </div> ); } } class ExpandBtn extends Component<{ treeNode: TreeNode; pluginContext: IPublicModelPluginContext; }, { expanded: boolean; expandable: boolean; }> { state = { expanded: false, expandable: false, }; offExpandedChanged: () => void; componentDidMount(): void { const { treeNode, pluginContext } = this.props; const { id } = treeNode; const { pluginEvent } = pluginContext; this.setState({ expanded: treeNode.expanded, expandable: treeNode.expandable, }); this.offExpandedChanged = pluginEvent.on('tree-node.expandedChanged', (payload: any) => { const { expanded, nodeId } = payload; if (nodeId === id) { this.setState({ expanded }); } }); } componentWillUnmount(): void { if (this.offExpandedChanged) { this.offExpandedChanged(); } } render() { const { treeNode } = this.props; if (!this.state.expandable) { return <i className="tree-node-expand-placeholder" />; } return ( <div className="tree-node-expand-btn" onClick={(e) => { if (this.state.expanded) { e.stopPropagation(); } emitOutlineEvent(this.props.pluginContext.event, this.state.expanded ? 'collapse' : 'expand', treeNode); treeNode.setExpanded(!this.state.expanded); }} > <IconArrowRight size="small" /> </div> ); } }