import { isLocationChildrenDetail } from '@alilc/lowcode-utils'; import { IPublicModelPluginContext, IPublicTypeActiveTarget, IPublicModelNode, IPublicTypeDisposable, IPublicEnumPluginRegisterLevel } from '@alilc/lowcode-types'; import TreeNode from './tree-node'; import { Tree } from './tree'; import EventEmitter from 'events'; import { enUS, zhCN } from '../locale'; import { ReactNode } from 'react'; export interface ITreeBoard { readonly at: string | symbol; scrollToNode(treeNode: TreeNode, detail?: any): void; } enum EVENT_NAMES { pluginContextChanged = 'pluginContextChanged', } export interface IOutlinePanelPluginContext extends IPublicModelPluginContext { extraTitle?: string; intlNode(id: string, params?: object): ReactNode; intl(id: string, params?: object): string; getLocale(): string; } export class TreeMaster { pluginContext: IOutlinePanelPluginContext; private boards = new Set(); private treeMap = new Map(); private disposeEvents: (IPublicTypeDisposable | undefined)[] = []; event = new EventEmitter(); constructor(pluginContext: IPublicModelPluginContext, readonly options: { extraTitle?: string; }) { this.setPluginContext(pluginContext); const { workspace } = this.pluginContext; this.initEvent(); if (pluginContext.registerLevel === IPublicEnumPluginRegisterLevel.Workspace) { this.setPluginContext(workspace.window?.currentEditorView); let dispose: IPublicTypeDisposable | undefined; const windowViewTypeChangeEvent = () => { dispose = workspace.window?.onChangeViewType(() => { this.setPluginContext(workspace.window?.currentEditorView); }); }; windowViewTypeChangeEvent(); workspace.onChangeActiveWindow(() => { windowViewTypeChangeEvent(); this.setPluginContext(workspace.window?.currentEditorView); dispose && dispose(); }); } } private setPluginContext(pluginContext: IPublicModelPluginContext | undefined | null) { if (!pluginContext) { return; } const { intl, intlNode, getLocale } = pluginContext.common.utils.createIntl({ 'en-US': enUS, 'zh-CN': zhCN, }); let _pluginContext: IOutlinePanelPluginContext = Object.assign(pluginContext, { intl, intlNode, getLocale, }); _pluginContext.extraTitle = this.options && this.options['extraTitle']; this.pluginContext = _pluginContext; this.disposeEvent(); this.initEvent(); this.emitPluginContextChange(); } private disposeEvent() { this.disposeEvents.forEach(d => { d && d(); }); } private initEvent() { let startTime: any; const { event, project, canvas } = this.pluginContext; const setExpandByActiveTracker = (target: IPublicTypeActiveTarget) => { const { node, detail } = target; const tree = this.currentTree; if (!tree/* || node.document !== tree.document */) { return; } const treeNode = tree.getTreeNode(node); if (detail && isLocationChildrenDetail(detail)) { treeNode.expand(true); } else { treeNode.expandParents(); } this.boards.forEach((board) => { board.scrollToNode(treeNode, detail); }); }; this.disposeEvents = [ canvas.dragon?.onDragstart(() => { startTime = Date.now() / 1000; // needs? this.toVision(); }), canvas.activeTracker?.onChange(setExpandByActiveTracker), canvas.dragon?.onDragend(() => { const endTime: any = Date.now() / 1000; const nodes = project.currentDocument?.selection?.getNodes(); event.emit('outlinePane.dragend', { selected: nodes ?.map((n) => { if (!n) { return; } const npm = n?.componentMeta?.npm; return ( [npm?.package, npm?.componentName].filter((item) => !!item).join('-') || n?.componentMeta?.componentName ); }) .join('&'), time: (endTime - startTime).toFixed(2), }); }), project.onRemoveDocument((data: {id: string}) => { const { id } = data; this.treeMap.delete(id); }), ]; if (canvas.activeTracker?.target) { setExpandByActiveTracker(canvas.activeTracker?.target); } } private toVision() { const tree = this.currentTree; if (tree) { const selection = this.pluginContext.project.getCurrentDocument()?.selection; selection?.getTopNodes().forEach((node: IPublicModelNode) => { tree.getTreeNode(node).setExpanded(false); }); } } addBoard(board: ITreeBoard) { this.boards.add(board); } removeBoard(board: ITreeBoard) { this.boards.delete(board); } purge() { // todo others purge } onPluginContextChange(fn: () => void) { this.event.on(EVENT_NAMES.pluginContextChanged, fn); } emitPluginContextChange() { this.event.emit(EVENT_NAMES.pluginContextChanged); } get currentTree(): Tree | null { const doc = this.pluginContext.project.getCurrentDocument(); if (doc) { const { id } = doc; if (this.treeMap.has(id)) { return this.treeMap.get(id)!; } const tree = new Tree(this); this.treeMap.set(id, tree); return tree; } return null; } }