mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-04-19 20:08:05 +00:00
chore: 🤖 update structure
This commit is contained in:
commit
11f9337be3
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,6 +13,7 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
# package-lock.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
|
||||
@ -3,11 +3,14 @@
|
||||
"version": "0.0.1",
|
||||
"description": "低代码编辑器",
|
||||
"dependencies": {
|
||||
"@ali/iceluna-addon-2": "^1.0.3",
|
||||
"@ali/iceluna-addon-component-list": "^1.0.11",
|
||||
"@ali/iceluna-sdk": "^1.0.5-beta.26",
|
||||
"@ali/lowcode-editor-core": "0.0.1",
|
||||
"@ali/lowcode-editor-skeleton": "0.0.1",
|
||||
"@ali/lowcode-plugin-logo": "0.0.1",
|
||||
"@ali/lowcode-plugin-undo-redo": "0.0.1",
|
||||
"@ali/lowcode-plugin-save": "0.0.1",
|
||||
"@ali/lowcode-plugin-designer": "0.0.1",
|
||||
"@ali/lowcode-plugin-components-pane": "0.0.1",
|
||||
"@alifd/next": "^1.x",
|
||||
"@alife/dpl-iceluna": "^2.3.2",
|
||||
"@alife/theme-lowcode-dark": "^0.1.0",
|
||||
"@alife/theme-lowcode-light": "^0.1.0",
|
||||
"@icedesign/theme": "^1.x",
|
||||
1589
packages/demo/public/assets.json
Normal file
1589
packages/demo/public/assets.json
Normal file
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
110
packages/demo/public/schema.json
Normal file
110
packages/demo/public/schema.json
Normal file
@ -0,0 +1,110 @@
|
||||
{
|
||||
"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": "重置"
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}]
|
||||
}
|
||||
@ -1255,7 +1255,7 @@ export default {
|
||||
{
|
||||
name: 'value',
|
||||
title: '受控值',
|
||||
setter: 'StringSetter',
|
||||
setter: 'StringSetter'
|
||||
},
|
||||
{
|
||||
name: 'hasBorder',
|
||||
@ -1452,57 +1452,67 @@ export default {
|
||||
componentName: 'ObjectSetter',
|
||||
props: {
|
||||
config: {
|
||||
items: [{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'phone',
|
||||
title: '电话',
|
||||
setter: 'StringSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter'
|
||||
}, {
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter'
|
||||
}, {
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
}, {
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
}, {
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}]
|
||||
items: [
|
||||
{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'phone',
|
||||
title: '电话',
|
||||
setter: 'StringSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter'
|
||||
},
|
||||
{
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter'
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [
|
||||
{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
]
|
||||
}],
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
initialValue: {},
|
||||
initialValue: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1517,72 +1527,82 @@ export default {
|
||||
componentName: 'ObjectSetter',
|
||||
props: {
|
||||
config: {
|
||||
items: [{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'log',
|
||||
title: '到访记录',
|
||||
setter: {
|
||||
componentName: 'ArraySetter',
|
||||
props: {
|
||||
itemSetter: 'StringSetter'
|
||||
}
|
||||
items: [
|
||||
{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true
|
||||
},
|
||||
important: true,
|
||||
}, {
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
}, {
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
}, {
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}]
|
||||
}
|
||||
{
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'log',
|
||||
title: '到访记录',
|
||||
setter: {
|
||||
componentName: 'ArraySetter',
|
||||
props: {
|
||||
itemSetter: 'StringSetter'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
]
|
||||
}],
|
||||
important: true
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [
|
||||
{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
initialValue: {},
|
||||
initialValue: {}
|
||||
},
|
||||
mode: 'popup'
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
],
|
||||
extraProps: {
|
||||
defaultCollapsed: false,
|
||||
defaultCollapsed: false
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -1596,64 +1616,74 @@ export default {
|
||||
componentName: 'ObjectSetter',
|
||||
props: {
|
||||
config: {
|
||||
items: [{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'log',
|
||||
title: '到访记录',
|
||||
setter: {
|
||||
componentName: 'ArraySetter',
|
||||
props: {
|
||||
itemSetter: 'StringSetter'
|
||||
}
|
||||
items: [
|
||||
{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true
|
||||
},
|
||||
important: true,
|
||||
}, {
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
}, {
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
}, {
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}]
|
||||
}
|
||||
{
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'log',
|
||||
title: '到访记录',
|
||||
setter: {
|
||||
componentName: 'ArraySetter',
|
||||
props: {
|
||||
itemSetter: 'StringSetter'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
]
|
||||
}],
|
||||
important: true
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [
|
||||
{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
initialValue: {},
|
||||
initialValue: {}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -1664,64 +1694,74 @@ export default {
|
||||
props: {
|
||||
mode: 'popup',
|
||||
config: {
|
||||
items: [{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter',
|
||||
important: true,
|
||||
}, {
|
||||
name: 'log',
|
||||
title: '到访记录',
|
||||
setter: {
|
||||
componentName: 'ArraySetter',
|
||||
props: {
|
||||
itemSetter: 'StringSetter'
|
||||
}
|
||||
items: [
|
||||
{
|
||||
name: 'username',
|
||||
title: '姓名',
|
||||
setter: 'StringSetter',
|
||||
important: true
|
||||
},
|
||||
important: true,
|
||||
}, {
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
}, {
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
}, {
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}]
|
||||
}
|
||||
{
|
||||
name: 'age',
|
||||
title: '年龄',
|
||||
setter: 'NumberSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'married',
|
||||
title: '婚否',
|
||||
setter: 'BoolSetter',
|
||||
important: true
|
||||
},
|
||||
{
|
||||
name: 'log',
|
||||
title: '到访记录',
|
||||
setter: {
|
||||
componentName: 'ArraySetter',
|
||||
props: {
|
||||
itemSetter: 'StringSetter'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
]
|
||||
}],
|
||||
important: true
|
||||
},
|
||||
{
|
||||
type: 'group',
|
||||
title: 'work',
|
||||
items: [
|
||||
{
|
||||
name: 'job',
|
||||
title: '工作岗位',
|
||||
setter: {
|
||||
componentName: 'SelectSetter',
|
||||
props: {
|
||||
dataSource: [
|
||||
{
|
||||
label: '工程师',
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: '高级工程师',
|
||||
value: 2
|
||||
},
|
||||
{
|
||||
label: '资深工程师',
|
||||
value: 3
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'address',
|
||||
title: '工作地点',
|
||||
setter: 'TextAreaSetter'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
initialValue: {},
|
||||
initialValue: {}
|
||||
}
|
||||
}
|
||||
]
|
||||
19
packages/demo/src/config/components.js
Normal file
19
packages/demo/src/config/components.js
Normal file
@ -0,0 +1,19 @@
|
||||
import componentsPane from '@ali/lowcode-plugin-components-pane';
|
||||
import Settings from '../../../plugin-settings';
|
||||
import undoRedo from '@ali/lowcode-plugin-undo-redo';
|
||||
import Designer from '../plugins/designer';
|
||||
import logo from '@ali/lowcode-plugin-logo';
|
||||
import save from '@ali/lowcode-plugin-save';
|
||||
import OutlineTree from '../../../plugin-outline-tree';
|
||||
|
||||
import { PluginFactory } from '@ali/lowcode-editor-core';
|
||||
|
||||
export default {
|
||||
logo: PluginFactory(logo),
|
||||
save: PluginFactory(save),
|
||||
designer: PluginFactory(Designer),
|
||||
settings: PluginFactory(Settings),
|
||||
outlineTree: PluginFactory(OutlineTree),
|
||||
undoRedo: PluginFactory(undoRedo),
|
||||
componentsPane: PluginFactory(componentsPane)
|
||||
};
|
||||
132
packages/demo/src/config/skeleton.js
Normal file
132
packages/demo/src/config/skeleton.js
Normal file
@ -0,0 +1,132 @@
|
||||
export default {
|
||||
version: '^1.0.2',
|
||||
theme: {
|
||||
fusion: {
|
||||
package: '@alife/dpl-iceluna',
|
||||
version: '^2.3.0'
|
||||
},
|
||||
scss: ''
|
||||
},
|
||||
constants: {
|
||||
namespace: 'page'
|
||||
},
|
||||
utils: [],
|
||||
plugins: {
|
||||
topArea: [
|
||||
{
|
||||
pluginKey: 'logo',
|
||||
type: 'Custom',
|
||||
props: {
|
||||
align: 'left',
|
||||
width: 100
|
||||
},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-logo',
|
||||
version: '1.0.0'
|
||||
},
|
||||
pluginProps: {
|
||||
logo: 'https://img.alicdn.com/tfs/TB1hoI9x1H2gK0jSZFEXXcqMpXa-146-40.png',
|
||||
href: '/'
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'undoRedo',
|
||||
type: 'Custom',
|
||||
props: {
|
||||
align: 'right',
|
||||
width: 88
|
||||
},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-undo-redo',
|
||||
version: '1.0.0'
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'divider',
|
||||
type: 'Divider',
|
||||
props: {
|
||||
align: 'right'
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'save',
|
||||
type: 'Custom',
|
||||
props: {
|
||||
align: 'right',
|
||||
width: 64
|
||||
},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-save',
|
||||
version: '1.0.0'
|
||||
}
|
||||
}
|
||||
],
|
||||
leftArea: [
|
||||
{
|
||||
pluginKey: 'componentsPane',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
icon: 'zujianku',
|
||||
title: '组件库'
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-plugin-components-pane',
|
||||
version: '0.0.1'
|
||||
},
|
||||
pluginProps: {
|
||||
disableAppComponent: true
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'outlineTree',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
icon: 'dengpao',
|
||||
title: '大纲树'
|
||||
},
|
||||
config: {
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
}
|
||||
],
|
||||
rightArea: [
|
||||
{
|
||||
pluginKey: 'settings',
|
||||
type: 'Panel',
|
||||
props: {},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-settings-pane',
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
}
|
||||
],
|
||||
centerArea: [
|
||||
{
|
||||
pluginKey: 'designer',
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-designer',
|
||||
version: '1.0.0'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
hooks: [],
|
||||
shortCuts: [],
|
||||
lifeCycles: {
|
||||
init: async function init(editor) {
|
||||
const assets = await editor.utils.get('/assets.json');
|
||||
editor.set({
|
||||
assets,
|
||||
componentsMap: assets.components
|
||||
});
|
||||
|
||||
editor.utils.get('/schema.json').then(res => {
|
||||
editor.emit('schema.reset', res);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
19
packages/demo/src/index.tsx
Normal file
19
packages/demo/src/index.tsx
Normal file
@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Skeleton from '@ali/lowcode-editor-skeleton';
|
||||
import config from './config/skeleton';
|
||||
import components from './config/components';
|
||||
import utils from './config/utils';
|
||||
import './config/locale';
|
||||
import '../../plugin-setters';
|
||||
|
||||
import './global.scss';
|
||||
import './config/theme.scss';
|
||||
|
||||
const ICE_CONTAINER = document.getElementById('ice-container');
|
||||
|
||||
if (!ICE_CONTAINER) {
|
||||
throw new Error('当前页面不存在 <div id="ice-container"></div> 节点.');
|
||||
}
|
||||
|
||||
ReactDOM.render(<Skeleton config={config} utils={utils} components={components} />, ICE_CONTAINER);
|
||||
@ -1,13 +1,11 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import Editor from '../../framework/index';
|
||||
import { PluginConfig } from '../../framework/definitions';
|
||||
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import { PluginConfig } from '@ali/lowcode-editor-core/lib/definitions';
|
||||
import assets from '../../config/assets';
|
||||
// @ts-ignore
|
||||
import Designer from '../../../../designer';
|
||||
|
||||
import assets from '../../config/assets';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export interface PluginProps {
|
||||
@ -30,13 +28,16 @@ const SCHEMA = {
|
||||
},
|
||||
props: {
|
||||
ref: 'outterView',
|
||||
autoLoading: true
|
||||
autoLoading: true,
|
||||
style: {
|
||||
padding: 20
|
||||
}
|
||||
},
|
||||
children: [
|
||||
{
|
||||
componentName: 'Form',
|
||||
props: {
|
||||
labelCol: 4,
|
||||
labelCol: 3,
|
||||
style: {},
|
||||
ref: 'testForm'
|
||||
},
|
||||
@ -165,14 +166,37 @@ const SCHEMA = {
|
||||
export default class DesignerPlugin extends PureComponent<PluginProps> {
|
||||
displayName: 'LowcodePluginDesigner';
|
||||
|
||||
componentDidMount(): void {
|
||||
const { editor } = this.props;
|
||||
editor.on('schema.reset', this.handleSchemaReset);
|
||||
}
|
||||
|
||||
componentWillUmount(): void {
|
||||
const { editor } = this.props;
|
||||
editor.off('schema.reset', this.handleSchemaReset);
|
||||
}
|
||||
|
||||
handleSchemaReset = (schema: object): void => {
|
||||
// const {editor} = this.props;
|
||||
// if (this.designer) {
|
||||
// this.designer.setSchema(schema);
|
||||
// } else {
|
||||
// editor.once('designer.ready', (designer): void => {
|
||||
// designer.setSchema(schema);
|
||||
// });
|
||||
// }
|
||||
};
|
||||
|
||||
handleDesignerMount = (designer): void => {
|
||||
const { editor } = this.props;
|
||||
this.designer = designer;
|
||||
editor.set('designer', designer);
|
||||
editor.emit('designer.ready', designer);
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { editor } = this.props;
|
||||
// const assets = editor.get('assets') || {};
|
||||
return (
|
||||
<Designer
|
||||
onMount={this.handleDesignerMount}
|
||||
@ -181,7 +205,7 @@ export default class DesignerPlugin extends PureComponent<PluginProps> {
|
||||
eventPipe={editor as any}
|
||||
componentMetadatas={Object.values(assets.components) as any}
|
||||
simulatorProps={{
|
||||
library: Object.values(assets.packages),
|
||||
library: Object.values(assets.packages || {})
|
||||
}}
|
||||
/>
|
||||
);
|
||||
16
packages/editor-core/.eslintrc.js
Normal file
16
packages/editor-core/.eslintrc.js
Normal file
@ -0,0 +1,16 @@
|
||||
const { tslint, deepmerge } = require('@ice/spec');
|
||||
|
||||
module.exports = deepmerge(tslint, {
|
||||
rules: {
|
||||
"global-require": 0,
|
||||
"comma-dangle": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"object-shorthand": 0,
|
||||
"jsx-a11y/anchor-has-content": 0,
|
||||
"react/sort-comp": 0,
|
||||
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx", "ts"] }],
|
||||
"@typescript-eslint/interface-name-prefix": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/explicit-member-accessibility": 0
|
||||
},
|
||||
});
|
||||
11
packages/editor-core/README.md
Normal file
11
packages/editor-core/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# demo component
|
||||
|
||||
t-s-demo
|
||||
|
||||
intro component
|
||||
|
||||
## API
|
||||
|
||||
| 参数名 | 说明 | 必填 | 类型 | 默认值 | 备注 |
|
||||
| ------ | ---- | ---- | ---- | ------ | ---- |
|
||||
| | | | | | |
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@ali/lowcode-editor",
|
||||
"name": "@ali/lowcode-editor-core",
|
||||
"version": "0.0.1",
|
||||
"description": "alibaba lowcode editor core",
|
||||
"files": [
|
||||
@ -13,7 +13,7 @@
|
||||
"stylePath": "style.js",
|
||||
"scripts": {
|
||||
"start": "build-scripts start",
|
||||
"build": "build-scripts build",
|
||||
"build": "build-scripts build --skip-demo",
|
||||
"prepublishOnly": "npm run prettier && npm run build",
|
||||
"lint": "eslint --cache --ext .js,.jsx ./",
|
||||
"prettier": "prettier --write \"./src/**/*.{ts,tsx,js,jsx,ejs,less,css,scss,json}\" "
|
||||
@ -29,7 +29,8 @@
|
||||
"intl-messageformat": "^7.8.4",
|
||||
"lodash": "^4.17.15",
|
||||
"prop-types": "^15.5.8",
|
||||
"store": "^2.0.12"
|
||||
"store": "^2.0.12",
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.3",
|
||||
@ -51,5 +52,5 @@
|
||||
"@alifd/next": "1.x"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://unpkg.com/editor-framework@0.0.1/build/index.html"
|
||||
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-editor-core@0.0.1/build/index.html"
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import { PluginConfig, PluginStatus } from './definitions';
|
||||
import { PluginConfig, PluginStatus, PluginClass, HOCPlugin } from './definitions';
|
||||
import Editor from './index';
|
||||
import { clone, deepEqual } from './utils';
|
||||
|
||||
@ -18,14 +18,16 @@ export default class AreaManager {
|
||||
this.pluginStatus = clone(editor.pluginStatus);
|
||||
}
|
||||
|
||||
public isPluginStatusUpdate(pluginType?: string): boolean {
|
||||
public isPluginStatusUpdate(pluginType?: string, notUpdateStatus?: boolean): boolean {
|
||||
const { pluginStatus } = this.editor;
|
||||
const list = pluginType ? this.config.filter((item): boolean => item.type === pluginType) : this.config;
|
||||
|
||||
const isUpdate = list.some(
|
||||
(item): boolean => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey])
|
||||
(item): boolean => !deepEqual(pluginStatus[item.pluginKey], this.pluginStatus[item.pluginKey]),
|
||||
);
|
||||
this.pluginStatus = clone(pluginStatus);
|
||||
if (!notUpdateStatus) {
|
||||
this.pluginStatus = clone(pluginStatus);
|
||||
}
|
||||
return isUpdate;
|
||||
}
|
||||
|
||||
@ -36,7 +38,28 @@ export default class AreaManager {
|
||||
return pluginType ? res.filter((item): boolean => item.type === pluginType) : res;
|
||||
}
|
||||
|
||||
public getPluginConfig(): PluginConfig[] {
|
||||
public getPlugin(pluginKey: string): HOCPlugin | void {
|
||||
if (pluginKey) {
|
||||
return this.editor && this.editor.plugins && this.editor.plugins[pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
public 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 {
|
||||
if (pluginKey) {
|
||||
return this.editor && this.editor.components && this.editor.components[pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
public getPluginStatus(pluginKey: string): PluginStatus | void {
|
||||
if (pluginKey) {
|
||||
return this.editor && this.editor.pluginStatus && this.editor.pluginStatus[pluginKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -25,7 +25,7 @@ export interface NpmConfig {
|
||||
export interface SkeletonConfig {
|
||||
config: NpmConfig;
|
||||
props?: object;
|
||||
handler?: (EditorConfig) => EditorConfig;
|
||||
handler?: (config: EditorConfig) => EditorConfig;
|
||||
}
|
||||
|
||||
export interface FusionTheme {
|
||||
@ -38,7 +38,7 @@ export interface ThemeConfig {
|
||||
}
|
||||
|
||||
export interface PluginsConfig {
|
||||
[propName: string]: PluginConfig[];
|
||||
[key: string]: PluginConfig[];
|
||||
}
|
||||
|
||||
export interface PluginConfig {
|
||||
@ -68,7 +68,7 @@ export type HooksConfig = HookConfig[];
|
||||
export interface HookConfig {
|
||||
message: string;
|
||||
type: 'on' | 'once';
|
||||
handler: (editor: Editor, ...args) => void;
|
||||
handler: (editor: Editor, ...args: []) => void;
|
||||
}
|
||||
|
||||
export type ShortCutsConfig = ShortCutConfig[];
|
||||
@ -83,7 +83,7 @@ export type UtilsConfig = UtilConfig[];
|
||||
export interface UtilConfig {
|
||||
name: string;
|
||||
type: 'npm' | 'function';
|
||||
content: NpmConfig | ((...args) => any);
|
||||
content: NpmConfig | ((...args: []) => any);
|
||||
}
|
||||
|
||||
export type ConstantsConfig = object;
|
||||
@ -109,7 +109,7 @@ export interface I18nConfig {
|
||||
export type I18nFunction = (key: string, params: any) => string;
|
||||
|
||||
export interface Utils {
|
||||
[key: string]: (...args) => any;
|
||||
[key: string]: (...args: []) => any;
|
||||
}
|
||||
|
||||
export interface PluginProps {
|
||||
@ -136,6 +136,10 @@ export interface PluginSet {
|
||||
|
||||
export type PluginClass = React.ComponentType<PluginProps> & {
|
||||
init?: (editor: Editor) => void;
|
||||
defaultProps?: {
|
||||
locale?: LocaleType;
|
||||
messages?: I18nMessages;
|
||||
};
|
||||
};
|
||||
|
||||
export interface PluginClassSet {
|
||||
@ -1,5 +1,5 @@
|
||||
import Debug from 'debug';
|
||||
import EventEmitter from 'events';
|
||||
import { EventEmitter } from 'events';
|
||||
import store from 'store';
|
||||
import {
|
||||
EditorConfig,
|
||||
@ -8,7 +8,7 @@ import {
|
||||
PluginStatusSet,
|
||||
Utils,
|
||||
PluginClassSet,
|
||||
PluginSet
|
||||
PluginSet,
|
||||
} from './definitions';
|
||||
|
||||
import * as editorUtils from './utils';
|
||||
@ -64,7 +64,7 @@ const debug = Debug('editor');
|
||||
EventEmitter.defaultMaxListeners = 100;
|
||||
|
||||
export interface HooksFuncs {
|
||||
[idx: number]: (msg: string, handler: (...args) => void) => void;
|
||||
[idx: number]: (msg: string, handler: (...args: []) => void) => void;
|
||||
}
|
||||
|
||||
export default class Editor extends EventEmitter {
|
||||
@ -87,14 +87,6 @@ export default class Editor extends EventEmitter {
|
||||
|
||||
public locale: LocaleType;
|
||||
|
||||
public emit: (msg: string, ...args) => void;
|
||||
|
||||
public on: (msg: string, handler: (...args) => void) => void;
|
||||
|
||||
public once: (msg: string, handler: (...args) => void) => void;
|
||||
|
||||
public off: (msg: string, handler: (...args) => void) => void;
|
||||
|
||||
private hooksFuncs: HooksFuncs;
|
||||
|
||||
constructor(config: EditorConfig, components: PluginClassSet, utils?: Utils) {
|
||||
@ -103,7 +95,6 @@ export default class Editor extends EventEmitter {
|
||||
this.components = components;
|
||||
this.utils = { ...editorUtils, ...utils };
|
||||
instance = this;
|
||||
this.init();
|
||||
}
|
||||
|
||||
public init(): Promise<any> {
|
||||
@ -165,21 +156,27 @@ export default class Editor extends EventEmitter {
|
||||
if (!Array.isArray(events)) {
|
||||
return;
|
||||
}
|
||||
events.forEach((event): void => this.on(event, lisenter));
|
||||
events.forEach((event): void => {
|
||||
this.on(event, lisenter);
|
||||
});
|
||||
}
|
||||
|
||||
public batchOnce(events: string[], lisenter: (...args) => void): void {
|
||||
if (!Array.isArray(events)) {
|
||||
return;
|
||||
}
|
||||
events.forEach((event): void => this.once(event, lisenter));
|
||||
events.forEach((event): void => {
|
||||
this.once(event, lisenter);
|
||||
});
|
||||
}
|
||||
|
||||
public batchOff(events: string[], lisenter: (...args) => void): void {
|
||||
if (!Array.isArray(events)) {
|
||||
return;
|
||||
}
|
||||
events.forEach((event): void => this.off(event, lisenter));
|
||||
events.forEach((event): void => {
|
||||
this.off(event, lisenter);
|
||||
});
|
||||
}
|
||||
|
||||
// 销毁hooks中的消息监听
|
||||
@ -216,7 +213,7 @@ export default class Editor extends EventEmitter {
|
||||
res[plugin.pluginKey] = {
|
||||
visible: typeof visible === 'boolean' ? visible : true,
|
||||
disabled: typeof disabled === 'boolean' ? disabled : false,
|
||||
marked: typeof marked === 'boolean' ? marked : false
|
||||
marked: typeof marked === 'boolean' ? marked : false,
|
||||
};
|
||||
const pluginClass = this.components[plugin.pluginKey];
|
||||
// 判断如果编辑器插件有init静态方法,则在此执行init方法
|
||||
@ -1,12 +1,12 @@
|
||||
import Editor from './editor';
|
||||
|
||||
import * as editorUtils from './utils';
|
||||
import * as editorDefinitions from './definitions';
|
||||
import * as utils from './utils';
|
||||
import * as definitions from './definitions';
|
||||
|
||||
export { default as PluginFactory } from './pluginFactory';
|
||||
export { default as EditorContext } from './context';
|
||||
export { default as AreaManager } from './areaManager';
|
||||
|
||||
export default Editor;
|
||||
|
||||
export const utils = editorUtils;
|
||||
export const definitions = editorDefinitions;
|
||||
export { utils, definitions };
|
||||
@ -27,21 +27,26 @@ export default function pluginFactory(Comp: PluginClass): React.ComponentType<Pl
|
||||
console.warn('lowcode editor plugin has wrong config');
|
||||
return;
|
||||
}
|
||||
const { locale, messages, editor } = props;
|
||||
const { editor } = props;
|
||||
this.ref = createRef<React.ReactElement>();
|
||||
// 注册插件
|
||||
this.editor = editor;
|
||||
this.i18n = generateI18n(locale, messages);
|
||||
this.pluginKey = props.config.pluginKey;
|
||||
const defaultProps = Comp.defaultProps || {};
|
||||
const locale = this.editor.get('locale') || defaultProps.locale || 'zh-CN';
|
||||
const editorMessages = this.editor.get('messages') || {};
|
||||
const messages = editorMessages[this.pluginKey] || defaultProps.messages || {};
|
||||
this.i18n = generateI18n(locale, messages);
|
||||
|
||||
editor.set('plugins', {
|
||||
...editor.plugins,
|
||||
[this.pluginKey]: this
|
||||
[this.pluginKey]: this,
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount(): void {
|
||||
// 销毁插件
|
||||
if (this.editor && this.editor.plugins) {
|
||||
if (this.pluginKey && this.editor && this.editor.plugins) {
|
||||
delete this.editor.plugins[this.pluginKey];
|
||||
}
|
||||
}
|
||||
@ -66,7 +71,7 @@ export default function pluginFactory(Comp: PluginClass): React.ComponentType<Pl
|
||||
i18n: this.i18n,
|
||||
editor: this.editor,
|
||||
config,
|
||||
...config.pluginProps
|
||||
...config.pluginProps,
|
||||
};
|
||||
if (acceptsRef(Comp)) {
|
||||
props.ref = this.ref;
|
||||
125
packages/editor-core/src/request.ts
Normal file
125
packages/editor-core/src/request.ts
Normal file
@ -0,0 +1,125 @@
|
||||
import 'whatwg-fetch';
|
||||
import Debug from 'debug';
|
||||
const debug = Debug('request');
|
||||
|
||||
export function serialize(obj: object): string {
|
||||
const rst: string[] = [];
|
||||
Object.entries(obj || {}).forEach(([key, val]): void => {
|
||||
if (val === null || val === undefined || val === '') return;
|
||||
if (typeof val === 'object') rst.push(`${key}=${encodeURIComponent(JSON.stringify(val))}`);
|
||||
else rst.push(`${key}=${encodeURIComponent(val)}`);
|
||||
});
|
||||
return rst.join('&');
|
||||
}
|
||||
|
||||
export function buildUrl(dataAPI: string, params: object): string {
|
||||
const paramStr = serialize(params);
|
||||
if (paramStr) {
|
||||
return dataAPI.indexOf('?') > 0 ? `${dataAPI}&${paramStr}` : `${dataAPI}?${paramStr}`;
|
||||
}
|
||||
return dataAPI;
|
||||
}
|
||||
|
||||
export function get(dataAPI: string, params?: object, headers?: object, otherProps?: object): Promise<any> {
|
||||
const fetchHeaders = {
|
||||
Accept: 'application/json',
|
||||
...headers,
|
||||
};
|
||||
return request(buildUrl(dataAPI, params), 'GET', null, fetchHeaders, otherProps);
|
||||
}
|
||||
|
||||
export function post(dataAPI: string, params?: object, headers?: object, otherProps?: object): Promise<any> {
|
||||
const fetchHeaders = {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
...headers,
|
||||
};
|
||||
return request(
|
||||
dataAPI,
|
||||
'POST',
|
||||
fetchHeaders['Content-Type'].indexOf('application/json') > -1 || Array.isArray(params)
|
||||
? JSON.stringify(params)
|
||||
: serialize(params),
|
||||
fetchHeaders,
|
||||
otherProps,
|
||||
);
|
||||
}
|
||||
|
||||
export function request(
|
||||
dataAPI: string,
|
||||
method: string = 'GET',
|
||||
data?: object | string,
|
||||
headers?: object,
|
||||
otherProps?: object,
|
||||
): Promise<any> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
if (otherProps && otherProps.timeout) {
|
||||
setTimeout((): void => {
|
||||
reject(new Error('timeout'));
|
||||
}, otherProps.timeout);
|
||||
}
|
||||
fetch(dataAPI, {
|
||||
method,
|
||||
credentials: 'include',
|
||||
headers,
|
||||
body: data,
|
||||
...otherProps,
|
||||
})
|
||||
.then((response: Response): any => {
|
||||
switch (response.status) {
|
||||
case 200:
|
||||
case 201:
|
||||
case 202:
|
||||
return response.json();
|
||||
case 204:
|
||||
if (method === 'DELETE') {
|
||||
return {
|
||||
success: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
__success: false,
|
||||
code: response.status,
|
||||
};
|
||||
}
|
||||
case 400:
|
||||
case 401:
|
||||
case 403:
|
||||
case 404:
|
||||
case 406:
|
||||
case 410:
|
||||
case 422:
|
||||
case 500:
|
||||
return response
|
||||
.json()
|
||||
.then((res: object): any => {
|
||||
return {
|
||||
__success: false,
|
||||
code: response.status,
|
||||
data: res,
|
||||
};
|
||||
})
|
||||
.catch((): object => {
|
||||
return {
|
||||
__success: false,
|
||||
code: response.status,
|
||||
};
|
||||
});
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.then((json: object): void => {
|
||||
if (json && json.__success !== false) {
|
||||
resolve(json);
|
||||
} else {
|
||||
delete json.__success;
|
||||
reject(json);
|
||||
}
|
||||
})
|
||||
.catch((err: Error): void => {
|
||||
debug(err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -9,6 +9,7 @@ import _pick from 'lodash/pick';
|
||||
import _throttle from 'lodash/throttle';
|
||||
|
||||
import _serialize from 'serialize-javascript';
|
||||
export { get, post, request } from './request';
|
||||
import Editor from './editor';
|
||||
import { EditorConfig, I18nFunction, I18nMessages, LocaleType, ShortCutsConfig } from './definitions';
|
||||
|
||||
@ -25,7 +26,7 @@ const ENV = {
|
||||
TBE: 'TBE',
|
||||
WEBIDE: 'WEB-IDE',
|
||||
VSCODE: 'VSCODE',
|
||||
WEB: 'WEB'
|
||||
WEB: 'WEB',
|
||||
};
|
||||
|
||||
declare global {
|
||||
@ -92,7 +93,7 @@ export function goldlog(gmKey: string, params: object = {}, logKey: string = 'ot
|
||||
const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage;
|
||||
const goKey = serializeParams({
|
||||
env: getEnv(),
|
||||
...params
|
||||
...params,
|
||||
});
|
||||
if (sendIDEMessage) {
|
||||
sendIDEMessage({
|
||||
@ -100,8 +101,8 @@ export function goldlog(gmKey: string, params: object = {}, logKey: string = 'ot
|
||||
data: {
|
||||
logKey: `/iceluna.core.${logKey}`,
|
||||
gmKey,
|
||||
goKey
|
||||
}
|
||||
goKey,
|
||||
},
|
||||
});
|
||||
}
|
||||
if (window.goldlog) {
|
||||
@ -214,7 +215,7 @@ export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig
|
||||
if (skeleton && skeleton.handler && typeof skeleton.handler === 'function') {
|
||||
return skeleton.handler({
|
||||
skeleton,
|
||||
...defaultConfig
|
||||
...defaultConfig,
|
||||
});
|
||||
}
|
||||
|
||||
@ -225,34 +226,34 @@ export function comboEditorConfig(defaultConfig: EditorConfig = {}, customConfig
|
||||
localeList.forEach((key): void => {
|
||||
i18nConfig[key] = {
|
||||
...(defaultConfig.i18n && defaultConfig.i18n[key]),
|
||||
...(i18n && i18n[key])
|
||||
...(i18n && i18n[key]),
|
||||
};
|
||||
});
|
||||
return {
|
||||
skeleton,
|
||||
theme: {
|
||||
...defaultConfig.theme,
|
||||
...theme
|
||||
...theme,
|
||||
},
|
||||
plugins: {
|
||||
...defaultConfig.plugins,
|
||||
...plugins
|
||||
...plugins,
|
||||
},
|
||||
hooks: [...(defaultConfig.hooks || []), ...(hooks || [])],
|
||||
shortCuts: Object.values({
|
||||
...defaultShortCuts,
|
||||
...customShortCuts
|
||||
...customShortCuts,
|
||||
}),
|
||||
lifeCycles: {
|
||||
...defaultConfig.lifeCycles,
|
||||
...lifeCycles
|
||||
...lifeCycles,
|
||||
},
|
||||
constants: {
|
||||
...defaultConfig.constants,
|
||||
...constants
|
||||
...constants,
|
||||
},
|
||||
utils: [...(defaultConfig.utils || []), ...(utils || [])],
|
||||
i18n: i18nConfig
|
||||
i18n: i18nConfig,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
const { eslint, deepmerge } = require('@ice/spec');
|
||||
|
||||
module.exports = deepmerge(eslint, {
|
||||
rules: {},
|
||||
});
|
||||
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: Simple Usage
|
||||
order: 1
|
||||
---
|
||||
|
||||
本 Demo 演示一行文字的用法。
|
||||
|
||||
````jsx
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<App />
|
||||
), mountNode);
|
||||
````
|
||||
@ -1,3 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
const context = createContext({});
|
||||
export default context;
|
||||
@ -1,48 +0,0 @@
|
||||
|
||||
export interface EditorConfig {
|
||||
|
||||
};
|
||||
|
||||
export interface NpmConfig {
|
||||
version: string,
|
||||
package: string,
|
||||
main?: string,
|
||||
exportName?: string,
|
||||
subName?: string,
|
||||
destructuring?: boolean
|
||||
};
|
||||
|
||||
export interface SkeletonConfig {
|
||||
config: NpmConfig,
|
||||
props?: object,
|
||||
handler?: (EditorConfig) => EditorConfig
|
||||
};
|
||||
|
||||
export interface FusionTheme {
|
||||
package: string,
|
||||
version: string
|
||||
};
|
||||
|
||||
export interface ThemeConfig {
|
||||
fusion?: FusionTheme
|
||||
}
|
||||
|
||||
export interface PluginsConfig {
|
||||
[key]: Array<PluginConfig>
|
||||
};
|
||||
|
||||
export interface PluginConfig {
|
||||
pluginKey: string,
|
||||
type: string,
|
||||
props: object,
|
||||
config: NpmConfig,
|
||||
pluginProps: object
|
||||
};
|
||||
|
||||
export type HooksConfig = Array<HookConfig>;
|
||||
|
||||
export interface HookConfig {
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -1,186 +0,0 @@
|
||||
import EventEmitter from 'events';
|
||||
import Debug from 'debug';
|
||||
import store from 'store';
|
||||
|
||||
import {
|
||||
unRegistShortCuts,
|
||||
registShortCuts,
|
||||
transformToPromise,
|
||||
generateI18n
|
||||
} from './utils';
|
||||
|
||||
// 根据url参数设置debug选项
|
||||
const res = /_?debug=(.*?)(&|$)/.exec(location.search);
|
||||
if (res && res[1]) {
|
||||
window.__isDebug = true;
|
||||
store.storage.write('debug', res[1] === 'true' ? '*' : res[1]);
|
||||
} else {
|
||||
window.__isDebug = false;
|
||||
store.remove('debug');
|
||||
}
|
||||
|
||||
//重要,用于矫正画布执行new Function的window对象上下文
|
||||
window.__newFunc = funContext => {
|
||||
return new Function(funContext);
|
||||
};
|
||||
|
||||
//关闭浏览器前提醒,只有产生过交互才会生效
|
||||
window.onbeforeunload = function(e) {
|
||||
e = e || window.event;
|
||||
// 本地调试不生效
|
||||
if (location.href.indexOf('localhost') > 0) return;
|
||||
var msg = '您确定要离开此页面吗?';
|
||||
e.cancelBubble = true;
|
||||
e.returnValue = msg;
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
|
||||
let instance = null;
|
||||
const debug = Debug('editor');
|
||||
EventEmitter.defaultMaxListeners = 100;
|
||||
|
||||
export interface editor {
|
||||
|
||||
};
|
||||
|
||||
export default class Editor extends EventEmitter {
|
||||
static getInstance = () => {
|
||||
if (!instance) {
|
||||
instance = new Editor();
|
||||
}
|
||||
return instance;
|
||||
};
|
||||
|
||||
constructor(config) {
|
||||
super();
|
||||
instance = this;
|
||||
Object.assign(this, config);
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
const {
|
||||
hooks,
|
||||
shortCuts,
|
||||
lifeCycles
|
||||
} = this.config || {};
|
||||
this.destroy();
|
||||
this.locale = store.get('lowcode-editor-locale') || 'zh-CN';
|
||||
this.messages = this.messagesSet[this.locale];
|
||||
this.i18n = generateI18n(this.locale, this.messages);
|
||||
this.pluginStatus = this.initPluginStatus();
|
||||
this.initHooks(hooks, appHelper);
|
||||
|
||||
appHelper.emit('editor.beforeInit');
|
||||
const init = lifeCycles && lifeCycles.init || () => {};
|
||||
// 用户可以通过设置extensions.init自定义初始化流程;
|
||||
transformToPromise(init(this))
|
||||
.then(() => {
|
||||
// 注册快捷键
|
||||
registShortCuts(shortCuts, this);
|
||||
this.emit('editor.afterInit');
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
}
|
||||
|
||||
destroy() {
|
||||
try {
|
||||
const {
|
||||
hooks = [],
|
||||
shortCuts = [],
|
||||
lifeCycles = {}
|
||||
} = this.config;
|
||||
unRegistShortCuts(shortCuts);
|
||||
this.destroyHooks(hooks);
|
||||
lifeCycles.destroy && lifeCycles.destroy();
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
get(key:string):any {
|
||||
return this[key];
|
||||
}
|
||||
|
||||
set(key:string|object, val:any):void {
|
||||
if (typeof key === 'string') {
|
||||
if (['init', 'destroy', 'get', 'set', 'batchOn', 'batchOff', 'batchOnce'].includes(key)) {
|
||||
console.warning('init, destroy, get, set, batchOn, batchOff, batchOnce is private attribute');
|
||||
return;
|
||||
}
|
||||
this[key] = val;
|
||||
} else if (typeof key === 'object') {
|
||||
Object.keys(key).forEach(item => {
|
||||
this[item] = key[item];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
batchOn(events:Array<string>, lisenter:function):void {
|
||||
if (!Array.isArray(events)) return;
|
||||
events.forEach(event => this.on(event, lisenter));
|
||||
}
|
||||
|
||||
batchOnce(events:Array<string>, lisenter:function):void {
|
||||
if (!Array.isArray(events)) return;
|
||||
events.forEach(event => this.once(event, lisenter));
|
||||
}
|
||||
|
||||
batchOff(events:Array<string>, lisenter:function):void {
|
||||
if (!Array.isArray(events)) return;
|
||||
events.forEach(event => this.off(event, lisenter));
|
||||
}
|
||||
|
||||
//销毁hooks中的消息监听
|
||||
private destroyHooks(hooks = []) {
|
||||
hooks.forEach((item, idx) => {
|
||||
if (typeof this.__hooksFuncs[idx] === 'function') {
|
||||
this.appHelper.off(item.message, this.__hooksFuncs[idx]);
|
||||
}
|
||||
});
|
||||
delete this.__hooksFuncs;
|
||||
};
|
||||
|
||||
//初始化hooks中的消息监听
|
||||
private initHooks(hooks = []) {
|
||||
this.__hooksFuncs = hooks.map(item => {
|
||||
const func = (...args) => {
|
||||
item.handler(this, ...args);
|
||||
};
|
||||
this[item.type](item.message, func);
|
||||
return func;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
private initPluginStatus () {
|
||||
const {plugins = {}} = this.config;
|
||||
const pluginAreas = Object.keys(plugins);
|
||||
const res = {};
|
||||
pluginAreas.forEach(area => {
|
||||
(plugins[area] || []).forEach(plugin => {
|
||||
if (plugin.type === 'Divider') return;
|
||||
const { visible, disabled, dotted } = plugin.props || {};
|
||||
res[plugin.pluginKey] = {
|
||||
visible: typeof visible === 'boolean' ? visible : true,
|
||||
disabled: typeof disabled === 'boolean' ? disabled : false,
|
||||
dotted: typeof dotted === 'boolean' ? dotted : false
|
||||
};
|
||||
const pluginClass = this.props.components[skeletonUtils.generateAddonCompName(addon.addonKey)];
|
||||
// 判断如果编辑器插件有init静态方法,则在此执行init方法
|
||||
if (pluginClass && pluginClass.init) {
|
||||
pluginClass.init(this);
|
||||
}
|
||||
});
|
||||
});
|
||||
return res;
|
||||
};
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
import Editor from './editor';
|
||||
|
||||
|
||||
export default Editor;
|
||||
@ -1,129 +0,0 @@
|
||||
import { PureComponent } from 'react';
|
||||
|
||||
import EditorContext from './context';
|
||||
import { isEmpty, generateI18n, goldlog } from './utils';
|
||||
|
||||
export interface pluginProps {
|
||||
config: object,
|
||||
editor: object,
|
||||
locale: string,
|
||||
messages: object
|
||||
}
|
||||
|
||||
export default function plugin(Comp) {
|
||||
|
||||
class Plugin extends PureComponent<pluginProps> {
|
||||
static displayName = 'lowcode-editor-plugin';
|
||||
static defaultProps = {
|
||||
config: {}
|
||||
};
|
||||
static contextType = EditorContext;
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
if (isEmpty(props.config) || !props.config.pluginKey) {
|
||||
console.warn('lowcode editor plugin has wrong config');
|
||||
return;
|
||||
}
|
||||
|
||||
const { locale, messages, editor } = props;
|
||||
// 注册插件
|
||||
this.editor = editor;
|
||||
this.i18n = generateI18n(locale, messages);
|
||||
this.pluginKey = props.config.pluginKey;
|
||||
editor.plugins = editor.plugins || {};
|
||||
editor.plugins[this.pluginKey] = this;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// 销毁插件
|
||||
if (this.editor && this.editor.plugins) {
|
||||
delete this.editor.plugins[this.pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
config
|
||||
} = this.props;
|
||||
return <Comp i18n={this.i18n} editor={this.editor} config={config} {...config.pluginProps}/>
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export class Plugin extends PureComponent<pluginProps> {
|
||||
static displayName = 'lowcode-editor-plugin';
|
||||
static defaultProps = {
|
||||
config: {}
|
||||
};
|
||||
static contextType = EditorContext;
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
if (isEmpty(props.config) || !props.config.addonKey) {
|
||||
console.warn('luna addon has wrong config');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const { locale, messages, editor } = props;
|
||||
// 注册插件
|
||||
this.editor = editor;
|
||||
this.i18n = generateI18n(locale, messages);
|
||||
this.pluginKey = props.config.pluginKey;
|
||||
editor.plugins = editor.plugins || {};
|
||||
editor.plugins[this.pluginKey] = this;
|
||||
}
|
||||
|
||||
async componentWillUnmount() {
|
||||
// 销毁插件
|
||||
if (this.editor && this.editor.plugins) {
|
||||
delete this.editor.plugins[this.pluginKey];
|
||||
}
|
||||
}
|
||||
|
||||
open = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
close = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
goldlog = (goKey:string, params:any) => {
|
||||
const { pluginKey, config = {} } = this.props.config || {};
|
||||
goldlog(
|
||||
goKey,
|
||||
{
|
||||
pluginKey,
|
||||
package: config.package,
|
||||
version: config.version,
|
||||
...this.editor.logParams,
|
||||
...params
|
||||
},
|
||||
'addon'
|
||||
);
|
||||
};
|
||||
|
||||
get utils() {
|
||||
return this.editor.utils;
|
||||
}
|
||||
|
||||
get constants() {
|
||||
return this.editor.constants;
|
||||
}
|
||||
|
||||
get history() {
|
||||
return this.editor.history;
|
||||
}
|
||||
|
||||
get location() {
|
||||
return this.editor.location;
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,242 +0,0 @@
|
||||
|
||||
import IntlMessageFormat from 'intl-messageformat';
|
||||
import _isEmpty from 'lodash/isEmpty';
|
||||
|
||||
export const isEmpty = _isEmpty;
|
||||
|
||||
/**
|
||||
* 用于构造国际化字符串处理函数
|
||||
* @param {*} locale 国际化标识,例如 zh-CN、en-US
|
||||
* @param {*} messages 国际化语言包
|
||||
*/
|
||||
export function generateI18n(locale = 'zh-CN', messages = {}) {
|
||||
return (key, values = {}) => {
|
||||
if (!messages || !messages[key]) return '';
|
||||
const formater = new IntlMessageFormat(messages[key], locale);
|
||||
return formater.format(values);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化参数
|
||||
* @param {*} obj 参数
|
||||
*/
|
||||
export function serializeParams(obj:object):string {
|
||||
if (typeof obj !== 'object') return '';
|
||||
|
||||
const res:Array<string> = [];
|
||||
Object.entries(obj).forEach(([key, val]) => {
|
||||
if (val === null || val === undefined || val === '') return;
|
||||
if (typeof val === 'object') {
|
||||
res.push(`${encodeURIComponent(key)}=${encodeURIComponent(JSON.stringify(val))}`);
|
||||
} else {
|
||||
res.push(`${encodeURIComponent(key)}=${encodeURIComponent(val)}`);
|
||||
}
|
||||
});
|
||||
return res.join('&');
|
||||
}
|
||||
|
||||
/**
|
||||
* 黄金令箭埋点
|
||||
* @param {String} gmKey 为黄金令箭业务类型
|
||||
* @param {Object} params 参数
|
||||
* @param {String} logKey 属性串
|
||||
*/
|
||||
export function goldlog(gmKey, params = {}, logKey = 'other') {
|
||||
const sendIDEMessage = window.sendIDEMessage || window.parent.sendIDEMessage;
|
||||
const goKey = serializeParams({
|
||||
sdkVersion: pkg.version,
|
||||
env: getEnv(),
|
||||
...params
|
||||
});
|
||||
if (sendIDEMessage) {
|
||||
sendIDEMessage({
|
||||
action: 'goldlog',
|
||||
data: {
|
||||
logKey: `/iceluna.core.${logKey}`,
|
||||
gmKey,
|
||||
goKey
|
||||
}
|
||||
});
|
||||
}
|
||||
window.goldlog && window.goldlog.record(`/iceluna.core.${logKey}`, gmKey, goKey, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前编辑器环境
|
||||
*/
|
||||
export function getEnv() {
|
||||
const userAgent = navigator.userAgent;
|
||||
const isVscode = /Electron\//.test(userAgent);
|
||||
if (isVscode) return ENV.VSCODE;
|
||||
const isTheia = window.is_theia === true;
|
||||
if (isTheia) return ENV.WEBIDE;
|
||||
return ENV.WEB;
|
||||
}
|
||||
|
||||
// 注册快捷键
|
||||
export function registShortCuts(config, editor) {
|
||||
const keyboardFilter = (keymaster.filter = event => {
|
||||
let eTarget = event.target || event.srcElement;
|
||||
let tagName = eTarget.tagName;
|
||||
let isInput = !!(tagName == 'INPUT' || tagName == 'SELECT' || tagName == 'TEXTAREA');
|
||||
let isContenteditable = !!eTarget.getAttribute('contenteditable');
|
||||
if (isInput || isContenteditable) {
|
||||
if (event.metaKey === true && [70, 83].includes(event.keyCode)) event.preventDefault(); //禁止触发chrome原生的页面保存或查找
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
const ideMessage = appHelper.utils && appHelper.utils.ideMessage;
|
||||
|
||||
//复制
|
||||
if (!document.copyListener) {
|
||||
document.copyListener = e => {
|
||||
if (!keyboardFilter(e) || appHelper.isCopying) return;
|
||||
const schema = appHelper.schemaHelper && appHelper.schemaHelper.schemaMap[appHelper.activeKey];
|
||||
if (!schema || !isSchema(schema)) return;
|
||||
appHelper.isCopying = true;
|
||||
const schemaStr = serialize(transformSchemaToPure(schema), {
|
||||
unsafe: true
|
||||
});
|
||||
setClipboardData(schemaStr)
|
||||
.then(() => {
|
||||
ideMessage && ideMessage('success', '当前内容已复制到剪贴板,请使用快捷键Command+v进行粘贴');
|
||||
appHelper.emit('schema.copy', schemaStr, schema);
|
||||
appHelper.isCopying = false;
|
||||
})
|
||||
.catch(errMsg => {
|
||||
ideMessage && ideMessage('error', errMsg);
|
||||
appHelper.isCopying = false;
|
||||
});
|
||||
};
|
||||
document.addEventListener('copy', document.copyListener);
|
||||
if (window.parent.vscode) {
|
||||
keymaster('command+c', document.copyListener);
|
||||
}
|
||||
}
|
||||
|
||||
//粘贴
|
||||
if (!document.pasteListener) {
|
||||
const doPaste = (e, text) => {
|
||||
if (!keyboardFilter(e) || appHelper.isPasting) return;
|
||||
const schemaHelper = appHelper.schemaHelper;
|
||||
let targetKey = appHelper.activeKey;
|
||||
let direction = 'after';
|
||||
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
|
||||
if (!targetKey || topKey === targetKey) {
|
||||
const schemaHelper = appHelper.schemaHelper;
|
||||
const topKey = schemaHelper.schema && schemaHelper.schema.__ctx && schemaHelper.schema.__ctx.lunaKey;
|
||||
if (!topKey) return;
|
||||
targetKey = topKey;
|
||||
direction = 'in';
|
||||
}
|
||||
appHelper.isPasting = true;
|
||||
const schema = parseObj(text);
|
||||
if (!isSchema(schema)) {
|
||||
appHelper.emit('illegalSchema.paste', text);
|
||||
// ideMessage && ideMessage('error', '当前内容不是模型结构,不能粘贴进来!');
|
||||
console.warn('paste schema illegal');
|
||||
appHelper.isPasting = false;
|
||||
return;
|
||||
}
|
||||
appHelper.emit('material.add', {
|
||||
schema,
|
||||
targetKey,
|
||||
direction
|
||||
});
|
||||
appHelper.isPasting = false;
|
||||
appHelper.emit('schema.paste', schema);
|
||||
};
|
||||
document.pasteListener = e => {
|
||||
const clipboardData = e.clipboardData || window.clipboardData;
|
||||
const text = clipboardData && clipboardData.getData('text');
|
||||
doPaste(e, text);
|
||||
};
|
||||
document.addEventListener('paste', document.pasteListener);
|
||||
if (window.parent.vscode) {
|
||||
keymaster('command+v', e => {
|
||||
const sendIDEMessage = window.parent.sendIDEMessage;
|
||||
sendIDEMessage &&
|
||||
sendIDEMessage({
|
||||
action: 'readClipboard'
|
||||
})
|
||||
.then(text => {
|
||||
doPaste(e, text);
|
||||
})
|
||||
.catch(err => {
|
||||
console.warn(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
(config || []).forEach(item => {
|
||||
keymaster(item.keyboard, ev => {
|
||||
ev.preventDefault();
|
||||
item.handler(ev, appHelper, keymaster);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 取消注册快捷
|
||||
export function unRegistShortCuts(config) {
|
||||
(config || []).forEach(item => {
|
||||
keymaster.unbind(item.keyboard);
|
||||
});
|
||||
if (window.parent.vscode) {
|
||||
keymaster.unbind('command+c');
|
||||
keymaster.unbind('command+v');
|
||||
}
|
||||
if (document.copyListener) {
|
||||
document.removeEventListener('copy', document.copyListener);
|
||||
delete document.copyListener;
|
||||
}
|
||||
if (document.pasteListener) {
|
||||
document.removeEventListener('paste', document.pasteListener);
|
||||
delete document.pasteListener;
|
||||
}
|
||||
}
|
||||
|
||||
// 将函数返回结果转成promise形式,如果函数有返回值则根据返回值的bool类型判断是reject还是resolve,若函数无返回值默认执行resolve
|
||||
export function transformToPromise(input) {
|
||||
if (input instanceof Promise) return input;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (input || input === undefined) {
|
||||
resolve();
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function comboEditorConfig(defaultConfig, customConfig) {
|
||||
const { ideConfig = {}, utils = {} } = this.props;
|
||||
const comboShortCuts = () => {
|
||||
const defaultShortCuts = defaultIdeConfig.shortCuts;
|
||||
const shortCuts = ideConfig.shortCuts || [];
|
||||
const configMap = skeletonUtils.transformArrayToMap(defaultShortCuts, 'keyboard');
|
||||
(shortCuts || []).forEach(item => {
|
||||
configMap[item.keyboard] = item;
|
||||
});
|
||||
return Object.keys(configMap).map(key => configMap[key]);
|
||||
};
|
||||
return {
|
||||
...ideConfig,
|
||||
utils: {
|
||||
...skeletonUtils,
|
||||
...utils
|
||||
},
|
||||
constants: {
|
||||
...defaultIdeConfig.constants,
|
||||
...ideConfig.constants
|
||||
},
|
||||
extensions: {
|
||||
...defaultIdeConfig.extensions,
|
||||
...ideConfig.extensions
|
||||
},
|
||||
shortCuts: comboShortCuts()
|
||||
};
|
||||
}
|
||||
@ -1,7 +1,16 @@
|
||||
const { eslint, deepmerge } = require('@ice/spec');
|
||||
const { tslint, deepmerge } = require('@ice/spec');
|
||||
|
||||
module.exports = deepmerge(eslint, {
|
||||
module.exports = deepmerge(tslint, {
|
||||
rules: {
|
||||
"global-require": 0,
|
||||
"comma-dangle": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"object-shorthand": 0,
|
||||
"jsx-a11y/anchor-has-content": 0,
|
||||
"react/sort-comp": 0,
|
||||
"react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx", ".tsx", "ts"] }],
|
||||
"@typescript-eslint/interface-name-prefix": 0,
|
||||
"@typescript-eslint/no-explicit-any": 0,
|
||||
"@typescript-eslint/explicit-member-accessibility": 0
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
---
|
||||
title: Simple Usage
|
||||
order: 1
|
||||
---
|
||||
|
||||
本 Demo 演示一行文字的用法。
|
||||
|
||||
````jsx
|
||||
import React, { Component } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
class App extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOM.render((
|
||||
<App />
|
||||
), mountNode);
|
||||
````
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@ali/lowcode-skeleton",
|
||||
"name": "@ali/lowcode-editor-skeleton",
|
||||
"version": "0.0.1",
|
||||
"description": "alibaba lowcode editor skeleton",
|
||||
"files": [
|
||||
@ -24,15 +24,13 @@
|
||||
],
|
||||
"author": "xiayang.xy",
|
||||
"dependencies": {
|
||||
"@ali/lowcode-editor-core": "0.0.1",
|
||||
"@alifd/next": "^1.x",
|
||||
"@icedesign/theme": "^1.x",
|
||||
"@types/react": "^16.8.3",
|
||||
"@types/react-dom": "^16.8.2",
|
||||
"moment": "^2.23.0",
|
||||
"prop-types": "^15.5.8",
|
||||
"react": "^16.4.1",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-router-dom": "^5.0.1"
|
||||
"react": "^16.8.1",
|
||||
"react-dom": "^16.8.1",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"store": "^2.0.12"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alib/build-scripts": "^0.1.3",
|
||||
|
||||
@ -1,59 +1,59 @@
|
||||
.luna-left-addon {
|
||||
font-size: 16px;
|
||||
.lowcode-left-plugin {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
line-height: 36px;
|
||||
height: 36px;
|
||||
line-height: 44px;
|
||||
height: 44px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: #777;
|
||||
&.collapse {
|
||||
height: 40px;
|
||||
color: #8c8c8c;
|
||||
border-bottom: 1px solid #bfbfbf;
|
||||
}
|
||||
color: $color-text1-3;
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: #fff !important;
|
||||
background-color: $color-brand1-9 !important;
|
||||
&.disabled {
|
||||
color: #fff;
|
||||
background-color: $color-fill1-7;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&:hover {
|
||||
background-color: $color-brand1-1;
|
||||
color: $color-brand1-6;
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
top: 5px;
|
||||
left: 45px;
|
||||
top: 8px;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
color: #fff;
|
||||
background: $balloon-normal-color-bg;
|
||||
border: 1px solid $balloon-normal-color-border;
|
||||
color: $color-text1-3;
|
||||
z-index: 100;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
transform: rotate(45deg);
|
||||
left: 40px;
|
||||
top: 15px;
|
||||
border: 5px solid transparent;
|
||||
border-right-color: rgba(0, 0, 0, 0.75);
|
||||
top: 18px;
|
||||
background: $balloon-normal-color-bg;
|
||||
border-left: 1px solid $balloon-normal-color-border;
|
||||
border-bottom: 1px solid $balloon-normal-color-border;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9;
|
||||
&.disabled {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-brand1-6;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,79 +1,74 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import AppContext from '@ali/iceluna-sdk/lib/context/appContext';
|
||||
import { Balloon, Dialog, Icon, Badge } from '@alife/next';
|
||||
|
||||
import { Balloon, Dialog, Icon, Badge } from '@alifd/next';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import {
|
||||
PluginConfig,
|
||||
PluginClass,
|
||||
} from '@ali/lowcode-editor-core/lib/definitions';
|
||||
import './index.scss';
|
||||
export default class LeftAddon extends PureComponent {
|
||||
static displayName = 'LunaLeftAddon';
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
config: PropTypes.shape({
|
||||
addonKey: PropTypes.string,
|
||||
addonProps: PropTypes.object,
|
||||
props: PropTypes.object,
|
||||
type: PropTypes.oneOf([
|
||||
'DialogIcon',
|
||||
'BalloonIcon',
|
||||
'PanelIcon',
|
||||
'LinkIcon',
|
||||
'Icon',
|
||||
'Custom',
|
||||
]),
|
||||
}),
|
||||
disabled: PropTypes.bool,
|
||||
dotted: PropTypes.bool,
|
||||
locked: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
export interface LeftPluginProps {
|
||||
active?: boolean;
|
||||
config: PluginConfig;
|
||||
disabled?: boolean;
|
||||
editor: Editor;
|
||||
locked?: boolean;
|
||||
marked?: boolean;
|
||||
onClick?: () => void;
|
||||
pluginClass: PluginClass | undefined;
|
||||
}
|
||||
|
||||
export interface LeftPluginState {
|
||||
dialogVisible: boolean;
|
||||
}
|
||||
|
||||
export default class LeftPlugin extends PureComponent<
|
||||
LeftPluginProps,
|
||||
LeftPluginState
|
||||
> {
|
||||
static displayName = 'LowcodeLeftPlugin';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
marked: false,
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
onClick: (): void => {},
|
||||
};
|
||||
static contextType = AppContext;
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
dialogVisible: false,
|
||||
};
|
||||
this.appHelper = context.appHelper;
|
||||
this.utils = this.appHelper.utils;
|
||||
this.constants = this.appHelper.constants;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { config } = this.props;
|
||||
const addonKey = config && config.addonKey;
|
||||
const appHelper = this.appHelper;
|
||||
if (appHelper && addonKey) {
|
||||
appHelper.on(`${addonKey}.dialog.show`, this.handleShow);
|
||||
appHelper.on(`${addonKey}.dialog.close`, this.handleClose);
|
||||
componentDidMount(): void {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (editor && pluginKey) {
|
||||
editor.on(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
editor.on(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { config } = this.props;
|
||||
const appHelper = this.appHelper;
|
||||
const addonKey = config && config.addonKey;
|
||||
if (appHelper && addonKey) {
|
||||
appHelper.off(`${addonKey}.dialog.show`, this.handleShow);
|
||||
appHelper.off(`${addonKey}.dialog.close`, this.handleClose);
|
||||
componentWillUnmount(): void {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (editor && pluginKey) {
|
||||
editor.off(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
editor.off(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
const addonKey = this.props.config && this.props.config.addonKey;
|
||||
const currentAddon =
|
||||
this.appHelper.addons && this.appHelper.addons[addonKey];
|
||||
if (currentAddon) {
|
||||
this.utils.transformToPromise(currentAddon.close()).then(() => {
|
||||
handleClose = (): void => {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
const plugin = editor.plugins && editor.plugins[pluginKey];
|
||||
if (plugin) {
|
||||
plugin.close().then((): void => {
|
||||
this.setState({
|
||||
dialogVisible: false,
|
||||
});
|
||||
@ -81,43 +76,48 @@ export default class LeftAddon extends PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
handleOpen = (): void => {
|
||||
// todo 对话框类型的插件初始时拿不到插件实例
|
||||
this.setState({
|
||||
dialogVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
handleShow = () => {
|
||||
const { disabled, config, onClick } = this.props;
|
||||
const addonKey = config && config.addonKey;
|
||||
if (disabled || !addonKey) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0);
|
||||
handleShow = (): void => {
|
||||
const { disabled, config, onClick, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (disabled || !pluginKey) return;
|
||||
this.handleOpen();
|
||||
onClick && onClick();
|
||||
// 考虑到弹窗情况,延时发送消息
|
||||
setTimeout((): void => {
|
||||
editor.emit(`${pluginKey}.plugin.activate`);
|
||||
}, 0);
|
||||
if (onClick) {
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
|
||||
renderIcon = clickCallback => {
|
||||
const { active, disabled, dotted, locked, onClick, config } = this.props;
|
||||
const { addonKey, props } = config || {};
|
||||
renderIcon = (clickCallback): React.ReactNode => {
|
||||
const { active, disabled, marked, locked, onClick, config } = this.props;
|
||||
const { pluginKey, props } = config || {};
|
||||
const { icon, title } = props || {};
|
||||
return (
|
||||
<div
|
||||
className={classNames('luna-left-addon', addonKey, {
|
||||
className={classNames('lowcode-left-plugin', pluginKey, {
|
||||
active,
|
||||
disabled,
|
||||
locked,
|
||||
})}
|
||||
data-tooltip={title}
|
||||
onClick={() => {
|
||||
onClick={(): void => {
|
||||
if (disabled) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
// 考虑到弹窗情况,延时发送消息
|
||||
clickCallback && clickCallback();
|
||||
|
||||
onClick && onClick();
|
||||
}}
|
||||
>
|
||||
{dotted ? (
|
||||
{marked ? (
|
||||
<Badge dot>
|
||||
<Icon type={icon} size="small" />
|
||||
</Badge>
|
||||
@ -128,67 +128,66 @@ export default class LeftAddon extends PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { dotted, locked, active, disabled, config } = this.props;
|
||||
const { addonKey, props, type, addonProps } = config || {};
|
||||
render(): React.ReactNode {
|
||||
const {
|
||||
marked,
|
||||
locked,
|
||||
active,
|
||||
disabled,
|
||||
config,
|
||||
editor,
|
||||
pluginClass: Comp,
|
||||
} = this.props;
|
||||
const { pluginKey, props, type, pluginProps } = config || {};
|
||||
const { onClick, title } = props || {};
|
||||
const { dialogVisible } = this.state;
|
||||
const { appHelper, components } = this.context;
|
||||
if (!addonKey || !type || !props) return null;
|
||||
const componentName = appHelper.utils.generateAddonCompName(addonKey);
|
||||
const localeProps = {};
|
||||
const { locale, messages } = appHelper;
|
||||
if (locale) {
|
||||
localeProps.locale = locale;
|
||||
}
|
||||
if (messages && messages[componentName]) {
|
||||
localeProps.messages = messages[componentName];
|
||||
}
|
||||
const AddonComp = components && components[componentName];
|
||||
const node =
|
||||
(AddonComp && (
|
||||
<AddonComp
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
}}
|
||||
{...localeProps}
|
||||
{...(addonProps || {})}
|
||||
/>
|
||||
)) ||
|
||||
null;
|
||||
if (!pluginKey || !type || !props) return null;
|
||||
|
||||
const node = Comp ? (
|
||||
<Comp
|
||||
editor={editor}
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={(): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
}}
|
||||
{...pluginProps}
|
||||
/>
|
||||
) : null;
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return (
|
||||
<a {...(props.linkProps || {})}>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
{this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
</a>
|
||||
);
|
||||
case 'Icon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
return this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
});
|
||||
case 'DialogIcon':
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
{this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
})}
|
||||
<Dialog
|
||||
onOk={() => {
|
||||
appHelper.emit(`${addonKey}.dialog.onOk`);
|
||||
onOk={(): void => {
|
||||
editor.emit(`${pluginKey}.dialog.onOk`);
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={this.handleClose}
|
||||
onClose={this.handleClose}
|
||||
title={title}
|
||||
style={{
|
||||
width: 500,
|
||||
...(props.dialogProps && props.dialogProps.style),
|
||||
}}
|
||||
{...(props.dialogProps || {})}
|
||||
visible={dialogVisible}
|
||||
>
|
||||
@ -199,8 +198,8 @@ export default class LeftAddon extends PureComponent {
|
||||
case 'BalloonIcon':
|
||||
return (
|
||||
<Balloon
|
||||
trigger={this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
trigger={this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
align="r"
|
||||
triggerType={['click', 'hover']}
|
||||
@ -210,12 +209,11 @@ export default class LeftAddon extends PureComponent {
|
||||
</Balloon>
|
||||
);
|
||||
case 'PanelIcon':
|
||||
return this.renderIcon(() => {
|
||||
onClick && onClick.call(null, appHelper);
|
||||
this.handleOpen();
|
||||
return this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
});
|
||||
case 'Custom':
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
return marked ? <Badge dot>{node}</Badge> : node;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -0,0 +1,60 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export interface PanelProps {
|
||||
align: 'left' | 'right';
|
||||
defaultWidth: number;
|
||||
minWidth: number;
|
||||
draggable: boolean;
|
||||
floatable: boolean;
|
||||
children: Plugin;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export interface PanelState {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export default class Panel extends PureComponent<PanelProps, PanelState> {
|
||||
static displayName = 'LowcodePanel';
|
||||
|
||||
static defaultProps = {
|
||||
align: 'left',
|
||||
defaultWidth: 240,
|
||||
minWidth: 100,
|
||||
draggable: true,
|
||||
floatable: false,
|
||||
visible: true,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
width: props.defaultWidth,
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { align, draggable, floatable, visible } = this.props;
|
||||
const { width } = this.state;
|
||||
return (
|
||||
<div
|
||||
className={classNames('lowcode-panel', align, {
|
||||
draggable,
|
||||
floatable,
|
||||
visible,
|
||||
})}
|
||||
style={{
|
||||
width,
|
||||
display: visible ? '' : 'none',
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
<div className="drag-area" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,22 +1,67 @@
|
||||
.next-btn.next-large.lowcode-top-btn {
|
||||
.lowcode-top-icon {
|
||||
display: inline-block;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
margin: 4px -2px;
|
||||
text-align: center;
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
color: #777;
|
||||
font-size: 20px;
|
||||
line-height: 48px;
|
||||
color: $color-text1-3;
|
||||
position: relative;
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
&:hover {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9;
|
||||
&:hover {
|
||||
color: $color-brand1-6;
|
||||
}
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-brand1-6;
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
height: auto;
|
||||
width: auto;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
bottom: -32px;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: $balloon-normal-color-bg;
|
||||
border: 1px solid $balloon-normal-color-border;
|
||||
color: $color-text1-3;
|
||||
z-index: 100;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0) rotate(45deg);
|
||||
bottom: -5px;
|
||||
background: $balloon-normal-color-bg;
|
||||
border-left: 1px solid $balloon-normal-color-border;
|
||||
border-top: 1px solid $balloon-normal-color-border;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
i.next-icon {
|
||||
&:before {
|
||||
font-size: 17px;
|
||||
font-size: 16px;
|
||||
}
|
||||
margin-right: 0;
|
||||
line-height: 18px;
|
||||
|
||||
@ -1,23 +1,25 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Icon, Button } from '@alifd/next';
|
||||
import { Icon } from '@alifd/next';
|
||||
|
||||
import './index.scss';
|
||||
export default class TopIcon extends PureComponent {
|
||||
static displayName = 'TopIcon';
|
||||
static propTypes = {
|
||||
active: PropTypes.bool,
|
||||
className: PropTypes.string,
|
||||
disabled: PropTypes.bool,
|
||||
icon: PropTypes.string,
|
||||
id: PropTypes.string,
|
||||
locked: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
showTitle: PropTypes.bool,
|
||||
style: PropTypes.object,
|
||||
title: PropTypes.string,
|
||||
};
|
||||
|
||||
export interface TopIconProps {
|
||||
active?: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
icon: string;
|
||||
id?: string;
|
||||
locked?: boolean;
|
||||
marked?: boolean;
|
||||
onClick?: () => void;
|
||||
style?: React.CSSProperties;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export default class TopIcon extends PureComponent<TopIconProps> {
|
||||
static displayName = 'LowcodeTopIcon';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
className: '',
|
||||
@ -25,13 +27,12 @@ export default class TopIcon extends PureComponent {
|
||||
icon: '',
|
||||
id: '',
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
showTitle: false,
|
||||
onClick: (): void => {},
|
||||
style: {},
|
||||
title: '',
|
||||
};
|
||||
|
||||
render() {
|
||||
render(): React.ReactNode {
|
||||
const {
|
||||
active,
|
||||
disabled,
|
||||
@ -41,28 +42,22 @@ export default class TopIcon extends PureComponent {
|
||||
className,
|
||||
id,
|
||||
style,
|
||||
showTitle,
|
||||
onClick,
|
||||
} = this.props;
|
||||
return (
|
||||
<Button
|
||||
type="normal"
|
||||
size="large"
|
||||
text={true}
|
||||
className={classNames('lowcode-top-btn', className, {
|
||||
<div
|
||||
className={classNames('lowcode-top-icon', className, {
|
||||
active,
|
||||
disabled,
|
||||
locked,
|
||||
})}
|
||||
data-tooltip={title}
|
||||
id={id}
|
||||
style={style}
|
||||
onClick={disabled ? null : onClick}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
>
|
||||
<div>
|
||||
<Icon size="large" type={icon} />
|
||||
{showTitle && <span>{title}</span>}
|
||||
</div>
|
||||
</Button>
|
||||
<Icon type={icon} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,43 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
|
||||
import PropTypes from 'prop-types';
|
||||
import TopIcon from '../TopIcon';
|
||||
import { Balloon, Badge, Dialog } from '@alifd/next';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import {
|
||||
PluginConfig,
|
||||
PluginClass,
|
||||
} from '@ali/lowcode-editor-core/lib/definitions';
|
||||
import TopIcon from '../TopIcon';
|
||||
|
||||
import './index.scss';
|
||||
export default class TopPlugin extends PureComponent {
|
||||
static displayName = 'lowcodeTopPlugin';
|
||||
|
||||
|
||||
export interface TopPluginProps {
|
||||
active?: boolean;
|
||||
config: PluginConfig;
|
||||
disabled?: boolean;
|
||||
editor: Editor;
|
||||
locked?: boolean;
|
||||
marked?: boolean;
|
||||
onClick?: () => void;
|
||||
pluginClass: PluginClass | undefined;
|
||||
}
|
||||
|
||||
export interface TopPluginState {
|
||||
dialogVisible: boolean;
|
||||
}
|
||||
|
||||
export default class TopPlugin extends PureComponent<
|
||||
TopPluginProps,
|
||||
TopPluginState
|
||||
> {
|
||||
static displayName = 'LowcodeTopPlugin';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
dotted: false,
|
||||
marked: false,
|
||||
locked: false,
|
||||
onClick: () => {},
|
||||
onClick: (): void => {},
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
@ -24,42 +47,42 @@ export default class TopPlugin extends PureComponent {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { config } = this.props;
|
||||
componentDidMount(): void {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.on(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.on(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
if (editor && pluginKey) {
|
||||
editor.on(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
editor.on(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// const { config } = this.props;
|
||||
// const addonKey = config && config.addonKey;
|
||||
// const appHelper = this.appHelper;
|
||||
// if (appHelper && addonKey) {
|
||||
// appHelper.off(`${addonKey}.dialog.show`, this.handleShow);
|
||||
// appHelper.off(`${addonKey}.dialog.close`, this.handleClose);
|
||||
// }
|
||||
componentWillUnmount(): void {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (editor && pluginKey) {
|
||||
editor.off(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
editor.off(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
handleShow = () => {
|
||||
const { disabled, config, onClick } = this.props;
|
||||
const addonKey = config && config.addonKey;
|
||||
if (disabled || !addonKey) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
setTimeout(() => this.appHelper.emit(`${addonKey}.addon.activate`), 0);
|
||||
handleShow = (): void => {
|
||||
const { disabled, config, onClick, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (disabled || !pluginKey) return;
|
||||
this.handleOpen();
|
||||
// 考虑到弹窗情况,延时发送消息
|
||||
setTimeout((): void => {
|
||||
editor.emit(`${pluginKey}.plugin.activate`);
|
||||
}, 0);
|
||||
onClick && onClick();
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
const addonKey = this.props.config && this.props.config.addonKey;
|
||||
const currentAddon =
|
||||
this.appHelper.addons && this.appHelper.addons[addonKey];
|
||||
if (currentAddon) {
|
||||
this.utils.transformToPromise(currentAddon.close()).then(() => {
|
||||
handleClose = (): void => {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
const plugin = editor.plugins && editor.plugins[pluginKey];
|
||||
if (plugin) {
|
||||
plugin.close().then((): void => {
|
||||
this.setState({
|
||||
dialogVisible: false,
|
||||
});
|
||||
@ -67,85 +90,107 @@ export default class TopPlugin extends PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
handleOpen = () => {
|
||||
handleOpen = (): void => {
|
||||
// todo dialog类型的插件初始时拿不动插件实例
|
||||
this.setState({
|
||||
dialogVisible: true,
|
||||
});
|
||||
};
|
||||
|
||||
renderIcon = clickCallback => {
|
||||
const { active, disabled, dotted, locked, config, onClick } = this.props;
|
||||
renderIcon = (clickCallback): React.ReactNode => {
|
||||
const {
|
||||
active,
|
||||
disabled,
|
||||
marked,
|
||||
locked,
|
||||
config,
|
||||
onClick,
|
||||
editor,
|
||||
} = this.props;
|
||||
const { pluginKey, props } = config || {};
|
||||
const { icon, title } = props || {};
|
||||
const node = (
|
||||
<TopIcon
|
||||
className={`lowcode-top-addon ${pluginKey}`}
|
||||
className={`lowcode-top-plugin ${pluginKey}`}
|
||||
active={active}
|
||||
disabled={disabled}
|
||||
locked={locked}
|
||||
icon={icon}
|
||||
title={title}
|
||||
onClick={() => {
|
||||
onClick={(): void => {
|
||||
if (disabled) return;
|
||||
//考虑到弹窗情况,延时发送消息
|
||||
setTimeout(
|
||||
() => this.appHelper.emit(`${pluginKey}.addon.activate`),
|
||||
0,
|
||||
);
|
||||
// 考虑到弹窗情况,延时发送消息
|
||||
setTimeout((): void => {
|
||||
editor.emit(`${pluginKey}.plugin.activate`);
|
||||
}, 0);
|
||||
clickCallback && clickCallback();
|
||||
onClick && onClick();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
return marked ? <Badge dot>{node}</Badge> : node;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { active, dotted, locked, disabled, config, editor, pluginClass: Comp } = this.props;
|
||||
render(): React.ReactNode {
|
||||
const {
|
||||
active,
|
||||
marked,
|
||||
locked,
|
||||
disabled,
|
||||
config,
|
||||
editor,
|
||||
pluginClass: Comp,
|
||||
} = this.props;
|
||||
const { pluginKey, pluginProps, props, type } = config || {};
|
||||
const { onClick, title } = props || {};
|
||||
const { dialogVisible } = this.state;
|
||||
if (!pluginKey || !type || !Comp) return null;
|
||||
const node = <Comp
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={() => {
|
||||
onClick && onClick.call(null, editor);
|
||||
}}
|
||||
{...pluginProps}
|
||||
/>;
|
||||
if (!pluginKey || !type) return null;
|
||||
const node = Comp ? (
|
||||
<Comp
|
||||
editor={editor}
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={(): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
}}
|
||||
{...pluginProps}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return (
|
||||
<a {...props.linkProps}>
|
||||
{this.renderIcon(() => {
|
||||
{this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
</a>
|
||||
);
|
||||
case 'Icon':
|
||||
return this.renderIcon(() => {
|
||||
return this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
});
|
||||
case 'DialogIcon':
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderIcon(() => {
|
||||
{this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
})}
|
||||
<Dialog
|
||||
onOk={() => {
|
||||
onOk={(): void => {
|
||||
editor.emit(`${pluginKey}.dialog.onOk`);
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={this.handleClose}
|
||||
onClose={this.handleClose}
|
||||
title={title}
|
||||
style={{
|
||||
width: 500,
|
||||
...(props.dialogProps && props.dialogProps.style),
|
||||
}}
|
||||
{...props.dialogProps}
|
||||
visible={dialogVisible}
|
||||
>
|
||||
@ -156,7 +201,7 @@ export default class TopPlugin extends PureComponent {
|
||||
case 'BalloonIcon':
|
||||
return (
|
||||
<Balloon
|
||||
trigger={this.renderIcon(() => {
|
||||
trigger={this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
triggerType={['click', 'hover']}
|
||||
@ -166,7 +211,7 @@ export default class TopPlugin extends PureComponent {
|
||||
</Balloon>
|
||||
);
|
||||
case 'Custom':
|
||||
return dotted ? <Badge dot>{node}</Badge> : node;
|
||||
return marked ? <Badge dot>{node}</Badge> : node;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -1,21 +1 @@
|
||||
import Dashboard from '@/pages/Dashboard';
|
||||
import BasicLayout from '@/layouts/BasicLayout';
|
||||
|
||||
const routerConfig = [
|
||||
{
|
||||
path: '/',
|
||||
component: BasicLayout,
|
||||
children: [
|
||||
{
|
||||
path: '/dashboard',
|
||||
component: Dashboard,
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/dashboard',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default routerConfig;
|
||||
export default {};
|
||||
|
||||
@ -1,5 +1 @@
|
||||
// 菜单配置
|
||||
|
||||
const asideMenuConfig = [];
|
||||
|
||||
export { asideMenuConfig };
|
||||
export default {};
|
||||
|
||||
@ -7,7 +7,9 @@ body {
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
color: $color-text1-3;
|
||||
}
|
||||
|
||||
.next-loading {
|
||||
.next-loading-wrap {
|
||||
height: 100%;
|
||||
@ -16,18 +18,17 @@ body {
|
||||
.lowcode-editor {
|
||||
.lowcode-main-content {
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
background-color: #d8d8d8;
|
||||
background-color: rgba(31, 56, 88, 0.06);
|
||||
}
|
||||
.lowcode-center-area {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
7
packages/editor-skeleton/src/index.ts
Normal file
7
packages/editor-skeleton/src/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import Skeleton from './skeleton';
|
||||
import Panel from './components/Panel';
|
||||
import TopIcon from './components/TopIcon';
|
||||
|
||||
export default Skeleton;
|
||||
|
||||
export { Panel, TopIcon };
|
||||
@ -1,60 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// import Editor from '@ali/lowcode-engine-editor';
|
||||
import { Loading, ConfigProvider } from '@alifd/next';
|
||||
import defaultConfig from './config/skeleton';
|
||||
|
||||
import TopArea from './layouts/TopArea';
|
||||
import LeftArea from './layouts/LeftArea';
|
||||
import CenterArea from './layouts/CenterArea';
|
||||
import RightArea from './layouts/RightArea';
|
||||
|
||||
import './global.scss';
|
||||
|
||||
export default class Skeleton extends PureComponent {
|
||||
static displayName = 'lowcodeEditorSkeleton';
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// this.editor = new Editor(props.config, props.utils);
|
||||
this.editor = {
|
||||
on: () => {},
|
||||
off: () => {},
|
||||
config: props.config,
|
||||
pluginComponents: props.pluginComponents
|
||||
};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// this.editor && this.editor.destroy();
|
||||
// this.editor = null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { location, history, messages } = this.props;
|
||||
this.editor.location = location;
|
||||
this.editor.history = history;
|
||||
this.editor.messages = messages;
|
||||
return (
|
||||
<ConfigProvider>
|
||||
<Loading
|
||||
tip="Loading"
|
||||
size="large"
|
||||
visible={false}
|
||||
shape="fusion-reactor"
|
||||
fullScreen
|
||||
>
|
||||
<div className="lowcode-editor">
|
||||
<TopArea editor={this.editor}/>
|
||||
<div className="lowcode-main-content">
|
||||
<LeftArea.Nav editor={this.editor}/>
|
||||
<LeftArea.Panel editor={this.editor}/>
|
||||
<CenterArea editor={this.editor}/>
|
||||
<RightArea editor={this.editor}/>
|
||||
</div>
|
||||
</div>
|
||||
</Loading>
|
||||
</ConfigProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
.lowcode-center-area {
|
||||
padding: 12px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@ -1,15 +1,60 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import Editor, { AreaManager } from '@ali/lowcode-editor-core';
|
||||
import './index.scss';
|
||||
|
||||
export default class CenterArea extends PureComponent {
|
||||
static displayName = 'lowcodeCenterArea';
|
||||
export interface CenterAreaProps {
|
||||
editor: Editor;
|
||||
}
|
||||
|
||||
export default class CenterArea extends PureComponent<CenterAreaProps> {
|
||||
static displayName = 'LowcodeCenterArea';
|
||||
|
||||
private editor: Editor;
|
||||
|
||||
private areaManager: AreaManager;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.areaManager = new AreaManager(this.editor, 'centerArea');
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="lowcode-center-area"></div>;
|
||||
componentDidMount(): void {
|
||||
this.editor.on('skeleton.update', this.handleSkeletonUpdate);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.editor.off('skeleton.update', this.handleSkeletonUpdate);
|
||||
}
|
||||
|
||||
handleSkeletonUpdate = (): void => {
|
||||
// 当前区域插件状态改变是更新区域
|
||||
if (this.areaManager.isPluginStatusUpdate()) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const visiblePluginList = this.areaManager.getVisiblePluginList();
|
||||
return (
|
||||
<div className="lowcode-center-area">
|
||||
{visiblePluginList.map(
|
||||
(item): React.ReactNode => {
|
||||
const Comp = this.areaManager.getPluginClass(item.pluginKey);
|
||||
if (Comp) {
|
||||
return (
|
||||
<Comp
|
||||
key={item.pluginKey}
|
||||
editor={this.editor}
|
||||
config={item}
|
||||
{...item.pluginProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,21 +1,23 @@
|
||||
.lowcode-left-area-nav {
|
||||
width: 48px;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-right: 1px solid #e8ebee;
|
||||
background-color: $card-background;
|
||||
border-right: 2px solid $color-line1-1;
|
||||
position: relative;
|
||||
.top-area {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
padding: 12px 0;
|
||||
background-color: $card-background;
|
||||
max-height: 100%;
|
||||
}
|
||||
.bottom-area {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
padding: 12px 0;
|
||||
background-color: $card-background;
|
||||
max-height: calc(100% - 20px);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,148 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import LeftPlugin from '../../components/LeftPlugin';
|
||||
import Editor, { utils, AreaManager } from '@ali/lowcode-editor-core';
|
||||
import { PluginConfig } from '@ali/lowcode-editor-core/lib/definitions';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export default class LeftAreaPanel extends PureComponent {
|
||||
static displayName = 'lowcodeLeftAreaNav';
|
||||
const { isEmpty } = utils;
|
||||
|
||||
export interface LeftAreaNavProps {
|
||||
editor: Editor;
|
||||
}
|
||||
|
||||
export interface LeftAreaNavState {
|
||||
activeKey: string;
|
||||
}
|
||||
|
||||
export default class LeftAreaNav extends PureComponent<
|
||||
LeftAreaNavProps,
|
||||
LeftAreaNavState
|
||||
> {
|
||||
static displayName = 'LowcodeLeftAreaNav';
|
||||
|
||||
private editor: Editor;
|
||||
|
||||
private areaManager: AreaManager;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.areaManager = new AreaManager(this.editor, 'leftArea');
|
||||
|
||||
this.state = {
|
||||
activeKey: 'none',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="lowcode-left-area-nav" />;
|
||||
componentDidMount(): void {
|
||||
this.editor.on('skeleton.update', this.handleSkeletonUpdate);
|
||||
this.editor.on('leftNav.change', this.handlePluginChange);
|
||||
const visiblePanelPluginList = this.areaManager.getVisiblePluginList(
|
||||
'IconPanel',
|
||||
);
|
||||
const defaultKey =
|
||||
(visiblePanelPluginList[0] && visiblePanelPluginList[0].pluginKey) ||
|
||||
'componentAttr';
|
||||
this.handlePluginChange(defaultKey);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.editor.off('skeleton.update', this.handleSkeletonUpdate);
|
||||
this.editor.off('leftNav.change', this.handlePluginChange);
|
||||
}
|
||||
|
||||
handleSkeletonUpdate = (): void => {
|
||||
// 当前区域插件状态改变是更新区域
|
||||
if (this.areaManager.isPluginStatusUpdate()) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
handlePluginChange = (key: string): void => {
|
||||
const { activeKey } = this.state;
|
||||
const plugins = this.editor.plugins;
|
||||
const prePlugin = plugins[activeKey];
|
||||
const nextPlugin = plugins[key];
|
||||
if (activeKey === 'none') {
|
||||
if (nextPlugin) {
|
||||
nextPlugin.open().then((): void => {
|
||||
this.updateActiveKey(key);
|
||||
});
|
||||
}
|
||||
} else if (activeKey === key) {
|
||||
if (prePlugin) {
|
||||
prePlugin.close().then((): void => {
|
||||
this.updateActiveKey('none');
|
||||
});
|
||||
}
|
||||
} else if (prePlugin) {
|
||||
// 先关后开
|
||||
prePlugin.close().then((): void => {
|
||||
if (nextPlugin) {
|
||||
nextPlugin.open().then((): void => {
|
||||
this.updateActiveKey(key);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handlePluginClick = (item: PluginConfig): void => {
|
||||
if (item.type === 'PanelIcon') {
|
||||
this.handlePluginChange(item.pluginKey);
|
||||
}
|
||||
};
|
||||
|
||||
updateActiveKey = (key: string): void => {
|
||||
this.editor.set('leftNav', key);
|
||||
this.setState({ activeKey: key });
|
||||
this.editor.emit('leftPanel.show', key);
|
||||
};
|
||||
|
||||
renderPluginList = (list: PluginConfig[] = []): React.ReactElement[] => {
|
||||
const { activeKey } = this.state;
|
||||
return list.map(
|
||||
(item): React.ReactElement => {
|
||||
const pluginStatus = this.areaManager.getPluginStatus(item.pluginKey);
|
||||
const pluginClass = this.areaManager.getPluginClass(item.pluginKey);
|
||||
return (
|
||||
<LeftPlugin
|
||||
key={item.pluginKey}
|
||||
config={item}
|
||||
editor={this.editor}
|
||||
pluginClass={pluginClass}
|
||||
onClick={(): void => this.handlePluginClick(item)}
|
||||
active={activeKey === item.pluginKey}
|
||||
{...pluginStatus}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const topList: PluginConfig[] = [];
|
||||
const bottomList: PluginConfig[] = [];
|
||||
const visiblePluginList = this.areaManager.getVisiblePluginList();
|
||||
if (isEmpty(visiblePluginList)) {
|
||||
return null;
|
||||
}
|
||||
visiblePluginList.forEach((item): void => {
|
||||
const align =
|
||||
item.props && item.props.align === 'bottom' ? 'bottom' : 'top';
|
||||
if (align === 'bottom') {
|
||||
bottomList.push(item);
|
||||
} else {
|
||||
topList.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="lowcode-left-area-nav">
|
||||
<div className="bottom-area">{this.renderPluginList(bottomList)}</div>
|
||||
<div className="top-area">{this.renderPluginList(topList)}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,88 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import Editor, { AreaManager } from '@ali/lowcode-editor-core';
|
||||
import Panel from '../../components/Panel';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export default class LeftAreaPanel extends PureComponent {
|
||||
static displayName = 'lowcodeLeftAreaPanel';
|
||||
export interface LeftAreaPanelProps {
|
||||
editor: Editor;
|
||||
}
|
||||
|
||||
export interface LeftAreaPanelState {
|
||||
activeKey: string;
|
||||
}
|
||||
|
||||
export default class LeftAreaPanel extends PureComponent<
|
||||
LeftAreaPanelProps,
|
||||
LeftAreaPanelState
|
||||
> {
|
||||
static displayName = 'LowcodeLeftAreaPanel';
|
||||
|
||||
private editor: Editor;
|
||||
|
||||
private areaManager: AreaManager;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.areaManager = new AreaManager(this.editor, 'leftArea');
|
||||
|
||||
this.state = {
|
||||
activeKey: 'none',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="lowcode-left-area-panel" />;
|
||||
componentDidMount(): void {
|
||||
this.editor.on('skeleton.update', this.handleSkeletonUpdate);
|
||||
this.editor.on('leftPanel.show', this.handlePluginChange);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.editor.off('skeleton.update', this.handleSkeletonUpdate);
|
||||
this.editor.off('leftPanel.show', this.handlePluginChange);
|
||||
}
|
||||
|
||||
handleSkeletonUpdate = (): void => {
|
||||
// 当前区域插件状态改变是更新区域
|
||||
if (this.areaManager.isPluginStatusUpdate('PanelIcon')) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
handlePluginChange = (key: string): void => {
|
||||
this.setState({
|
||||
activeKey: key,
|
||||
});
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { activeKey } = this.state;
|
||||
const list = this.areaManager.getVisiblePluginList('PanelIcon');
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{list.map(
|
||||
(item): React.ReactNode => {
|
||||
const Comp = this.areaManager.getPluginClass(item.pluginKey);
|
||||
if (Comp) {
|
||||
return (
|
||||
<Panel
|
||||
key={item.pluginKey}
|
||||
visible={item.pluginKey === activeKey}
|
||||
{...(item.props && item.props.panelProps)}
|
||||
>
|
||||
<Comp
|
||||
editor={this.editor}
|
||||
config={item}
|
||||
{...item.pluginProps}
|
||||
/>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,155 +1,37 @@
|
||||
.lowcode-right-area {
|
||||
width: 300px;
|
||||
width: 262px;
|
||||
height: 100%;
|
||||
background-color: #ffffff;
|
||||
border-left: 1px solid #e8ebee;
|
||||
.right-plugin-title {
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9 !important;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
background-color: $card-background;
|
||||
border-left: 2px solid $color-line1-1;
|
||||
|
||||
.right-panel {
|
||||
overflow: auto;
|
||||
// border-top: 2px solid $color-line1-1;
|
||||
}
|
||||
|
||||
//tab定义
|
||||
.next-tabs-wrapped.right-tabs {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-top: -1px;
|
||||
.next-tabs-bar {
|
||||
z-index: 1;
|
||||
}
|
||||
.right-tabs.next-tabs {
|
||||
.next-tabs-nav {
|
||||
display: block;
|
||||
.next-tabs-tab {
|
||||
&:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
.next-tabs-tab-inner {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
.right-plugin-title {
|
||||
text-align: center;
|
||||
border-right: none !important;
|
||||
margin-right: 0 !important;
|
||||
width: 25%;
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&.active {
|
||||
background: none;
|
||||
border-bottom-color: #f7f7f7 !important;
|
||||
color: $color-brand1-9 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.next-tabs-content {
|
||||
flex: 1;
|
||||
.next-tabs-tabpane.active {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
//组件
|
||||
.select-comp {
|
||||
padding: 10px 16px;
|
||||
line-height: 16px;
|
||||
color: #989a9c;
|
||||
& > span {
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 400;
|
||||
}
|
||||
& > .btn-wrap,
|
||||
& > .next-btn {
|
||||
width: auto;
|
||||
margin: 0 5px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
.unselected {
|
||||
padding: 60px 0;
|
||||
text-align: center;
|
||||
}
|
||||
//右侧属性面板样式调整;
|
||||
.offset-56 {
|
||||
padding-left: 56px;
|
||||
margin-bottom: 16px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.fixedSpan.next-form-item {
|
||||
& > .next-form-item-label {
|
||||
width: 56px;
|
||||
flex: none;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
}
|
||||
}
|
||||
.fixedSpan.next-form-item,
|
||||
.offset-56 .next-form-item {
|
||||
display: flex;
|
||||
& > .next-form-item-control {
|
||||
width: auto;
|
||||
flex: 1;
|
||||
max-width: none;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.topSpan.next-form-item {
|
||||
margin-bottom: 4px;
|
||||
& > .next-form-item-control {
|
||||
padding-right: 24px;
|
||||
.next-input,
|
||||
.next-select,
|
||||
.next-radio-group,
|
||||
.next-number-picker,
|
||||
.luna-reactnode-btn,
|
||||
.luna-monaco-button button,
|
||||
.luna-object-button button {
|
||||
width: 100%;
|
||||
}
|
||||
.next-number-picker {
|
||||
width: 100%;
|
||||
.next-after {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
.next-radio-group {
|
||||
display: flex;
|
||||
label {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
.next-icon {
|
||||
line-height: 15px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,214 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Tab, Badge, Icon } from '@alifd/next';
|
||||
import classNames from 'classnames';
|
||||
import Editor, { AreaManager, utils } from '@ali/lowcode-editor-core';
|
||||
import { PluginConfig } from '@ali/lowcode-editor-core/lib/definitions';
|
||||
import './index.scss';
|
||||
|
||||
export default class RightArea extends PureComponent {
|
||||
static displayName = 'lowcodeRightArea';
|
||||
const { isEmpty } = utils;
|
||||
|
||||
export interface RightAreaProps {
|
||||
editor: Editor;
|
||||
}
|
||||
|
||||
export interface RightAreaState {
|
||||
activeKey: string;
|
||||
}
|
||||
|
||||
export default class RightArea extends PureComponent<
|
||||
RightAreaProps,
|
||||
RightAreaState
|
||||
> {
|
||||
static displayName = 'LowcodeRightArea';
|
||||
|
||||
private editor: Editor;
|
||||
|
||||
private areaManager: AreaManager;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.areaManager = new AreaManager(this.editor, 'rightArea');
|
||||
this.state = {
|
||||
activeKey: '',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className="lowcode-right-area" />;
|
||||
componentDidMount(): void {
|
||||
this.editor.on('skeleton.update', this.handleSkeletonUpdate);
|
||||
this.editor.on('rightNav.change', this.handlePluginChange);
|
||||
const visiblePluginList = this.areaManager.getVisiblePluginList('TabPanel');
|
||||
const defaultKey =
|
||||
(visiblePluginList[0] && visiblePluginList[0].pluginKey) ||
|
||||
'componentAttr';
|
||||
this.handlePluginChange(defaultKey, true);
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
this.editor.off('skeleton.update', this.handleSkeletonUpdate);
|
||||
this.editor.off('rightNav.change', this.handlePluginChange);
|
||||
}
|
||||
|
||||
handleSkeletonUpdate = (): void => {
|
||||
// 当前区域插件状态改变是更新区域
|
||||
if (this.areaManager.isPluginStatusUpdate()) {
|
||||
const activeKey = this.state.activeKey;
|
||||
const activePluginStatus = this.areaManager.getPluginStatus(activeKey);
|
||||
if (activePluginStatus && activePluginStatus.visible) {
|
||||
this.forceUpdate();
|
||||
} else {
|
||||
const currentPlugin = this.areaManager.getPlugin(activeKey);
|
||||
if (currentPlugin) {
|
||||
currentPlugin.close().then((): void => {
|
||||
this.setState(
|
||||
{
|
||||
activeKey: '',
|
||||
},
|
||||
(): void => {
|
||||
const visiblePluginList = this.areaManager.getVisiblePluginList(
|
||||
'TabPanel',
|
||||
);
|
||||
const firstPlugin = visiblePluginList && visiblePluginList[0];
|
||||
if (firstPlugin) {
|
||||
this.handlePluginChange(firstPlugin.pluginKey);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handlePluginChange = (key: string, isinit?: boolean): void => {
|
||||
const activeKey = this.state.activeKey;
|
||||
const currentPlugin = this.areaManager.getPlugin(activeKey);
|
||||
const nextPlugin = this.areaManager.getPlugin(key);
|
||||
const openPlugin = (): void => {
|
||||
if (!nextPlugin) {
|
||||
console.error(`plugin ${key} has not regist in the editor`);
|
||||
return;
|
||||
}
|
||||
nextPlugin.open().then((): void => {
|
||||
this.editor.set('rightNav', key);
|
||||
this.setState({
|
||||
activeKey: key,
|
||||
});
|
||||
});
|
||||
};
|
||||
if (key === activeKey && !isinit) return;
|
||||
if (currentPlugin) {
|
||||
currentPlugin.close().then((): void => {
|
||||
openPlugin();
|
||||
});
|
||||
} else {
|
||||
openPlugin();
|
||||
}
|
||||
};
|
||||
|
||||
renderTabTitle = (config: PluginConfig): React.ReactElement => {
|
||||
const { icon, title } = config.props || {};
|
||||
const pluginStatus = this.editor.pluginStatus[config.pluginKey];
|
||||
const { marked, disabled, locked } = pluginStatus;
|
||||
const active = this.state.activeKey === config.pluginKey;
|
||||
|
||||
const renderTitle = (): React.ReactElement => (
|
||||
<div
|
||||
className={classNames('right-plugin-title', {
|
||||
active,
|
||||
locked,
|
||||
disabled,
|
||||
})}
|
||||
>
|
||||
{!!icon && <Icon size="xs" type={icon} />}
|
||||
{title}
|
||||
</div>
|
||||
);
|
||||
if (marked) {
|
||||
return <Badge dot>{renderTitle()}</Badge>;
|
||||
}
|
||||
return renderTitle();
|
||||
};
|
||||
|
||||
renderTabPanels = (list: PluginConfig[], height: string): React.ReactNode => {
|
||||
if (isEmpty(list)) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Tab
|
||||
className="right-tabs"
|
||||
style={{
|
||||
height,
|
||||
}}
|
||||
activeKey={this.state.activeKey}
|
||||
lazyLoad={false}
|
||||
onChange={this.handlePluginChange}
|
||||
>
|
||||
{list.map(
|
||||
(item): React.ReactNode => {
|
||||
const Comp = this.areaManager.getPluginClass(item.pluginKey);
|
||||
if (Comp) {
|
||||
return (
|
||||
<Tab.Item
|
||||
key={item.pluginKey}
|
||||
title={this.renderTabTitle(item)}
|
||||
disabled={this.editor.pluginStatus[item.pluginKey].disabled}
|
||||
style={{
|
||||
width: `${100 / list.length}%`,
|
||||
}}
|
||||
>
|
||||
<Comp
|
||||
editor={this.editor}
|
||||
config={item}
|
||||
{...item.pluginProps}
|
||||
/>
|
||||
</Tab.Item>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
)}
|
||||
</Tab>
|
||||
);
|
||||
};
|
||||
|
||||
renderPanels = (list: PluginConfig[], height: string): React.ReactNode => {
|
||||
return list.map(
|
||||
(item): React.ReactNode => {
|
||||
const Comp = this.areaManager.getPluginClass(item.pluginKey);
|
||||
if (Comp) {
|
||||
return (
|
||||
<div
|
||||
className="right-panel"
|
||||
style={{ height }}
|
||||
key={item.pluginKey}
|
||||
>
|
||||
<Comp editor={this.editor} config={item} {...item.pluginProps} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const tabList = this.areaManager.getVisiblePluginList('TabPanel');
|
||||
const panelList = this.areaManager.getVisiblePluginList('Panel');
|
||||
if (isEmpty(panelList) && isEmpty(tabList)) {
|
||||
return null;
|
||||
} else if (tabList.length === 1) {
|
||||
panelList.unshift(tabList[0]);
|
||||
tabList.splice(0, 1);
|
||||
}
|
||||
const height = `${Math.floor(
|
||||
100 / (panelList.length + (tabList.length > 0 ? 1 : 0)),
|
||||
)}%`;
|
||||
return (
|
||||
<div className="lowcode-right-area">
|
||||
{this.renderTabPanels(tabList, height)}
|
||||
{this.renderPanels(panelList, height)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,30 @@
|
||||
.lowcode-top-area {
|
||||
height: 48px;
|
||||
background-color: #ffffff;
|
||||
border-bottom: 1px solid #e8ebee;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
background-color: $card-background;
|
||||
border-bottom: 2px solid $color-line1-1;
|
||||
user-select: none;
|
||||
.divider {
|
||||
max-width: 0;
|
||||
margin: 12px 16px;
|
||||
height: 24px;
|
||||
border-right: 1px solid $color-line1-2;
|
||||
}
|
||||
.next-col {
|
||||
text-align: center;
|
||||
}
|
||||
.left-area {
|
||||
padding: 0 16px;
|
||||
}
|
||||
.right-area {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
padding: 0 16px;
|
||||
height: 100%;
|
||||
background: $card-background;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,61 +1,81 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { Grid } from '@alifd/next';
|
||||
import Editor, { AreaManager } from '@ali/lowcode-editor-core';
|
||||
import { PluginConfig } from '@ali/lowcode-editor-core/lib/definitions';
|
||||
import TopPlugin from '../../components/TopPlugin';
|
||||
import './index.scss';
|
||||
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
export default class TopArea extends PureComponent {
|
||||
static displayName = 'lowcodeTopArea';
|
||||
export interface TopAreaProps {
|
||||
editor: Editor;
|
||||
}
|
||||
|
||||
export default class TopArea extends PureComponent<TopAreaProps> {
|
||||
static displayName = 'LowcodeTopArea';
|
||||
|
||||
private areaManager: AreaManager;
|
||||
|
||||
private editor: Editor;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.editor = props.editor;
|
||||
this.config = this.editor.config.plugins && this.editor.config.plugins.topArea;
|
||||
this.areaManager = new AreaManager(props.editor, 'topArea');
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
}
|
||||
componentWillUnmount() {
|
||||
componentDidMount(): void {
|
||||
this.editor.on('skeleton.update', this.handleSkeletonUpdate);
|
||||
}
|
||||
|
||||
handlePluginStatusChange = () => {};
|
||||
componentWillUnmount(): void {
|
||||
this.editor.off('skeleton.update', this.handleSkeletonUpdate);
|
||||
}
|
||||
|
||||
renderPluginList = (list = []) => {
|
||||
return list.map((item, idx) => {
|
||||
const isDivider = item.type === 'Divider';
|
||||
|
||||
return (
|
||||
<Col
|
||||
className={isDivider ? 'divider' : ''}
|
||||
key={isDivider ? idx : item.pluginKey}
|
||||
style={{
|
||||
width: (item.props && item.props.width) || 40,
|
||||
flex: 'none',
|
||||
}}
|
||||
>
|
||||
{!isDivider && (
|
||||
<TopPlugin
|
||||
config={item}
|
||||
pluginClass={this.editor.pluginComponents[item.pluginKey]}
|
||||
status={this.editor.pluginStatus[item.pluginKey]}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
});
|
||||
handleSkeletonUpdate = (): void => {
|
||||
// 当前区域插件状态改变是更新区域
|
||||
if (this.areaManager.isPluginStatusUpdate()) {
|
||||
this.forceUpdate();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.config) return null;
|
||||
const leftList = [];
|
||||
const rightList = [];
|
||||
this.config.forEach(item => {
|
||||
renderPluginList = (list: PluginConfig[] = []): React.ReactElement[] => {
|
||||
return list.map(
|
||||
(item, idx): React.ReactElement => {
|
||||
const isDivider = item.type === 'Divider';
|
||||
const PluginClass = this.areaManager.getPluginClass(item.pluginKey);
|
||||
return (
|
||||
<Col
|
||||
className={isDivider ? 'divider' : ''}
|
||||
key={isDivider ? idx : item.pluginKey}
|
||||
style={{
|
||||
width: (item.props && item.props.width) || 36,
|
||||
flex: 'none',
|
||||
}}
|
||||
>
|
||||
{!isDivider && (
|
||||
<TopPlugin
|
||||
config={item}
|
||||
pluginClass={PluginClass}
|
||||
editor={this.editor}
|
||||
/>
|
||||
)}
|
||||
</Col>
|
||||
);
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const leftList: PluginConfig[] = [];
|
||||
const rightList: PluginConfig[] = [];
|
||||
const visiblePluginList = this.areaManager.getVisiblePluginList();
|
||||
visiblePluginList.forEach((item): void => {
|
||||
const align =
|
||||
item.props && item.props.align === 'right' ? 'right' : 'left';
|
||||
// 分隔符不允许相邻
|
||||
if (item.type === 'Divider') {
|
||||
const currentList = align === 'right' ? rightList : leftList;
|
||||
const currList = align === 'right' ? rightList : leftList;
|
||||
if (
|
||||
currList.length === 0 ||
|
||||
currList[currList.length - 1].type === 'Divider'
|
||||
@ -68,11 +88,14 @@ export default class TopArea extends PureComponent {
|
||||
leftList.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="lowcode-top-area">
|
||||
<div className="left-area">{this.renderPluginList(leftList)}</div>
|
||||
<div classname="right-area">{this.renderPluginList(rightList)}</div>
|
||||
<div className="left-area">
|
||||
<Row>{this.renderPluginList(leftList)}</Row>
|
||||
</div>
|
||||
<div className="right-area">
|
||||
<Row justify="end">{this.renderPluginList(rightList)}</Row>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,10 +1 @@
|
||||
export default {
|
||||
loading: 'loading...',
|
||||
rejectRedirect: 'Redirect is not allowed',
|
||||
expand: 'Unfold',
|
||||
fold: 'Fold',
|
||||
pageNotExist: 'The current Page not exist',
|
||||
enterFromAppCenter: 'Please enter from the app center',
|
||||
noPermission: 'Sorry, you do not have the develop permission',
|
||||
getPermission: 'Please connect the app owners {owners} to get the permission',
|
||||
};
|
||||
export default {};
|
||||
|
||||
@ -1,10 +1 @@
|
||||
export default {
|
||||
loading: '加载中...',
|
||||
rejectRedirect: '开发中,已阻止发生跳转',
|
||||
expand: '展开',
|
||||
fold: '收起',
|
||||
pageNotExist: '当前访问地址不存在',
|
||||
enterFromAppCenter: '请从应用中心入口重新进入',
|
||||
noPermission: '抱歉,您暂无开发权限',
|
||||
getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限',
|
||||
};
|
||||
export default {};
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { Loading, ConfigProvider } from '@alifd/next';
|
||||
|
||||
import Editor from '../framework/editor';
|
||||
import { EditorConfig, Utils, PluginClassSet } from '../framework/definitions';
|
||||
import { comboEditorConfig, parseSearch } from '../framework/utils';
|
||||
|
||||
import { HashRouter as Router, Route } from 'react-router-dom';
|
||||
import Editor, { utils } from '@ali/lowcode-editor-core';
|
||||
import {
|
||||
EditorConfig,
|
||||
Utils,
|
||||
PluginClassSet,
|
||||
} from '@ali/lowcode-editor-core/lib/definitions';
|
||||
import defaultConfig from './config/skeleton';
|
||||
import skeletonUtils from './config/utils';
|
||||
|
||||
@ -13,9 +15,10 @@ import TopArea from './layouts/TopArea';
|
||||
import LeftArea from './layouts/LeftArea';
|
||||
import CenterArea from './layouts/CenterArea';
|
||||
import RightArea from './layouts/RightArea';
|
||||
|
||||
import './global.scss';
|
||||
|
||||
const { comboEditorConfig, parseSearch } = utils;
|
||||
|
||||
let renderIdx = 0;
|
||||
|
||||
declare global {
|
||||
@ -42,12 +45,12 @@ export interface SkeletonState {
|
||||
__hasError?: boolean;
|
||||
}
|
||||
|
||||
export default class Skeleton extends PureComponent<SkeletonProps, SkeletonState> {
|
||||
export class Skeleton extends PureComponent<SkeletonProps, SkeletonState> {
|
||||
static displayName = 'LowcodeEditorSkeleton';
|
||||
|
||||
static getDerivedStateFromError(): SkeletonState {
|
||||
return {
|
||||
__hasError: true
|
||||
__hasError: true,
|
||||
};
|
||||
}
|
||||
|
||||
@ -58,7 +61,7 @@ export default class Skeleton extends PureComponent<SkeletonProps, SkeletonState
|
||||
|
||||
this.state = {
|
||||
initReady: false,
|
||||
skeletonKey: `skeleton${renderIdx}`
|
||||
skeletonKey: `skeleton${renderIdx}`,
|
||||
};
|
||||
|
||||
this.init();
|
||||
@ -77,19 +80,23 @@ export default class Skeleton extends PureComponent<SkeletonProps, SkeletonState
|
||||
this.editor.destroy();
|
||||
}
|
||||
const { utils, config, components } = this.props;
|
||||
const editor = new Editor(comboEditorConfig(defaultConfig, config), components, {
|
||||
...skeletonUtils,
|
||||
...utils
|
||||
});
|
||||
const editor = new Editor(
|
||||
comboEditorConfig(defaultConfig, config),
|
||||
components,
|
||||
{
|
||||
...skeletonUtils,
|
||||
...utils,
|
||||
},
|
||||
);
|
||||
this.editor = editor;
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
window.__ctx = {
|
||||
editor,
|
||||
appHelper: editor
|
||||
appHelper: editor,
|
||||
};
|
||||
editor.once('editor.reset', (): void => {
|
||||
this.setState({
|
||||
initReady: false
|
||||
initReady: false,
|
||||
});
|
||||
editor.emit('editor.beforeReset');
|
||||
this.init(true);
|
||||
@ -100,13 +107,14 @@ export default class Skeleton extends PureComponent<SkeletonProps, SkeletonState
|
||||
{
|
||||
initReady: true,
|
||||
// 刷新IDE时生成新的skeletonKey保证插件生命周期重新执行
|
||||
skeletonKey: isReset ? `skeleton${++renderIdx}` : this.state.skeletonKey
|
||||
skeletonKey: isReset
|
||||
? `skeleton${++renderIdx}`
|
||||
: this.state.skeletonKey,
|
||||
},
|
||||
(): void => {
|
||||
editor.emit('editor.ready');
|
||||
editor.emit('ide.ready');
|
||||
isReset && editor.emit('ide.afterReset');
|
||||
}
|
||||
isReset && editor.emit('editor.afterReset');
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
@ -125,7 +133,7 @@ export default class Skeleton extends PureComponent<SkeletonProps, SkeletonState
|
||||
|
||||
return (
|
||||
<ConfigProvider>
|
||||
<Loading tip="Loading" size="large" visible={!initReady} shape="fusion-reactor" fullScreen>
|
||||
<Loading tip="Loading" size="large" visible={!initReady} fullScreen>
|
||||
<div className="lowcode-editor" key={skeletonKey}>
|
||||
<TopArea editor={this.editor} />
|
||||
<div className="lowcode-main-content">
|
||||
@ -140,3 +148,33 @@ export default class Skeleton extends PureComponent<SkeletonProps, SkeletonState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 通过React-Router包裹,支持编辑器内页面根据路由切换
|
||||
export interface SkeletonWithRouterProps {
|
||||
components: PluginClassSet;
|
||||
config: EditorConfig;
|
||||
utils: Utils;
|
||||
}
|
||||
|
||||
const SkeletonWithRouter: React.FC<SkeletonWithRouterProps> = (
|
||||
props,
|
||||
): React.ReactElement => {
|
||||
const { config, ...otherProps } = props;
|
||||
return (
|
||||
<Router>
|
||||
<Route
|
||||
path="/*"
|
||||
component={routerProps => (
|
||||
<Skeleton
|
||||
{...routerProps}
|
||||
{...otherProps}
|
||||
{...(config.skeleton && config.skeleton.props)}
|
||||
config={config}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkeletonWithRouter;
|
||||
@ -1,39 +0,0 @@
|
||||
import topBalloonIcon from '@ali/iceluna-addon-2';
|
||||
import topDialogIcon from '@ali/iceluna-addon-2';
|
||||
import leftPanelIcon from '@ali/iceluna-addon-2';
|
||||
import leftPanelIcon2 from '@ali/iceluna-addon-2';
|
||||
import leftBalloonIcon from '@ali/iceluna-addon-2';
|
||||
import leftDialogIcon from '@ali/iceluna-addon-2';
|
||||
import rightPanel1 from '@ali/iceluna-addon-2';
|
||||
import rightPanel2 from '@ali/iceluna-addon-2';
|
||||
import rightPanel3 from '@ali/iceluna-addon-2';
|
||||
import rightPanel4 from '@ali/iceluna-addon-2';
|
||||
import componentList from '@ali/iceluna-addon-component-list';
|
||||
import Settings from '../../../plugin-settings';
|
||||
import undoRedo from '../plugins/undoRedo';
|
||||
import Designer from '../plugins/designer';
|
||||
import logo from '../plugins/logo';
|
||||
import save from '../plugins/save';
|
||||
import OutlineTree from '../../../plugin-outline-tree';
|
||||
|
||||
import PluginFactory from '../framework/pluginFactory';
|
||||
|
||||
export default {
|
||||
logo: PluginFactory(logo),
|
||||
save: PluginFactory(save),
|
||||
designer: PluginFactory(Designer),
|
||||
settings: PluginFactory(Settings),
|
||||
outlineTree: PluginFactory(OutlineTree),
|
||||
undoRedo: PluginFactory(undoRedo),
|
||||
topBalloonIcon: PluginFactory(topBalloonIcon),
|
||||
topDialogIcon: PluginFactory(topDialogIcon),
|
||||
leftPanelIcon: PluginFactory(leftPanelIcon),
|
||||
leftPanelIcon2: PluginFactory(leftPanelIcon2),
|
||||
leftBalloonIcon: PluginFactory(leftBalloonIcon),
|
||||
leftDialogIcon: PluginFactory(leftDialogIcon),
|
||||
rightPanel1: PluginFactory(rightPanel1),
|
||||
rightPanel2: PluginFactory(rightPanel2),
|
||||
rightPanel3: PluginFactory(rightPanel3),
|
||||
rightPanel4: PluginFactory(rightPanel4),
|
||||
componentList: PluginFactory(componentList)
|
||||
};
|
||||
@ -1,331 +0,0 @@
|
||||
import assets from './assets';
|
||||
|
||||
export default {
|
||||
version: '^1.0.2',
|
||||
theme: {
|
||||
dpl: {
|
||||
package: '@alife/dpl-iceluna',
|
||||
version: '^2.3.0'
|
||||
},
|
||||
scss: ''
|
||||
},
|
||||
constants: {
|
||||
namespace: 'page'
|
||||
},
|
||||
utils: [],
|
||||
plugins: {
|
||||
topArea: [
|
||||
{
|
||||
pluginKey: 'logo',
|
||||
type: 'Custom',
|
||||
props: {
|
||||
align: 'left',
|
||||
width: 100
|
||||
},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-logo',
|
||||
version: '1.0.0'
|
||||
},
|
||||
pluginProps: {
|
||||
logo: 'https://img.alicdn.com/tfs/TB1hoI9x1H2gK0jSZFEXXcqMpXa-146-40.png',
|
||||
href: '/'
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'topBalloonIcon',
|
||||
type: 'BalloonIcon',
|
||||
props: {
|
||||
align: 'left',
|
||||
title: 'balloon',
|
||||
icon: 'dengpao',
|
||||
balloonProps: {
|
||||
triggerType: 'click'
|
||||
}
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'divider',
|
||||
type: 'Divider',
|
||||
props: {
|
||||
align: 'left'
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'topDialogIcon',
|
||||
type: 'DialogIcon',
|
||||
props: {
|
||||
align: 'left',
|
||||
title: 'dialog',
|
||||
icon: 'dengpao'
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'topLinkIcon',
|
||||
type: 'LinkIcon',
|
||||
props: {
|
||||
align: 'left',
|
||||
title: 'link',
|
||||
icon: 'dengpao',
|
||||
linkProps: {
|
||||
href: '//www.taobao.com',
|
||||
target: 'blank'
|
||||
}
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'topIcon',
|
||||
type: 'Icon',
|
||||
props: {
|
||||
align: 'left',
|
||||
title: 'icon',
|
||||
icon: 'dengpao',
|
||||
onClick(editor) {
|
||||
alert(`icon addon invoke, current activeKey: ${editor.activeKey}`);
|
||||
}
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'undoRedo',
|
||||
type: 'Custom',
|
||||
props: {
|
||||
align: 'right',
|
||||
width: 88
|
||||
},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-undo-redo',
|
||||
version: '1.0.0'
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'divider',
|
||||
type: 'Divider',
|
||||
props: {
|
||||
align: 'right'
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'save',
|
||||
type: 'Custom',
|
||||
props: {
|
||||
align: 'right',
|
||||
width: 64
|
||||
},
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-save',
|
||||
version: '1.0.0'
|
||||
}
|
||||
}
|
||||
],
|
||||
leftArea: [
|
||||
{
|
||||
pluginKey: 'componentList',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
icon: 'zujianku',
|
||||
title: '组件库'
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-component-list',
|
||||
version: '^1.0.4'
|
||||
},
|
||||
pluginProps: {
|
||||
disableAppComponent: true
|
||||
}
|
||||
},
|
||||
{
|
||||
pluginKey: 'outlineTree',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
icon: 'dengpao',
|
||||
title: '大纲树'
|
||||
},
|
||||
config: {
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftPanelIcon',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
title: 'panel',
|
||||
icon: 'dengpao'
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftBalloonIcon',
|
||||
type: 'BalloonIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
title: 'balloon',
|
||||
icon: 'dengpao'
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftPanelIcon2',
|
||||
type: 'PanelIcon',
|
||||
props: {
|
||||
align: 'top',
|
||||
title: 'panel2',
|
||||
icon: 'dengpao',
|
||||
panelProps: {
|
||||
defaultWidth: 400,
|
||||
floatable: true
|
||||
}
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftDialogIcon',
|
||||
type: 'DialogIcon',
|
||||
props: {
|
||||
align: 'bottom',
|
||||
title: 'dialog',
|
||||
icon: 'dengpao'
|
||||
},
|
||||
config: {
|
||||
package: '@ali/iceluna-addon-2',
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftLinkIcon',
|
||||
type: 'LinkIcon',
|
||||
props: {
|
||||
align: 'bottom',
|
||||
title: 'link',
|
||||
icon: 'dengpao',
|
||||
linkProps: {
|
||||
href: '//www.taobao.com',
|
||||
target: 'blank'
|
||||
}
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {}
|
||||
},
|
||||
{
|
||||
pluginKey: 'leftIcon',
|
||||
type: 'Icon',
|
||||
props: {
|
||||
align: 'bottom',
|
||||
title: 'icon',
|
||||
icon: 'dengpao',
|
||||
onClick(editor) {
|
||||
alert(`icon addon invoke, current activeKey: ${editor.activeKey}`);
|
||||
}
|
||||
},
|
||||
config: {},
|
||||
pluginProps: {}
|
||||
}
|
||||
],
|
||||
rightArea: [
|
||||
{
|
||||
pluginKey: 'settings',
|
||||
type: 'Panel',
|
||||
props: {},
|
||||
config: {
|
||||
version: '^1.0.0'
|
||||
},
|
||||
pluginProps: {}
|
||||
},
|
||||
],
|
||||
centerArea: [
|
||||
{
|
||||
pluginKey: 'designer',
|
||||
config: {
|
||||
package: '@ali/lowcode-plugin-designer',
|
||||
version: '1.0.0'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
hooks: [],
|
||||
shortCuts: [],
|
||||
lifeCycles: {
|
||||
init: function init(editor) {
|
||||
const transformMaterial = componentList => {
|
||||
return componentList.map(category => {
|
||||
return {
|
||||
name: category.title,
|
||||
items: category.children.map(comp => {
|
||||
return {
|
||||
...comp,
|
||||
name: comp.componentName,
|
||||
snippets: comp.snippets.map(snippet => {
|
||||
return {
|
||||
name: snippet.title,
|
||||
screenshot: snippet.screenshot,
|
||||
code: JSON.stringify(snippet.schema)
|
||||
};
|
||||
})
|
||||
};
|
||||
})
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const list = transformMaterial(assets.componentList);
|
||||
console.log('++++', list);
|
||||
editor.set({
|
||||
componentsMap: assets.components,
|
||||
componentMaterial: {
|
||||
library: [
|
||||
{
|
||||
name: 'Fusion组件库',
|
||||
id: '2'
|
||||
}, {
|
||||
name: '其他',
|
||||
id: '3'
|
||||
}
|
||||
],
|
||||
list
|
||||
}
|
||||
});
|
||||
|
||||
editor.set('dndHelper', {
|
||||
handleResourceDragStart: function(ev, tagName, schema) {
|
||||
// 物料面板中组件snippet的dragStart回调
|
||||
// ev: 原始的domEvent;tagName: 组件的描述文案;schema: snippet的schema
|
||||
if (editor.designer) {
|
||||
editor.designer.dragon.boost({
|
||||
type: 'nodedata',
|
||||
data: schema
|
||||
}, ev.nativeEvent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
8
packages/editor/src/index.d.ts
vendored
8
packages/editor/src/index.d.ts
vendored
@ -1,8 +0,0 @@
|
||||
interface Window {
|
||||
sendIDEMessage?: (IDEMessageParams) => void;
|
||||
goldlog?: {
|
||||
record: (logKey: string, gmKey: string, goKey: string, method: 'GET' | 'POST') => void;
|
||||
};
|
||||
is_theia?: boolean;
|
||||
vscode?: boolean;
|
||||
}
|
||||
@ -1,39 +0,0 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
// import Skeleton from '@ali/lowcode-engine-skeleton';
|
||||
import { HashRouter as Router, Route } from 'react-router-dom';
|
||||
import Skeleton from './skeleton';
|
||||
import config from './config/skeleton';
|
||||
import components from './config/components';
|
||||
import utils from './config/utils';
|
||||
import constants from './config/constants';
|
||||
import './config/locale';
|
||||
import '../../plugin-setters';
|
||||
|
||||
import './global.scss';
|
||||
import './config/theme.scss';
|
||||
|
||||
const ICE_CONTAINER = document.getElementById('ice-container');
|
||||
|
||||
if (!ICE_CONTAINER) {
|
||||
throw new Error('当前页面不存在 <div id="ice-container"></div> 节点.');
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<Router>
|
||||
<Route
|
||||
path="/*"
|
||||
component={(props): React.ReactNode => (
|
||||
<Skeleton
|
||||
{...props}
|
||||
{...(config.skeleton && config.skeleton.props)}
|
||||
config={config}
|
||||
utils={utils}
|
||||
constants={constants}
|
||||
components={components}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Router>,
|
||||
ICE_CONTAINER
|
||||
);
|
||||
@ -1,72 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import './index.scss';
|
||||
import { PluginProps } from '../../framework/definitions';
|
||||
import TopIcon from '../../skeleton/components/TopIcon/index';
|
||||
|
||||
export interface IProps {
|
||||
editor: any;
|
||||
logo?: string;
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
undoEnable: boolean;
|
||||
redoEnable: boolean;
|
||||
}
|
||||
|
||||
export default class UndoRedo extends PureComponent<IProps & PluginProps, IState> {
|
||||
public static display = 'LowcodeUndoRedo';
|
||||
|
||||
private history: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
undoEnable: false,
|
||||
redoEnable: false
|
||||
};
|
||||
if (props.editor.designer) {
|
||||
this.init();
|
||||
} else {
|
||||
props.editor.on('designer.ready', () => {
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
init = (): void => {
|
||||
const { editor } = this.props;
|
||||
|
||||
this.history = editor.designer?.currentHistory;
|
||||
this.updateState(this.history?.getState() || 0);
|
||||
|
||||
editor.on('designer.history-change', (history): void => {
|
||||
this.history = history;
|
||||
this.updateState(this.history?.getState() || 0);
|
||||
});
|
||||
};
|
||||
|
||||
updateState = (state: number): void => {
|
||||
this.setState({
|
||||
undoEnable: !!(state & 1),
|
||||
redoEnable: !!(state & 2)
|
||||
});
|
||||
};
|
||||
|
||||
handleUndoClick = (): void => {
|
||||
this.history?.back();
|
||||
};
|
||||
|
||||
handleRedoClick = (): void => {
|
||||
this.history?.forward();
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { undoEnable, redoEnable } = this.state;
|
||||
return (
|
||||
<div className="lowcode-plugin-undo-redo">
|
||||
<TopIcon icon="houtui" title="后退" disabled={!undoEnable} onClick={this.handleUndoClick} />
|
||||
<TopIcon icon="qianjin" title="前进" disabled={!redoEnable} onClick={this.handleRedoClick} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
.lowcode-left-plugin {
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
line-height: 44px;
|
||||
height: 44px;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
color: $color-text1-3;
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-brand1-6;
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
top: 5px;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: $balloon-tooltip-color-bg;
|
||||
color: $color-text1-3;
|
||||
z-index: 100;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 40px;
|
||||
top: 15px;
|
||||
border: 5px solid transparent;
|
||||
border-right: 5px solid $balloon-tooltip-color-bg;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9;
|
||||
&.disabled {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-brand1-6;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
@ -1,210 +0,0 @@
|
||||
import React, { PureComponent, Fragment } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Balloon, Dialog, Icon, Badge } from '@alifd/next';
|
||||
|
||||
import './index.scss';
|
||||
import Editor from '../../../framework/editor';
|
||||
import { PluginConfig, PluginClass } from '../../../framework/definitions';
|
||||
|
||||
export interface LeftPluginProps {
|
||||
active?: boolean;
|
||||
config: PluginConfig;
|
||||
disabled?: boolean;
|
||||
editor: Editor;
|
||||
locked?: boolean;
|
||||
marked?: boolean;
|
||||
onClick?: () => void;
|
||||
pluginClass: PluginClass;
|
||||
}
|
||||
|
||||
export interface LeftPluginState {
|
||||
dialogVisible: boolean;
|
||||
}
|
||||
|
||||
export default class LeftPlugin extends PureComponent<LeftPluginProps, LeftPluginState> {
|
||||
static displayName = 'LowcodeLeftPlugin';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
config: {},
|
||||
disabled: false,
|
||||
marked: false,
|
||||
locked: false,
|
||||
onClick: (): void => {}
|
||||
};
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
dialogVisible: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (editor && pluginKey) {
|
||||
editor.on(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
editor.on(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (editor && pluginKey) {
|
||||
editor.off(`${pluginKey}.dialog.show`, this.handleShow);
|
||||
editor.off(`${pluginKey}.dialog.close`, this.handleClose);
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = (): void => {
|
||||
const { config, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
const plugin = editor.plugins && editor.plugins[pluginKey];
|
||||
if (plugin && plugin.close) {
|
||||
plugin.close().then((): void => {
|
||||
this.setState({
|
||||
dialogVisible: false
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleOpen = (): void => {
|
||||
// todo 对话框类型的插件初始时拿不到插件实例
|
||||
this.setState({
|
||||
dialogVisible: true
|
||||
});
|
||||
};
|
||||
|
||||
handleShow = (): void => {
|
||||
const { disabled, config, onClick, editor } = this.props;
|
||||
const pluginKey = config && config.pluginKey;
|
||||
if (disabled || !pluginKey) return;
|
||||
// 考虑到弹窗情况,延时发送消息
|
||||
setTimeout((): void => editor.emit(`${pluginKey}.plugin.activate`), 0);
|
||||
this.handleOpen();
|
||||
if (onClick) {
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
|
||||
renderIcon = (clickCallback): React.ReactNode => {
|
||||
const { active, disabled, marked, locked, onClick, config } = this.props;
|
||||
const { pluginKey, props } = config || {};
|
||||
const { icon, title } = props || {};
|
||||
return (
|
||||
<div
|
||||
className={classNames('lowcode-left-plugin', pluginKey, {
|
||||
active,
|
||||
disabled,
|
||||
locked
|
||||
})}
|
||||
data-tooltip={title}
|
||||
onClick={(): void => {
|
||||
if (disabled) return;
|
||||
// 考虑到弹窗情况,延时发送消息
|
||||
clickCallback && clickCallback();
|
||||
|
||||
onClick && onClick();
|
||||
}}
|
||||
>
|
||||
{marked ? (
|
||||
<Badge dot>
|
||||
<Icon type={icon} size="small" />
|
||||
</Badge>
|
||||
) : (
|
||||
<Icon type={icon} size="small" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { marked, locked, active, disabled, config, editor, pluginClass: Comp } = this.props;
|
||||
const { pluginKey, props, type, pluginProps } = config || {};
|
||||
const { onClick, title } = props || {};
|
||||
const { dialogVisible } = this.state;
|
||||
if (!pluginKey || !type || !props) return null;
|
||||
|
||||
const node =
|
||||
(Comp && (
|
||||
<Comp
|
||||
editor={editor}
|
||||
active={active}
|
||||
locked={locked}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
onClick={(): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
}}
|
||||
{...pluginProps}
|
||||
/>
|
||||
)) ||
|
||||
null;
|
||||
|
||||
switch (type) {
|
||||
case 'LinkIcon':
|
||||
return (
|
||||
<a {...(props.linkProps || {})}>
|
||||
{this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
</a>
|
||||
);
|
||||
case 'Icon':
|
||||
return this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
});
|
||||
case 'DialogIcon':
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
})}
|
||||
<Dialog
|
||||
onOk={(): void => {
|
||||
editor.emit(`${pluginKey}.dialog.onOk`);
|
||||
this.handleClose();
|
||||
}}
|
||||
onCancel={this.handleClose}
|
||||
onClose={this.handleClose}
|
||||
title={title}
|
||||
style={{
|
||||
width: 500,
|
||||
...(props.dialogProps && props.dialogProps.style)
|
||||
}}
|
||||
{...(props.dialogProps || {})}
|
||||
visible={dialogVisible}
|
||||
>
|
||||
{node}
|
||||
</Dialog>
|
||||
</Fragment>
|
||||
);
|
||||
case 'BalloonIcon':
|
||||
return (
|
||||
<Balloon
|
||||
trigger={this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
})}
|
||||
align="r"
|
||||
triggerType={['click', 'hover']}
|
||||
{...(props.balloonProps || {})}
|
||||
>
|
||||
{node}
|
||||
</Balloon>
|
||||
);
|
||||
case 'PanelIcon':
|
||||
return this.renderIcon((): void => {
|
||||
onClick && onClick.call(null, editor);
|
||||
this.handleOpen();
|
||||
});
|
||||
case 'Custom':
|
||||
return marked ? <Badge dot>{node}</Badge> : node;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,60 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export interface PanelProps {
|
||||
align: 'left' | 'right';
|
||||
defaultWidth: number;
|
||||
minWidth: number;
|
||||
draggable: boolean;
|
||||
floatable: boolean;
|
||||
children: Plugin;
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
export interface PanelState {
|
||||
width: number;
|
||||
}
|
||||
|
||||
export default class Panel extends PureComponent<PanelProps, PanelState> {
|
||||
static displayName = 'LowcodePanel';
|
||||
|
||||
static defaultProps = {
|
||||
align: 'left',
|
||||
defaultWidth: 240,
|
||||
minWidth: 100,
|
||||
draggable: true,
|
||||
floatable: false,
|
||||
visible: true
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
width: props.defaultWidth
|
||||
};
|
||||
}
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { align, draggable, floatable, visible } = this.props;
|
||||
const { width } = this.state;
|
||||
return (
|
||||
<div
|
||||
className={classNames('lowcode-panel', align, {
|
||||
draggable,
|
||||
floatable,
|
||||
visible
|
||||
})}
|
||||
style={{
|
||||
width,
|
||||
display: visible ? '' : 'none'
|
||||
}}
|
||||
>
|
||||
{this.props.children}
|
||||
<div className="drag-area" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
.lowcode-top-icon {
|
||||
display: inline-block;
|
||||
width: 44px;
|
||||
font-size: 20px;
|
||||
line-height: 48px;
|
||||
color: $color-text1-3;
|
||||
position: relative;
|
||||
&.disabled {
|
||||
cursor: not-allowed;
|
||||
color: $color-text1-1;
|
||||
&:hover {
|
||||
color: $color-text1-1;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
color: $color-brand1-9;
|
||||
&:hover {
|
||||
color: $color-brand1-6;
|
||||
}
|
||||
}
|
||||
&.locked {
|
||||
color: red !important;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-brand1-6;
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
display: block;
|
||||
height: auto;
|
||||
width: auto;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
bottom: -35px;
|
||||
line-height: 18px;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 6px 8px;
|
||||
border-radius: 4px;
|
||||
background: $balloon-tooltip-color-bg;
|
||||
color: $color-text1-3;
|
||||
z-index: 100;
|
||||
}
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
bottom: -5px;
|
||||
border: 5px solid transparent;
|
||||
border-bottom-color: $balloon-tooltip-color-bg;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
i.next-icon {
|
||||
&:before {
|
||||
font-size: 16px;
|
||||
}
|
||||
margin-right: 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
margin: 0px -5px 0;
|
||||
line-height: 16px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
@ -1,53 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Icon } from '@alifd/next';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export interface TopIconProps {
|
||||
active?: boolean;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
icon: string;
|
||||
id?: string;
|
||||
locked?: boolean;
|
||||
marked?: boolean;
|
||||
onClick?: () => void;
|
||||
style?: React.CSSProperties;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export default class TopIcon extends PureComponent<TopIconProps> {
|
||||
static displayName = 'LowcodeTopIcon';
|
||||
|
||||
static defaultProps = {
|
||||
active: false,
|
||||
className: '',
|
||||
disabled: false,
|
||||
icon: '',
|
||||
id: '',
|
||||
locked: false,
|
||||
onClick: (): void => {},
|
||||
style: {},
|
||||
title: ''
|
||||
};
|
||||
|
||||
render(): React.ReactNode {
|
||||
const { active, disabled, icon, locked, title, className, id, style, onClick } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={classNames('lowcode-top-icon', className, {
|
||||
active,
|
||||
disabled,
|
||||
locked
|
||||
})}
|
||||
data-tooltip={title}
|
||||
id={id}
|
||||
style={style}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
>
|
||||
<Icon type={icon} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user