fix: plugin-designer

This commit is contained in:
kangwei 2020-04-15 15:29:14 +08:00
parent e49a02e37c
commit 2dfbcd4fff
23 changed files with 516 additions and 125 deletions

View File

@ -82,6 +82,7 @@ export default class DesignerPlugin extends PureComponent<PluginProps, DesignerP
onMount={this.handleDesignerMount} onMount={this.handleDesignerMount}
className="lowcode-plugin-designer" className="lowcode-plugin-designer"
eventPipe={editor} eventPipe={editor}
designer={editor.get('designer')}
componentMetadatas={componentMetadatas} componentMetadatas={componentMetadatas}
simulatorProps={{ simulatorProps={{
library, library,

View File

@ -150,7 +150,8 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable {
if (at === '__IN_SETTINGS__') { if (at === '__IN_SETTINGS__') {
setup(); setup();
} else { } else {
editor.on('leftPanel.show', (key: string) => { editor.on('skeleton.panel.show', (key: string) => {
console.info('show', key);
if (key === at) { if (key === at) {
setup(); setup();
if (this.master) { if (this.master) {

View File

@ -6,10 +6,10 @@ import TreeView from './tree';
import './style.less'; import './style.less';
@observer @observer
export default class OutlinePane extends Component<{ config?: any; editor: any; inSettings?: boolean }> { export default class OutlinePane extends Component<{ config: any; editor: any; inSettings?: boolean }> {
private main = new OutlineMain( private main = new OutlineMain(
this.props.editor, this.props.editor,
this.props.config ? this.props.config.pluginKey : this.props.inSettings ? '__IN_SETTINGS__' : null, this.props.config.name || this.props.config.pluginKey,
); );
shouldComponentUpdate() { shouldComponentUpdate() {
@ -21,6 +21,7 @@ export default class OutlinePane extends Component<{ config?: any; editor: any;
} }
render() { render() {
console.info(this.props);
const tree = this.main.currentTree; const tree = this.main.currentTree;
if (!tree) { if (!tree) {

View File

@ -138,7 +138,9 @@ class OutlinePaneEntry extends PureComponent<{ main: SettingsMain }> {
if (!this.state.outlineInited) { if (!this.state.outlineInited) {
return null; return null;
} }
return <OutlinePane editor={this.props.main.editor} inSettings />; return <OutlinePane editor={this.props.main.editor} config={{
name: '__IN_SETTINGS__'
}} />;
} }
} }

View File

@ -3,7 +3,7 @@ import { camelCase, find, findIndex, upperFirst } from 'lodash';
import { ComponentClass, ReactElement, ComponentType } from 'react'; import { ComponentClass, ReactElement, ComponentType } from 'react';
import { UnknownComponent } from '../../ui/placeholders'; import { UnknownComponent } from '../../ui/placeholders';
import Trunk, { IComponentBundle } from '../bak/trunk'; import Trunk, { IComponentBundle } from './trunk';
import Prototype from './prototype'; import Prototype from './prototype';
function basename(name: string) { function basename(name: string) {

View File

@ -2,8 +2,8 @@ import lg from '@ali/vu-logger';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { ComponentClass } from 'react'; import { ComponentClass } from 'react';
import Bundle from '../bundle/bundle'; import Bundle from './bundle';
import Prototype, { setPackages } from '../bundle/prototype'; import Prototype, { setPackages } from './prototype';
import Bus from '../bus'; import Bus from '../bus';
interface IComponentInfo { interface IComponentInfo {

View File

@ -0,0 +1,220 @@
export interface IPropConfig {
/**
* composite share the namespace
* group just be tie up together
*/
type?: 'composite' | 'group';
/**
* when type is composite or group
*/
items?: IPropConfig[];
/**
* property name: the field key in props of schema
*/
name: string;
title?: string;
tip?: {
title?: string;
content?: string;
url?: string;
};
initialValue?: any;
defaultValue?: any;
display?: DISPLAY_TYPE;
fieldStyle?: DISPLAY_TYPE;
setter?: ComponentClass | ISetterConfig[] | string | SetterGetter;
/**
* if a prop is dynamicProp, every-time while rendering setting field
* - getValue() will not include value of getHotValue()
* - getValue() will trigger accessor() to calculate a new value
* this prop usually work out when we need to generate prop value
* from node of current page
*/
isDynamicProp?: boolean;
supportVariable?: boolean;
/**
* the prop should be collapsed while display value is accordion
*/
collapse?: boolean;
/**
* alias to collapse
*/
collapsed?: boolean;
fieldCollapsed?: boolean;
/**
* if a prop is declared as disabled, it will not be saved into
* schema
*/
disabled?: boolean | ReturnBooleanFunction;
/**
* will not export data to schema
*/
ignore?: boolean | ReturnBooleanFunction;
/**
* if a prop is declared as virtual, it will not be saved in
* schema props, instead it will be saved into context field
*/
virtual?: boolean | ReturnBooleanFunction;
hidden?: boolean | ReturnBooleanFunction;
/**
* if a prop is a lifeCycle function
*/
lifeCycle?: boolean;
destroy?: () => any;
initial?(this: Prop, value: any, initialValue: any): any;
/**
* when use getValue(), accessor shall be called as initializer
*/
accessor?(this: Prop): any;
/**
* when current prop value mutate, the mutator function shall be called
*/
mutator?(
this: Prop,
value: any,
hotValue: any,
preValue: any,
preHotValue: any,
): void;
/**
* other values' change will trigger sync function here
*/
sync?(this: Prop, value: any): void;
/**
* transform runtime data between view and setter
* @param toHotValue hot value for the setter
* @param toViewValue static value for the view
*/
transformer?(
toHotValue: (data: any) => any,
toViewValue: (str: string) => any,
): any;
/**
* user click var to change current field to
* variable setting field
*/
useVariableChange?(data: { isUseVariable: boolean }): any;
}
export interface SettingFieldConfig {
type?: 'field';
title?: string;
name: string;
setter: ComponentClass | ISetterConfig[] | string | SetterGetter;
extraProps?: {
[key: string]: any;
};
}
export interface SettingGroupConfig {
type: 'group';
title?: string;
items: Array<SettingGroupConfig | SettingFieldConfig>;
extraProps?: {
[key: string]: any;
};
}
export declare interface IComponentPrototypeConfigure {
packageName: string; // => npm.package
uri?: string;
/**
* category display in the component pane
* component will be hidden while the value is: null
*/
category: string; // => tags
componentName: string;// =>
docUrl?: string; // =>
defaultProps?: any; // => ?
/**
* extra actions on the outline of current selected node
* by default we have: remove / clone
*/
extraActions?: Component[]; // => configure.component.actions
title?: string; // =>
icon?: Component; // =>
view: Component; // => ?
initialChildren?: (props: any) => any[]; // => snippets
/**
* Props configurations of node
*/
configure: IPropConfig[]; // => configure.props
snippets?: ISnippet[]; // => snippets
transducers?: any; // => ?
reducers?: any; // ?
/**
* Selector expression rectangle of a node, it is usually a querySelector string
* @example '.classname > div'
*/
rectSelector?: string; // => configure.component.rectSelector
context?: { // => ?
[contextInfoName: string]: any;
};
isContainer?: boolean; // => configure.component.isContainer
isInline?: boolean; // x
isModal?: boolean; // => configure.component.isModal
isFloating?: boolean; // => configure.component.isFloating
descriptor?: string; // => configure.component.descriptor
/**
* enable slot-mode
* @see https://yuque.antfin-inc.com/legao/solutions/atgtdl
*/
hasSlot?: boolean; // => ?
// alias to canDragging
canDraging?: boolean; // => ?
canDragging?: boolean; // => ?
canOperating?: boolean; // => disabledActions
canHovering?: boolean; // x
canSelecting?: boolean;
canUseCondition?: boolean; // x
canLoop?: boolean; // x
canContain?: (dragment: Node) => boolean; // => nestingRule
canDropTo?: ((container: Node) => boolean) | string | string[]; // => nestingRule
canDropto?: (container: Node) => boolean; // => nestingRule
canDropIn?: ((dragment: Node) => boolean) | string | string[]; // => nestingRule
canDroping?: (dragment: Node) => boolean; // => nestingRule
didDropOut?: (container: any | Prototype, dragment: any) => boolean; // => hooks
didDropIn?: (container: any | Prototype, dragment: any) => boolean; // => hooks
// => ?
canResizing?:
| ((dragment: Node, triggerDirection: string) => boolean)
| boolean;
onResizeStart?: (
e: MouseEvent,
triggerDirection: string,
dragment: Node,
) => void;
onResize?: (
e: MouseEvent,
triggerDirection: string,
dragment: Node,
moveX: number,
moveY: number,
) => void;
onResizeEnd?: (
e: MouseEvent,
triggerDirection: string,
dragment: Node,
) => void;
/**
* when sub-node of the current node changed
* including: sub-node insert / remove
*/
subtreeModified?(this: Node): any; // => ?
}
export interface IComponentPrototypeExtraConfigs {
autoGenerated?: boolean;
}

View File

@ -11,6 +11,10 @@ export const editor = new Editor();
export const skeleton = new Skeleton(editor); export const skeleton = new Skeleton(editor);
export const designer = new Designer();
editor.set('designer', designer)
skeleton.mainArea.add({ skeleton.mainArea.add({
name: 'designer', name: 'designer',
type: 'Widget', type: 'Widget',
@ -31,7 +35,7 @@ skeleton.leftArea.add({
}, },
content: OutlinePane, content: OutlinePane,
panelProps: { panelProps: {
area: 'leftFloatArea' area: 'leftFixedArea'
} }
}); });

View File

@ -1,5 +1,5 @@
import { Selection, DocumentModel, Node } from '@ali/lowcode-designer'; import { Selection, DocumentModel, Node } from '@ali/lowcode-designer';
import editor from './editor'; import { editor, designer } from './editor';
let currentSelection: Selection; let currentSelection: Selection;
// let currentDocument: DocumentModel; // let currentDocument: DocumentModel;
@ -27,7 +27,7 @@ editor.once('designer.ready', () => {
export default { export default {
select: (node: Node) => { select: (node: Node) => {
if (!node) { if (!node) {
return currentSelection.clear(); return designer.currentSelection?.clear();
} }
currentSelection.select(node.id); currentSelection.select(node.id);
}, },

View File

@ -1,4 +1,4 @@
import { skeleton } from './editor'; import { skeleton, editor } from './editor';
import { ReactElement } from 'react'; import { ReactElement } from 'react';
import { IWidgetBaseConfig } from './skeleton/types'; import { IWidgetBaseConfig } from './skeleton/types';
@ -151,11 +151,27 @@ const dockPane = Object.assign(skeleton.leftArea, {
/** /**
* compatible *VE.dockPane.onDockShow* * compatible *VE.dockPane.onDockShow*
*/ */
onDockShow() {}, onDockShow(fn: (dock: any) => void): () => void {
const f = (_: any, dock: any) => {
fn(dock);
};
editor.on('skeleton.panel-dock.show', f);
return () => {
editor.removeListener('skeleton.panel-dock.show', f);
};
},
/** /**
* compatible *VE.dockPane.onDockHide* * compatible *VE.dockPane.onDockHide*
*/ */
onDockHide() {}, onDockHide(fn: (dock: any) => void): () => void {
const f = (_: any, dock: any) => {
fn(dock);
};
editor.on('skeleton.panel-dock.hide', f);
return () => {
editor.removeListener('skeleton.panel-dock.hide', f);
};
},
/** /**
* compatible *VE.dockPane.setFixed* * compatible *VE.dockPane.setFixed*
*/ */

View File

@ -45,4 +45,8 @@ export default class Area<C extends IWidgetBaseConfig = any, T extends IWidget =
hide() { hide() {
this.setVisible(false); this.setVisible(false);
} }
show() {
this.setVisible(true);
}
} }

View File

@ -1,9 +1,8 @@
import { Component, Fragment } from 'react'; import { Component, Fragment } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { observer } from '@ali/recore'; import { observer } from '@ali/lowcode-globals';
import Area from './area'; import Area from './area';
import Panel from './panel'; import Panel from './panel';
import { PanelWrapper } from './widget-views';
@observer @observer
export default class BottomArea extends Component<{ area: Area<any, Panel> }> { export default class BottomArea extends Component<{ area: Area<any, Panel> }> {
@ -31,7 +30,7 @@ class Contents extends Component<{ area: Area<any, Panel> }> {
const { area } = this.props; const { area } = this.props;
return ( return (
<Fragment> <Fragment>
{area.container.items.map((item) => <PanelWrapper key={item.id} panel={item} />)} {area.container.items.map((item) => item.content)}
</Fragment> </Fragment>
); );
} }

View File

@ -2,7 +2,7 @@ import { ReactNode, createElement } from 'react';
import { uniqueId, createContent, obx } from '@ali/lowcode-globals'; import { uniqueId, createContent, obx } from '@ali/lowcode-globals';
import { DockConfig } from "./types"; import { DockConfig } from "./types";
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
import { DockView } from './widget-views'; import { DockView, WidgetView } from './widget-views';
import { IWidget } from './widget'; import { IWidget } from './widget';
/** /**
@ -19,30 +19,34 @@ export default class Dock implements IWidget {
return this._visible; return this._visible;
} }
private inited: boolean = false; get content(): ReactNode {
private _content: ReactNode; return createElement(WidgetView, {
get content() { widget: this,
if (this.inited) { key: this.id,
return this._content; });
} }
this.inited = true;
private inited: boolean = false;
private _body: ReactNode;
get body() {
if (this.inited) {
return this._body;
}
const { props, content, contentProps } = this.config; const { props, content, contentProps } = this.config;
if (content) { if (content) {
this._content = createContent(content, { this._body = createContent(content, {
...contentProps, ...contentProps,
config: this.content,
editor: this.skeleton.editor, editor: this.skeleton.editor,
key: this.id,
}); });
} else { } else {
this._content = createElement(DockView, { this._body = createElement(DockView, props);
...props, }
key: this.id, return this._body;
});
} }
return this._content;
}
constructor(readonly skeleton: Skeleton, private config: DockConfig) { constructor(readonly skeleton: Skeleton, private config: DockConfig) {
const { props = {}, name } = config; const { props = {}, name } = config;
this.name = name; this.name = name;
@ -75,4 +79,8 @@ export default class Dock implements IWidget {
show() { show() {
this.setVisible(true); this.setVisible(true);
} }
toggle() {
this.setVisible(!this._visible);
}
} }

View File

@ -5,7 +5,6 @@ import { Button } from '@alifd/next';
import Area from './area'; import Area from './area';
import { PanelConfig } from './types'; import { PanelConfig } from './types';
import Panel from './panel'; import Panel from './panel';
import { PanelWrapper } from './widget-views';
@observer @observer
export default class LeftFixedPane extends Component<{ area: Area<PanelConfig, Panel> }> { export default class LeftFixedPane extends Component<{ area: Area<PanelConfig, Panel> }> {
@ -41,9 +40,7 @@ class Contents extends Component<{ area: Area<PanelConfig, Panel> }> {
const { area } = this.props; const { area } = this.props;
return ( return (
<Fragment> <Fragment>
{area.container.items.map((panel) => ( {area.container.items.map((panel) => panel.content)}
<PanelWrapper key={panel.id} panel={panel} />
))}
</Fragment> </Fragment>
); );
} }

View File

@ -4,7 +4,6 @@ import { observer } from '@ali/lowcode-globals';
import { Button } from '@alifd/next'; import { Button } from '@alifd/next';
import Area from './area'; import Area from './area';
import Panel from './panel'; import Panel from './panel';
import { PanelWrapper } from './widget-views';
@observer @observer
export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }> { export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }> {
@ -42,9 +41,7 @@ class Contents extends Component<{ area: Area<any, Panel> }> {
const { area } = this.props; const { area } = this.props;
return ( return (
<Fragment> <Fragment>
{area.container.items.map((panel) => ( {area.container.items.map((panel) => panel.content)}
<PanelWrapper key={panel.id} panel={panel} />
))}
</Fragment> </Fragment>
); );
} }

View File

@ -1,9 +1,8 @@
import { Component } from 'react'; import { Component } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { observer } from '@ali/recore'; import { observer } from '@ali/lowcode-globals';
import Area from './area'; import Area from './area';
import Panel, { isPanel } from './panel'; import Panel from './panel';
import { PanelWrapper } from './widget-views';
import Widget from './widget'; import Widget from './widget';
@observer @observer
@ -15,13 +14,7 @@ export default class MainArea extends Component<{ area: Area<any, Panel | Widget
const { area } = this.props; const { area } = this.props;
return ( return (
<div className={classNames('lc-main-area')}> <div className={classNames('lc-main-area')}>
{area.container.items.map((item) => { {area.container.items.map((item) => item.content)}
// todo?
if (isPanel(item)) {
return <PanelWrapper key={item.id} panel={item} />;
}
return item.content;
})}
</div> </div>
); );
} }

View File

@ -3,7 +3,7 @@ import { createElement, ReactNode } from 'react';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
import { PanelDockConfig } from './types'; import { PanelDockConfig } from './types';
import Panel from './panel'; import Panel from './panel';
import { PanelDockView } from './widget-views'; import { PanelDockView, WidgetView } from './widget-views';
import { IWidget } from './widget'; import { IWidget } from './widget';
export default class PanelDock implements IWidget { export default class PanelDock implements IWidget {
@ -13,21 +13,32 @@ export default class PanelDock implements IWidget {
readonly align?: string; readonly align?: string;
private inited: boolean = false; private inited: boolean = false;
private _content: ReactNode; private _body: ReactNode;
get content() { get body() {
if (this.inited) { if (this.inited) {
return this._content; return this._body;
} }
this.inited = true; this.inited = true;
const { props } = this.config; const { props } = this.config;
this._content = createElement(PanelDockView, { this._body = createElement(PanelDockView, {
...props, ...props,
key: this.id,
dock: this, dock: this,
}); });
return this._content; return this._body;
}
get content(): ReactNode {
return createElement(WidgetView, {
widget: this,
key: this.id,
});
}
@obx.ref private _visible: boolean = true;
get visible() {
return this._visible;
} }
@computed get actived(): boolean { @computed get actived(): boolean {
@ -52,12 +63,36 @@ export default class PanelDock implements IWidget {
props: panelProps || {}, props: panelProps || {},
contentProps, contentProps,
content, content,
// FIXME! dirty fixed
area: panelProps?.area || 'leftFloatArea' area: panelProps?.area || 'leftFloatArea'
}) as Panel; }) as Panel;
} }
} }
setVisible(flag: boolean) {
if (flag === this._visible) {
return;
}
if (flag) {
this._visible = true;
} else if (this.inited) {
this._visible = false;
}
}
hide() {
this.setVisible(false);
}
show() {
this.setVisible(true);
}
toggle() { toggle() {
this.setVisible(!this._visible);
}
togglePanel() {
this.panel?.toggle(); this.panel?.toggle();
} }
@ -69,11 +104,11 @@ export default class PanelDock implements IWidget {
return this.content; return this.content;
} }
hide() { hidePanel() {
this.panel?.setActive(false); this.panel?.setActive(false);
} }
show() { showPanel() {
this.panel?.setActive(true); this.panel?.setActive(true);
} }
} }

View File

@ -1,8 +1,8 @@
import {createElement, ReactNode } from 'react'; import { createElement, ReactNode } from 'react';
import { obx, uniqueId, createContent, TitleContent } from '@ali/lowcode-globals'; import { obx, uniqueId, createContent, TitleContent } from '@ali/lowcode-globals';
import WidgetContainer from './widget-container'; import WidgetContainer from './widget-container';
import { PanelConfig, HelpTipConfig } from './types'; import { PanelConfig, HelpTipConfig } from './types';
import { PanelView, TabsPanelView } from './widget-views'; import { TitledPanelView, TabsPanelView, PanelView } from './widget-views';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
import { composeTitle } from './utils'; import { composeTitle } from './utils';
import { IWidget } from './widget'; import { IWidget } from './widget';
@ -16,32 +16,13 @@ export default class Panel implements IWidget {
get actived(): boolean { get actived(): boolean {
return this._actived; return this._actived;
} }
get visible(): boolean { get visible(): boolean {
if (this.parent?.visible) { if (this.parent?.visible) {
return this._actived; return this._actived;
} }
return false; return false;
} }
setActive(flag: boolean) {
if (flag === this._actived) {
// TODO: 如果移动到另外一个 container会有问题
return;
}
if (flag) {
if (!this.inited) {
this.initBody();
}
this._actived = true;
this.parent?.active(this);
} else if (this.inited) {
this._actived = false;
this.parent?.unactive(this);
}
}
toggle() {
this.setActive(!this._actived);
}
readonly isPanel = true; readonly isPanel = true;
@ -52,7 +33,13 @@ export default class Panel implements IWidget {
} }
get content() { get content() {
return this.plain ? this.body : createElement(PanelView, { panel: this }); if (this.plain) {
return createElement(PanelView, {
panel: this,
key: this.id,
});
}
return createElement(TitledPanelView, { panel: this, key: this.id });
} }
readonly title: TitleContent; readonly title: TitleContent;
@ -71,13 +58,19 @@ export default class Panel implements IWidget {
this.plain = hideTitleBar || !title; this.plain = hideTitleBar || !title;
this.help = help; this.help = help;
if (Array.isArray(content)) { if (Array.isArray(content)) {
this.container = this.skeleton.createContainer(name, (item) => { this.container = this.skeleton.createContainer(
name,
(item) => {
if (isPanel(item)) { if (isPanel(item)) {
return item; return item;
} }
return this.skeleton.createPanel(item); return this.skeleton.createPanel(item);
}, true, () => this.visible, true); },
content.forEach(item => this.add(item)); true,
() => this.visible,
true,
);
content.forEach((item) => this.add(item));
} }
// todo: process shortcut // todo: process shortcut
} }
@ -90,18 +83,18 @@ export default class Panel implements IWidget {
if (this.container) { if (this.container) {
this._body = createElement(TabsPanelView, { this._body = createElement(TabsPanelView, {
container: this.container, container: this.container,
key: this.id,
}); });
} else { } else {
const { content, contentProps } = this.config; const { content, contentProps } = this.config;
this._body = createContent(content, { this._body = createContent(content, {
...contentProps, ...contentProps,
editor: this.skeleton.editor, editor: this.skeleton.editor,
config: this.config,
panel: this, panel: this,
key: this.id,
}); });
} }
} }
setParent(parent: WidgetContainer) { setParent(parent: WidgetContainer) {
if (parent === this.parent) { if (parent === this.parent) {
return; return;
@ -136,6 +129,27 @@ export default class Panel implements IWidget {
return this.content; return this.content;
} }
setActive(flag: boolean) {
if (flag === this._actived) {
// TODO: 如果移动到另外一个 container会有问题
return;
}
if (flag) {
if (!this.inited) {
this.initBody();
}
this._actived = true;
this.parent?.active(this);
} else if (this.inited) {
this._actived = false;
this.parent?.unactive(this);
}
}
toggle() {
this.setActive(!this._actived);
}
hide() { hide() {
this.setActive(false); this.setActive(false);
} }

View File

@ -1,9 +1,8 @@
import { Component, Fragment } from 'react'; import { Component, Fragment } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { observer } from '@ali/recore'; import { observer } from '@ali/lowcode-globals';
import Area from './area'; import Area from './area';
import Panel from './panel'; import Panel from './panel';
import { PanelWrapper } from './widget-views';
@observer @observer
export default class RightArea extends Component<{ area: Area<any, Panel> }> { export default class RightArea extends Component<{ area: Area<any, Panel> }> {
@ -29,7 +28,7 @@ class Contents extends Component<{ area: Area<any, Panel> }> {
const { area } = this.props; const { area } = this.props;
return ( return (
<Fragment> <Fragment>
{area.container.items.map((item) => <PanelWrapper key={item.id} panel={item} />)} {area.container.items.map((item) => item.content)}
</Fragment> </Fragment>
); );
} }

View File

@ -6,7 +6,7 @@ import PanelDock from './panel-dock';
import { composeTitle } from './utils'; import { composeTitle } from './utils';
import WidgetContainer from './widget-container'; import WidgetContainer from './widget-container';
import Panel from './panel'; import Panel from './panel';
import Widget from './widget'; import { IWidget } from './widget';
export function DockView({ title, icon, description, size, className, onClick }: DockProps) { export function DockView({ title, icon, description, size, className, onClick }: DockProps) {
return ( return (
@ -22,6 +22,25 @@ export function DockView({ title, icon, description, size, className, onClick }:
@observer @observer
export class PanelDockView extends Component<DockProps & { dock: PanelDock }> { export class PanelDockView extends Component<DockProps & { dock: PanelDock }> {
componentDidMount() {
this.checkActived();
}
componentDidUpdate() {
this.checkActived();
}
private lastActived: boolean = false;
checkActived() {
const { dock } = this.props;
if (dock.actived !== this.lastActived) {
this.lastActived = dock.actived;
if (this.lastActived) {
dock.skeleton.editor.emit('skeleton.panel-dock.active', dock.name, dock);
} else {
dock.skeleton.editor.emit('skeleton.panel-dock.unactive', dock.name, dock);
}
}
}
render() { render() {
const { dock, className, onClick, ...props } = this.props; const { dock, className, onClick, ...props } = this.props;
return DockView({ return DockView({
@ -31,7 +50,7 @@ export class PanelDockView extends Component<DockProps & { dock: PanelDock }> {
}), }),
onClick: () => { onClick: () => {
onClick && onClick(); onClick && onClick();
dock.toggle(); dock.togglePanel();
}, },
}); });
} }
@ -41,16 +60,82 @@ export class DialogDockView extends Component {
} }
@observer
export class TitledPanelView extends Component<{ panel: Panel }> {
shouldComponentUpdate() {
return false;
}
componentDidMount() {
this.checkVisible();
}
componentDidUpdate() {
this.checkVisible();
}
private lastVisible: boolean = false;
checkVisible() {
const { panel } = this.props;
const currentVisible = panel.inited && panel.visible;
if (currentVisible !== this.lastVisible) {
this.lastVisible = currentVisible;
if (this.lastVisible) {
panel.skeleton.editor.emit('skeleton.panel.show', panel.name, panel);
} else {
panel.skeleton.editor.emit('skeleton.panel.hide', panel.name, panel);
}
}
}
render() {
const { panel } = this.props;
if (!panel.inited) {
return null;
}
return (
<div className={classNames('lc-titled-panel', {
hidden: !panel.visible,
})}>
<PanelTitle panel={panel} />
<div className="lc-pane-body">{panel.body}</div>
</div>
);
}
}
@observer
export class PanelView extends Component<{ panel: Panel }> { export class PanelView extends Component<{ panel: Panel }> {
shouldComponentUpdate() { shouldComponentUpdate() {
return false; return false;
} }
componentDidMount() {
this.checkVisible();
}
componentDidUpdate() {
this.checkVisible();
}
private lastVisible: boolean = false;
checkVisible() {
const { panel } = this.props;
const currentVisible = panel.inited && panel.visible;
if (currentVisible !== this.lastVisible) {
this.lastVisible = currentVisible;
if (this.lastVisible) {
panel.skeleton.editor.emit('skeleton.panel.show', panel.name, panel);
} else {
panel.skeleton.editor.emit('skeleton.panel.hide', panel.name, panel);
}
}
}
render() { render() {
const { panel } = this.props; const { panel } = this.props;
if (!panel.inited) {
return null;
}
return ( return (
<div className="lc-panel"> <div
<PanelTitle panel={panel} /> className={classNames('lc-panel', {
<div className="lc-pane-body">{panel.body}</div> hidden: !panel.visible,
})}
>
{panel.body}
</div> </div>
); );
} }
@ -64,7 +149,7 @@ export class TabsPanelView extends Component<{ container: WidgetContainer<Panel>
const contents: ReactElement[] = []; const contents: ReactElement[] = [];
container.items.forEach((item: any) => { container.items.forEach((item: any) => {
titles.push(<PanelTitle key={item.id} panel={item} className="lc-tab-title" />); titles.push(<PanelTitle key={item.id} panel={item} className="lc-tab-title" />);
contents.push(<PanelWrapper key={item.id} panel={item} />); contents.push(<PanelView key={item.id} panel={item} />);
}); });
return ( return (
@ -113,26 +198,29 @@ class PanelTitle extends Component<{ panel: Panel; className?: string }> {
} }
@observer @observer
export class PanelWrapper extends Component<{ panel: Panel }> { export class WidgetView extends Component<{ widget: IWidget }> {
render() { shouldComponentUpdate() {
const { panel } = this.props; return false;
if (!panel.inited) { }
return null; componentDidMount() {
this.checkVisible();
}
componentDidUpdate() {
this.checkVisible();
}
private lastVisible: boolean = false;
checkVisible() {
const { widget } = this.props;
const currentVisible = widget.visible;
if (currentVisible !== this.lastVisible) {
this.lastVisible = currentVisible;
if (this.lastVisible) {
widget.skeleton.editor.emit('skeleton.widget.show', widget.name, widget);
} else {
widget.skeleton.editor.emit('skeleton.widget.hide', widget.name, widget);
}
} }
return (
<div
className={classNames('lc-panel-wrapper', {
hidden: !panel.actived,
})}
>
{panel.body}
</div>
);
} }
}
@observer
export class WidgetWrapper extends Component<{ widget: Widget }> {
render() { render() {
const { widget } = this.props; const { widget } = this.props;
if (!widget.visible) { if (!widget.visible) {

View File

@ -2,18 +2,22 @@ import { ReactNode, createElement } from 'react';
import { createContent, uniqueId, obx } from '@ali/lowcode-globals'; import { createContent, uniqueId, obx } from '@ali/lowcode-globals';
import { WidgetConfig } from './types'; import { WidgetConfig } from './types';
import { Skeleton } from './skeleton'; import { Skeleton } from './skeleton';
import { WidgetWrapper } from './widget-views'; import { WidgetView } from './widget-views';
export interface IWidget { export interface IWidget {
readonly name: string; readonly name: string;
readonly content: any; readonly content: ReactNode;
readonly align?: string; readonly align?: string;
readonly isWidget: true; readonly isWidget: true;
readonly visible: boolean;
readonly body: ReactNode;
readonly skeleton: Skeleton;
getName(): string; getName(): string;
getContent(): any; getContent(): any;
show(): void; show(): void;
hide(): void; hide(): void;
toggle(): void;
} }
export default class Widget implements IWidget { export default class Widget implements IWidget {
@ -42,8 +46,8 @@ export default class Widget implements IWidget {
return this._body; return this._body;
} }
get content() { get content(): ReactNode {
return createElement(WidgetWrapper, { return createElement(WidgetView, {
widget: this, widget: this,
key: this.id, key: this.id,
}); });

View File

@ -45,7 +45,7 @@ body {
} }
.my-pane { .lc-titled-panel {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: relative; position: relative;
@ -104,7 +104,7 @@ body {
top: var(--pane-title-height); top: var(--pane-title-height);
} }
} }
.lc-panel-wrapper { .lc-panel {
height: 100%; height: 100%;
width: 100%; width: 100%;
overflow: auto; overflow: auto;
@ -173,8 +173,6 @@ body {
} }
.lc-workbench { .lc-workbench {
height: 100%; height: 100%;
display: flex; display: flex;
@ -197,7 +195,8 @@ body {
bottom: 0; bottom: 0;
width: var(--dock-pane-width); width: var(--dock-pane-width);
left: calc(var(--left-area-width) + 1px); left: calc(var(--left-area-width) + 1px);
z-index: 20; background-color: var(--color-pane-background);
z-index: 820;
display: none; display: none;
&.lc-area-visible { &.lc-area-visible {
display: block; display: block;

View File

@ -69,3 +69,12 @@ export {
Panes, Panes,
modules, modules,
}; };
/*
console.log(
`%cLowcodeEngine %cv${VERSION}`,
"color:#000;font-weight:bold;",
"color:green;font-weight:bold;"
);
*/