diff --git a/packages/editor-skeleton/src/layouts/workbench.less b/packages/editor-skeleton/src/layouts/workbench.less index 4985d82eb..019835cb7 100644 --- a/packages/editor-skeleton/src/layouts/workbench.less +++ b/packages/editor-skeleton/src/layouts/workbench.less @@ -331,6 +331,12 @@ body { display: none; flex-shrink: 0; margin-left: 2px; + position: relative; + >.lc-panel { + position: absolute; + left: 0; + top: 0; + } &.lc-area-visible { display: block; } diff --git a/packages/editor-skeleton/src/skeleton.ts b/packages/editor-skeleton/src/skeleton.ts index a844d4377..c03be99c1 100644 --- a/packages/editor-skeleton/src/skeleton.ts +++ b/packages/editor-skeleton/src/skeleton.ts @@ -110,7 +110,7 @@ export class Skeleton { } return this.createPanel(config); }, - true, + false, true, ); this.mainArea = new Area( diff --git a/packages/editor-skeleton/src/types.ts b/packages/editor-skeleton/src/types.ts index 09c669985..43896fee2 100644 --- a/packages/editor-skeleton/src/types.ts +++ b/packages/editor-skeleton/src/types.ts @@ -1,5 +1,6 @@ import { ReactElement, ComponentType } from 'react'; import { TitleContent, IconType, I18nData, TipContent } from '@ali/lowcode-types'; +import { IWidget } from './widget/widget'; export interface IWidgetBaseConfig { type: string; @@ -16,6 +17,7 @@ export interface WidgetConfig extends IWidgetBaseConfig { type: "Widget"; props?: { align?: "left" | "right" | "bottom" | "center" | "top"; + onInit?: (widget: IWidget) => void; }; content?: string | ReactElement | ComponentType; // children } @@ -47,6 +49,7 @@ export function isDividerConfig(obj: any): obj is DividerConfig { export interface IDockBaseConfig extends IWidgetBaseConfig { props?: DockProps & { align?: "left" | "right" | "bottom" | "center" | "top"; + onInit?: (widget: IWidget) => void; }; } @@ -95,7 +98,8 @@ export interface PanelProps { height?: number; // panel.props maxWidth?: number; // panel.props maxHeight?: number; // panel.props - onInit?: () => any; + condition?: (widget: IWidget) => any; + onInit?: (widget: IWidget) => any; onDestroy?: () => any; shortcut?: string; // 只有在特定位置,可触发 toggle show } diff --git a/packages/editor-skeleton/src/widget/dock.ts b/packages/editor-skeleton/src/widget/dock.ts index 4b2081e53..c4ac58ad2 100644 --- a/packages/editor-skeleton/src/widget/dock.ts +++ b/packages/editor-skeleton/src/widget/dock.ts @@ -46,7 +46,7 @@ export default class Dock implements IWidget { this._body = createElement(DockView, props); } this.inited = true; - + return this._body; } @@ -54,6 +54,9 @@ export default class Dock implements IWidget { const { props = {}, name } = config; this.name = name; this.align = props.align; + if (props.onInit) { + props.onInit.call(this, this); + } } setVisible(flag: boolean) { diff --git a/packages/editor-skeleton/src/widget/panel-dock.ts b/packages/editor-skeleton/src/widget/panel-dock.ts index a7d90f5c1..949f32613 100644 --- a/packages/editor-skeleton/src/widget/panel-dock.ts +++ b/packages/editor-skeleton/src/widget/panel-dock.ts @@ -83,6 +83,9 @@ export default class PanelDock implements IWidget { area: panelProps?.area, }) as Panel; } + if (props?.onInit) { + props.onInit.call(this, this); + } } setVisible(flag: boolean) { diff --git a/packages/editor-skeleton/src/widget/panel.ts b/packages/editor-skeleton/src/widget/panel.ts index 866d1190d..2c2fb7edd 100644 --- a/packages/editor-skeleton/src/widget/panel.ts +++ b/packages/editor-skeleton/src/widget/panel.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'events'; import { createElement, ReactNode } from 'react'; -import { obx } from '@ali/lowcode-editor-core'; +import { obx, computed } from '@ali/lowcode-editor-core'; import { uniqueId, createContent } from '@ali/lowcode-utils'; import { TitleContent } from '@ali/lowcode-types'; import WidgetContainer from './widget-container'; @@ -16,14 +16,18 @@ export default class Panel implements IWidget { readonly name: string; readonly id: string; @obx.ref inited = false; - @obx.ref private _actived = false; + @obx.ref private _actived: boolean = false; private emitter = new EventEmitter(); get actived(): boolean { return this._actived; } - get visible(): boolean { - if (this.parent?.visible) { + @computed get visible(): boolean { + if (!this.parent || this.parent.visible) { + const { props } = this.config; + if (props?.condition) { + return props.condition(this); + } return this._actived; } return false; @@ -80,6 +84,9 @@ export default class Panel implements IWidget { ); content.forEach((item) => this.add(item)); } + if (props.onInit) { + props.onInit.call(this, this); + } // todo: process shortcut } diff --git a/packages/editor-skeleton/src/widget/widget.ts b/packages/editor-skeleton/src/widget/widget.ts index 4fc91ea3d..92bd7ae0f 100644 --- a/packages/editor-skeleton/src/widget/widget.ts +++ b/packages/editor-skeleton/src/widget/widget.ts @@ -60,6 +60,9 @@ export default class Widget implements IWidget { const { props = {}, name } = config; this.name = name; this.align = props.align; + if (props.onInit) { + props.onInit.call(this, this); + } } getId() { diff --git a/packages/plugin-outline-pane/src/index.ts b/packages/plugin-outline-pane/src/index.ts index 1c96608bc..32161abb5 100644 --- a/packages/plugin-outline-pane/src/index.ts +++ b/packages/plugin-outline-pane/src/index.ts @@ -1,6 +1,8 @@ -import Pane from './views/pane'; +import { OutlinePane } from './views/pane'; +import { OutlineBackupPane } from './views/backup-pane'; import { IconOutline } from './icons/outline'; import { intlNode } from './locale'; +import { getTreeMaster } from './tree-master'; export default { name: 'outline-pane', @@ -8,9 +10,7 @@ export default { icon: IconOutline, description: intlNode('Outline Tree'), }, - content: Pane, + content: OutlinePane, }; -export { getTreeMaster } from './main'; - -export { Pane }; +export { OutlinePane, OutlineBackupPane, getTreeMaster }; diff --git a/packages/plugin-outline-pane/src/main.ts b/packages/plugin-outline-pane/src/main.ts index 3903f166d..651981aa7 100644 --- a/packages/plugin-outline-pane/src/main.ts +++ b/packages/plugin-outline-pane/src/main.ts @@ -1,5 +1,4 @@ -import { EventEmitter } from 'events'; -import { computed, obx, Editor } from '@ali/lowcode-editor-core'; +import { computed, obx } from '@ali/lowcode-editor-core'; import { Designer, ISensor, @@ -18,108 +17,15 @@ import { contains, Node, } from '@ali/lowcode-designer'; -import { Tree } from './tree'; import TreeNode from './tree-node'; import { IndentTrack } from './helper/indent-track'; import DwellTimer from './helper/dwell-timer'; import { uniqueId } from '@ali/lowcode-utils'; +import { Backup } from './views/backup-pane'; +import { IEditor } from '@ali/lowcode-types'; +import { ITreeBoard, TreeMaster, getTreeMaster } from './tree-master'; -export interface IScrollBoard { - scrollToNode(treeNode: TreeNode, detail?: any): void; -} - -class TreeMaster { - private emitter = new EventEmitter(); - private currentFixed?: OutlineMain; - constructor(readonly designer: Designer) { - designer.dragon.onDragstart((e) => { - const tree = this.currentTree; - if (tree) { - tree.document.selection.getTopNodes().forEach((node) => { - tree.getTreeNode(node).setExpanded(false); - }); - } - if (!this.currentFixed) { - this.emitter.emit('enable-builtin'); - } - }); - designer.activeTracker.onChange(({ node, detail }) => { - 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); - }); - }); - } - - setFixed(entry: OutlineMain) { - this.currentFixed = entry; - } - - unFixed(entry: OutlineMain) { - if (entry === this.currentFixed) { - this.currentFixed = undefined; - } - } - - onceEnableBuiltin(fn: () => void): () => void { - this.emitter.once('enable-builtin', fn); - return () => { - this.emitter.removeListener('enable-builtin', fn); - } - } - - private boards = new Set(); - addBoard(board: IScrollBoard) { - this.boards.add(board); - } - removeBoard(board: IScrollBoard) { - this.boards.delete(board); - } - - purge() { - this.emitter.removeAllListeners(); - // todo others purge - } - - private treeMap = new Map(); - @computed get currentTree(): Tree | null { - const doc = this.designer?.currentDocument; - if (doc) { - const id = doc.id; - if (this.treeMap.has(id)) { - return this.treeMap.get(id)!; - } - const tree = new Tree(doc); - // TODO: listen purge event to remove - this.treeMap.set(id, tree); - return tree; - } - return null; - } -} - -const mastersMap = new Map(); -export function getTreeMaster(designer: Designer): TreeMaster { - let master = mastersMap.get(designer); - if (!master) { - master = new TreeMaster(designer); - mastersMap.set(designer, master); - } - return master; -} - -export class OutlineMain implements ISensor, IScrollBoard, IScrollable { +export class OutlineMain implements ISensor, ITreeBoard, IScrollable { private _designer?: Designer; @obx.ref private _master?: TreeMaster; get master() { @@ -130,8 +36,11 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable { } readonly id = uniqueId('outline'); - private fixed = false; - constructor(readonly editor: Editor, at?: string) { + @obx.ref _visible: boolean = false; + get visible() { + return this._visible; + } + constructor(readonly editor: IEditor, readonly at: string | Symbol) { let inited = false; const setup = async () => { if (inited) { @@ -142,29 +51,18 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable { this.setupDesigner(designer); }; - // FIXME: dirty connect to others - if (at === '__IN_SETTINGS__') { + if (at === Backup) { setup(); } else { editor.on('skeleton.panel.show', (key: string) => { if (key === at) { setup(); - if (this.master) { - this.master.setFixed(this); - } else { - this.fixed = true; - } - document.documentElement.classList.add('lowcode-has-fixed-tree'); + this._visible = true; } }); editor.on('skeleton.panel.hide', (key: string) => { if (key === at) { - document.documentElement.classList.remove('lowcode-has-fixed-tree'); - if (this.master) { - this.master.unFixed(this); - } else { - this.fixed = false; - } + this._visible = false; } }); } @@ -628,9 +526,6 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable { this._designer = designer; this._master = getTreeMaster(designer); this._master.addBoard(this); - if (this.fixed) { - this._master.setFixed(this); - } designer.dragon.addSensor(this); this.scroller = designer.createScroller(this); } diff --git a/packages/plugin-outline-pane/src/tree-master.ts b/packages/plugin-outline-pane/src/tree-master.ts new file mode 100644 index 000000000..2775d2c42 --- /dev/null +++ b/packages/plugin-outline-pane/src/tree-master.ts @@ -0,0 +1,93 @@ +import { computed, obx } from '@ali/lowcode-editor-core'; +import { Designer, isLocationChildrenDetail } from '@ali/lowcode-designer'; +import TreeNode from './tree-node'; +import { Tree } from './tree'; +import { Backup } from './views/backup-pane'; + +export interface ITreeBoard { + readonly visible: boolean; + readonly at: string | Symbol; + scrollToNode(treeNode: TreeNode, detail?: any): void; +} + +export class TreeMaster { + constructor(readonly designer: Designer) { + designer.dragon.onDragstart(() => { + // needs? + this.toVision(); + }); + designer.activeTracker.onChange(({ node, detail }) => { + 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); + }); + }); + } + + private toVision() { + const tree = this.currentTree; + if (tree) { + tree.document.selection.getTopNodes().forEach((node) => { + tree.getTreeNode(node).setExpanded(false); + }); + } + } + + @obx.val private boards = new Set(); + addBoard(board: ITreeBoard) { + this.boards.add(board); + } + removeBoard(board: ITreeBoard) { + this.boards.delete(board); + } + + @computed hasVisibleTreeBoard() { + for (const item of this.boards) { + if (item.visible && item.at !== Backup) { + return true; + } + } + return false; + } + + purge() { + // todo others purge + } + + private treeMap = new Map(); + @computed get currentTree(): Tree | null { + const doc = this.designer?.currentDocument; + if (doc) { + const id = doc.id; + if (this.treeMap.has(id)) { + return this.treeMap.get(id)!; + } + const tree = new Tree(doc); + // TODO: listen purge event to remove + this.treeMap.set(id, tree); + return tree; + } + return null; + } +} + +const mastersMap = new Map(); +export function getTreeMaster(designer: Designer): TreeMaster { + let master = mastersMap.get(designer); + if (!master) { + master = new TreeMaster(designer); + mastersMap.set(designer, master); + } + return master; +} diff --git a/packages/plugin-outline-pane/src/views/backup-pane.tsx b/packages/plugin-outline-pane/src/views/backup-pane.tsx index f74eceaff..eb933149f 100644 --- a/packages/plugin-outline-pane/src/views/backup-pane.tsx +++ b/packages/plugin-outline-pane/src/views/backup-pane.tsx @@ -1,28 +1,16 @@ import { PureComponent } from 'react'; import { PluginProps } from '@ali/lowcode-types'; -import OutlinePane from './pane'; +import { OutlinePane } from './pane'; + +export const Backup = Symbol.for('backup-outline'); export class OutlineBackupPane extends PureComponent { - state = { - outlineInited: false, - }; - private dispose = this.props.main.onceOutlineVisible(() => { - this.setState({ - outlineInited: true, - }); - }); - componentWillUnmount() { - this.dispose(); - } render() { - if (!this.state.outlineInited) { - return null; - } return ( ); diff --git a/packages/plugin-outline-pane/src/views/pane.tsx b/packages/plugin-outline-pane/src/views/pane.tsx index b11c5e4c1..d17ee1929 100644 --- a/packages/plugin-outline-pane/src/views/pane.tsx +++ b/packages/plugin-outline-pane/src/views/pane.tsx @@ -4,13 +4,11 @@ import { intl } from '../locale'; import { OutlineMain } from '../main'; import TreeView from './tree'; import './style.less'; +import { IEditor } from '@ali/lowcode-types'; @observer -export default class OutlinePane extends Component<{ config: any; editor: any; inSettings?: boolean }> { - private main = new OutlineMain( - this.props.editor, - this.props.config.name || this.props.config.pluginKey, - ); +export class OutlinePane extends Component<{ config: any; editor: IEditor }> { + private main = new OutlineMain(this.props.editor, this.props.config.name || this.props.config.pluginKey); shouldComponentUpdate() { return false; diff --git a/packages/plugin-outline-pane/src/views/style.less b/packages/plugin-outline-pane/src/views/style.less index 9ca351f8f..129d2968d 100644 --- a/packages/plugin-outline-pane/src/views/style.less +++ b/packages/plugin-outline-pane/src/views/style.less @@ -2,6 +2,8 @@ height: 100%; width: 100%; position: relative; + z-index: 20; + background-color: white; > .lc-outline-tree-container { top: 0; diff --git a/packages/types/src/editor.ts b/packages/types/src/editor.ts index 9932a174a..308122251 100644 --- a/packages/types/src/editor.ts +++ b/packages/types/src/editor.ts @@ -139,8 +139,6 @@ export interface Utils { export interface PluginProps { editor: IEditor; config: PluginConfig; - i18n?: I18nFunction; - ref?: RefObject; [key: string]: any; } diff --git a/packages/vision-preset/src/bundle/upgrade-metadata.ts b/packages/vision-preset/src/bundle/upgrade-metadata.ts index adecb0624..45a1662ac 100644 --- a/packages/vision-preset/src/bundle/upgrade-metadata.ts +++ b/packages/vision-preset/src/bundle/upgrade-metadata.ts @@ -732,11 +732,9 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) { const supports: any = {}; if (canUseCondition != null) { - console.info('canUseCondition', componentName); supports.condition = canUseCondition; } if (canLoop != null) { - console.info('canLoop', componentName); supports.loop = canLoop; } meta.configure = { props, component, supports }; diff --git a/packages/vision-preset/src/editor.ts b/packages/vision-preset/src/editor.ts index 65e064c37..56b3df925 100644 --- a/packages/vision-preset/src/editor.ts +++ b/packages/vision-preset/src/editor.ts @@ -2,7 +2,7 @@ import { isJSBlock, isJSExpression, isJSSlot } from '@ali/lowcode-types'; import { isPlainObject } from '@ali/lowcode-utils'; import { globalContext, Editor } from '@ali/lowcode-editor-core'; import { Designer, TransformStage, addBuiltinComponentAction } from '@ali/lowcode-designer'; -import Outline from '@ali/lowcode-plugin-outline-pane'; +import Outline, { OutlineBackupPane, getTreeMaster } from '@ali/lowcode-plugin-outline-pane'; import { toCss } from '@ali/vu-css-style'; import DesignerPlugin from '@ali/lowcode-plugin-designer'; @@ -16,9 +16,11 @@ globalContext.register(editor, Editor); export const skeleton = new Skeleton(editor); editor.set(Skeleton, skeleton); +editor.set('skeleton', skeleton); export const designer = new Designer({ editor: editor }); editor.set(Designer, designer); +editor.set('designer', designer); // 节点 props 初始化 designer.addPropsReducer((props, node) => { @@ -146,6 +148,17 @@ skeleton.add({ area: 'leftFixedArea', }, }); +skeleton.add({ + area: 'rightArea', + name: 'backupOutline', + type: 'Panel', + props: { + condition: () => { + return designer.dragon.dragging && !getTreeMaster(designer).hasVisibleTreeBoard(); + } + }, + content: OutlineBackupPane, +}); // skeleton.add({ // name: 'sourceEditor', diff --git a/packages/vision-preset/src/panes.ts b/packages/vision-preset/src/panes.ts index e398715fe..fdbd75fc9 100644 --- a/packages/vision-preset/src/panes.ts +++ b/packages/vision-preset/src/panes.ts @@ -59,8 +59,6 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin title, description, align: place, - onInit: init, - onDestroy: destroy, }, contentProps: props, index: index || props?.index, @@ -84,6 +82,8 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin maxWidth, height, maxHeight, + onInit: init, + onDestroy: destroy, }; if (contents && Array.isArray(contents)) { @@ -100,18 +100,22 @@ function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: strin }); } } - } else if (type === 'action') { - newConfig.area = 'top'; - newConfig.type = 'Dock'; - } else if (type === 'tab') { - newConfig.area = 'right'; - newConfig.type = 'Panel'; - } else if (type === 'stage') { - newConfig.area = 'stages'; - newConfig.type = 'Widget'; } else { - newConfig.area = 'main'; - newConfig.type = 'Widget'; + newConfig.props.onInit = init; + newConfig.props.onDestroy = destroy; + if (type === 'action') { + newConfig.area = 'top'; + newConfig.type = 'Dock'; + } else if (type === 'tab') { + newConfig.area = 'right'; + newConfig.type = 'Panel'; + } else if (type === 'stage') { + newConfig.area = 'stages'; + newConfig.type = 'Widget'; + } else { + newConfig.area = 'main'; + newConfig.type = 'Widget'; + } } return newConfig;