mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 09:41:57 +00:00
polyfill panes
This commit is contained in:
parent
33750b7fef
commit
89196c536c
@ -46,12 +46,12 @@
|
||||
}
|
||||
|
||||
&-device-default {
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
left: 15px;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
left: 16px;
|
||||
width: auto;
|
||||
box-shadow: 0 2px 10px 0 rgba(31,56,88,.15);
|
||||
box-shadow: 0 1px 4px 0 rgba(31, 50, 88, 0.125);
|
||||
}
|
||||
|
||||
&-content {
|
||||
|
||||
@ -8,7 +8,7 @@ import './designer.less';
|
||||
import clipboard from './clipboard';
|
||||
|
||||
export class DesignerView extends Component<DesignerProps & {
|
||||
designer: Designer;
|
||||
designer?: Designer;
|
||||
}> {
|
||||
readonly designer: Designer;
|
||||
|
||||
|
||||
@ -7,18 +7,28 @@ export default class AreaManager {
|
||||
|
||||
private config: PluginConfig[];
|
||||
|
||||
private editor: Editor;
|
||||
|
||||
private area: string;
|
||||
|
||||
constructor(editor: Editor, area: string) {
|
||||
this.editor = editor;
|
||||
this.area = area;
|
||||
this.config = (editor && editor.config && editor.config.plugins && editor.config.plugins[this.area]) || [];
|
||||
constructor(private editor: Editor, private name: string) {
|
||||
this.config = (editor && editor.config && editor.config.plugins && editor.config.plugins[name]) || [];
|
||||
this.pluginStatus = clone(editor.pluginStatus);
|
||||
}
|
||||
|
||||
public isPluginStatusUpdate(pluginType?: string, notUpdateStatus?: boolean): boolean {
|
||||
setVisible(flag: boolean) {
|
||||
|
||||
}
|
||||
|
||||
isEnable() {
|
||||
|
||||
}
|
||||
|
||||
isVisible() {
|
||||
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
|
||||
}
|
||||
|
||||
isPluginStatusUpdate(pluginType?: string, notUpdateStatus?: boolean): boolean {
|
||||
const { pluginStatus } = this.editor;
|
||||
const list = pluginType ? this.config.filter((item): boolean => item.type === pluginType) : this.config;
|
||||
|
||||
@ -31,33 +41,33 @@ export default class AreaManager {
|
||||
return isUpdate;
|
||||
}
|
||||
|
||||
public getVisiblePluginList(pluginType?: string): PluginConfig[] {
|
||||
getVisiblePluginList(pluginType?: string): PluginConfig[] {
|
||||
const res = this.config.filter((item): boolean => {
|
||||
return !!(!this.pluginStatus[item.pluginKey] || this.pluginStatus[item.pluginKey].visible);
|
||||
});
|
||||
return pluginType ? res.filter((item): boolean => item.type === pluginType) : res;
|
||||
}
|
||||
|
||||
public getPlugin(pluginKey: string): HOCPlugin | void {
|
||||
getPlugin(pluginKey: string): HOCPlugin | void {
|
||||
if (pluginKey) {
|
||||
return this.editor && this.editor.plugins && this.editor.plugins[pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
public getPluginConfig(pluginKey?: string): PluginConfig[] | PluginConfig | undefined {
|
||||
getPluginConfig(pluginKey?: string): PluginConfig[] | PluginConfig | undefined {
|
||||
if (pluginKey) {
|
||||
return this.config.find(item => item.pluginKey === pluginKey);
|
||||
}
|
||||
return this.config;
|
||||
}
|
||||
|
||||
public getPluginClass(pluginKey: string): PluginClass | void {
|
||||
getPluginClass(pluginKey: string): PluginClass | void {
|
||||
if (pluginKey) {
|
||||
return this.editor && this.editor.components && this.editor.components[pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
public getPluginStatus(pluginKey: string): PluginStatus | void {
|
||||
getPluginStatus(pluginKey: string): PluginStatus | void {
|
||||
if (pluginKey) {
|
||||
return this.editor && this.editor.pluginStatus && this.editor.pluginStatus[pluginKey];
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ export default class Editor extends EventEmitter {
|
||||
|
||||
private hooksFuncs: HooksFuncs;
|
||||
|
||||
constructor(config: EditorConfig, components: PluginClassSet, utils?: Utils) {
|
||||
constructor(config: EditorConfig = {}, components: PluginClassSet = {}, utils?: Utils) {
|
||||
super();
|
||||
this.config = config;
|
||||
this.components = {};
|
||||
|
||||
@ -1,152 +1,12 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Editor, PluginConfig } from '@ali/lowcode-editor-core';
|
||||
import { Editor } from '@ali/lowcode-editor-core';
|
||||
import { DesignerView, Designer } from '@ali/lowcode-designer';
|
||||
import './index.scss';
|
||||
|
||||
export interface PluginProps {
|
||||
editor: Editor;
|
||||
config: PluginConfig;
|
||||
}
|
||||
|
||||
const SCHEMA = {
|
||||
version: '1.0',
|
||||
componentsMap: [],
|
||||
componentsTree: [
|
||||
{
|
||||
componentName: 'Page',
|
||||
fileName: 'test',
|
||||
dataSource: {
|
||||
list: [],
|
||||
},
|
||||
state: {
|
||||
text: 'outter',
|
||||
},
|
||||
props: {
|
||||
ref: 'outterView',
|
||||
autoLoading: true,
|
||||
style: {
|
||||
padding: 20,
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form',
|
||||
props: {
|
||||
labelCol: 3,
|
||||
style: {},
|
||||
ref: 'testForm',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
props: {
|
||||
label: '姓名:',
|
||||
name: 'name',
|
||||
initValue: '李雷',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Input',
|
||||
props: {
|
||||
placeholder: '请输入',
|
||||
size: 'medium',
|
||||
style: {
|
||||
width: 320,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
props: {
|
||||
label: '年龄:',
|
||||
name: 'age',
|
||||
initValue: '22',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'NumberPicker',
|
||||
props: {
|
||||
size: 'medium',
|
||||
type: 'normal',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Form.Item',
|
||||
props: {
|
||||
label: '职业:',
|
||||
name: 'profession',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Select',
|
||||
props: {
|
||||
dataSource: [
|
||||
{
|
||||
label: '教师',
|
||||
value: 't',
|
||||
},
|
||||
{
|
||||
label: '医生',
|
||||
value: 'd',
|
||||
},
|
||||
{
|
||||
label: '歌手',
|
||||
value: 's',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
componentName: 'Div',
|
||||
props: {
|
||||
style: {
|
||||
textAlign: 'center',
|
||||
},
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Button.Group',
|
||||
props: {},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
type: 'primary',
|
||||
style: {
|
||||
margin: '0 5px 0 5px',
|
||||
},
|
||||
htmlType: 'submit',
|
||||
},
|
||||
children: '提交',
|
||||
},
|
||||
{
|
||||
componentName: 'Button',
|
||||
props: {
|
||||
type: 'normal',
|
||||
style: {
|
||||
margin: '0 5px 0 5px',
|
||||
},
|
||||
htmlType: 'reset',
|
||||
},
|
||||
children: '重置',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
interface DesignerPluginState {
|
||||
componentMetadatas?: any[] | null;
|
||||
library?: any[] | null;
|
||||
|
||||
@ -168,7 +168,6 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable {
|
||||
}
|
||||
}
|
||||
});
|
||||
// editor.once('outlinePane.visible', setup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
"@ali/lowcode-plugin-zh-en": "^0.8.6",
|
||||
"@ali/lowcode-setters": "^0.8.6",
|
||||
"@alifd/next": "^1.19.12",
|
||||
"@ali/ve-less-variables": "2.0.3",
|
||||
"@alife/theme-lowcode-dark": "^0.1.0",
|
||||
"@alife/theme-lowcode-light": "^0.1.0",
|
||||
"react": "^16.8.1",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { init } from "./vision";
|
||||
import editor from './editor';
|
||||
import { init } from './vision'; // VisualEngine
|
||||
import { editor } from './editor';
|
||||
|
||||
init();
|
||||
|
||||
|
||||
@ -1,68 +1,44 @@
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import outlinePane from '@ali/lowcode-plugin-outline-pane';
|
||||
import settingsPane from '@ali/lowcode-plugin-settings-pane';
|
||||
import designer from '@ali/lowcode-plugin-designer';
|
||||
import OutlinePane from '@ali/lowcode-plugin-outline-pane';
|
||||
import SettingsPane from '@ali/lowcode-plugin-settings-pane';
|
||||
import Designer from '@ali/lowcode-plugin-designer';
|
||||
import { registerSetters } from '@ali/lowcode-setters';
|
||||
import { Skeleton } from './skeleton/skeleton';
|
||||
|
||||
registerSetters();
|
||||
|
||||
export default new Editor(
|
||||
{
|
||||
plugins: {
|
||||
topArea: [],
|
||||
leftArea: [
|
||||
{
|
||||
pluginKey: 'outlinePane',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
icon: 'shuxingkongjian',
|
||||
title: '大纲树',
|
||||
},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-outline-pane',
|
||||
version: '^0.8.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
],
|
||||
rightArea: [
|
||||
{
|
||||
pluginKey: 'settingsPane',
|
||||
type: 'Panel',
|
||||
props: {},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-settings-pane',
|
||||
version: '^0.8.0',
|
||||
},
|
||||
pluginProps: {},
|
||||
},
|
||||
],
|
||||
centerArea: [
|
||||
{
|
||||
pluginKey: 'designer',
|
||||
type: '',
|
||||
props: {},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-designer',
|
||||
version: '^0.8.0',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
outlinePane,
|
||||
settingsPane,
|
||||
designer,
|
||||
},
|
||||
);
|
||||
export const editor = new Editor();
|
||||
|
||||
export const skeleton = new Skeleton(editor);
|
||||
|
||||
skeleton.mainArea.add({
|
||||
name: 'designer',
|
||||
type: 'Widget',
|
||||
content: Designer,
|
||||
});
|
||||
skeleton.rightArea.add({
|
||||
name: 'settingsPane',
|
||||
type: 'Panel',
|
||||
content: SettingsPane,
|
||||
});
|
||||
skeleton.leftArea.add({
|
||||
name: 'outlinePane',
|
||||
type: 'PanelDock',
|
||||
props: {
|
||||
align: 'top',
|
||||
icon: 'shuxingkongjian',
|
||||
description: '大纲树',
|
||||
},
|
||||
content: OutlinePane,
|
||||
panelProps: {
|
||||
area: 'leftFloatArea'
|
||||
}
|
||||
});
|
||||
|
||||
// editor-core
|
||||
// 1. di 实现
|
||||
// 2. skeleton 区域管理
|
||||
// 3. general bus: pub/sub
|
||||
// 2. general bus: pub/sub
|
||||
// editor-skeleton/workbench 视图实现
|
||||
// 1. skeleton 区域划分 panes
|
||||
// provide fixed left pane
|
||||
// provide float left pane
|
||||
|
||||
148
packages/vision-polyfill/src/flags.ts
Normal file
148
packages/vision-polyfill/src/flags.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import domReady = require('domready');
|
||||
import * as EventEmitter from 'events';
|
||||
|
||||
const Shells = ['iphone6'];
|
||||
|
||||
export class Flags {
|
||||
|
||||
public emitter: EventEmitter;
|
||||
public flags: string[];
|
||||
public ready: boolean;
|
||||
public lastFlags: string[];
|
||||
public lastShell: string;
|
||||
|
||||
private lastSimulatorDevice: string;
|
||||
|
||||
constructor() {
|
||||
this.emitter = new EventEmitter();
|
||||
this.flags = ['design-mode'];
|
||||
|
||||
domReady(() => {
|
||||
this.ready = true;
|
||||
this.applyFlags();
|
||||
});
|
||||
}
|
||||
|
||||
public setDragMode(flag: boolean) {
|
||||
if (flag) {
|
||||
this.add('drag-mode');
|
||||
} else {
|
||||
this.remove('drag-mode');
|
||||
}
|
||||
}
|
||||
|
||||
public setPreviewMode(flag: boolean) {
|
||||
if (flag) {
|
||||
this.add('preview-mode');
|
||||
this.remove('design-mode');
|
||||
} else {
|
||||
this.add('design-mode');
|
||||
this.remove('preview-mode');
|
||||
}
|
||||
}
|
||||
|
||||
public setWithShell(shell: string) {
|
||||
if (shell === this.lastShell) {
|
||||
return;
|
||||
}
|
||||
if (this.lastShell) {
|
||||
this.remove(`with-${this.lastShell}shell`);
|
||||
}
|
||||
if (shell) {
|
||||
if (Shells.indexOf(shell) < 0) {
|
||||
shell = Shells[0];
|
||||
}
|
||||
this.add(`with-${shell}shell`);
|
||||
this.lastShell = shell;
|
||||
}
|
||||
}
|
||||
|
||||
public setSimulator(device: string) {
|
||||
if (this.lastSimulatorDevice) {
|
||||
this.remove(`simulator-${this.lastSimulatorDevice}`);
|
||||
}
|
||||
if (device !== '' && device !== 'pc') {
|
||||
this.add(`simulator-${device}`);
|
||||
}
|
||||
this.lastSimulatorDevice = device;
|
||||
}
|
||||
|
||||
public setHideSlate(flag: boolean) {
|
||||
if (this.has('slate-fixed')) {
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
this.add('hide-slate');
|
||||
} else {
|
||||
this.remove('hide-slate');
|
||||
}
|
||||
}
|
||||
|
||||
public setSlateFixedMode(flag: boolean) {
|
||||
if (flag) {
|
||||
this.remove('hide-slate');
|
||||
this.add('slate-fixed');
|
||||
} else {
|
||||
this.remove('slate-fixed');
|
||||
}
|
||||
}
|
||||
|
||||
public setSlateFullMode(flag: boolean) {
|
||||
if (flag) {
|
||||
this.add('slate-full-screen');
|
||||
} else {
|
||||
this.remove('slate-full-screen');
|
||||
}
|
||||
}
|
||||
|
||||
public getFlags() {
|
||||
return this.flags;
|
||||
}
|
||||
|
||||
public applyFlags(modifiedFlag?: string) {
|
||||
if (!this.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doe = document.documentElement;
|
||||
if (this.lastFlags) {
|
||||
this.lastFlags.filter((flag: string) => this.flags.indexOf(flag) < 0).forEach((flag) => {
|
||||
doe.classList.remove(`engine-${flag}`);
|
||||
});
|
||||
}
|
||||
this.flags.forEach((flag) => {
|
||||
doe.classList.add(`engine-${flag}`);
|
||||
});
|
||||
|
||||
this.lastFlags = this.flags.slice(0);
|
||||
this.emitter.emit('flagschange', this.flags, modifiedFlag);
|
||||
}
|
||||
|
||||
public has(flag: string) {
|
||||
return this.flags.indexOf(flag) > -1;
|
||||
}
|
||||
|
||||
public add(flag: string) {
|
||||
if (!this.has(flag)) {
|
||||
this.flags.push(flag);
|
||||
this.applyFlags(flag);
|
||||
}
|
||||
}
|
||||
|
||||
public remove(flag: string) {
|
||||
const i = this.flags.indexOf(flag);
|
||||
if (i > -1) {
|
||||
this.flags.splice(i, 1);
|
||||
this.applyFlags(flag);
|
||||
}
|
||||
}
|
||||
|
||||
public onFlagsChange(func: () => any) {
|
||||
this.emitter.on('flagschange', func);
|
||||
return () => {
|
||||
this.emitter.removeListener('flagschange', func);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default new Flags();
|
||||
206
packages/vision-polyfill/src/panes.ts
Normal file
206
packages/vision-polyfill/src/panes.ts
Normal file
@ -0,0 +1,206 @@
|
||||
import { skeleton } from './editor';
|
||||
import { ReactElement } from 'react';
|
||||
import { IWidgetBaseConfig } from './skeleton/types';
|
||||
|
||||
export interface IContentItemConfig {
|
||||
title: string;
|
||||
content: JSX.Element;
|
||||
tip?: {
|
||||
content: string;
|
||||
url?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface OldPaneConfig {
|
||||
// 'dock' | 'action' | 'tab' | 'widget' | 'stage'
|
||||
type?: string; // where
|
||||
|
||||
id?: string;
|
||||
name: string;
|
||||
title?: string;
|
||||
content?: any;
|
||||
|
||||
place?: string; // align: left|right|top|center|bottom
|
||||
description?: string; // tip?
|
||||
tip?:
|
||||
| string
|
||||
| {
|
||||
// as help tip
|
||||
url?: string;
|
||||
content?: string | JSX.Element;
|
||||
}; // help
|
||||
|
||||
init?: () => any;
|
||||
destroy?: () => any;
|
||||
props?: any;
|
||||
|
||||
contents?: IContentItemConfig[];
|
||||
hideTitleBar?: boolean;
|
||||
width?: number;
|
||||
maxWidth?: number;
|
||||
height?: number;
|
||||
maxHeight?: number;
|
||||
position?: string | string[]; // todo
|
||||
menu?: JSX.Element; // as title
|
||||
index?: number; // todo
|
||||
isAction?: boolean; // as normal dock
|
||||
fullScreen?: boolean; // todo
|
||||
}
|
||||
|
||||
function upgradeConfig(config: OldPaneConfig): IWidgetBaseConfig & { area: string } {
|
||||
const { type, id, name, title, content, place, description, init, destroy, props, index } = config;
|
||||
|
||||
const newConfig: any = {
|
||||
id,
|
||||
name,
|
||||
content,
|
||||
props: {
|
||||
title,
|
||||
description,
|
||||
align: place,
|
||||
onInit: init,
|
||||
onDestroy: destroy,
|
||||
},
|
||||
contentProps: props,
|
||||
index,
|
||||
};
|
||||
if (type === 'dock') {
|
||||
newConfig.type = 'PanelDock';
|
||||
newConfig.area = 'left';
|
||||
const { contents, hideTitleBar, tip, width, maxWidth, height, maxHeight, position, menu, isAction } = config;
|
||||
if (menu) {
|
||||
newConfig.props.title = menu;
|
||||
}
|
||||
if (!isAction) {
|
||||
newConfig.panelProps = {
|
||||
hideTitleBar,
|
||||
help: tip,
|
||||
width,
|
||||
maxWidth,
|
||||
height,
|
||||
maxHeight,
|
||||
};
|
||||
|
||||
if (contents && Array.isArray(contents)) {
|
||||
newConfig.content = contents.map(({ title, content, tip }) => {
|
||||
return {
|
||||
type: "Panel",
|
||||
content,
|
||||
props: {
|
||||
title,
|
||||
help: tip,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} 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';
|
||||
}
|
||||
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
function add(config: (() => OldPaneConfig) | OldPaneConfig, extraConfig?: any) {
|
||||
if (typeof config === 'function') {
|
||||
config = config.call(null);
|
||||
}
|
||||
if (!config || !config.type) {
|
||||
return null;
|
||||
}
|
||||
if (extraConfig) {
|
||||
config = { ...config, ...extraConfig };
|
||||
}
|
||||
|
||||
skeleton.add(upgradeConfig(config));
|
||||
}
|
||||
|
||||
const actionPane = Object.assign(skeleton.topArea, {
|
||||
/**
|
||||
* compatible *VE.actionPane.getActions*
|
||||
*/
|
||||
getActions(): any {
|
||||
return skeleton.topArea.container.items;
|
||||
},
|
||||
/**
|
||||
* compatible *VE.actionPane.activeDock*
|
||||
*/
|
||||
setActions() {
|
||||
// empty
|
||||
},
|
||||
});
|
||||
const dockPane = Object.assign(skeleton.leftArea, {
|
||||
/**
|
||||
* compatible *VE.dockPane.activeDock*
|
||||
*/
|
||||
activeDock(item: any) {
|
||||
const name = item.name || item;
|
||||
skeleton.getPanel(name)?.active();
|
||||
},
|
||||
|
||||
/**
|
||||
* compatible *VE.dockPane.onDockShow*
|
||||
*/
|
||||
onDockShow() {},
|
||||
/**
|
||||
* compatible *VE.dockPane.onDockHide*
|
||||
*/
|
||||
onDockHide() {},
|
||||
/**
|
||||
* compatible *VE.dockPane.setFixed*
|
||||
*/
|
||||
setFixed(flag: boolean) {
|
||||
// todo:
|
||||
},
|
||||
});
|
||||
const tabPane = Object.assign(skeleton.rightArea, {
|
||||
setFloat(flag: boolean) {
|
||||
// todo:
|
||||
},
|
||||
});
|
||||
const toolbar = Object.assign(skeleton.toolbar, {
|
||||
setContents(contents: ReactElement) {
|
||||
// todo:
|
||||
},
|
||||
});
|
||||
const widgets = skeleton.mainArea;
|
||||
|
||||
const stages = Object.assign(skeleton.stages, {
|
||||
getStage(name: string) {
|
||||
skeleton.stages.container.get(name);
|
||||
},
|
||||
|
||||
createStage(config: any) {
|
||||
config = upgradeConfig(config);
|
||||
if (config.id) {
|
||||
config.name = config.id;
|
||||
}
|
||||
|
||||
const stage = skeleton.stages.add(config);
|
||||
return stage.getName();
|
||||
},
|
||||
});
|
||||
|
||||
export default {
|
||||
ActionPane: actionPane, // topArea
|
||||
actionPane, //
|
||||
DockPane: dockPane, // leftArea
|
||||
dockPane,
|
||||
TabPane: tabPane, // rightArea
|
||||
tabPane,
|
||||
add,
|
||||
toolbar, // toolbar
|
||||
Stages: stages,
|
||||
Widgets: widgets, // centerArea
|
||||
widgets,
|
||||
};
|
||||
48
packages/vision-polyfill/src/skeleton/area.ts
Normal file
48
packages/vision-polyfill/src/skeleton/area.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { obx, computed } from '@ali/lowcode-globals';
|
||||
import WidgetContainer from './widget-container';
|
||||
import { Skeleton } from './skeleton';
|
||||
import { IWidget } from './widget';
|
||||
import { IWidgetBaseConfig } from './types';
|
||||
|
||||
export default class Area<C extends IWidgetBaseConfig = any, T extends IWidget = IWidget> {
|
||||
@obx private _visible: boolean = true;
|
||||
|
||||
@computed get visible() {
|
||||
if (this.exclusive) {
|
||||
return this.container.current != null;
|
||||
}
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
readonly container: WidgetContainer<T, C>;
|
||||
constructor(readonly skeleton: Skeleton, readonly name: string, handle: (item: T | C) => T, private exclusive?: boolean, defaultSetCurrent: boolean = false) {
|
||||
this.container = skeleton.createContainer(name, handle, exclusive, () => this.visible, defaultSetCurrent);
|
||||
}
|
||||
|
||||
@computed isEmpty(): boolean {
|
||||
return this.container.items.length < 1;
|
||||
}
|
||||
|
||||
add(config: T | C): T {
|
||||
return this.container.add(config);
|
||||
}
|
||||
|
||||
private lastCurrent: T | null = null;
|
||||
setVisible(flag: boolean) {
|
||||
if (this.exclusive) {
|
||||
const current = this.container.current;
|
||||
if (flag && !current) {
|
||||
this.container.active(this.lastCurrent || this.container.getAt(0))
|
||||
} else if (current) {
|
||||
this.lastCurrent = this.container.current;
|
||||
this.container.unactive();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this._visible = flag;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setVisible(false);
|
||||
}
|
||||
}
|
||||
38
packages/vision-polyfill/src/skeleton/bottom-area.tsx
Normal file
38
packages/vision-polyfill/src/skeleton/bottom-area.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/recore';
|
||||
import Area from './area';
|
||||
import Panel from './panel';
|
||||
import { PanelWrapper } from './widget-views';
|
||||
|
||||
@observer
|
||||
export default class BottomArea extends Component<{ area: Area<any, Panel> }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
if (area.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className={classNames('lc-bottom-area', {
|
||||
'lc-area-visible': area.visible,
|
||||
})}>
|
||||
<Contents area={area} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area<any, Panel> }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
{area.container.items.map((item) => <PanelWrapper key={item.id} panel={item} />)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
78
packages/vision-polyfill/src/skeleton/dock.ts
Normal file
78
packages/vision-polyfill/src/skeleton/dock.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { ReactNode, createElement } from 'react';
|
||||
import { uniqueId, createContent, obx } from '@ali/lowcode-globals';
|
||||
import { DockConfig } from "./types";
|
||||
import { Skeleton } from './skeleton';
|
||||
import { DockView } from './widget-views';
|
||||
import { IWidget } from './widget';
|
||||
|
||||
/**
|
||||
* 带图标(主要)/标题(次要)的扩展
|
||||
*/
|
||||
export default class Dock implements IWidget {
|
||||
readonly isWidget = true;
|
||||
readonly id = uniqueId('dock');
|
||||
readonly name: string;
|
||||
readonly align?: string;
|
||||
|
||||
@obx.ref private _visible: boolean = true;
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
private inited: boolean = false;
|
||||
private _content: ReactNode;
|
||||
get content() {
|
||||
if (this.inited) {
|
||||
return this._content;
|
||||
}
|
||||
this.inited = true;
|
||||
const { props, content, contentProps } = this.config;
|
||||
|
||||
if (content) {
|
||||
this._content = createContent(content, {
|
||||
...contentProps,
|
||||
editor: this.skeleton.editor,
|
||||
key: this.id,
|
||||
});
|
||||
} else {
|
||||
this._content = createElement(DockView, {
|
||||
...props,
|
||||
key: this.id,
|
||||
});
|
||||
}
|
||||
|
||||
return this._content;
|
||||
}
|
||||
constructor(readonly skeleton: Skeleton, private config: DockConfig) {
|
||||
const { props = {}, name } = config;
|
||||
this.name = name;
|
||||
this.align = props.align;
|
||||
}
|
||||
|
||||
setVisible(flag: boolean) {
|
||||
if (flag === this._visible) {
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
this._visible = true;
|
||||
} else if (this.inited) {
|
||||
this._visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setVisible(false);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.setVisible(true);
|
||||
}
|
||||
}
|
||||
41
packages/vision-polyfill/src/skeleton/left-area.tsx
Normal file
41
packages/vision-polyfill/src/skeleton/left-area.tsx
Normal file
@ -0,0 +1,41 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-globals';
|
||||
import Area from './area';
|
||||
|
||||
@observer
|
||||
export default class LeftArea extends Component<{ area: Area }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<div className={classNames("lc-left-area", {
|
||||
'lc-area-visible': area.visible
|
||||
})}>
|
||||
<Contents area={area} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
const top: any[] = [];
|
||||
const bottom: any[] = [];
|
||||
area.container.items.forEach(item => {
|
||||
if (item.align === 'bottom') {
|
||||
bottom.push(item.content);
|
||||
} else {
|
||||
top.push(item.content);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="lc-left-area-top">{top}</div>
|
||||
<div className="lc-left-area-bottom">{bottom}</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
50
packages/vision-polyfill/src/skeleton/left-fixed-pane.tsx
Normal file
50
packages/vision-polyfill/src/skeleton/left-fixed-pane.tsx
Normal file
@ -0,0 +1,50 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-globals';
|
||||
import { Button } from '@alifd/next';
|
||||
import Area from './area';
|
||||
import { PanelConfig } from './types';
|
||||
import Panel from './panel';
|
||||
import { PanelWrapper } from './widget-views';
|
||||
|
||||
@observer
|
||||
export default class LeftFixedPane extends Component<{ area: Area<PanelConfig, Panel> }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={classNames('lc-left-fixed-pane', {
|
||||
'lc-area-visible': area.visible,
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
className="lc-pane-close"
|
||||
onClick={() => {
|
||||
area.setVisible(false);
|
||||
}}
|
||||
/>
|
||||
<Contents area={area} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area<PanelConfig, Panel> }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
{area.container.items.map((panel) => (
|
||||
<PanelWrapper key={panel.id} panel={panel} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
51
packages/vision-polyfill/src/skeleton/left-float-pane.tsx
Normal file
51
packages/vision-polyfill/src/skeleton/left-float-pane.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-globals';
|
||||
import { Button } from '@alifd/next';
|
||||
import Area from './area';
|
||||
import Panel from './panel';
|
||||
import { PanelWrapper } from './widget-views';
|
||||
|
||||
@observer
|
||||
export default class LeftFloatPane extends Component<{ area: Area<any, Panel> }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
// TODO: add focusingManager
|
||||
// TODO: dragstart close
|
||||
return (
|
||||
<div
|
||||
className={classNames('lc-left-float-pane', {
|
||||
'lc-area-visible': area.visible,
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
className="lc-pane-close"
|
||||
onClick={() => {
|
||||
area.setVisible(false);
|
||||
}}
|
||||
/>
|
||||
<Contents area={area} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area<any, Panel> }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
{area.container.items.map((panel) => (
|
||||
<PanelWrapper key={panel.id} panel={panel} />
|
||||
))}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
28
packages/vision-polyfill/src/skeleton/main-area.tsx
Normal file
28
packages/vision-polyfill/src/skeleton/main-area.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import { Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/recore';
|
||||
import Area from './area';
|
||||
import Panel, { isPanel } from './panel';
|
||||
import { PanelWrapper } from './widget-views';
|
||||
import Widget from './widget';
|
||||
|
||||
@observer
|
||||
export default class MainArea extends Component<{ area: Area<any, Panel | Widget> }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<div className={classNames('lc-main-area')}>
|
||||
{area.container.items.map((item) => {
|
||||
// todo?
|
||||
if (isPanel(item)) {
|
||||
return <PanelWrapper key={item.id} panel={item} />;
|
||||
}
|
||||
return item.content;
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
79
packages/vision-polyfill/src/skeleton/panel-dock.ts
Normal file
79
packages/vision-polyfill/src/skeleton/panel-dock.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { uniqueId, obx, computed } from '@ali/lowcode-globals';
|
||||
import { createElement, ReactNode } from 'react';
|
||||
import { Skeleton } from './skeleton';
|
||||
import { PanelDockConfig } from './types';
|
||||
import Panel from './panel';
|
||||
import { PanelDockView } from './widget-views';
|
||||
import { IWidget } from './widget';
|
||||
|
||||
export default class PanelDock implements IWidget {
|
||||
readonly isWidget = true;
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly align?: string;
|
||||
|
||||
private inited: boolean = false;
|
||||
private _content: ReactNode;
|
||||
get content() {
|
||||
if (this.inited) {
|
||||
return this._content;
|
||||
}
|
||||
this.inited = true;
|
||||
const { props } = this.config;
|
||||
|
||||
this._content = createElement(PanelDockView, {
|
||||
...props,
|
||||
key: this.id,
|
||||
dock: this,
|
||||
});
|
||||
|
||||
return this._content;
|
||||
}
|
||||
|
||||
@computed get actived(): boolean {
|
||||
return this.panel?.visible || false;
|
||||
}
|
||||
|
||||
readonly panelName: string;
|
||||
private _panel?: Panel;
|
||||
@computed get panel() {
|
||||
return this._panel || this.skeleton.getPanel(this.panelName);
|
||||
}
|
||||
|
||||
constructor(readonly skeleton: Skeleton, private config: PanelDockConfig) {
|
||||
const { content, contentProps, panelProps, name } = config;
|
||||
this.name = name;
|
||||
this.id = uniqueId(`dock:${name}$`);
|
||||
this.panelName = config.panelName || name;
|
||||
if (content) {
|
||||
this._panel = this.skeleton.add({
|
||||
type: "Panel",
|
||||
name: this.panelName,
|
||||
props: panelProps || {},
|
||||
contentProps,
|
||||
content,
|
||||
area: panelProps?.area || 'leftFloatArea'
|
||||
}) as Panel;
|
||||
}
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.panel?.toggle();
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.panel?.setActive(false);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.panel?.setActive(true);
|
||||
}
|
||||
}
|
||||
150
packages/vision-polyfill/src/skeleton/panel.ts
Normal file
150
packages/vision-polyfill/src/skeleton/panel.ts
Normal file
@ -0,0 +1,150 @@
|
||||
import {createElement, ReactNode } from 'react';
|
||||
import { obx, uniqueId, createContent, TitleContent } from '@ali/lowcode-globals';
|
||||
import WidgetContainer from './widget-container';
|
||||
import { PanelConfig, HelpTipConfig } from './types';
|
||||
import { PanelView, TabsPanelView } from './widget-views';
|
||||
import { Skeleton } from './skeleton';
|
||||
import { composeTitle } from './utils';
|
||||
import { IWidget } from './widget';
|
||||
|
||||
export default class Panel implements IWidget {
|
||||
readonly isWidget = true;
|
||||
readonly name: string;
|
||||
readonly id: string;
|
||||
@obx.ref inited: boolean = false;
|
||||
@obx.ref private _actived: boolean = false;
|
||||
get actived(): boolean {
|
||||
return this._actived;
|
||||
}
|
||||
get visible(): boolean {
|
||||
if (this.parent?.visible) {
|
||||
return this._actived;
|
||||
}
|
||||
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;
|
||||
|
||||
private _body?: ReactNode;
|
||||
get body() {
|
||||
this.initBody();
|
||||
return this._body;
|
||||
}
|
||||
|
||||
get content() {
|
||||
return this.plain ? this.body : createElement(PanelView, { panel: this });
|
||||
}
|
||||
|
||||
readonly title: TitleContent;
|
||||
readonly help?: HelpTipConfig;
|
||||
private plain: boolean = false;
|
||||
|
||||
private container?: WidgetContainer<Panel, PanelConfig>;
|
||||
private parent?: WidgetContainer;
|
||||
|
||||
constructor(readonly skeleton: Skeleton, private config: PanelConfig) {
|
||||
const { name, content, props = {} } = config;
|
||||
const { hideTitleBar, title, icon, description, help, shortcut } = props;
|
||||
this.name = name;
|
||||
this.id = uniqueId(`pane:${name}$`);
|
||||
this.title = composeTitle(title || name, icon, description);
|
||||
this.plain = hideTitleBar || !title;
|
||||
this.help = help;
|
||||
if (Array.isArray(content)) {
|
||||
this.container = this.skeleton.createContainer(name, (item) => {
|
||||
if (isPanel(item)) {
|
||||
return item;
|
||||
}
|
||||
return this.skeleton.createPanel(item);
|
||||
}, true, () => this.visible, true);
|
||||
content.forEach(item => this.add(item));
|
||||
}
|
||||
// todo: process shortcut
|
||||
}
|
||||
|
||||
private initBody() {
|
||||
if (this.inited) {
|
||||
return;
|
||||
}
|
||||
this.inited = true;
|
||||
if (this.container) {
|
||||
this._body = createElement(TabsPanelView, {
|
||||
container: this.container,
|
||||
key: this.id,
|
||||
});
|
||||
} else {
|
||||
const { content, contentProps } = this.config;
|
||||
this._body = createContent(content, {
|
||||
...contentProps,
|
||||
editor: this.skeleton.editor,
|
||||
panel: this,
|
||||
key: this.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
setParent(parent: WidgetContainer) {
|
||||
if (parent === this.parent) {
|
||||
return;
|
||||
}
|
||||
if (this.parent) {
|
||||
this.parent.remove(this);
|
||||
}
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
add(item: Panel | PanelConfig) {
|
||||
return this.container?.add(item);
|
||||
}
|
||||
|
||||
getPane(name: string): Panel | null {
|
||||
return this.container?.get(name) || null;
|
||||
}
|
||||
|
||||
remove(item: Panel | string) {
|
||||
return this.container?.remove(item);
|
||||
}
|
||||
|
||||
active(item?: Panel | string | null) {
|
||||
this.container?.active(item);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setActive(false);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
export function isPanel(obj: any): obj is Panel {
|
||||
return obj && obj.isPanel;
|
||||
}
|
||||
36
packages/vision-polyfill/src/skeleton/right-area.tsx
Normal file
36
packages/vision-polyfill/src/skeleton/right-area.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/recore';
|
||||
import Area from './area';
|
||||
import Panel from './panel';
|
||||
import { PanelWrapper } from './widget-views';
|
||||
|
||||
@observer
|
||||
export default class RightArea extends Component<{ area: Area<any, Panel> }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<div className={classNames('lc-right-area', {
|
||||
'lc-area-visible': area.visible,
|
||||
})}>
|
||||
<Contents area={area} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area<any, Panel> }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<Fragment>
|
||||
{area.container.items.map((item) => <PanelWrapper key={item.id} panel={item} />)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
194
packages/vision-polyfill/src/skeleton/skeleton.ts
Normal file
194
packages/vision-polyfill/src/skeleton/skeleton.ts
Normal file
@ -0,0 +1,194 @@
|
||||
import {
|
||||
DockConfig,
|
||||
PanelConfig,
|
||||
WidgetConfig,
|
||||
IWidgetBaseConfig,
|
||||
PanelDockConfig,
|
||||
DialogDockConfig,
|
||||
isDockConfig,
|
||||
isPanelDockConfig,
|
||||
isPanelConfig,
|
||||
} from './types';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import Panel, { isPanel } from './panel';
|
||||
import WidgetContainer from './widget-container';
|
||||
import Area from './area';
|
||||
import Widget, { isWidget, IWidget } from './widget';
|
||||
import PanelDock from './panel-dock';
|
||||
import Dock from './dock';
|
||||
import { Stage, StageConfig } from './stage';
|
||||
|
||||
export class Skeleton {
|
||||
private panels = new Map<string, Panel>();
|
||||
private containers = new Map<string, WidgetContainer<any>>();
|
||||
readonly leftArea: Area<DockConfig | PanelDockConfig | DialogDockConfig>;
|
||||
readonly topArea: Area<DockConfig | PanelDockConfig | DialogDockConfig>;
|
||||
readonly toolbar: Area<DockConfig | PanelDockConfig | DialogDockConfig>;
|
||||
readonly leftFixedArea: Area<PanelConfig, Panel>;
|
||||
readonly leftFloatArea: Area<PanelConfig, Panel>;
|
||||
readonly rightArea: Area<PanelConfig, Panel>;
|
||||
readonly mainArea: Area<WidgetConfig | PanelConfig, Widget | Panel>;
|
||||
readonly bottomArea: Area<PanelConfig, Panel>;
|
||||
readonly stages: Area<StageConfig, Stage>;
|
||||
constructor(readonly editor: Editor) {
|
||||
this.leftArea = new Area(
|
||||
this,
|
||||
'leftArea',
|
||||
(config) => {
|
||||
if (isWidget(config)) {
|
||||
return config;
|
||||
}
|
||||
return this.createWidget(config);
|
||||
},
|
||||
false,
|
||||
);
|
||||
this.topArea = new Area(
|
||||
this,
|
||||
'topArea',
|
||||
(config) => {
|
||||
if (isWidget(config)) {
|
||||
return config;
|
||||
}
|
||||
return this.createWidget(config);
|
||||
},
|
||||
false,
|
||||
);
|
||||
this.toolbar = new Area(
|
||||
this,
|
||||
'toolbar',
|
||||
(config) => {
|
||||
if (isWidget(config)) {
|
||||
return config;
|
||||
}
|
||||
return this.createWidget(config);
|
||||
},
|
||||
false,
|
||||
);
|
||||
this.leftFixedArea = new Area(
|
||||
this,
|
||||
'leftFixedArea',
|
||||
(config) => {
|
||||
if (isPanel(config)) {
|
||||
return config;
|
||||
}
|
||||
return this.createPanel(config);
|
||||
},
|
||||
true,
|
||||
);
|
||||
this.leftFloatArea = new Area(
|
||||
this,
|
||||
'leftFloatArea',
|
||||
(config) => {
|
||||
if (isPanel(config)) {
|
||||
return config;
|
||||
}
|
||||
return this.createPanel(config);
|
||||
},
|
||||
true,
|
||||
);
|
||||
this.rightArea = new Area(
|
||||
this,
|
||||
'rightArea',
|
||||
(config) => {
|
||||
if (isPanel(config)) {
|
||||
return config;
|
||||
}
|
||||
return this.createPanel(config);
|
||||
},
|
||||
true,
|
||||
true,
|
||||
);
|
||||
this.mainArea = new Area(
|
||||
this,
|
||||
'mainArea',
|
||||
(config) => {
|
||||
if (isWidget(config)) {
|
||||
return config as Widget;
|
||||
}
|
||||
return this.createWidget(config) as Widget;
|
||||
},
|
||||
true,
|
||||
true,
|
||||
);
|
||||
this.bottomArea = new Area(
|
||||
this,
|
||||
'bottomArea',
|
||||
(config) => {
|
||||
if (isPanel(config)) {
|
||||
return config;
|
||||
}
|
||||
return this.createPanel(config);
|
||||
},
|
||||
true,
|
||||
);
|
||||
this.stages = new Area(this, 'stages', (config) => {
|
||||
if (isWidget(config)) {
|
||||
return config;
|
||||
}
|
||||
return new Stage(this, config);
|
||||
});
|
||||
}
|
||||
|
||||
createWidget(config: IWidgetBaseConfig | IWidget) {
|
||||
if (isWidget(config)) {
|
||||
return config;
|
||||
}
|
||||
if (isDockConfig(config)) {
|
||||
if (isPanelDockConfig(config)) {
|
||||
return new PanelDock(this, config);
|
||||
}
|
||||
|
||||
return new Dock(this, config);
|
||||
}
|
||||
if (isPanelConfig(config)) {
|
||||
return this.createPanel(config);
|
||||
}
|
||||
return new Widget(this, config as WidgetConfig);
|
||||
}
|
||||
|
||||
createPanel(config: PanelConfig) {
|
||||
const panel = new Panel(this, config);
|
||||
this.panels.set(panel.name, panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
getPanel(name: string): Panel | undefined {
|
||||
return this.panels.get(name);
|
||||
}
|
||||
|
||||
createContainer(
|
||||
name: string,
|
||||
handle: (item: any) => any,
|
||||
exclusive: boolean = false,
|
||||
checkVisible: () => boolean = () => true,
|
||||
defaultSetCurrent: boolean = false,
|
||||
) {
|
||||
const container = new WidgetContainer(name, handle, exclusive, checkVisible, defaultSetCurrent);
|
||||
this.containers.set(name, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
add(config: IWidgetBaseConfig & { area: string }) {
|
||||
const { area } = config;
|
||||
switch (area) {
|
||||
case 'leftArea': case 'left':
|
||||
return this.leftArea.add(config as any);
|
||||
case 'rightArea': case 'right':
|
||||
return this.rightArea.add(config as any);
|
||||
case 'topArea': case 'top':
|
||||
return this.topArea.add(config as any);
|
||||
case 'toolbar':
|
||||
return this.toolbar.add(config as any);
|
||||
case 'mainArea': case 'main': case 'center': case 'centerArea':
|
||||
return this.mainArea.add(config as any);
|
||||
case 'bottomArea': case 'bottom':
|
||||
return this.bottomArea.add(config as any);
|
||||
case 'leftFixedArea':
|
||||
return this.leftFixedArea.add(config as any);
|
||||
case 'leftFloatArea':
|
||||
return this.leftFloatArea.add(config as any);
|
||||
case 'stages':
|
||||
return this.stages.add(config as any);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
packages/vision-polyfill/src/skeleton/stage.ts
Normal file
51
packages/vision-polyfill/src/skeleton/stage.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import Widget from './widget';
|
||||
import { Skeleton } from './skeleton';
|
||||
import { WidgetConfig } from './types';
|
||||
|
||||
export interface StageConfig extends WidgetConfig {
|
||||
isRoot?: boolean;
|
||||
}
|
||||
|
||||
export class Stage extends Widget {
|
||||
readonly isRoot: boolean;
|
||||
private previous?: Stage;
|
||||
private refer?: {
|
||||
stage?: Stage;
|
||||
direction?: 'right' | 'left';
|
||||
};
|
||||
|
||||
constructor(skeleton: Skeleton, config: StageConfig) {
|
||||
super(skeleton, config);
|
||||
this.isRoot = config.isRoot || false;
|
||||
}
|
||||
|
||||
setPrevious(stage: Stage) {
|
||||
this.previous = stage;
|
||||
}
|
||||
|
||||
getPrevious() {
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
hasBack(): boolean {
|
||||
return this.previous && !this.isRoot ? true : false;
|
||||
}
|
||||
|
||||
setRefer(stage: Stage, direction: 'right' | 'left') {
|
||||
this.refer = { stage, direction };
|
||||
}
|
||||
|
||||
setReferRight(stage: Stage) {
|
||||
this.setRefer(stage, 'right');
|
||||
}
|
||||
|
||||
setReferLeft(stage: Stage) {
|
||||
this.setRefer(stage, 'left');
|
||||
}
|
||||
|
||||
getRefer() {
|
||||
const refer = this.refer;
|
||||
this.refer = undefined;
|
||||
return refer;
|
||||
}
|
||||
}
|
||||
60
packages/vision-polyfill/src/skeleton/theme.less
Normal file
60
packages/vision-polyfill/src/skeleton/theme.less
Normal file
@ -0,0 +1,60 @@
|
||||
@import '~@ali/ve-less-variables/index.less';
|
||||
|
||||
/*
|
||||
* Theme Colors
|
||||
*
|
||||
* 乐高设计器的主要主题色变量
|
||||
*/
|
||||
:root {
|
||||
--color-brand: @brand-color-1;
|
||||
--color-brand-light: @brand-color-2;
|
||||
--color-brand-dark: @brand-color-3;
|
||||
|
||||
--color-canvas-background: @normal-alpha-8;
|
||||
|
||||
--color-icon-normal: @normal-alpha-4;
|
||||
--color-icon-hover: @normal-alpha-3;
|
||||
--color-icon-active: @brand-color-1;
|
||||
--color-icon-reverse: @white-alpha-1;
|
||||
|
||||
--color-line-normal: @normal-alpha-7;
|
||||
--color-line-darken: darken(@normal-alpha-7, 10%);
|
||||
|
||||
--color-title: @dark-alpha-2;
|
||||
--color-text: @dark-alpha-3;
|
||||
--color-text-dark: darken(@dark-alpha-3, 10%);
|
||||
--color-text-light: lighten(@dark-alpha-3, 10%);
|
||||
--color-text-reverse: @white-alpha-2;
|
||||
--color-text-regular: @normal-alpha-2;
|
||||
|
||||
--color-field-label: @dark-alpha-4;
|
||||
--color-field-text: @dark-alpha-3;
|
||||
--color-field-placeholder: @normal-alpha-5;
|
||||
--color-field-border: @normal-alpha-5;
|
||||
--color-field-border-hover: @normal-alpha-4;
|
||||
--color-field-border-active: @normal-alpha-3;
|
||||
--color-field-background: @white-alpha-1;
|
||||
|
||||
--color-function-success: @brand-success;
|
||||
--color-function-success-dark: darken(@brand-success, 10%);
|
||||
--color-function-success-light: lighten(@brand-success, 10%);
|
||||
--color-function-warning: @brand-warning;
|
||||
--color-function-warning-dark: darken(@brand-warning, 10%);
|
||||
--color-function-warning-light: lighten(@brand-warning, 10%);
|
||||
--color-function-information: @brand-link-hover;
|
||||
--color-function-information-dark: darken(@brand-link-hover, 10%);
|
||||
--color-function-information-light: lighten(@brand-link-hover, 10%);
|
||||
--color-function-error: @brand-danger;
|
||||
--color-function-error-dark: darken(@brand-danger, 10%);
|
||||
--color-function-error-light: lighten(@brand-danger, 10%);
|
||||
|
||||
--color-pane-background: @white-alpha-1;
|
||||
--color-block-background-normal: @white-alpha-1;
|
||||
--color-block-background-light: @normal-alpha-9;
|
||||
--color-block-background-shallow: @normal-alpha-8;
|
||||
--color-block-background-dark: @normal-alpha-7;
|
||||
--color-block-background-disabled: @normal-alpha-6;
|
||||
--color-block-background-deep-dark: @normal-5;
|
||||
--color-layer-mask-background: @dark-alpha-7;
|
||||
--color-layer-tooltip-background: rgba(44,47,51,0.8);
|
||||
}
|
||||
49
packages/vision-polyfill/src/skeleton/toolbar.tsx
Normal file
49
packages/vision-polyfill/src/skeleton/toolbar.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-globals';
|
||||
import Area from './area';
|
||||
|
||||
@observer
|
||||
export default class Toolbar extends Component<{ area: Area }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
if (area.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={classNames('lc-toolbar', {
|
||||
'lc-area-visible': area.visible,
|
||||
})}
|
||||
>
|
||||
<Contents area={area} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
const left: any[] = [];
|
||||
const center: any[] = [];
|
||||
const right: any[] = [];
|
||||
area.container.items.forEach((item) => {
|
||||
if (item.align === 'center') {
|
||||
center.push(item.content);
|
||||
} else if (item.align === 'right') {
|
||||
right.push(item.content);
|
||||
} else {
|
||||
left.push(item.content);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="lc-toolbar-left">{left}</div>
|
||||
<div className="lc-toolbar-center">{center}</div>
|
||||
<div className="lc-toolbar-right">{right}</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
44
packages/vision-polyfill/src/skeleton/top-area.tsx
Normal file
44
packages/vision-polyfill/src/skeleton/top-area.tsx
Normal file
@ -0,0 +1,44 @@
|
||||
import { Component, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { observer } from '@ali/lowcode-globals';
|
||||
import Area from './area';
|
||||
|
||||
@observer
|
||||
export default class TopArea extends Component<{ area: Area }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
return (
|
||||
<div className={classNames("lc-top-area", {
|
||||
'lc-area-visible': area.visible
|
||||
})}>
|
||||
<Contents area={area} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class Contents extends Component<{ area: Area }> {
|
||||
render() {
|
||||
const { area } = this.props;
|
||||
const left: any[] = [];
|
||||
const center: any[] = [];
|
||||
const right: any[] = [];
|
||||
area.container.items.forEach(item => {
|
||||
if (item.align === 'center') {
|
||||
center.push(item.content);
|
||||
} else if (item.align === 'left') {
|
||||
right.push(item.content);
|
||||
} else {
|
||||
left.push(item.content);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="lc-top-area-left">{left}</div>
|
||||
<div className="lc-top-area-center">{center}</div>
|
||||
<div className="lc-top-area-right">{right}</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
104
packages/vision-polyfill/src/skeleton/types.ts
Normal file
104
packages/vision-polyfill/src/skeleton/types.ts
Normal file
@ -0,0 +1,104 @@
|
||||
import { ReactElement, ComponentType } from 'react';
|
||||
import { TitleContent, IconType, I18nData } from '@ali/lowcode-globals';
|
||||
|
||||
export interface IWidgetBaseConfig {
|
||||
type: string;
|
||||
name: string;
|
||||
area?: string; // 停靠位置, 默认 float, 如果添加非固定区,
|
||||
props?: object;
|
||||
content?: any;
|
||||
contentProps?: object;
|
||||
// index?: number;
|
||||
[extra: string]: any;
|
||||
}
|
||||
|
||||
export interface WidgetConfig extends IWidgetBaseConfig {
|
||||
type: "Widget";
|
||||
name: string; // as pluginKey
|
||||
props?: {
|
||||
align?: "left" | "right" | "bottom" | "center" | "top";
|
||||
};
|
||||
content?: string | ReactElement | ComponentType<any>; // children
|
||||
}
|
||||
|
||||
export function isWidgetConfig(obj: any): obj is WidgetConfig {
|
||||
return obj && obj.type === "Custom";
|
||||
}
|
||||
|
||||
export interface DockProps {
|
||||
title?: TitleContent;
|
||||
icon?: IconType;
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
className?: string;
|
||||
description?: string | I18nData;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export interface IDockBaseConfig extends IWidgetBaseConfig {
|
||||
props?: DockProps & {
|
||||
align?: "left" | "right" | "bottom" | "center" | "top";
|
||||
};
|
||||
}
|
||||
|
||||
export interface DockConfig extends IDockBaseConfig {
|
||||
type: "Dock";
|
||||
content?: string | ReactElement | ComponentType<any>;
|
||||
}
|
||||
|
||||
export function isDockConfig(obj: any): obj is DockConfig {
|
||||
return obj && /Dock$/.test(obj.type);
|
||||
}
|
||||
|
||||
// 按钮弹窗扩展
|
||||
export interface DialogDockConfig extends IDockBaseConfig {
|
||||
type: "DialogDock";
|
||||
dialogProps?: {
|
||||
title?: TitleContent;
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
export function isDialogDockConfig(obj: any): obj is DialogDockConfig {
|
||||
return obj && obj.type === 'DialogDock';
|
||||
}
|
||||
|
||||
// 窗格扩展
|
||||
export interface PanelConfig extends IWidgetBaseConfig {
|
||||
type: "Panel";
|
||||
content?: string | ReactElement | ComponentType<any> | PanelConfig[]; // as children
|
||||
props?: PanelProps;
|
||||
}
|
||||
|
||||
export function isPanelConfig(obj: any): obj is PanelConfig {
|
||||
return obj && obj.type === 'Panel';
|
||||
}
|
||||
|
||||
export type HelpTipConfig = string | { url?: string; content?: string | ReactElement };
|
||||
|
||||
export interface PanelProps {
|
||||
title?: TitleContent;
|
||||
icon?: any; // 冗余字段
|
||||
description?: string | I18nData;
|
||||
hideTitleBar?: boolean; // panel.props 兼容,不暴露
|
||||
help?: HelpTipConfig; // 显示问号帮助
|
||||
width?: number; // panel.props
|
||||
height?: number; // panel.props
|
||||
maxWidth?: number; // panel.props
|
||||
maxHeight?: number; // panel.props
|
||||
onInit?: () => any;
|
||||
onDestroy?: () => any;
|
||||
shortcut?: string; // 只有在特定位置,可触发 toggle show
|
||||
}
|
||||
|
||||
export interface PanelDockConfig extends IDockBaseConfig {
|
||||
type: "PanelDock";
|
||||
panelName?: string;
|
||||
panelProps?: PanelProps & {
|
||||
area?: string;
|
||||
};
|
||||
content?: string | ReactElement | ComponentType<any> | PanelConfig[]; // content for pane
|
||||
}
|
||||
|
||||
export function isPanelDockConfig(obj: any): obj is PanelDockConfig {
|
||||
return obj && obj.type === 'PanelDock';
|
||||
}
|
||||
28
packages/vision-polyfill/src/skeleton/utils.ts
Normal file
28
packages/vision-polyfill/src/skeleton/utils.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { IconType, TitleContent, I18nData, isI18nData } from '@ali/lowcode-globals';
|
||||
import { isValidElement } from 'react';
|
||||
|
||||
export function composeTitle(title?: TitleContent, icon?: IconType, tip?: string | I18nData) {
|
||||
if (!title) {
|
||||
title = {};
|
||||
if (!icon) {
|
||||
title.label = tip;
|
||||
tip = undefined;
|
||||
}
|
||||
}
|
||||
if (icon || tip) {
|
||||
if (typeof title !== 'object' || isValidElement(title) || isI18nData(title)) {
|
||||
title = {
|
||||
label: title,
|
||||
icon,
|
||||
tip,
|
||||
};
|
||||
} else {
|
||||
title = {
|
||||
...title,
|
||||
icon,
|
||||
tip
|
||||
};
|
||||
}
|
||||
}
|
||||
return title;
|
||||
}
|
||||
134
packages/vision-polyfill/src/skeleton/widget-container.ts
Normal file
134
packages/vision-polyfill/src/skeleton/widget-container.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import { obx, hasOwnProperty, computed } from '@ali/lowcode-globals';
|
||||
import { isPanel } from './panel';
|
||||
export interface WidgetItem {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface Activeable {
|
||||
setActive(flag: boolean): void;
|
||||
}
|
||||
|
||||
function isActiveable(obj: any): obj is Activeable {
|
||||
return obj && obj.setActive;
|
||||
}
|
||||
|
||||
export default class WidgetContainer<T extends WidgetItem = any, G extends WidgetItem = any> {
|
||||
@obx.val items: T[] = [];
|
||||
private maps: { [name: string]: T } = {};
|
||||
@obx.ref private _current: T & Activeable | null = null;
|
||||
|
||||
get current() {
|
||||
return this._current;
|
||||
}
|
||||
|
||||
constructor(
|
||||
readonly name: string,
|
||||
private handle: (item: T | G) => T,
|
||||
private exclusive: boolean = false,
|
||||
private checkVisible: () => boolean = () => true,
|
||||
private defaultSetCurrent: boolean = false,
|
||||
) {}
|
||||
|
||||
@computed get visible() {
|
||||
return this.checkVisible();
|
||||
}
|
||||
|
||||
active(nameOrItem?: T | string | null) {
|
||||
let item: any = nameOrItem;
|
||||
if (nameOrItem && typeof nameOrItem === 'string') {
|
||||
item = this.get(nameOrItem);
|
||||
}
|
||||
if (!isActiveable(nameOrItem)) {
|
||||
item = null;
|
||||
}
|
||||
|
||||
if (this.exclusive) {
|
||||
if (this._current === item) {
|
||||
return;
|
||||
}
|
||||
if (this._current) {
|
||||
this._current.setActive(false);
|
||||
}
|
||||
this._current = item;
|
||||
}
|
||||
|
||||
if (item) {
|
||||
item.setActive(true);
|
||||
}
|
||||
}
|
||||
|
||||
unactive(nameOrItem?: T | string | null) {
|
||||
let item: any = nameOrItem;
|
||||
if (nameOrItem && typeof nameOrItem === 'string') {
|
||||
item = this.get(nameOrItem);
|
||||
}
|
||||
if (!isActiveable(nameOrItem)) {
|
||||
item = null;
|
||||
}
|
||||
if (this._current === item) {
|
||||
this._current = null;
|
||||
}
|
||||
if (item) {
|
||||
item.setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
add(item: T | G): T {
|
||||
item = this.handle(item);
|
||||
const origin = this.get(item.name);
|
||||
if (origin === item) {
|
||||
return origin;
|
||||
}
|
||||
const i = origin ? this.items.indexOf(origin) : -1;
|
||||
if (i > -1) {
|
||||
this.items[i] = item;
|
||||
} else {
|
||||
this.items.push(item);
|
||||
}
|
||||
this.maps[item.name] = item;
|
||||
if (isPanel(item)) {
|
||||
item.setParent(this);
|
||||
}
|
||||
if (this.defaultSetCurrent) {
|
||||
if (!this._current) {
|
||||
this.active(item);
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
get(name: string): T | null {
|
||||
return this.maps[name] || null;
|
||||
}
|
||||
|
||||
getAt(index: number): T | null {
|
||||
return this.items[index] || null;
|
||||
}
|
||||
|
||||
has(name: string): boolean {
|
||||
return hasOwnProperty(this.maps, name);
|
||||
}
|
||||
|
||||
indexOf(item: T): number {
|
||||
return this.items.indexOf(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* return indexOf the deletion
|
||||
*/
|
||||
remove(item: string | T): number {
|
||||
const thing = typeof item === 'string' ? this.get(item) : item;
|
||||
if (!thing) {
|
||||
return -1;
|
||||
}
|
||||
const i = this.items.indexOf(thing);
|
||||
if (i > -1) {
|
||||
this.items.splice(i, 1);
|
||||
}
|
||||
delete this.maps[thing.name];
|
||||
if (thing === this.current) {
|
||||
this._current = null;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
143
packages/vision-polyfill/src/skeleton/widget-views.tsx
Normal file
143
packages/vision-polyfill/src/skeleton/widget-views.tsx
Normal file
@ -0,0 +1,143 @@
|
||||
import { Component, ReactElement } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Title, observer } from '@ali/lowcode-globals';
|
||||
import { DockProps } from './types';
|
||||
import PanelDock from './panel-dock';
|
||||
import { composeTitle } from './utils';
|
||||
import WidgetContainer from './widget-container';
|
||||
import Panel from './panel';
|
||||
import Widget from './widget';
|
||||
|
||||
export function DockView({ title, icon, description, size, className, onClick }: DockProps) {
|
||||
return (
|
||||
<Title
|
||||
title={composeTitle(title, icon, description)}
|
||||
className={classNames('lc-dock', className, {
|
||||
[`lc-dock-${size}`]: size,
|
||||
})}
|
||||
onClick={onClick}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@observer
|
||||
export class PanelDockView extends Component<DockProps & { dock: PanelDock }> {
|
||||
render() {
|
||||
const { dock, className, onClick, ...props } = this.props;
|
||||
return DockView({
|
||||
...props,
|
||||
className: classNames(className, {
|
||||
actived: dock.actived,
|
||||
}),
|
||||
onClick: () => {
|
||||
onClick && onClick();
|
||||
dock.toggle();
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class DialogDockView extends Component {
|
||||
|
||||
}
|
||||
|
||||
export class PanelView extends Component<{ panel: Panel }> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
const { panel } = this.props;
|
||||
return (
|
||||
<div className="lc-panel">
|
||||
<PanelTitle panel={panel} />
|
||||
<div className="lc-pane-body">{panel.body}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class TabsPanelView extends Component<{ container: WidgetContainer<Panel> }> {
|
||||
render() {
|
||||
const { container } = this.props;
|
||||
const titles: ReactElement[] = [];
|
||||
const contents: ReactElement[] = [];
|
||||
container.items.forEach((item: any) => {
|
||||
titles.push(<PanelTitle key={item.id} panel={item} className="lc-tab-title" />);
|
||||
contents.push(<PanelWrapper key={item.id} panel={item} />);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="lc-tabs">
|
||||
<div
|
||||
className="lc-tabs-title"
|
||||
onClick={(e) => {
|
||||
const shell = e.currentTarget;
|
||||
const t = e.target as Element;
|
||||
let elt = shell.firstElementChild;
|
||||
while (elt) {
|
||||
if (elt.contains(t)) {
|
||||
break;
|
||||
}
|
||||
elt = elt.nextElementSibling;
|
||||
}
|
||||
if (elt) {
|
||||
container.active((elt as any).dataset.name);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{titles}
|
||||
</div>
|
||||
<div className="lc-tabs-content">{contents}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
class PanelTitle extends Component<{ panel: Panel; className?: string }> {
|
||||
render() {
|
||||
const { panel, className } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={classNames('lc-panel-title', className, {
|
||||
actived: panel.actived,
|
||||
})}
|
||||
data-name={panel.name}
|
||||
>
|
||||
<Title title={panel.title || panel.name} />
|
||||
{/*pane.help ? <HelpTip tip={panel.help} /> : null*/}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class PanelWrapper extends Component<{ panel: Panel }> {
|
||||
render() {
|
||||
const { panel } = this.props;
|
||||
if (!panel.inited) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={classNames('lc-panel-wrapper', {
|
||||
hidden: !panel.actived,
|
||||
})}
|
||||
>
|
||||
{panel.body}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@observer
|
||||
export class WidgetWrapper extends Component<{ widget: Widget }> {
|
||||
render() {
|
||||
const { widget } = this.props;
|
||||
if (!widget.visible) {
|
||||
return null;
|
||||
}
|
||||
return widget.body;
|
||||
}
|
||||
}
|
||||
94
packages/vision-polyfill/src/skeleton/widget.ts
Normal file
94
packages/vision-polyfill/src/skeleton/widget.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import { ReactNode, createElement } from 'react';
|
||||
import { createContent, uniqueId, obx } from '@ali/lowcode-globals';
|
||||
import { WidgetConfig } from './types';
|
||||
import { Skeleton } from './skeleton';
|
||||
import { WidgetWrapper } from './widget-views';
|
||||
|
||||
export interface IWidget {
|
||||
readonly name: string;
|
||||
readonly content: any;
|
||||
readonly align?: string;
|
||||
readonly isWidget: true;
|
||||
|
||||
getName(): string;
|
||||
getContent(): any;
|
||||
show(): void;
|
||||
hide(): void;
|
||||
}
|
||||
|
||||
export default class Widget implements IWidget {
|
||||
readonly isWidget = true;
|
||||
readonly id = uniqueId('widget');
|
||||
readonly name: string;
|
||||
readonly align?: string;
|
||||
|
||||
@obx.ref private _visible: boolean = true;
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
@obx.ref inited: boolean = false;
|
||||
private _body: ReactNode;
|
||||
get body() {
|
||||
if (this.inited) {
|
||||
return this._body;
|
||||
}
|
||||
this.inited = true;
|
||||
const { content, contentProps } = this.config;
|
||||
this._body = createContent(content, {
|
||||
...contentProps,
|
||||
editor: this.skeleton.editor,
|
||||
});
|
||||
return this._body;
|
||||
}
|
||||
|
||||
get content() {
|
||||
return createElement(WidgetWrapper, {
|
||||
widget: this,
|
||||
key: this.id,
|
||||
});
|
||||
}
|
||||
|
||||
constructor(readonly skeleton: Skeleton, private config: WidgetConfig) {
|
||||
const { props = {}, name } = config;
|
||||
this.name = name;
|
||||
this.align = props.align;
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
getContent() {
|
||||
return this.content;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setVisible(false);
|
||||
}
|
||||
|
||||
show() {
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
setVisible(flag: boolean) {
|
||||
if (flag === this._visible) {
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
this._visible = true;
|
||||
} else if (this.inited) {
|
||||
this._visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.setVisible(!this._visible);
|
||||
}
|
||||
}
|
||||
|
||||
export function isWidget(obj: any): obj is IWidget {
|
||||
return obj && obj.isWidget;
|
||||
}
|
||||
|
||||
|
||||
261
packages/vision-polyfill/src/skeleton/workbench.less
Normal file
261
packages/vision-polyfill/src/skeleton/workbench.less
Normal file
@ -0,0 +1,261 @@
|
||||
@import "./theme.less";
|
||||
|
||||
:root {
|
||||
--font-family: @font-family;
|
||||
--font-size-label: @fontSize-4;
|
||||
--font-size-text: @fontSize-5;
|
||||
--font-size-btn-large: @fontSize-3;
|
||||
--font-size-btn-medium: @fontSize-4;
|
||||
--font-size-btn-small: @fontSize-5;
|
||||
|
||||
--global-border-radius: @global-border-radius;
|
||||
--input-border-radius: @input-border-radius;
|
||||
--popup-border-radius: @popup-border-radius;
|
||||
|
||||
--left-area-width: 46px;
|
||||
--right-area-width: 300px;
|
||||
--top-area-height: 48px;
|
||||
--toolbar-height: 32px;
|
||||
--dock-pane-width: 372px;
|
||||
}
|
||||
|
||||
|
||||
@media (min-width: 1860px) {
|
||||
:root {
|
||||
--right-area-width: 400px;
|
||||
--dock-pane-width: 452px;
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
font-family: var(--font-family);
|
||||
font-size: var(--font-size-text);
|
||||
color: var(--color-text);
|
||||
background-color:#EDEFF3;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
.my-pane {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.pane-title {
|
||||
// height: var(--pane-title-height);
|
||||
background-color: var(--pane-title-bg-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
.my-help-tip {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.pane-body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: auto;
|
||||
.my-tabs {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.tabs-title {
|
||||
display: flex;
|
||||
height: var(--pane-title-height);
|
||||
> .tab-title {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
border-bottom: 2px solid transparent;
|
||||
&.actived {
|
||||
cursor: default;
|
||||
color: var(--color-text-avtived);
|
||||
border-bottom-color: #3896ee;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabs-content {
|
||||
position: absolute;
|
||||
top: var(--pane-title-height);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: calc(100% - var(--pane-title-height));
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.titled > .pane-body {
|
||||
top: var(--pane-title-height);
|
||||
}
|
||||
}
|
||||
.lc-panel-wrapper {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
.pane-title {
|
||||
height: var(--pane-title-height);
|
||||
background-color: var(--pane-title-bg-color);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
.my-help-tip {
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.my-tabs {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.tabs-title {
|
||||
display: flex;
|
||||
height: var(--pane-title-height);
|
||||
margin-right: 30px;
|
||||
> .tab-title {
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
justify-content: center;
|
||||
border-bottom: 2px solid transparent;
|
||||
&.actived {
|
||||
cursor: default;
|
||||
color: var(--color-text-avtived);
|
||||
border-bottom-color: #3896ee;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tabs-content {
|
||||
position: absolute;
|
||||
top: var(--pane-title-height);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: calc(100% - var(--pane-title-height));
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.my-dock {
|
||||
padding: 0px 10px;
|
||||
cursor: pointer;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.my-title-label {
|
||||
user-select: none;
|
||||
}
|
||||
&.actived, &:hover {
|
||||
background-color: var(--pane-title-bg-color);
|
||||
.my-title {
|
||||
color: var(--color-actived);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.lc-workbench {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #EDEFF3;
|
||||
.lc-top-area {
|
||||
height: var(--top-area-height);
|
||||
background-color: var(--color-pane-background);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
.lc-workbench-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
.lc-left-float-pane {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: var(--dock-pane-width);
|
||||
left: calc(var(--left-area-width) + 1px);
|
||||
z-index: 20;
|
||||
display: none;
|
||||
&.lc-area-visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.lc-left-area {
|
||||
height: 100%;
|
||||
width: var(--left-area-width);
|
||||
background-color: var(--color-pane-background);
|
||||
display: none;
|
||||
&.lc-area-visible {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.lc-left-fixed-pane {
|
||||
width: var(--dock-pane-width);
|
||||
background-color: var(--color-pane-background);
|
||||
height: 100%;
|
||||
display: none;
|
||||
&.lc-area-visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.lc-left-area.lc-area-visible ~ .lc-left-fixed-pane {
|
||||
margin-left: 1px;
|
||||
}
|
||||
.lc-left-area.lc-area-visible ~ .lc-workbench-center {
|
||||
margin-left: 2px;
|
||||
}
|
||||
.lc-workbench-center {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.lc-toolbar {
|
||||
height: var(--toolbar-height);
|
||||
background-color: var(--color-pane-background);
|
||||
}
|
||||
.lc-main-area {
|
||||
flex: 1;
|
||||
}
|
||||
.lc-bottom-area {
|
||||
height: var(--bottom-area-height);
|
||||
background-color: var(--color-pane-background);
|
||||
display: none;
|
||||
&.lc-area-visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
.lc-right-area {
|
||||
height: 100%;
|
||||
width: var(--right-area-width);
|
||||
background-color: var(--color-pane-background);
|
||||
display: none;
|
||||
margin-left: 2px;
|
||||
&.lc-area-visible {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
40
packages/vision-polyfill/src/skeleton/workbench.tsx
Normal file
40
packages/vision-polyfill/src/skeleton/workbench.tsx
Normal file
@ -0,0 +1,40 @@
|
||||
import { Component } from 'react';
|
||||
import { TipContainer, observer } from '@ali/lowcode-globals';
|
||||
import { Skeleton } from './skeleton';
|
||||
import TopArea from './top-area';
|
||||
import LeftArea from './left-area';
|
||||
import LeftFixedPane from './left-fixed-pane';
|
||||
import LeftFloatPane from './left-float-pane';
|
||||
import Toolbar from './toolbar';
|
||||
import MainArea from './main-area';
|
||||
import BottomArea from './bottom-area';
|
||||
import RightArea from './right-area';
|
||||
import './workbench.less';
|
||||
|
||||
@observer
|
||||
export class VisionWorkbench extends Component<{ skeleton: Skeleton}> {
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { skeleton } = this.props;
|
||||
return (
|
||||
<div className="lc-workbench">
|
||||
<TopArea area={skeleton.topArea} />
|
||||
<div className="lc-workbench-body">
|
||||
<LeftArea area={skeleton.leftArea} />
|
||||
<LeftFloatPane area={skeleton.leftFloatArea} />
|
||||
<LeftFixedPane area={skeleton.leftFixedArea} />
|
||||
<div className="lc-workbench-center">
|
||||
<Toolbar area={skeleton.toolbar} />
|
||||
<MainArea area={skeleton.mainArea} />
|
||||
<BottomArea area={skeleton.bottomArea} />
|
||||
</div>
|
||||
<RightArea area={skeleton.rightArea} />
|
||||
</div>
|
||||
<TipContainer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -6,8 +6,9 @@ import { createElement } from 'react';
|
||||
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './const';
|
||||
import Bus from './bus';
|
||||
import Symbols from './symbols';
|
||||
import Skeleton from '@ali/lowcode-editor-skeleton';
|
||||
import editor from './editor';
|
||||
import { editor, skeleton } from './editor';
|
||||
import { VisionWorkbench } from './skeleton/workbench';
|
||||
import Panes from './panes';
|
||||
|
||||
function init(container?: Element) {
|
||||
if (!container) {
|
||||
@ -16,9 +17,12 @@ function init(container?: Element) {
|
||||
}
|
||||
container.id = 'engine';
|
||||
|
||||
render(createElement(Skeleton, {
|
||||
editor,
|
||||
}), container);
|
||||
render(
|
||||
createElement(VisionWorkbench, {
|
||||
skeleton,
|
||||
}),
|
||||
container,
|
||||
);
|
||||
}
|
||||
|
||||
const ui = {
|
||||
@ -51,4 +55,5 @@ export {
|
||||
*/
|
||||
init,
|
||||
ui,
|
||||
Panes,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user