diff --git a/packages/editor-core/src/editor.ts b/packages/editor-core/src/editor.ts index 737a9fa26..e1d25bdff 100644 --- a/packages/editor-core/src/editor.ts +++ b/packages/editor-core/src/editor.ts @@ -2,7 +2,7 @@ /* eslint-disable max-len */ import { StrictEventEmitter } from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; -import { EventBus } from './event-bus'; +import { EventBus, IEventBus } from './event-bus'; import { IPublicModelEditor, EditorConfig, @@ -52,15 +52,18 @@ export declare interface Editor extends StrictEventEmitter; } +export interface IEditor extends IPublicModelEditor { + config?: EditorConfig; + + components?: PluginClassSet; + + eventBus: IEventBus; + + init(config?: EditorConfig, components?: PluginClassSet): Promise; +} + // eslint-disable-next-line no-redeclare -export class Editor extends (EventEmitter as any) implements IPublicModelEditor { - constructor(readonly viewName: string = 'global', readonly workspaceMode: boolean = false) { - // eslint-disable-next-line constructor-super - super(); - // set global emitter maxListeners - this.setMaxListeners(200); - this.eventBus = new EventBus(this); - } +export class Editor extends EventEmitter implements IEditor { /** * Ioc Container @@ -71,10 +74,32 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor return globalLocale.getLocale(); } + config?: EditorConfig; + + eventBus: EventBus; + + components?: PluginClassSet; + // readonly utils = utils; private hooks: HookConfig[] = []; + private waits = new Map< + IPublicTypeEditorValueKey, + Array<{ + once?: boolean; + resolve: (data: any) => void; + }> + >(); + + constructor(readonly viewName: string = 'global', readonly workspaceMode: boolean = false) { + // eslint-disable-next-line constructor-super + super(); + // set global emitter maxListeners + this.setMaxListeners(200); + this.eventBus = new EventBus(this); + } + get( keyOrType: KeyOrType, ): IPublicTypeEditorGetResult | undefined { @@ -202,12 +227,6 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor this.notifyGot(key || data); } - config?: EditorConfig; - - eventBus: EventBus; - - components?: PluginClassSet; - async init(config?: EditorConfig, components?: PluginClassSet): Promise { this.config = config || {}; this.components = components || {}; @@ -270,16 +289,6 @@ export class Editor extends (EventEmitter as any) implements IPublicModelEditor }); }; - /* eslint-disable */ - private waits = new Map< - IPublicTypeEditorValueKey, - Array<{ - once?: boolean; - resolve: (data: any) => void; - }> - >(); - /* eslint-enable */ - private notifyGot(key: IPublicTypeEditorValueKey) { let waits = this.waits.get(key); if (!waits) { diff --git a/packages/editor-skeleton/src/area.ts b/packages/editor-skeleton/src/area.ts index e96103ad6..8dc711084 100644 --- a/packages/editor-skeleton/src/area.ts +++ b/packages/editor-skeleton/src/area.ts @@ -3,7 +3,7 @@ import { obx, computed, makeObservable } from '@alilc/lowcode-editor-core'; import { Logger } from '@alilc/lowcode-utils'; import { IPublicTypeWidgetBaseConfig } from '@alilc/lowcode-types'; import { WidgetContainer } from './widget/widget-container'; -import { Skeleton } from './skeleton'; +import { ISkeleton } from './skeleton'; import { IWidget } from './widget/widget'; const logger = new Logger({ level: 'warn', bizName: 'skeleton:area' }); @@ -35,7 +35,9 @@ export class Area; - constructor(readonly skeleton: Skeleton, readonly name: string, handle: (item: T | C) => T, private exclusive?: boolean, defaultSetCurrent = false) { + private lastCurrent: T | null = null; + + constructor(readonly skeleton: ISkeleton, readonly name: string, handle: (item: T | C) => T, private exclusive?: boolean, defaultSetCurrent = false) { makeObservable(this); this.container = skeleton.createContainer(name, handle, exclusive, () => this.visible, defaultSetCurrent); } @@ -57,8 +59,6 @@ export class Area void; diff --git a/packages/editor-skeleton/src/context.ts b/packages/editor-skeleton/src/context.ts index ee213e886..58eb48ac6 100644 --- a/packages/editor-skeleton/src/context.ts +++ b/packages/editor-skeleton/src/context.ts @@ -1,4 +1,4 @@ import { createContext } from 'react'; -import { Skeleton } from './skeleton'; +import { ISkeleton } from './skeleton'; -export const SkeletonContext = createContext({} as any); +export const SkeletonContext = createContext({} as any); diff --git a/packages/editor-skeleton/src/layouts/left-float-pane.tsx b/packages/editor-skeleton/src/layouts/left-float-pane.tsx index f44f60240..0be5ea6c3 100644 --- a/packages/editor-skeleton/src/layouts/left-float-pane.tsx +++ b/packages/editor-skeleton/src/layouts/left-float-pane.tsx @@ -3,9 +3,10 @@ import classNames from 'classnames'; import { observer, Focusable, focusTracker } from '@alilc/lowcode-editor-core'; import { Area } from '../area'; import { Panel } from '../widget/panel'; +import { PanelConfig } from '../types'; @observer -export default class LeftFloatPane extends Component<{ area: Area }> { +export default class LeftFloatPane extends Component<{ area: Area }> { private dispose?: () => void; private focusing?: Focusable; diff --git a/packages/editor-skeleton/src/layouts/workbench.tsx b/packages/editor-skeleton/src/layouts/workbench.tsx index e50bec4ec..1e412ed67 100644 --- a/packages/editor-skeleton/src/layouts/workbench.tsx +++ b/packages/editor-skeleton/src/layouts/workbench.tsx @@ -1,7 +1,7 @@ import { Component } from 'react'; import { TipContainer, observer } from '@alilc/lowcode-editor-core'; import classNames from 'classnames'; -import { Skeleton } from '../skeleton'; +import { ISkeleton } from '../skeleton'; import TopArea from './top-area'; import LeftArea from './left-area'; import LeftFixedPane from './left-fixed-pane'; @@ -16,7 +16,7 @@ import { EditorConfig, PluginClassSet } from '@alilc/lowcode-types'; @observer export class Workbench extends Component<{ - skeleton: Skeleton; + skeleton: ISkeleton; config?: EditorConfig; components?: PluginClassSet; className?: string; diff --git a/packages/editor-skeleton/src/skeleton.ts b/packages/editor-skeleton/src/skeleton.ts index 22f165b1d..180859f1d 100644 --- a/packages/editor-skeleton/src/skeleton.ts +++ b/packages/editor-skeleton/src/skeleton.ts @@ -1,4 +1,4 @@ -import { Editor, action, makeObservable, obx, engineConfig } from '@alilc/lowcode-editor-core'; +import { action, makeObservable, obx, engineConfig, IEditor } from '@alilc/lowcode-editor-core'; import { DockConfig, PanelConfig, @@ -27,6 +27,7 @@ import { IPublicTypeWidgetBaseConfig, IPublicTypeWidgetConfigArea, IPublicTypeSkeletonConfig, + IPublicApiSkeleton, } from '@alilc/lowcode-types'; const logger = new Logger({ level: 'warn', bizName: 'skeleton' }); @@ -42,6 +43,66 @@ export enum SkeletonEvents { WIDGET_ENABLE = 'skeleton.widget.enable', } +export interface ISkeleton extends Omit { + editor: IEditor; + + readonly leftArea: Area; + + readonly topArea: Area; + + readonly subTopArea: Area; + + readonly toolbar: Area; + + readonly leftFixedArea: Area; + + readonly leftFloatArea: Area; + + readonly rightArea: Area; + + readonly mainArea: Area; + + readonly bottomArea: Area; + + readonly stages: Area; + + readonly widgets: IWidget[]; + + getPanel(name: string): Panel | undefined; + + getWidget(name: string): IWidget | undefined; + + buildFromConfig(config?: EditorConfig, components?: PluginClassSet): void; + + createStage(config: any): string | undefined; + + getStage(name: string): Stage | null; + + createContainer( + name: string, + handle: (item: any) => any, + exclusive?: boolean, + checkVisible?: () => boolean, + defaultSetCurrent?: boolean, + ): WidgetContainer; + + createPanel(config: PanelConfig): Panel; +} + export class Skeleton { private panels = new Map(); @@ -69,7 +130,7 @@ export class Skeleton { readonly widgets: IWidget[] = []; - constructor(readonly editor: Editor, readonly viewName: string = 'global') { + constructor(readonly editor: IEditor, readonly viewName: string = 'global') { makeObservable(this); this.leftArea = new Area( this, @@ -244,7 +305,7 @@ export class Skeleton { Object.keys(plugins).forEach((area) => { plugins[area].forEach((item) => { const { pluginKey, type, props = {}, pluginProps } = item; - const config: Partial = { + const config: IPublicTypeWidgetBaseConfig = { area: area as IPublicTypeWidgetConfigArea, type: 'Widget', name: pluginKey, @@ -272,7 +333,7 @@ export class Skeleton { if (pluginKey in components) { config.content = components[pluginKey]; } - this.add(config as IPublicTypeWidgetBaseConfig); + this.add(config); }); }); } diff --git a/packages/editor-skeleton/src/transducers/parse-func.ts b/packages/editor-skeleton/src/transducers/parse-func.ts index d6e83667a..b25310350 100644 --- a/packages/editor-skeleton/src/transducers/parse-func.ts +++ b/packages/editor-skeleton/src/transducers/parse-func.ts @@ -4,6 +4,7 @@ import { isPlainObject, isJSFunction, getLogger } from '@alilc/lowcode-utils'; const leadingFnRe = /^function/; const leadingFnNameRe = /^\w+\s*\(/; const logger = getLogger({ level: 'warn', bizName: 'skeleton:transducers' }); + /** * 将函数字符串转成函数,支持几种类型 * 类型一:() => {} / val => {} diff --git a/packages/editor-skeleton/src/types.ts b/packages/editor-skeleton/src/types.ts index a51369f15..b73dc4adc 100644 --- a/packages/editor-skeleton/src/types.ts +++ b/packages/editor-skeleton/src/types.ts @@ -1,12 +1,11 @@ import { ReactElement, ComponentType } from 'react'; import { IPublicTypeTitleContent, - IPublicTypeIconType, IPublicTypeI18nData, - TipContent, IPublicTypeWidgetConfigArea, IPublicTypeWidgetBaseConfig, IPublicTypePanelDockPanelProps, + IPublicTypePanelDockProps, } from '@alilc/lowcode-types'; import { IWidget } from './widget/widget'; @@ -24,13 +23,7 @@ export function isWidgetConfig(obj: any): obj is WidgetConfig { return obj && obj.type === 'Widget'; } -export interface DockProps { - title?: IPublicTypeTitleContent; - icon?: IPublicTypeIconType; - size?: 'small' | 'medium' | 'large'; - className?: string; - description?: TipContent; - onClick?: () => void; +export interface DockProps extends IPublicTypePanelDockProps { } export interface DividerConfig extends IPublicTypeWidgetBaseConfig { diff --git a/packages/editor-skeleton/src/widget/dock.ts b/packages/editor-skeleton/src/widget/dock.ts index 819d5c616..20cdd425d 100644 --- a/packages/editor-skeleton/src/widget/dock.ts +++ b/packages/editor-skeleton/src/widget/dock.ts @@ -3,7 +3,7 @@ import { makeObservable, obx } from '@alilc/lowcode-editor-core'; import { uniqueId, createContent } from '@alilc/lowcode-utils'; import { getEvent } from '@alilc/lowcode-shell'; import { DockConfig } from '../types'; -import { Skeleton } from '../skeleton'; +import { ISkeleton } from '../skeleton'; import { DockView, WidgetView } from '../components/widget-views'; import { IWidget } from './widget'; @@ -59,7 +59,7 @@ export class Dock implements IWidget { return this._body; } - constructor(readonly skeleton: Skeleton, readonly config: DockConfig) { + constructor(readonly skeleton: ISkeleton, readonly config: DockConfig) { makeObservable(this); const { props = {}, name } = config; this.name = name; diff --git a/packages/editor-skeleton/src/widget/panel-dock.ts b/packages/editor-skeleton/src/widget/panel-dock.ts index f2a56e80c..896849706 100644 --- a/packages/editor-skeleton/src/widget/panel-dock.ts +++ b/packages/editor-skeleton/src/widget/panel-dock.ts @@ -1,7 +1,7 @@ import { obx, computed, makeObservable } from '@alilc/lowcode-editor-core'; import { uniqueId } from '@alilc/lowcode-utils'; import { createElement, ReactNode, ReactInstance } from 'react'; -import { Skeleton } from '../skeleton'; +import { ISkeleton } from '../skeleton'; import { PanelDockConfig } from '../types'; import { Panel } from './panel'; import { PanelDockView, WidgetView } from '../components/widget-views'; @@ -18,7 +18,7 @@ export class PanelDock implements IWidget { readonly name: string; - readonly align?: string; + readonly align?: 'left' | 'right' | 'bottom' | 'center' | 'top' | undefined; private inited = false; @@ -51,11 +51,6 @@ export class PanelDock implements IWidget { }); } - getDOMNode() { - // eslint-disable-next-line react/no-find-dom-node - return this._shell ? findDOMNode(this._shell) : null; - } - @obx.ref private _visible = true; get visible() { @@ -76,7 +71,7 @@ export class PanelDock implements IWidget { return this._panel || this.skeleton.getPanel(this.panelName); } - constructor(readonly skeleton: Skeleton, readonly config: PanelDockConfig) { + constructor(readonly skeleton: ISkeleton, readonly config: PanelDockConfig) { makeObservable(this); const { content, contentProps, panelProps, name, props } = config; this.name = name; @@ -84,7 +79,7 @@ export class PanelDock implements IWidget { this.panelName = config.panelName || name; this.align = props?.align; if (content) { - const _panelProps: any = { ...panelProps }; + const _panelProps = { ...panelProps }; if (_panelProps.title == null && props) { _panelProps.title = composeTitle(props.title, undefined, props.description, true, true); } @@ -102,6 +97,11 @@ export class PanelDock implements IWidget { } } + getDOMNode() { + // eslint-disable-next-line react/no-find-dom-node + return this._shell ? findDOMNode(this._shell) : null; + } + setVisible(flag: boolean) { if (flag === this._visible) { return; @@ -170,7 +170,6 @@ export class PanelDock implements IWidget { } } - export function isPanelDock(obj: any): obj is PanelDock { return obj && obj.isPanelDock; } diff --git a/packages/editor-skeleton/src/widget/panel.ts b/packages/editor-skeleton/src/widget/panel.ts index b74c87b94..feec08da7 100644 --- a/packages/editor-skeleton/src/widget/panel.ts +++ b/packages/editor-skeleton/src/widget/panel.ts @@ -6,7 +6,7 @@ import { WidgetContainer } from './widget-container'; import { getEvent } from '@alilc/lowcode-shell'; import { PanelConfig, HelpTipConfig } from '../types'; import { TitledPanelView, TabsPanelView, PanelView } from '../components/widget-views'; -import { Skeleton } from '../skeleton'; +import { ISkeleton } from '../skeleton'; import { composeTitle } from './utils'; import { IWidget } from './widget'; import { isPanelDock, PanelDock } from './panel-dock'; @@ -80,7 +80,7 @@ export class Panel implements IWidget { @obx.ref public parent?: WidgetContainer; - constructor(readonly skeleton: Skeleton, readonly config: PanelConfig) { + constructor(readonly skeleton: ISkeleton, readonly config: PanelConfig) { makeObservable(this); const { name, content, props = {} } = config; const { hideTitleBar, title, icon, description, help } = props; @@ -111,7 +111,7 @@ export class Panel implements IWidget { props.onInit.call(this, this); } - if (content.onInit) { + if (typeof content !== 'string' && content && content.onInit) { content.onInit.call(this, this); } // todo: process shortcut diff --git a/packages/editor-skeleton/src/widget/stage.ts b/packages/editor-skeleton/src/widget/stage.ts index bd233ad95..2b177af61 100644 --- a/packages/editor-skeleton/src/widget/stage.ts +++ b/packages/editor-skeleton/src/widget/stage.ts @@ -1,6 +1,6 @@ // import { uniqueId } from '@alilc/lowcode-utils'; import { Widget } from './widget'; -import { Skeleton } from '../skeleton'; +import { ISkeleton } from '../skeleton'; import { WidgetConfig } from '../types'; export interface StageConfig extends WidgetConfig { @@ -17,7 +17,7 @@ export class Stage extends Widget { direction?: 'right' | 'left'; }; - constructor(skeleton: Skeleton, config: StageConfig) { + constructor(skeleton: ISkeleton, config: StageConfig) { super(skeleton, config); this.isRoot = config.isRoot || false; } diff --git a/packages/editor-skeleton/src/widget/utils.ts b/packages/editor-skeleton/src/widget/utils.ts index a5d105969..c4096d2e2 100644 --- a/packages/editor-skeleton/src/widget/utils.ts +++ b/packages/editor-skeleton/src/widget/utils.ts @@ -3,45 +3,51 @@ import { isI18nData, isTitleConfig } from '@alilc/lowcode-utils'; import { isValidElement } from 'react'; export function composeTitle(title?: IPublicTypeTitleContent, icon?: IPublicTypeIconType, tip?: TipContent, tipAsTitle?: boolean, noIcon?: boolean) { + let _title: IPublicTypeTitleContent | undefined; if (!title) { - title = {}; + _title = {}; if (!icon || tipAsTitle) { - title.label = tip; + _title = { + label: tip, + }; tip = undefined; } + } else { + _title = title; } + if (icon || tip) { - if (typeof title !== 'object' || isValidElement(title) || isI18nData(title)) { - if (isValidElement(title)) { - if (title.type === 'svg' || (title.type as any).getIcon) { + if (typeof _title !== 'object' || isValidElement(_title) || isI18nData(_title)) { + if (isValidElement(_title)) { + if (_title.type === 'svg' || _title.type.getIcon) { if (!icon) { - icon = title as any; + icon = _title; } if (tipAsTitle) { - title = tip as any; + _title = tip; tip = null; } else { - title = undefined; + _title = undefined; } } } - title = { - label: title, + _title = { + label: _title, icon, tip, }; } else { - title = { - ...title, + _title = { + ..._title, icon, tip, }; } } - if (isTitleConfig(title) && noIcon) { + if (isTitleConfig(_title) && noIcon) { if (!isValidElement(title)) { - title.icon = undefined; + _title.icon = undefined; } } - return title; + return _title; } diff --git a/packages/editor-skeleton/src/widget/widget.ts b/packages/editor-skeleton/src/widget/widget.ts index d0df99a1e..c95673877 100644 --- a/packages/editor-skeleton/src/widget/widget.ts +++ b/packages/editor-skeleton/src/widget/widget.ts @@ -3,7 +3,7 @@ import { makeObservable, obx } from '@alilc/lowcode-editor-core'; import { createContent, uniqueId } from '@alilc/lowcode-utils'; import { getEvent } from '@alilc/lowcode-shell'; import { WidgetConfig } from '../types'; -import { Skeleton } from '../skeleton'; +import { ISkeleton } from '../skeleton'; import { WidgetView } from '../components/widget-views'; import { IPublicTypeTitleContent, IPublicTypeWidgetBaseConfig } from '@alilc/lowcode-types'; @@ -15,7 +15,7 @@ export interface IWidget { readonly visible: boolean; readonly disabled?: boolean; readonly body: ReactNode; - readonly skeleton: Skeleton; + readonly skeleton: ISkeleton; readonly config: IPublicTypeWidgetBaseConfig; getName(): string; @@ -71,7 +71,7 @@ export class Widget implements IWidget { readonly title: IPublicTypeTitleContent; - constructor(readonly skeleton: Skeleton, readonly config: WidgetConfig) { + constructor(readonly skeleton: ISkeleton, readonly config: WidgetConfig) { makeObservable(this); const { props = {}, name } = config; this.name = name; diff --git a/packages/shell/src/api/event.ts b/packages/shell/src/api/event.ts index 746d9ae6a..0fb41966e 100644 --- a/packages/shell/src/api/event.ts +++ b/packages/shell/src/api/event.ts @@ -1,4 +1,4 @@ -import { Editor as InnerEditor, EventBus } from '@alilc/lowcode-editor-core'; +import { IEditor, IEventBus } from '@alilc/lowcode-editor-core'; import { getLogger, isPluginEventName } from '@alilc/lowcode-utils'; import { IPublicApiEvent, IPublicTypeDisposable } from '@alilc/lowcode-types'; @@ -11,10 +11,10 @@ type EventOptions = { const eventBusSymbol = Symbol('eventBus'); export class Event implements IPublicApiEvent { - private readonly [eventBusSymbol]: EventBus; + private readonly [eventBusSymbol]: IEventBus; private readonly options: EventOptions; - constructor(eventBus: EventBus, options: EventOptions, public workspaceMode = false) { + constructor(eventBus: IEventBus, options: EventOptions, public workspaceMode = false) { this[eventBusSymbol] = eventBus; this.options = options; if (!this.options.prefix) { @@ -69,6 +69,6 @@ export class Event implements IPublicApiEvent { } } -export function getEvent(editor: InnerEditor, options: any = { prefix: 'common' }) { +export function getEvent(editor: IEditor, options: any = { prefix: 'common' }) { return new Event(editor.eventBus, options); } diff --git a/packages/shell/src/api/skeleton.ts b/packages/shell/src/api/skeleton.ts index e9bb28328..928c55a0d 100644 --- a/packages/shell/src/api/skeleton.ts +++ b/packages/shell/src/api/skeleton.ts @@ -1,17 +1,18 @@ import { globalContext } from '@alilc/lowcode-editor-core'; import { - Skeleton as InnerSkeleton, + ISkeleton, SkeletonEvents, } from '@alilc/lowcode-editor-skeleton'; import { skeletonSymbol } from '../symbols'; import { IPublicApiSkeleton, IPublicTypeDisposable, IPublicTypeSkeletonConfig, IPublicTypeWidgetConfigArea } from '@alilc/lowcode-types'; const innerSkeletonSymbol = Symbol('skeleton'); + export class Skeleton implements IPublicApiSkeleton { - private readonly [innerSkeletonSymbol]: InnerSkeleton; + private readonly [innerSkeletonSymbol]: ISkeleton; private readonly pluginName: string; - get [skeletonSymbol](): InnerSkeleton { + get [skeletonSymbol](): ISkeleton { if (this.workspaceMode) { return this[innerSkeletonSymbol]; } @@ -24,7 +25,7 @@ export class Skeleton implements IPublicApiSkeleton { } constructor( - skeleton: InnerSkeleton, + skeleton: ISkeleton, pluginName: string, readonly workspaceMode: boolean = false, ) { @@ -57,7 +58,7 @@ export class Skeleton implements IPublicApiSkeleton { if (!normalizeArea(area)) { return; } - skeleton[normalizeArea(area)!].container?.remove(name); + skeleton[normalizeArea(area)].container?.remove(name); } /** @@ -185,7 +186,7 @@ export class Skeleton implements IPublicApiSkeleton { } } -function normalizeArea(area: IPublicTypeWidgetConfigArea | undefined) { +function normalizeArea(area: IPublicTypeWidgetConfigArea | undefined): 'leftArea' | 'rightArea' | 'topArea' | 'toolbar' | 'mainArea' | 'bottomArea' | 'leftFixedArea' | 'leftFloatArea' | 'stages' { switch (area) { case 'leftArea': case 'left': diff --git a/packages/types/src/shell/type/widget-base-config.ts b/packages/types/src/shell/type/widget-base-config.ts index 08c14c8db..8a8cda24f 100644 --- a/packages/types/src/shell/type/widget-base-config.ts +++ b/packages/types/src/shell/type/widget-base-config.ts @@ -1,4 +1,4 @@ -import { IPublicTypeWidgetConfigArea } from './'; +import { IPublicTypeIconType, IPublicTypeTitleContent, IPublicTypeWidgetConfigArea, TipContent } from './'; export interface IPublicTypeWidgetBaseConfig { [extra: string]: any; @@ -21,6 +21,24 @@ export interface IPublicTypePanelDockConfig extends IPublicTypeWidgetBaseConfig type: 'PanelDock'; panelProps?: IPublicTypePanelDockPanelProps; + + props?: IPublicTypePanelDockProps; +} + +export interface IPublicTypePanelDockProps { + [key: string]: any; + + size?: 'small' | 'medium' | 'large'; + + className?: string; + + description?: TipContent; + + onClick?: () => void; + + icon?: IPublicTypeIconType; + + title?: IPublicTypeTitleContent; } export interface IPublicTypePanelDockPanelProps {