diff --git a/.gitignore b/.gitignore index 39e7cc176..aea751e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* +# package-lock.json # Runtime data pids diff --git a/packages/editor/.editorconfig b/packages/demo/.editorconfig similarity index 100% rename from packages/editor/.editorconfig rename to packages/demo/.editorconfig diff --git a/packages/editor/.eslintignore b/packages/demo/.eslintignore similarity index 100% rename from packages/editor/.eslintignore rename to packages/demo/.eslintignore diff --git a/packages/editor/.eslintrc.js b/packages/demo/.eslintrc.js similarity index 100% rename from packages/editor/.eslintrc.js rename to packages/demo/.eslintrc.js diff --git a/packages/editor/.gitignore b/packages/demo/.gitignore similarity index 100% rename from packages/editor/.gitignore rename to packages/demo/.gitignore diff --git a/packages/editor/.prettierrc b/packages/demo/.prettierrc similarity index 100% rename from packages/editor/.prettierrc rename to packages/demo/.prettierrc diff --git a/packages/editor/.stylelintignore b/packages/demo/.stylelintignore similarity index 100% rename from packages/editor/.stylelintignore rename to packages/demo/.stylelintignore diff --git a/packages/editor/.stylelintrc.js b/packages/demo/.stylelintrc.js similarity index 100% rename from packages/editor/.stylelintrc.js rename to packages/demo/.stylelintrc.js diff --git a/packages/editor/README.md b/packages/demo/README.md similarity index 100% rename from packages/editor/README.md rename to packages/demo/README.md diff --git a/packages/editor/abc.json b/packages/demo/abc.json similarity index 100% rename from packages/editor/abc.json rename to packages/demo/abc.json diff --git a/packages/editor/ice.config.js b/packages/demo/ice.config.js similarity index 100% rename from packages/editor/ice.config.js rename to packages/demo/ice.config.js diff --git a/packages/editor/jsconfig.json b/packages/demo/jsconfig.json similarity index 100% rename from packages/editor/jsconfig.json rename to packages/demo/jsconfig.json diff --git a/packages/editor/package.json b/packages/demo/package.json similarity index 84% rename from packages/editor/package.json rename to packages/demo/package.json index c9864f35f..42a8da6a9 100644 --- a/packages/editor/package.json +++ b/packages/demo/package.json @@ -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", diff --git a/packages/demo/public/assets.json b/packages/demo/public/assets.json new file mode 100644 index 000000000..7d86b9ed3 --- /dev/null +++ b/packages/demo/public/assets.json @@ -0,0 +1,1589 @@ +{ + "version": "1.0.0", + "packages": { + "@alifd/next": { + "title": "fusion组件库", + "package": "@alifd/next", + "version": "1.19.18", + "urls": ["https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.js", "https://unpkg.antfin-inc.com/@alife/next@1.19.18/dist/next.css"], + "library": "Next" + } + }, + "components": { + "Page": { + "componentName": "Page", + "title": "页面", + "configure": { + "events": { + "supportedLifecycles": [{ + "description": "初始化时", + "name": "constructor" + }, { + "description": "装载后", + "name": "componentDidMount" + }, { + "description": "更新时", + "name": "componentDidMount" + }, { + "description": "卸载时", + "name": "componentWillUnmount" + }] + }, + "component": { + "isContainer": true + } + } + }, + "Div": { + "componentName": "Div", + "title": "容器", + "configure": { + "component": { + "isContainer": true + } + } + }, + "Button": { + "componentName": "Button", + "title": "按钮", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Button" + }, + "props": [{ + "name": "prefix", + "propType": "string", + "defaultValue": "next-" + }, { + "name": "rtl", + "propType": "bool" + }, { + "name": "type", + "propType": { + "type": "oneOf", + "value": ["primary", "secondary", "normal"] + }, + "description": "按钮的类型", + "defaultValue": "normal" + }, { + "name": "size", + "propType": { + "type": "oneOf", + "value": ["small", "medium", "large"] + }, + "description": "按钮的尺寸", + "defaultValue": "medium" + }, { + "name": "iconSize", + "propType": { + "type": "oneOf", + "value": ["xxs", "xs", "small", "medium", "large", "xl", "xxl", "xxxl"] + }, + "description": "按钮中 Icon 的尺寸,用于替代 Icon 的默认大小" + }, { + "name": "htmlType", + "propType": { + "type": "oneOf", + "value": ["submit", "reset", "button"] + }, + "description": "当 component = 'button' 时,设置 button 标签的 type 值", + "defaultValue": "button" + }, { + "name": "component", + "propType": { + "type": "oneOf", + "value": ["button", "a", "div", "span"] + }, + "description": "设置标签类型", + "defaultValue": "button" + }, { + "name": "loading", + "propType": "bool", + "description": "设置按钮的载入状态", + "defaultValue": false + }, { + "name": "ghost", + "propType": { + "type": "oneOf", + "value": [true, false, "light", "dark"] + }, + "description": "是否为幽灵按钮", + "defaultValue": false + }, { + "name": "text", + "propType": "bool", + "description": "是否为文本按钮", + "defaultValue": false + }, { + "name": "warning", + "propType": "bool", + "description": "是否为警告按钮", + "defaultValue": false + }, { + "name": "disabled", + "propType": "bool", + "description": "是否禁用", + "defaultValue": false + }, { + "name": "onClick", + "propType": "func", + "description": "点击按钮的回调\n@param {Object} e Event Object" + }, { + "name": "className", + "propType": "string" + }, { + "name": "onMouseUp", + "propType": "func" + }, { + "name": "children", + "propType": "node" + }] + }, + "Button.Group": { + "componentName": "Button.Group", + "title": "按钮组", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Button", + "subName": "Group" + }, + "props": [{ + "name": "rtl", + "propType": "bool" + }, { + "name": "prefix", + "propType": "string", + "defaultValue": "next-" + }, { + "name": "size", + "propType": "string", + "description": "统一设置 Button 组件的按钮大小", + "defaultValue": "medium" + }, { + "name": "className", + "propType": "string" + }, { + "name": "children", + "propType": "node" + }], + "configure": { + "component": { + "isContainer": true, + "nestingRule": { + "childWhitelist": "Button" + } + } + } + }, + "Input": { + "componentName": "Input", + "title": "输入框", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Input" + }, + "props": [{ + "name": "label", + "propType": "node", + "description": "label" + }, { + "name": "hasClear", + "propType": "bool", + "description": "是否出现clear按钮" + }, { + "name": "hasBorder", + "propType": "bool", + "description": "是否有边框", + "defaultValue": "true" + }, { + "name": "state", + "propType": { + "type": "oneOf", + "value": ["error", "loading", "success", "warning"] + }, + "description": "状态\n@enumdesc 错误, 校验中, 成功, 警告" + }, { + "name": "size", + "propType": { + "type": "oneOf", + "value": ["small", "medium", "large"] + }, + "description": "尺寸\n@enumdesc 小, 中, 大", + "defaultValue": "medium" + }, { + "name": "onPressEnter", + "propType": "func", + "description": "按下回车的回调", + "defaultValue": "func.noop" + }, { + "name": "onClear", + "propType": "func" + }, { + "name": "htmlType", + "propType": "string", + "description": "原生type" + }, { + "name": "htmlSize", + "propType": "string" + }, { + "name": "hint", + "propType": "string", + "description": "水印 (Icon的type类型,和hasClear占用一个地方)" + }, { + "name": "innerBefore", + "propType": "node", + "description": "文字前附加内容" + }, { + "name": "innerAfter", + "propType": "node", + "description": "文字后附加内容" + }, { + "name": "addonBefore", + "propType": "node", + "description": "输入框前附加内容" + }, { + "name": "addonAfter", + "propType": "node", + "description": "输入框后附加内容" + }, { + "name": "addonTextBefore", + "propType": "node", + "description": "输入框前附加文字" + }, { + "name": "addonTextAfter", + "propType": "node", + "description": "输入框后附加文字" + }, { + "name": "autoComplete", + "propType": "string", + "description": "(原生input支持)", + "defaultValue": "off" + }, { + "name": "autoFocus", + "propType": "bool", + "description": "自动聚焦(原生input支持)" + }, { + "name": "inputRender", + "propType": "func", + "defaultValue": "el => el" + }, { + "name": "extra", + "propType": "node" + }, { + "name": "innerBeforeClassName", + "propType": "string" + }, { + "name": "innerAfterClassName", + "propType": "string" + }, { + "name": "isPreview", + "propType": "bool", + "description": "是否为预览态", + "defaultValue": "false" + }, { + "name": "renderPreview", + "propType": "func", + "description": "预览态模式下渲染的内容\n@param {number} value 评分值" + }] + }, + "Form": { + "componentName": "Form", + "title": "表单容器", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Form" + }, + "props": [{ + "name": "prefix", + "propType": "string", + "description": "样式前缀", + "defaultValue": "next-" + }, { + "name": "inline", + "propType": "bool", + "description": "内联表单" + }, { + "name": "size", + "propType": { + "type": "oneOf", + "value": ["large", "medium", "small"] + }, + "description": "单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。\n@enumdesc 大, 中, 小", + "defaultValue": "medium" + }, { + "name": "fullWidth", + "propType": "bool", + "description": "单个 Item 中表单类组件宽度是否是100%" + }, { + "name": "labelAlign", + "propType": { + "type": "oneOf", + "value": ["top", "left", "inset"] + }, + "description": "标签的位置\n@enumdesc 上, 左, 内", + "defaultValue": "left" + }, { + "name": "labelTextAlign", + "propType": { + "type": "oneOf", + "value": ["left", "right"] + }, + "description": "标签的左右对齐方式\n@enumdesc 左, 右" + }, { + "name": "field", + "propType": "any", + "description": "field 实例, 传 false 会禁用 field" + }, { + "name": "saveField", + "propType": "func", + "description": "保存 Form 自动生成的 field 对象" + }, { + "name": "labelCol", + "propType": "object", + "description": "控制第一级 Item 的 labelCol" + }, { + "name": "wrapperCol", + "propType": "object", + "description": "控制第一级 Item 的 wrapperCol" + }, { + "name": "onSubmit", + "propType": "func", + "description": "form内有 `htmlType=\"submit\"` 的元素的时候会触发" + }, { + "name": "children", + "propType": "any", + "description": "子元素" + }, { + "name": "className", + "propType": "string", + "description": "扩展class" + }, { + "name": "style", + "propType": "object", + "description": "自定义内联样式" + }, { + "name": "value", + "propType": "object", + "description": "表单数值" + }, { + "name": "onChange", + "propType": "func", + "description": "表单变化回调\n@param {Object} values 表单数据\n@param {Object} item 详细\n@param {String} item.name 变化的组件名\n@param {String} item.value 变化的数据\n@param {Object} item.field field 实例" + }, { + "name": "component", + "propType": { + "type": "oneOfType", + "value": ["string", "func"] + }, + "description": "设置标签类型", + "defaultValue": "form" + }, { + "name": "fieldOptions", + "propType": "object" + }, { + "name": "rtl", + "propType": "bool" + }, { + "name": "device", + "propType": { + "type": "oneOf", + "value": ["phone", "tablet", "desktop"] + }, + "description": "预设屏幕宽度", + "defaultValue": "desktop" + }, { + "name": "responsive", + "propType": "bool", + "description": "是否开启内置的响应式布局 (使用ResponsiveGrid)" + }, { + "name": "isPreview", + "propType": "bool", + "description": "是否开启预览态" + }], + "configure": { + "component": { + "isContainer": true + } + } + }, + "Form.Item": { + "componentName": "Form.Item", + "title": "表单项", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Form", + "subName": "Item" + }, + "props": [{ + "name": "prefix", + "propType": "string", + "description": "样式前缀", + "defaultValue": "next-" + }, { + "name": "rtl", + "propType": "bool" + }, { + "name": "label", + "propType": "node", + "description": "label 标签的文本" + }, { + "name": "labelCol", + "propType": "object", + "description": "label 标签布局,通 `` 组件,设置 span offset 值,如 {span: 8, offset: 16},该项仅在垂直表单有效" + }, { + "name": "wrapperCol", + "propType": "object", + "description": "需要为输入控件设置布局样式时,使用该属性,用法同 labelCol" + }, { + "name": "help", + "propType": "node", + "description": "自定义提示信息,如不设置,则会根据校验规则自动生成." + }, { + "name": "extra", + "propType": "node", + "description": "额外的提示信息,和 help 类似,当需要错误信息和提示文案同时出现时,可以使用这个。 位于错误信息后面" + }, { + "name": "validateState", + "propType": { + "type": "oneOf", + "value": ["error", "success", "loading", "warning"] + }, + "description": "校验状态,如不设置,则会根据校验规则自动生成\n@enumdesc 失败, 成功, 校验中, 警告" + }, { + "name": "hasFeedback", + "propType": "bool", + "description": "配合 validateState 属性使用,是否展示 success/loading 的校验状态图标, 目前只有Input支持", + "defaultValue": false + }, { + "name": "style", + "propType": "object", + "description": "自定义内联样式" + }, { + "name": "id", + "propType": "string" + }, { + "name": "children", + "propType": { + "type": "oneOfType", + "value": ["node", "func"] + }, + "description": "node 或者 function(values)" + }, { + "name": "size", + "propType": { + "type": "oneOf", + "value": ["large", "small", "medium"] + }, + "description": "单个 Item 的 size 自定义,优先级高于 Form 的 size, 并且当组件与 Item 一起使用时,组件自身设置 size 属性无效。" + }, { + "name": "fullWidth", + "propType": "bool", + "description": "单个 Item 中表单类组件宽度是否是100%" + }, { + "name": "labelAlign", + "propType": { + "type": "oneOf", + "value": ["top", "left", "inset"] + }, + "description": "标签的位置\n@enumdesc 上, 左, 内" + }, { + "name": "labelTextAlign", + "propType": { + "type": "oneOf", + "value": ["left", "right"] + }, + "description": "标签的左右对齐方式\n@enumdesc 左, 右" + }, { + "name": "className", + "propType": "string", + "description": "扩展class" + }, { + "name": "required", + "propType": "bool", + "description": "[表单校验] 不能为空" + }, { + "name": "asterisk", + "propType": "bool", + "description": "required 的星号是否显示" + }, { + "name": "requiredMessage", + "propType": "string", + "description": "required 自定义错误信息" + }, { + "name": "requiredTrigger", + "propType": { + "type": "oneOfType", + "value": ["string", "array"] + }, + "description": "required 自定义触发方式" + }, { + "name": "min", + "propType": "number", + "description": "[表单校验] 最小值" + }, { + "name": "max", + "propType": "number", + "description": "[表单校验] 最大值" + }, { + "name": "minmaxMessage", + "propType": "string", + "description": "min/max 自定义错误信息" + }, { + "name": "minmaxTrigger", + "propType": { + "type": "oneOfType", + "value": ["string", "array"] + }, + "description": "min/max 自定义触发方式" + }, { + "name": "minLength", + "propType": "number", + "description": "[表单校验] 字符串最小长度 / 数组最小个数" + }, { + "name": "maxLength", + "propType": "number", + "description": "[表单校验] 字符串最大长度 / 数组最大个数" + }, { + "name": "minmaxLengthMessage", + "propType": "string", + "description": "minLength/maxLength 自定义错误信息" + }, { + "name": "minmaxLengthTrigger", + "propType": { + "type": "oneOfType", + "value": ["string", "array"] + }, + "description": "minLength/maxLength 自定义触发方式" + }, { + "name": "length", + "propType": "number", + "description": "[表单校验] 字符串精确长度 / 数组精确个数" + }, { + "name": "lengthMessage", + "propType": "string", + "description": "length 自定义错误信息" + }, { + "name": "lengthTrigger", + "propType": { + "type": "oneOfType", + "value": ["string", "array"] + }, + "description": "length 自定义触发方式" + }, { + "name": "pattern", + "propType": "any", + "description": "正则校验" + }, { + "name": "patternMessage", + "propType": "string", + "description": "pattern 自定义错误信息" + }, { + "name": "patternTrigger", + "propType": { + "type": "oneOfType", + "value": ["string", "array"] + }, + "description": "pattern 自定义触发方式" + }, { + "name": "format", + "propType": { + "type": "oneOf", + "value": ["number", "email", "url", "tel"] + }, + "description": "[表单校验] 四种常用的 pattern" + }, { + "name": "formatMessage", + "propType": "string", + "description": "format 自定义错误信息" + }, { + "name": "formatTrigger", + "propType": { + "type": "oneOfType", + "value": ["string", "array"] + }, + "description": "format 自定义触发方式" + }, { + "name": "validator", + "propType": "func", + "description": "[表单校验] 自定义校验函数" + }, { + "name": "validatorTrigger", + "propType": { + "type": "oneOfType", + "value": ["string", "array"] + }, + "description": "validator 自定义触发方式" + }, { + "name": "autoValidate", + "propType": "bool", + "description": "是否修改数据时自动触发校验" + }, { + "name": "device", + "propType": { + "type": "oneOf", + "value": ["phone", "tablet", "desktop"] + }, + "description": "预设屏幕宽度" + }, { + "name": "responsive", + "propType": "bool" + }, { + "name": "colSpan", + "propType": "number", + "description": "在响应式布局模式下,表单项占多少列" + }, { + "name": "labelWidth", + "propType": { + "type": "oneOfType", + "value": ["string", "number"] + }, + "description": "在响应式布局下,且label在左边时,label的宽度是多少", + "defaultValue": 100 + }, { + "name": "isPreview", + "propType": "bool", + "description": "是否开启预览态" + }, { + "name": "renderPreview", + "propType": "func", + "description": "预览态模式下渲染的内容\n@param {any} value 根据包裹的组件的 value 类型而决定" + }], + "configure": { + "component": { + "isContainer": true, + "nestingRule": { + "parentWhitelist": "Form" + } + } + } + }, + "NumberPicker": { + "componentName": "NumberPicker", + "title": "数字输入", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "NumberPicker" + }, + "props": [{ + "name": "prefix", + "propType": "string", + "description": "样式前缀", + "defaultValue": "next-" + }, { + "name": "type", + "propType": { + "type": "oneOf", + "value": ["normal", "inline"] + }, + "description": "设置类型\n@enumdesc 普通, 内联", + "defaultValue": "normal" + }, { + "name": "size", + "propType": { + "type": "oneOf", + "value": ["large", "medium"] + }, + "description": "大小", + "defaultValue": "medium" + }, { + "name": "value", + "propType": "number", + "description": "当前值" + }, { + "name": "defaultValue", + "propType": "number", + "description": "默认值" + }, { + "name": "disabled", + "propType": "bool", + "description": "是否禁用" + }, { + "name": "step", + "propType": { + "type": "oneOfType", + "value": ["number", "string"] + }, + "description": "步长", + "defaultValue": 1 + }, { + "name": "precision", + "propType": "number", + "description": "保留小数点后位数", + "defaultValue": 0 + }, { + "name": "editable", + "propType": "bool", + "description": "用户是否可以输入", + "defaultValue": true + }, { + "name": "autoFocus", + "propType": "bool", + "description": "自动焦点" + }, { + "name": "onChange", + "propType": "func", + "description": "数值被改变的事件\n@param {Number} value 数据\n@param {Event} e DOM事件对象" + }, { + "name": "onKeyDown", + "propType": "func", + "description": "键盘按下" + }, { + "name": "onFocus", + "propType": "func", + "description": "焦点获得" + }, { + "name": "onBlur", + "propType": "func", + "description": "焦点失去" + }, { + "name": "onCorrect", + "propType": "func", + "description": "数值订正后的回调\n@param {Object} obj {currentValue,oldValue:String}" + }, { + "name": "onDisabled", + "propType": "func" + }, { + "name": "max", + "propType": "number", + "description": "最大值", + "defaultValue": null + }, { + "name": "min", + "propType": "number", + "description": "最小值", + "defaultValue": null + }, { + "name": "className", + "propType": "string", + "description": "自定义class" + }, { + "name": "style", + "propType": "object", + "description": "自定义内联样式" + }, { + "name": "state", + "propType": { + "type": "oneOf", + "value": ["error"] + } + }, { + "name": "format", + "propType": "func", + "description": "格式化当前值\n@param {Number} value\n@return {String|Number}" + }, { + "name": "upBtnProps", + "propType": "object", + "description": "增加按钮的props" + }, { + "name": "downBtnProps", + "propType": "object", + "description": "减少按钮的props" + }, { + "name": "label", + "propType": "node", + "description": "内联 label" + }, { + "name": "innerAfter", + "propType": "node", + "description": "inner after" + }, { + "name": "rtl", + "propType": "bool" + }, { + "name": "isPreview", + "propType": "bool", + "description": "是否为预览态" + }, { + "name": "renderPreview", + "propType": "func", + "description": "预览态模式下渲染的内容\n@param {number} value 评分值" + }, { + "name": "device", + "propType": { + "type": "oneOf", + "value": ["phone", "tablet", "desktop"] + }, + "description": "预设屏幕宽度" + }] + }, + "Select": { + "componentName": "Select", + "title": "下拉", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Select" + }, + "props": [{ + "name": "mode", + "propType": { + "type": "oneOf", + "value": ["single", "multiple", "tag"] + }, + "description": "选择器模式", + "defaultValue": "single" + }, { + "name": "value", + "propType": "any", + "description": "当前值,用于受控模式" + }, { + "name": "defaultValue", + "propType": "any", + "description": "初始的默认值" + }, { + "name": "onChange", + "propType": "func", + "description": "Select发生改变时触发的回调\n@param {*} value 选中的值\n@param {String} actionType 触发的方式, 'itemClick', 'enter', 'tag'\n@param {*} item 选中的值的对象数据 (useDetailValue=false有效)" + }, { + "name": "dataSource", + "propType": { + "type": "arrayOf", + "value": { + "type": "oneOfType", + "value": [{ + "type": "shape", + "value": [{ + "name": "value", + "propType": "any" + }, { + "name": "label", + "propType": "any" + }, { + "name": "disabled", + "propType": "bool" + }, { + "name": "children", + "propType": "array" + }] + }, "bool", "number", "string"] + } + }, + "description": "传入的数据源,可以动态渲染子项,详见 [dataSource的使用](#dataSource的使用)" + }, { + "name": "hasBorder", + "propType": "bool", + "description": "是否有边框" + }, { + "name": "hasArrow", + "propType": "bool", + "description": "是否有下拉箭头", + "defaultValue": true + }, { + "name": "showSearch", + "propType": "bool", + "description": "展开后是否能搜索(tag 模式下固定为true)", + "defaultValue": false + }, { + "name": "onSearch", + "propType": "func", + "description": "当搜索框值变化时回调\n@param {String} value 数据" + }, { + "name": "onSearchClear", + "propType": "func", + "description": "当搜索框值被清空时候的回调\n@param {String} actionType 触发的方式, 'select'(选择清空), 'popupClose'(弹窗关闭清空)" + }, { + "name": "hasSelectAll", + "propType": { + "type": "oneOfType", + "value": ["bool", "string"] + }, + "description": "多选模式下是否有全选功能" + }, { + "name": "fillProps", + "propType": "string", + "description": "填充到选择框里的值的 key\b\b" + }, { + "name": "useDetailValue", + "propType": "bool", + "description": "onChange 返回的 value 使用 dataSource 的对象" + }, { + "name": "cacheValue", + "propType": "bool", + "description": "dataSource 变化的时是否保留已选的内容", + "defaultValue": true + }, { + "name": "valueRender", + "propType": "func", + "description": "渲染 Select 展现内容的方法\n@param {Object} item 渲染节点的item\n@return {ReactNode} 展现内容\n@default item => item.label \\|\\| item.value" + }, { + "name": "itemRender", + "propType": "func", + "description": "渲染 MenuItem 内容的方法\n@param {Object} item 渲染节点的item\n@param {String} searchValue 搜索关键字(如果开启搜索)\n@return {ReactNode} item node" + }, { + "name": "notFoundContent", + "propType": "node", + "description": "弹层内容为空的文案" + }, { + "name": "style", + "propType": "object" + }, { + "name": "searchValue", + "propType": "string", + "description": "受控搜索值,一般不需要设置\n@type {[type]}" + }, { + "name": "tagInline", + "propType": "bool", + "description": "是否一行显示,仅在 mode 为 multiple 的时候生效", + "defaultValue": false + }, { + "name": "maxTagCount", + "propType": "number", + "description": "最多显示多少个 tag" + }, { + "name": "maxTagPlaceholder", + "propType": "func", + "description": "隐藏多余 tag 时显示的内容,在 maxTagCount 生效时起作用\n@param {number} selectedValues 当前已选中的元素\n@param {number} totalValues 总待选元素" + }, { + "name": "hiddenSelected", + "propType": "bool", + "description": "选择后是否立即隐藏菜单 (mode=multiple/tag 模式生效)" + }, { + "name": "onRemove", + "propType": "func", + "description": "tag 删除回调\n@param {object} item 渲染节点的item" + }, { + "name": "onFocus", + "propType": "func", + "description": "焦点事件" + }, { + "name": "onBlur", + "propType": "func", + "description": "失去焦点事件" + }, { + "name": "onMouseEnter", + "propType": "func" + }, { + "name": "onMouseLeave", + "propType": "func" + }, { + "name": "onKeyDown", + "propType": "func" + }, { + "name": "locale", + "propType": "object" + }], + "configure": { + "component": { + "isContainer": true, + "nestingRule": { + "childWhitelist": "Select.Option" + } + }, + "props": [{ + "name": "mode", + "title": "选择器模式", + "setter": { + "componentName": "RadioGroupSetter", + "props": { + "defaultValue": "single", + "dataSource": [{ + "value": "single", + "label": "single" + }, { + "value": "multiple", + "label": "multiple" + }, { + "value": "tag", + "label": "tag" + }] + } + } + }, { + "name": "mode", + "title": "选择器模式", + "setter": { + "componentName": "SelectSetter", + "props": { + "defaultValue": "single", + "dataSource": [{ + "value": "single", + "label": "single" + }, { + "value": "multiple", + "label": "multiple" + }, { + "value": "tag", + "label": "tag" + }] + } + } + }, { + "name": "value", + "title": "受控值", + "setter": "StringSetter" + }, { + "name": "hasBorder", + "title": "是否有边框", + "setter": { + "componentName": "BoolSetter", + "props": { + "defaultValue": true + } + } + }, { + "name": "maxTagCount", + "title": "最多显示多少个 tag", + "setter": "NumberSetter" + }, { + "name": "maxTagCount", + "title": "最多显示多少个 tag", + "setter": "ExpressionSetter" + }, { + "name": "MixinSetter", + "placeholder": "混合", + "setter": { + "componentName": "MixinSetter", + "props": { + "types": [{ + "name": "StringSetter", + "props": {} + }, { + "name": "TextAreaSetter", + "props": {} + }, { + "name": "SelectSetter", + "props": { + "hasClear": true, + "dataSource": [{ + "label": "上", + "value": "t" + }, { + "label": "右", + "value": "r" + }, { + "label": "下", + "value": "b" + }, { + "label": "左", + "value": "l" + }] + } + }, { + "name": "NumberSetter", + "props": {} + }, { + "name": "BoolSetter", + "props": {} + }], + "defaultType": "SelectSetter" + } + } + }, { + "type": "group", + "name": "扩展 Setter", + "items": [{ + "name": "TextAreaSetter", + "setter": "TextAreaSetter" + }, { + "name": "date", + "title": "测试日期", + "setter": "DateSetter" + }, { + "name": "date", + "title": "测试日期-年", + "setter": "DateYearSetter" + }, { + "name": "date", + "title": "测试日期-月", + "setter": "DateMonthSetter" + }, { + "name": "date", + "title": "测试日期-区间", + "setter": "DateRangeSetter" + }] + }, { + "type": "group", + "name": "ArraySetter", + "items": [{ + "name": "arrayValue1", + "title": "字符数组", + "setter": { + "componentName": "ArraySetter", + "props": { + "itemSetter": { + "componentName": "StringSetter", + "initialValue": "" + } + } + } + }, { + "name": "arrayValue2", + "title": "数字数组", + "setter": { + "componentName": "ArraySetter", + "props": { + "itemSetter": { + "componentName": "NumberSetter", + "initialValue": 0 + } + } + } + }, { + "name": "arrayValue3", + "title": "混合数组", + "setter": { + "componentName": "ArraySetter", + "props": { + "itemSetter": { + "componentName": "MixinSetter", + "props": { + "types": [{ + "name": "StringSetter", + "props": {} + }, { + "name": "ExpressionSetter", + "props": {} + }, { + "name": "RadioGroupSetter", + "props": { + "hasClear": true, + "dataSource": [{ + "label": "上", + "value": "t" + }, { + "label": "右", + "value": "r" + }, { + "label": "下", + "value": "b" + }, { + "label": "左", + "value": "l" + }] + } + }], + "defaultType": "SelectSetter" + } + } + } + } + }, { + "name": "arrayValue4", + "title": "对象数组", + "setter": { + "componentName": "ArraySetter", + "props": { + "itemSetter": { + "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 + }] + } + } + }, { + "name": "address", + "title": "工作地点", + "setter": "TextAreaSetter" + }] + }] + } + }, + "initialValue": {} + } + } + } + }, { + "name": "arrayValue5", + "title": "对象数组", + "setter": { + "componentName": "ArraySetter", + "props": { + "itemSetter": { + "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" + } + }, + "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": {} + }, + "mode": "popup" + } + } + }], + "extraProps": { + "defaultCollapsed": false + } + }, { + "type": "group", + "name": "ObjectSetter", + "items": [{ + "name": "objectValue1", + "title": "对象数据1", + "setter": { + "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" + } + }, + "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": {} + } + }, { + "name": "objectValue2", + "title": "对象数据2", + "setter": { + "componentName": "ObjectSetter", + "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" + } + }, + "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": {} + } + }] + }] + } + }, + "Select.Option": { + "componentName": "Select.Option", + "title": "选择项", + "devMode": "proCode", + "npm": { + "package": "@alifd/next", + "version": "1.19.18", + "destructuring": true, + "exportName": "Select", + "subName": "Option" + }, + "props": [{ + "name": "value", + "propType": { + "type": "any", + "isRequired": true + }, + "description": "选项值" + }, { + "name": "disabled", + "propType": "bool", + "description": "是否禁用" + }, { + "name": "children", + "propType": "any" + }], + "configure": { + "component": { + "isContainer": true, + "nestingRule": { + "parentWhitelist": "Select" + } + } + } + } + }, + "componentList": [{ + "title": "基础", + "icon": "", + "children": [{ + "componentName": "Button", + "title": "按钮", + "icon": "", + "package": "@alife/next", + "library": "Next", + "snippets": [{ + "title": "private", + "screenshot": "https://img.alicdn.com/tfs/TB16gZhi.H1gK0jSZSyXXXtlpXa-192-144.png", + "schema": { + "componentName": "Button", + "props": { + "type": "primary" + }, + "children": "Primary" + } + }, { + "title": "secondary", + "screenshot": "https://img.alicdn.com/tfs/TB11Hkji1H2gK0jSZFEXXcqMpXa-192-144.png", + "schema": { + "componentName": "Button", + "props": { + "type": "secondary" + }, + "children": "secondary" + } + }, { + "title": "normal", + "screenshot": "", + "schema": { + "componentName": "Button", + "props": { + "type": "normal" + }, + "children": "normal" + } + }] + }] + }, { + "title": "表单", + "icon": "", + "children": [{ + "componentName": "Input", + "library": "Next", + "title": "输入框", + "icon": "", + "package": "@alife/next", + "snippets": [{ + "title": "普通", + "screenshot": "", + "schema": { + "componentName": "Input", + "props": {} + } + }] + }, { + "componentName": "Select", + "library": "Next", + "title": "选择框", + "icon": "", + "package": "@alife/next", + "snippets": [{ + "title": "默认", + "screenshot": "", + "schema": { + "componentName": "Select", + "props": {} + } + }] + }, { + "componentName": "NumberPicker", + "library": "Next", + "title": "数字", + "icon": "", + "package": "@alife/next", + "snippets": [{ + "title": "默认", + "screenshot": "", + "schema": { + "componentName": "NumberPicker", + "props": {} + } + }] + }] + }, { + "title": "其他", + "icon": "", + "children": [{ + "componentName": "Div", + "library": "Next", + "title": "容器", + "icon": "", + "snippets": [{ + "title": "默认", + "screenshot": "", + "schema": { + "componentName": "Div", + "props": {} + } + }] + }] + }] +} diff --git a/packages/editor/public/favicon.png b/packages/demo/public/favicon.png similarity index 100% rename from packages/editor/public/favicon.png rename to packages/demo/public/favicon.png diff --git a/packages/editor/public/index.html b/packages/demo/public/index.html similarity index 100% rename from packages/editor/public/index.html rename to packages/demo/public/index.html diff --git a/packages/demo/public/schema.json b/packages/demo/public/schema.json new file mode 100644 index 000000000..424628106 --- /dev/null +++ b/packages/demo/public/schema.json @@ -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": "重置" + }] + }] + }] + }] +} \ No newline at end of file diff --git a/packages/editor/src/config/assets.js b/packages/demo/src/config/assets.js similarity index 82% rename from packages/editor/src/config/assets.js rename to packages/demo/src/config/assets.js index e134d2630..3668cc44f 100644 --- a/packages/editor/src/config/assets.js +++ b/packages/demo/src/config/assets.js @@ -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: {} } } ] diff --git a/packages/demo/src/config/components.js b/packages/demo/src/config/components.js new file mode 100644 index 000000000..4dcb82b9b --- /dev/null +++ b/packages/demo/src/config/components.js @@ -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) +}; diff --git a/packages/editor/src/config/constants.js b/packages/demo/src/config/constants.js similarity index 100% rename from packages/editor/src/config/constants.js rename to packages/demo/src/config/constants.js diff --git a/packages/editor/src/config/locale/en-US.js b/packages/demo/src/config/locale/en-US.js similarity index 100% rename from packages/editor/src/config/locale/en-US.js rename to packages/demo/src/config/locale/en-US.js diff --git a/packages/editor/src/config/locale/index.js b/packages/demo/src/config/locale/index.js similarity index 100% rename from packages/editor/src/config/locale/index.js rename to packages/demo/src/config/locale/index.js diff --git a/packages/editor/src/config/locale/ja-JP.js b/packages/demo/src/config/locale/ja-JP.js similarity index 100% rename from packages/editor/src/config/locale/ja-JP.js rename to packages/demo/src/config/locale/ja-JP.js diff --git a/packages/editor/src/config/locale/zh-CN.js b/packages/demo/src/config/locale/zh-CN.js similarity index 100% rename from packages/editor/src/config/locale/zh-CN.js rename to packages/demo/src/config/locale/zh-CN.js diff --git a/packages/editor/src/config/locale/zh-TW.js b/packages/demo/src/config/locale/zh-TW.js similarity index 100% rename from packages/editor/src/config/locale/zh-TW.js rename to packages/demo/src/config/locale/zh-TW.js diff --git a/packages/demo/src/config/skeleton.js b/packages/demo/src/config/skeleton.js new file mode 100644 index 000000000..46fbd550f --- /dev/null +++ b/packages/demo/src/config/skeleton.js @@ -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); + }); + } + } +}; diff --git a/packages/editor/src/config/theme.scss b/packages/demo/src/config/theme.scss similarity index 100% rename from packages/editor/src/config/theme.scss rename to packages/demo/src/config/theme.scss diff --git a/packages/editor/src/config/utils.js b/packages/demo/src/config/utils.js similarity index 100% rename from packages/editor/src/config/utils.js rename to packages/demo/src/config/utils.js diff --git a/packages/editor/src/global.scss b/packages/demo/src/global.scss similarity index 100% rename from packages/editor/src/global.scss rename to packages/demo/src/global.scss diff --git a/packages/demo/src/index.tsx b/packages/demo/src/index.tsx new file mode 100644 index 000000000..37b7de293 --- /dev/null +++ b/packages/demo/src/index.tsx @@ -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('当前页面不存在
节点.'); +} + +ReactDOM.render(, ICE_CONTAINER); diff --git a/packages/editor/src/plugins/designer/index.scss b/packages/demo/src/plugins/designer/index.scss similarity index 100% rename from packages/editor/src/plugins/designer/index.scss rename to packages/demo/src/plugins/designer/index.scss diff --git a/packages/editor/src/plugins/designer/index.tsx b/packages/demo/src/plugins/designer/index.tsx similarity index 83% rename from packages/editor/src/plugins/designer/index.tsx rename to packages/demo/src/plugins/designer/index.tsx index 1043308e3..52be98ec1 100644 --- a/packages/editor/src/plugins/designer/index.tsx +++ b/packages/demo/src/plugins/designer/index.tsx @@ -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 { 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 ( { eventPipe={editor as any} componentMetadatas={Object.values(assets.components) as any} simulatorProps={{ - library: Object.values(assets.packages), + library: Object.values(assets.packages || {}) }} /> ); diff --git a/packages/editor/statics/assets.json b/packages/demo/statics/assets.json similarity index 100% rename from packages/editor/statics/assets.json rename to packages/demo/statics/assets.json diff --git a/packages/editor/statics/lowcode-renderer.js b/packages/demo/statics/lowcode-renderer.js similarity index 100% rename from packages/editor/statics/lowcode-renderer.js rename to packages/demo/statics/lowcode-renderer.js diff --git a/packages/editor-skeleton/tests/index.js b/packages/demo/tests/index.js similarity index 100% rename from packages/editor-skeleton/tests/index.js rename to packages/demo/tests/index.js diff --git a/packages/editor/tsconfig.json b/packages/demo/tsconfig.json similarity index 100% rename from packages/editor/tsconfig.json rename to packages/demo/tsconfig.json diff --git a/packages/editor-framework/.eslintignore b/packages/editor-core/.eslintignore similarity index 100% rename from packages/editor-framework/.eslintignore rename to packages/editor-core/.eslintignore diff --git a/packages/editor-core/.eslintrc.js b/packages/editor-core/.eslintrc.js new file mode 100644 index 000000000..4d6be3c66 --- /dev/null +++ b/packages/editor-core/.eslintrc.js @@ -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 + }, +}); diff --git a/packages/editor-framework/.gitignore b/packages/editor-core/.gitignore similarity index 100% rename from packages/editor-framework/.gitignore rename to packages/editor-core/.gitignore diff --git a/packages/editor-framework/.prettierrc b/packages/editor-core/.prettierrc similarity index 100% rename from packages/editor-framework/.prettierrc rename to packages/editor-core/.prettierrc diff --git a/packages/editor-core/README.md b/packages/editor-core/README.md new file mode 100644 index 000000000..6e9e79f5b --- /dev/null +++ b/packages/editor-core/README.md @@ -0,0 +1,11 @@ +# demo component + +t-s-demo + +intro component + +## API + +| 参数名 | 说明 | 必填 | 类型 | 默认值 | 备注 | +| ------ | ---- | ---- | ---- | ------ | ---- | +| | | | | | | diff --git a/packages/editor-framework/build.json b/packages/editor-core/build.json similarity index 100% rename from packages/editor-framework/build.json rename to packages/editor-core/build.json diff --git a/packages/editor-framework/package.json b/packages/editor-core/package.json similarity index 84% rename from packages/editor-framework/package.json rename to packages/editor-core/package.json index 3088159e2..1f50249c7 100644 --- a/packages/editor-framework/package.json +++ b/packages/editor-core/package.json @@ -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" } diff --git a/packages/editor/src/framework/areaManager.ts b/packages/editor-core/src/areaManager.ts similarity index 52% rename from packages/editor/src/framework/areaManager.ts rename to packages/editor-core/src/areaManager.ts index 28f24eb0e..f6ce5e782 100644 --- a/packages/editor/src/framework/areaManager.ts +++ b/packages/editor-core/src/areaManager.ts @@ -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]; + } + } } diff --git a/packages/editor/src/framework/context.ts b/packages/editor-core/src/context.ts similarity index 100% rename from packages/editor/src/framework/context.ts rename to packages/editor-core/src/context.ts diff --git a/packages/editor/src/framework/definitions.ts b/packages/editor-core/src/definitions.ts similarity index 90% rename from packages/editor/src/framework/definitions.ts rename to packages/editor-core/src/definitions.ts index 6bc6d2653..f9db0bd01 100644 --- a/packages/editor/src/framework/definitions.ts +++ b/packages/editor-core/src/definitions.ts @@ -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 & { init?: (editor: Editor) => void; + defaultProps?: { + locale?: LocaleType; + messages?: I18nMessages; + }; }; export interface PluginClassSet { diff --git a/packages/editor/src/framework/editor.ts b/packages/editor-core/src/editor.ts similarity index 90% rename from packages/editor/src/framework/editor.ts rename to packages/editor-core/src/editor.ts index cf1147e2c..6a0b938a0 100644 --- a/packages/editor/src/framework/editor.ts +++ b/packages/editor-core/src/editor.ts @@ -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 { @@ -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方法 diff --git a/packages/editor/src/framework/index.ts b/packages/editor-core/src/index.ts similarity index 50% rename from packages/editor/src/framework/index.ts rename to packages/editor-core/src/index.ts index 72af6e859..fba0e30c9 100644 --- a/packages/editor/src/framework/index.ts +++ b/packages/editor-core/src/index.ts @@ -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 }; diff --git a/packages/editor/src/framework/pluginFactory.tsx b/packages/editor-core/src/pluginFactory.tsx similarity index 82% rename from packages/editor/src/framework/pluginFactory.tsx rename to packages/editor-core/src/pluginFactory.tsx index f32edd9e9..2eff60e9e 100644 --- a/packages/editor/src/framework/pluginFactory.tsx +++ b/packages/editor-core/src/pluginFactory.tsx @@ -27,21 +27,26 @@ export default function pluginFactory(Comp: PluginClass): React.ComponentType(); // 注册插件 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 { + 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 { + 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 { + 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 { + 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); + }); + }); +} diff --git a/packages/editor/src/framework/utils.ts b/packages/editor-core/src/utils.ts similarity index 96% rename from packages/editor/src/framework/utils.ts rename to packages/editor-core/src/utils.ts index aced7d318..dd6d6d251 100644 --- a/packages/editor/src/framework/utils.ts +++ b/packages/editor-core/src/utils.ts @@ -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, }; } diff --git a/packages/editor-framework/tsconfig.json b/packages/editor-core/tsconfig.json similarity index 100% rename from packages/editor-framework/tsconfig.json rename to packages/editor-core/tsconfig.json diff --git a/packages/editor-framework/.eslintrc.js b/packages/editor-framework/.eslintrc.js deleted file mode 100644 index ebda54735..000000000 --- a/packages/editor-framework/.eslintrc.js +++ /dev/null @@ -1,5 +0,0 @@ -const { eslint, deepmerge } = require('@ice/spec'); - -module.exports = deepmerge(eslint, { - rules: {}, -}); diff --git a/packages/editor-framework/demo/usage.md b/packages/editor-framework/demo/usage.md deleted file mode 100644 index 9f19eae0b..000000000 --- a/packages/editor-framework/demo/usage.md +++ /dev/null @@ -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 ( -
-
- ); - } -} - -ReactDOM.render(( - -), mountNode); -```` diff --git a/packages/editor-framework/src/context.ts b/packages/editor-framework/src/context.ts deleted file mode 100644 index 78d3ce177..000000000 --- a/packages/editor-framework/src/context.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createContext } from 'react'; -const context = createContext({}); -export default context; diff --git a/packages/editor-framework/src/definitions.ts b/packages/editor-framework/src/definitions.ts deleted file mode 100644 index 87dd5dca2..000000000 --- a/packages/editor-framework/src/definitions.ts +++ /dev/null @@ -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 -}; - -export interface PluginConfig { - pluginKey: string, - type: string, - props: object, - config: NpmConfig, - pluginProps: object -}; - -export type HooksConfig = Array; - -export interface HookConfig { - -}; - - diff --git a/packages/editor-framework/src/editor.ts b/packages/editor-framework/src/editor.ts deleted file mode 100644 index b3328df2d..000000000 --- a/packages/editor-framework/src/editor.ts +++ /dev/null @@ -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, lisenter:function):void { - if (!Array.isArray(events)) return; - events.forEach(event => this.on(event, lisenter)); - } - - batchOnce(events:Array, lisenter:function):void { - if (!Array.isArray(events)) return; - events.forEach(event => this.once(event, lisenter)); - } - - batchOff(events:Array, 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; - }; -} diff --git a/packages/editor-framework/src/index.ts b/packages/editor-framework/src/index.ts deleted file mode 100644 index aac18d138..000000000 --- a/packages/editor-framework/src/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -import Editor from './editor'; - - -export default Editor; \ No newline at end of file diff --git a/packages/editor-framework/src/plugin.ts b/packages/editor-framework/src/plugin.ts deleted file mode 100644 index 33d81cc48..000000000 --- a/packages/editor-framework/src/plugin.ts +++ /dev/null @@ -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 { - 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 - } - } - - return Plugin; -} - - - -export class Plugin extends PureComponent { - 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; - } -} diff --git a/packages/editor-framework/src/utils.ts b/packages/editor-framework/src/utils.ts deleted file mode 100644 index d1c5eaf2b..000000000 --- a/packages/editor-framework/src/utils.ts +++ /dev/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 = []; - 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() - }; -} \ No newline at end of file diff --git a/packages/editor-skeleton/.eslintrc.js b/packages/editor-skeleton/.eslintrc.js index 18ae6baa7..4d6be3c66 100644 --- a/packages/editor-skeleton/.eslintrc.js +++ b/packages/editor-skeleton/.eslintrc.js @@ -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 }, }); diff --git a/packages/editor-skeleton/demo/usage.md b/packages/editor-skeleton/demo/usage.md deleted file mode 100644 index 9f19eae0b..000000000 --- a/packages/editor-skeleton/demo/usage.md +++ /dev/null @@ -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 ( -
-
- ); - } -} - -ReactDOM.render(( - -), mountNode); -```` diff --git a/packages/editor-skeleton/package.json b/packages/editor-skeleton/package.json index 73adadb4c..d3b4976db 100644 --- a/packages/editor-skeleton/package.json +++ b/packages/editor-skeleton/package.json @@ -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", diff --git a/packages/editor-skeleton/src/components/LeftIcon/index.tsx b/packages/editor-skeleton/src/components/LeftIcon/index.tsx deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.scss b/packages/editor-skeleton/src/components/LeftPlugin/index.scss index 9c6922129..f78fca1ee 100644 --- a/packages/editor-skeleton/src/components/LeftPlugin/index.scss +++ b/packages/editor-skeleton/src/components/LeftPlugin/index.scss @@ -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; + } } diff --git a/packages/editor-skeleton/src/components/LeftPlugin/index.tsx b/packages/editor-skeleton/src/components/LeftPlugin/index.tsx index d9a637a21..7770c942c 100644 --- a/packages/editor-skeleton/src/components/LeftPlugin/index.tsx +++ b/packages/editor-skeleton/src/components/LeftPlugin/index.tsx @@ -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 (
{ + onClick={(): void => { if (disabled) return; - //考虑到弹窗情况,延时发送消息 + // 考虑到弹窗情况,延时发送消息 clickCallback && clickCallback(); + onClick && onClick(); }} > - {dotted ? ( + {marked ? ( @@ -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 && ( - { - onClick && onClick.call(null, appHelper); - }} - {...localeProps} - {...(addonProps || {})} - /> - )) || - null; + if (!pluginKey || !type || !props) return null; + const node = Comp ? ( + { + onClick && onClick.call(null, editor); + }} + {...pluginProps} + /> + ) : null; switch (type) { case 'LinkIcon': return ( - {this.renderIcon(() => { - onClick && onClick.call(null, appHelper); + {this.renderIcon((): void => { + onClick && onClick.call(null, editor); })} ); case 'Icon': - return this.renderIcon(() => { - onClick && onClick.call(null, appHelper); + return this.renderIcon((): void => { + onClick && onClick.call(null, editor); }); case 'DialogIcon': return ( - {this.renderIcon(() => { - onClick && onClick.call(null, appHelper); + {this.renderIcon((): void => { + onClick && onClick.call(null, editor); this.handleOpen(); })} { - 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 ( { - 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 { ); 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 ? {node} : node; + return marked ? {node} : node; default: return null; } diff --git a/packages/editor/src/skeleton/components/Panel/index.scss b/packages/editor-skeleton/src/components/Panel/index.scss similarity index 100% rename from packages/editor/src/skeleton/components/Panel/index.scss rename to packages/editor-skeleton/src/components/Panel/index.scss diff --git a/packages/editor-skeleton/src/components/Panel/index.tsx b/packages/editor-skeleton/src/components/Panel/index.tsx index e69de29bb..6cdb1bd40 100644 --- a/packages/editor-skeleton/src/components/Panel/index.tsx +++ b/packages/editor-skeleton/src/components/Panel/index.tsx @@ -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 { + 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 ( +
+ {this.props.children} +
+
+ ); + } +} diff --git a/packages/editor-skeleton/src/components/TopIcon/index.scss b/packages/editor-skeleton/src/components/TopIcon/index.scss index 1cb3bdfdf..065fb859e 100644 --- a/packages/editor-skeleton/src/components/TopIcon/index.scss +++ b/packages/editor-skeleton/src/components/TopIcon/index.scss @@ -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; diff --git a/packages/editor-skeleton/src/components/TopIcon/index.tsx b/packages/editor-skeleton/src/components/TopIcon/index.tsx index 8e81c9c3a..939f77ffc 100644 --- a/packages/editor-skeleton/src/components/TopIcon/index.tsx +++ b/packages/editor-skeleton/src/components/TopIcon/index.tsx @@ -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 { + 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 ( - + +
); } } diff --git a/packages/editor-skeleton/src/components/TopPlugin/index.tsx b/packages/editor-skeleton/src/components/TopPlugin/index.tsx index 04f5d0e59..71eccf28a 100644 --- a/packages/editor-skeleton/src/components/TopPlugin/index.tsx +++ b/packages/editor-skeleton/src/components/TopPlugin/index.tsx @@ -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 = ( { + 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 ? {node} : node; + return marked ? {node} : 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 = { - onClick && onClick.call(null, editor); - }} - {...pluginProps} - />; + if (!pluginKey || !type) return null; + const node = Comp ? ( + { + onClick && onClick.call(null, editor); + }} + {...pluginProps} + /> + ) : null; switch (type) { case 'LinkIcon': return ( - {this.renderIcon(() => { + {this.renderIcon((): void => { onClick && onClick.call(null, editor); })} ); case 'Icon': - return this.renderIcon(() => { + return this.renderIcon((): void => { onClick && onClick.call(null, editor); }); case 'DialogIcon': return ( - {this.renderIcon(() => { + {this.renderIcon((): void => { onClick && onClick.call(null, editor); this.handleOpen(); })} { + 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 ( { + trigger={this.renderIcon((): void => { onClick && onClick.call(null, editor); })} triggerType={['click', 'hover']} @@ -166,7 +211,7 @@ export default class TopPlugin extends PureComponent { ); case 'Custom': - return dotted ? {node} : node; + return marked ? {node} : node; default: return null; } diff --git a/packages/editor-skeleton/src/config/skeleton.ts b/packages/editor-skeleton/src/config/skeleton.ts index 9e3f6898f..ff8b4c563 100644 --- a/packages/editor-skeleton/src/config/skeleton.ts +++ b/packages/editor-skeleton/src/config/skeleton.ts @@ -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 {}; diff --git a/packages/editor-skeleton/src/config/utils.ts b/packages/editor-skeleton/src/config/utils.ts index e3c4c9e37..ff8b4c563 100644 --- a/packages/editor-skeleton/src/config/utils.ts +++ b/packages/editor-skeleton/src/config/utils.ts @@ -1,5 +1 @@ -// 菜单配置 - -const asideMenuConfig = []; - -export { asideMenuConfig }; +export default {}; diff --git a/packages/editor-skeleton/src/global.scss b/packages/editor-skeleton/src/global.scss index 0a710b895..a6b6aea91 100644 --- a/packages/editor-skeleton/src/global.scss +++ b/packages/editor-skeleton/src/global.scss @@ -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; } } diff --git a/packages/editor-skeleton/src/index.ts b/packages/editor-skeleton/src/index.ts new file mode 100644 index 000000000..c519f0552 --- /dev/null +++ b/packages/editor-skeleton/src/index.ts @@ -0,0 +1,7 @@ +import Skeleton from './skeleton'; +import Panel from './components/Panel'; +import TopIcon from './components/TopIcon'; + +export default Skeleton; + +export { Panel, TopIcon }; diff --git a/packages/editor-skeleton/src/index.tsx b/packages/editor-skeleton/src/index.tsx deleted file mode 100644 index 3f1c89522..000000000 --- a/packages/editor-skeleton/src/index.tsx +++ /dev/null @@ -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 ( - - -
- -
- - - - -
-
-
-
- ); - } -} diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.scss b/packages/editor-skeleton/src/layouts/CenterArea/index.scss index b2584ed2b..40a1806a6 100644 --- a/packages/editor-skeleton/src/layouts/CenterArea/index.scss +++ b/packages/editor-skeleton/src/layouts/CenterArea/index.scss @@ -1,3 +1,3 @@ .lowcode-center-area { - padding: 12px; + padding: 0; } diff --git a/packages/editor-skeleton/src/layouts/CenterArea/index.tsx b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx index dc2b38d25..3261b4bbe 100644 --- a/packages/editor-skeleton/src/layouts/CenterArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/CenterArea/index.tsx @@ -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 { + 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
; + 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 ( +
+ {visiblePluginList.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( + + ); + } + return null; + }, + )} +
+ ); } } diff --git a/packages/editor-skeleton/src/layouts/LeftArea/index.scss b/packages/editor-skeleton/src/layouts/LeftArea/index.scss index dac1b6b0a..8c69f42fc 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/index.scss +++ b/packages/editor-skeleton/src/layouts/LeftArea/index.scss @@ -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); } } diff --git a/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx index 6c58c12ef..735b77a27 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx +++ b/packages/editor-skeleton/src/layouts/LeftArea/nav.tsx @@ -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
; + 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 ( + 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 ( +
+
{this.renderPluginList(bottomList)}
+
{this.renderPluginList(topList)}
+
+ ); } } diff --git a/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx index ab41860fb..ccb81cfe0 100644 --- a/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx +++ b/packages/editor-skeleton/src/layouts/LeftArea/panel.tsx @@ -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
; + 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 ( + + {list.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( + + + + ); + } + return null; + }, + )} + + ); } } diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.scss b/packages/editor-skeleton/src/layouts/RightArea/index.scss index 120ef4f11..7373933b0 100644 --- a/packages/editor-skeleton/src/layouts/RightArea/index.scss +++ b/packages/editor-skeleton/src/layouts/RightArea/index.scss @@ -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; } } } diff --git a/packages/editor-skeleton/src/layouts/RightArea/index.tsx b/packages/editor-skeleton/src/layouts/RightArea/index.tsx index 31273a4e2..5925284da 100644 --- a/packages/editor-skeleton/src/layouts/RightArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/RightArea/index.tsx @@ -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
; + 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 => ( +
+ {!!icon && } + {title} +
+ ); + if (marked) { + return {renderTitle()}; + } + return renderTitle(); + }; + + renderTabPanels = (list: PluginConfig[], height: string): React.ReactNode => { + if (isEmpty(list)) { + return null; + } + return ( + + {list.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( + + + + ); + } + return null; + }, + )} + + ); + }; + + renderPanels = (list: PluginConfig[], height: string): React.ReactNode => { + return list.map( + (item): React.ReactNode => { + const Comp = this.areaManager.getPluginClass(item.pluginKey); + if (Comp) { + return ( +
+ +
+ ); + } + 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 ( +
+ {this.renderTabPanels(tabList, height)} + {this.renderPanels(panelList, height)} +
+ ); } } diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.scss b/packages/editor-skeleton/src/layouts/TopArea/index.scss index ca8bbd825..c0a132f84 100644 --- a/packages/editor-skeleton/src/layouts/TopArea/index.scss +++ b/packages/editor-skeleton/src/layouts/TopArea/index.scss @@ -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; + } } diff --git a/packages/editor-skeleton/src/layouts/TopArea/index.tsx b/packages/editor-skeleton/src/layouts/TopArea/index.tsx index c2f60a637..68c73f202 100644 --- a/packages/editor-skeleton/src/layouts/TopArea/index.tsx +++ b/packages/editor-skeleton/src/layouts/TopArea/index.tsx @@ -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 { + 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 ( - - {!isDivider && ( - - )} - - ); - }); + 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 ( + + {!isDivider && ( + + )} + + ); + }, + ); + }; + + 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 (
-
{this.renderPluginList(leftList)}
-
{this.renderPluginList(rightList)}
+
+ {this.renderPluginList(leftList)} +
+
+ {this.renderPluginList(rightList)} +
); } diff --git a/packages/editor-skeleton/src/locale/en-US.js b/packages/editor-skeleton/src/locale/en-US.js index 36e3b219c..ff8b4c563 100644 --- a/packages/editor-skeleton/src/locale/en-US.js +++ b/packages/editor-skeleton/src/locale/en-US.js @@ -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 {}; diff --git a/packages/editor-skeleton/src/locale/zh-CN.js b/packages/editor-skeleton/src/locale/zh-CN.js index 2d5229d2c..ff8b4c563 100644 --- a/packages/editor-skeleton/src/locale/zh-CN.js +++ b/packages/editor-skeleton/src/locale/zh-CN.js @@ -1,10 +1 @@ -export default { - loading: '加载中...', - rejectRedirect: '开发中,已阻止发生跳转', - expand: '展开', - fold: '收起', - pageNotExist: '当前访问地址不存在', - enterFromAppCenter: '请从应用中心入口重新进入', - noPermission: '抱歉,您暂无开发权限', - getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限', -}; +export default {}; diff --git a/packages/editor/src/skeleton/index.tsx b/packages/editor-skeleton/src/skeleton.tsx similarity index 65% rename from packages/editor/src/skeleton/index.tsx rename to packages/editor-skeleton/src/skeleton.tsx index c30a0fc88..cf04022ec 100644 --- a/packages/editor/src/skeleton/index.tsx +++ b/packages/editor-skeleton/src/skeleton.tsx @@ -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 { +export class Skeleton extends PureComponent { static displayName = 'LowcodeEditorSkeleton'; static getDerivedStateFromError(): SkeletonState { return { - __hasError: true + __hasError: true, }; } @@ -58,7 +61,7 @@ export default class Skeleton extends PureComponent { this.setState({ - initReady: false + initReady: false, }); editor.emit('editor.beforeReset'); this.init(true); @@ -100,13 +107,14 @@ export default class Skeleton extends PureComponent { 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 - +
@@ -140,3 +148,33 @@ export default class Skeleton extends PureComponent = ( + props, +): React.ReactElement => { + const { config, ...otherProps } = props; + return ( + + ( + + )} + /> + + ); +}; + +export default SkeletonWithRouter; diff --git a/packages/editor/tests/index.js b/packages/editor-skeleton/test/index.js similarity index 100% rename from packages/editor/tests/index.js rename to packages/editor-skeleton/test/index.js diff --git a/packages/editor/src/config/components.js b/packages/editor/src/config/components.js deleted file mode 100644 index 596f984c8..000000000 --- a/packages/editor/src/config/components.js +++ /dev/null @@ -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) -}; diff --git a/packages/editor/src/config/skeleton.js b/packages/editor/src/config/skeleton.js deleted file mode 100644 index d28706570..000000000 --- a/packages/editor/src/config/skeleton.js +++ /dev/null @@ -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); - } - } - }); - } - } -}; diff --git a/packages/editor/src/index.d.ts b/packages/editor/src/index.d.ts deleted file mode 100644 index 1fe34f40d..000000000 --- a/packages/editor/src/index.d.ts +++ /dev/null @@ -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; -} diff --git a/packages/editor/src/index.tsx b/packages/editor/src/index.tsx deleted file mode 100644 index a16cd4b4e..000000000 --- a/packages/editor/src/index.tsx +++ /dev/null @@ -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('当前页面不存在
节点.'); -} - -ReactDOM.render( - - ( - - )} - /> - , - ICE_CONTAINER -); diff --git a/packages/editor/src/plugins/undoRedo/index.scss b/packages/editor/src/plugins/undoRedo/index.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/editor/src/plugins/undoRedo/index.tsx b/packages/editor/src/plugins/undoRedo/index.tsx deleted file mode 100644 index 345a84356..000000000 --- a/packages/editor/src/plugins/undoRedo/index.tsx +++ /dev/null @@ -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 { - 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 ( -
- - -
- ); - } -} diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.scss b/packages/editor/src/skeleton/components/LeftPlugin/index.scss deleted file mode 100644 index f1e7876d3..000000000 --- a/packages/editor/src/skeleton/components/LeftPlugin/index.scss +++ /dev/null @@ -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; - } -} diff --git a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx b/packages/editor/src/skeleton/components/LeftPlugin/index.tsx deleted file mode 100644 index eafe894ee..000000000 --- a/packages/editor/src/skeleton/components/LeftPlugin/index.tsx +++ /dev/null @@ -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 { - 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 ( -
{ - if (disabled) return; - // 考虑到弹窗情况,延时发送消息 - clickCallback && clickCallback(); - - onClick && onClick(); - }} - > - {marked ? ( - - - - ) : ( - - )} -
- ); - }; - - 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 && ( - { - onClick && onClick.call(null, editor); - }} - {...pluginProps} - /> - )) || - null; - - switch (type) { - case 'LinkIcon': - return ( - - {this.renderIcon((): void => { - onClick && onClick.call(null, editor); - })} - - ); - case 'Icon': - return this.renderIcon((): void => { - onClick && onClick.call(null, editor); - }); - case 'DialogIcon': - return ( - - {this.renderIcon((): void => { - onClick && onClick.call(null, editor); - this.handleOpen(); - })} - { - 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} - - - ); - case 'BalloonIcon': - return ( - { - onClick && onClick.call(null, editor); - })} - align="r" - triggerType={['click', 'hover']} - {...(props.balloonProps || {})} - > - {node} - - ); - case 'PanelIcon': - return this.renderIcon((): void => { - onClick && onClick.call(null, editor); - this.handleOpen(); - }); - case 'Custom': - return marked ? {node} : node; - default: - return null; - } - } -} diff --git a/packages/editor/src/skeleton/components/Panel/index.tsx b/packages/editor/src/skeleton/components/Panel/index.tsx deleted file mode 100644 index 611877d46..000000000 --- a/packages/editor/src/skeleton/components/Panel/index.tsx +++ /dev/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 { - 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 ( -
- {this.props.children} -
-
- ); - } -} diff --git a/packages/editor/src/skeleton/components/TopIcon/index.scss b/packages/editor/src/skeleton/components/TopIcon/index.scss deleted file mode 100644 index 2dd2e3bb7..000000000 --- a/packages/editor/src/skeleton/components/TopIcon/index.scss +++ /dev/null @@ -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); - } -} diff --git a/packages/editor/src/skeleton/components/TopIcon/index.tsx b/packages/editor/src/skeleton/components/TopIcon/index.tsx deleted file mode 100644 index 2c14d8d33..000000000 --- a/packages/editor/src/skeleton/components/TopIcon/index.tsx +++ /dev/null @@ -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 { - 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 ( -
- -
- ); - } -} diff --git a/packages/editor/src/skeleton/components/TopPlugin/index.scss b/packages/editor/src/skeleton/components/TopPlugin/index.scss deleted file mode 100644 index 4bdd7b8d2..000000000 --- a/packages/editor/src/skeleton/components/TopPlugin/index.scss +++ /dev/null @@ -1,2 +0,0 @@ -.lowcode-top-addon { -} diff --git a/packages/editor/src/skeleton/components/TopPlugin/index.tsx b/packages/editor/src/skeleton/components/TopPlugin/index.tsx deleted file mode 100644 index 70c653f4f..000000000 --- a/packages/editor/src/skeleton/components/TopPlugin/index.tsx +++ /dev/null @@ -1,195 +0,0 @@ -import React, { PureComponent, Fragment, CSSProperties } from 'react'; - -import { Balloon, Badge, Dialog } from '@alifd/next'; -import TopIcon from '../TopIcon'; - -import './index.scss'; -import { PluginConfig, PluginClass } from '../../../framework/definitions'; -import Editor from '../../../framework/editor'; - -export interface TopPluginProps { - active?: boolean; - config: PluginConfig; - disabled?: boolean; - editor: Editor; - locked?: boolean; - marked?: boolean; - onClick?: () => void; - pluginClass: PluginClass; -} - -export interface TopPluginState { - dialogVisible: boolean; -} - -export default class TopPlugin extends PureComponent { - static displayName = 'LowcodeTopPlugin'; - - 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); - } - } - - 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(); - onClick && onClick(); - }; - - 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 dialog类型的插件初始时拿不动插件实例 - this.setState({ - dialogVisible: true - }); - }; - - renderIcon = (clickCallback): React.ReactNode => { - const { active, disabled, marked, locked, config, onClick, editor } = this.props; - const { pluginKey, props } = config || {}; - const { icon, title } = props || {}; - const node = ( - { - if (disabled) return; - // 考虑到弹窗情况,延时发送消息 - setTimeout((): void => editor.emit(`${pluginKey}.plugin.activate`), 0); - clickCallback && clickCallback(); - onClick && onClick(); - }} - /> - ); - return marked ? {node} : node; - }; - - render(): React.ReactNode { - const { active, marked, locked, disabled, config, editor, pluginClass: Comp, style } = this.props; - const { pluginKey, pluginProps, props, type } = config || {}; - const { onClick, title } = props || {}; - const { dialogVisible } = this.state; - if (!pluginKey || !type) return null; - const node = - (Comp && ( - { - onClick && onClick.call(null, editor); - }} - {...pluginProps} - /> - )) || - null; - - switch (type) { - case 'LinkIcon': - return ( - - {this.renderIcon((): void => { - onClick && onClick.call(null, editor); - })} - - ); - case 'Icon': - return this.renderIcon((): void => { - onClick && onClick.call(null, editor); - }); - case 'DialogIcon': - return ( - - {this.renderIcon((): void => { - onClick && onClick.call(null, editor); - this.handleOpen(); - })} - { - 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} - - - ); - case 'BalloonIcon': - return ( - { - onClick && onClick.call(null, editor); - })} - triggerType={['click', 'hover']} - {...props.balloonProps} - > - {node} - - ); - case 'Custom': - return marked ? {node} : node; - default: - return null; - } - } -} diff --git a/packages/editor/src/skeleton/config/skeleton.ts b/packages/editor/src/skeleton/config/skeleton.ts deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/config/skeleton.ts +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/config/utils.ts b/packages/editor/src/skeleton/config/utils.ts deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/config/utils.ts +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/global.scss b/packages/editor/src/skeleton/global.scss deleted file mode 100644 index 72f7b8525..000000000 --- a/packages/editor/src/skeleton/global.scss +++ /dev/null @@ -1,33 +0,0 @@ -body { - font-family: PingFangSC-Regular, Roboto, Helvetica Neue, Helvetica, Tahoma, Arial, PingFang SC-Light, Microsoft YaHei; - font-size: 12px; - padding: 0; - margin: 0; - * { - box-sizing: border-box; - } - color: $color-text1-3; -} - -.next-loading { - .next-loading-wrap { - height: 100%; - } -} -.lowcode-editor { - .lowcode-main-content { - position: absolute; - top: 50px; - left: 0; - right: 0; - bottom: 0; - display: flex; - background-color: rgba(31, 56, 88, 0.06); - } - .lowcode-center-area { - flex: 1; - display: flex; - flex-direction: column; - overflow: auto; - } -} diff --git a/packages/editor/src/skeleton/layouts/CenterArea/index.scss b/packages/editor/src/skeleton/layouts/CenterArea/index.scss deleted file mode 100644 index 40a1806a6..000000000 --- a/packages/editor/src/skeleton/layouts/CenterArea/index.scss +++ /dev/null @@ -1,3 +0,0 @@ -.lowcode-center-area { - padding: 0; -} diff --git a/packages/editor/src/skeleton/layouts/CenterArea/index.tsx b/packages/editor/src/skeleton/layouts/CenterArea/index.tsx deleted file mode 100644 index cf27d66db..000000000 --- a/packages/editor/src/skeleton/layouts/CenterArea/index.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React, { PureComponent } from 'react'; - -import Editor from '../../../framework/editor'; -import './index.scss'; -import AreaManager from '../../../framework/areaManager'; - -export interface CenterAreaProps { - editor: Editor; -} - -export default class CenterArea extends PureComponent { - static displayName = 'LowcodeCenterArea'; - - private editor: Editor; - - private areaManager: AreaManager; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(this.editor, 'centerArea'); - } - - 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 ( -
- {visiblePluginList.map( - (item): React.ReactNode => { - const Comp = this.editor.components[item.pluginKey]; - return ; - } - )} -
- ); - } -} diff --git a/packages/editor/src/skeleton/layouts/LeftArea/index.scss b/packages/editor/src/skeleton/layouts/LeftArea/index.scss deleted file mode 100644 index 8c69f42fc..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/index.scss +++ /dev/null @@ -1,23 +0,0 @@ -.lowcode-left-area-nav { - width: 50px; - height: 100%; - background-color: $card-background; - border-right: 2px solid $color-line1-1; - position: relative; - .top-area { - position: absolute; - top: 0; - width: 100%; - padding: 12px 0; - background-color: $card-background; - max-height: 100%; - } - .bottom-area { - position: absolute; - bottom: 0; - width: 100%; - padding: 12px 0; - background-color: $card-background; - max-height: calc(100% - 20px); - } -} diff --git a/packages/editor/src/skeleton/layouts/LeftArea/index.tsx b/packages/editor/src/skeleton/layouts/LeftArea/index.tsx deleted file mode 100644 index 7f4684546..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import Nav from './nav'; -import Panel from './panel'; - -export default { - Nav, - Panel -}; diff --git a/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx b/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx deleted file mode 100644 index cd749073a..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/nav.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import React, { PureComponent } from 'react'; -import LeftPlugin from '../../components/LeftPlugin'; -import './index.scss'; -import Editor from '../../../framework/editor'; -import { PluginConfig } from '../../../framework/definitions'; -import AreaManager from '../../../framework/areaManager'; -import { isEmpty } from '../../../framework/utils'; - -export interface LeftAreaNavProps { - editor: Editor; -} - -export interface LeftAreaNavState { - activeKey: string; -} - -export default class LeftAreaNav extends PureComponent { - static displayName = 'LowcodeLeftAreaNav'; - - private editor: Editor; - - private areaManager: AreaManager; - - // private cacheActiveKey: string; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(this.editor, 'leftArea'); - - this.state = { - activeKey: 'none' - }; - // this.cacheActiveKey = 'none'; - } - - componentDidMount(): void { - this.editor.on('skeleton.update', this.handleSkeletonUpdate); - this.editor.on('leftNav.change', this.handlePluginChange); - const visiblePanelPluginList = this.areaManager.getVisiblePluginList().filter(item => item.type === '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); - }); - } - }); - } - }; - - // handleCollapseClick = (): void => { - // const { activeKey } = this.state; - // if (activeKey === 'none') { - // const plugin = this.editor.plugins[this.cacheActiveKey]; - // if (plugin) { - // plugin.open().then(() => { - // this.updateActiveKey(this.cacheActiveKey); - // }); - // } - // } else { - // const plugin = this.editor.plugins[activeKey]; - // if (plugin) { - // plugin.close().then(() => { - // this.updateActiveKey('none'); - // }); - // } - // } - // }; - - handlePluginClick = (item: PluginConfig): void => { - if (item.type === 'PanelIcon') { - this.handlePluginChange(item.pluginKey); - } - }; - - updateActiveKey = (key: string): void => { - // if (key === 'none') { - // this.cacheActiveKey = this.state.activeKey; - // } - 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.editor.pluginStatus[item.pluginKey]; - return ( - 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 ( -
-
{this.renderPluginList(bottomList)}
-
- {/* */} - {this.renderPluginList(topList)} -
-
- ); - } -} diff --git a/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx b/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx deleted file mode 100644 index 31f2c5343..000000000 --- a/packages/editor/src/skeleton/layouts/LeftArea/panel.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React, { PureComponent, Fragment } from 'react'; -import Panel from '../../components/Panel'; -import './index.scss'; -import Editor from '../../../framework/editor'; -import AreaManager from '../../../framework/areaManager'; - -export interface LeftAreaPanelProps { - editor: Editor; -} - -export interface LeftAreaPanelState { - activeKey: string; -} - -export default class LeftAreaPanel extends PureComponent { - 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' - }; - } - - 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 ( - - {list.map( - (item): React.ReactElement => { - const Comp = this.editor.components[item.pluginKey]; - return ( - - - - ); - } - )} - - ); - } -} diff --git a/packages/editor/src/skeleton/layouts/RightArea/index.scss b/packages/editor/src/skeleton/layouts/RightArea/index.scss deleted file mode 100644 index 7373933b0..000000000 --- a/packages/editor/src/skeleton/layouts/RightArea/index.scss +++ /dev/null @@ -1,39 +0,0 @@ -.lowcode-right-area { - width: 262px; - height: 100%; - background-color: $card-background; - border-left: 2px solid $color-line1-1; - - .right-panel { - overflow: auto; - // border-top: 2px solid $color-line1-1; - } - - //tab定义 - .right-tabs.next-tabs { - .next-tabs-nav { - width: 100%; - .next-tabs-tab-inner { - padding-left: 0; - padding-right: 0; - } - .right-plugin-title { - text-align: center; - &.locked { - color: red !important; - } - &.active { - color: $color-brand1-9 !important; - } - &.disabled { - cursor: not-allowed; - color: $color-text1-1; - } - .next-icon { - line-height: 15px; - margin-right: 2px; - } - } - } - } -} diff --git a/packages/editor/src/skeleton/layouts/RightArea/index.tsx b/packages/editor/src/skeleton/layouts/RightArea/index.tsx deleted file mode 100644 index 64c44af50..000000000 --- a/packages/editor/src/skeleton/layouts/RightArea/index.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import React, { PureComponent } from 'react'; -import { Tab, Badge, Icon } from '@alifd/next'; -import './index.scss'; -import Editor from '../../../framework/editor'; -import AreaManager from '../../../framework/areaManager'; -import { PluginConfig } from '../../../framework/definitions'; -import { isEmpty } from '../../../framework/utils'; - -export interface RightAreaProps { - editor: Editor; -} - -export interface RightAreaState { - activeKey: string; -} - -export default class RightArea extends PureComponent { - 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: '' - }; - } - - 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 pluginStatus = this.editor.pluginStatus; - const activeKey = this.state.activeKey; - if (pluginStatus[activeKey] && pluginStatus[activeKey].visible) { - this.forceUpdate(); - } else { - const currentPlugin = this.editor.plugins[activeKey]; - if (currentPlugin) { - currentPlugin.close().then((): void => { - this.setState( - { - activeKey: '' - }, - (): void => { - const visiblePluginList = this.areaManager.getVisiblePluginList(); - const firstPlugin = visiblePluginList && visiblePluginList[0]; - if (firstPlugin) { - this.handlePluginChange(firstPlugin.pluginKey); - } - } - ); - }); - } - } - } - }; - - handlePluginChange = (key: string, isinit?: boolean): void => { - const activeKey = this.state.activeKey; - const plugins = this.editor.plugins || {}; - const openPlugin = (): void => { - if (!plugins[key]) { - console.error(`plugin ${key} has not regist in the editor`); - return; - } - plugins[key].open().then((): void => { - this.editor.set('rightNav', key); - this.setState({ - activeKey: key - }); - }); - }; - if (key === activeKey && !isinit) return; - if (activeKey && plugins[activeKey]) { - plugins[activeKey].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 => ( -
- {!!icon && } - {title} -
- ); - if (marked) { - return {renderTitle()}; - } - return renderTitle(); - }; - - renderTabPanels = (list: PluginConfig[], height: string): React.ReactNode => { - if (isEmpty(list)) { - return null; - } - return ( - - {list.map( - (item): React.ReactElement => { - const Comp = this.editor.components[item.pluginKey]; - return ( - - - - ); - } - )} - - ); - }; - - renderPanels = (list: PluginConfig[], height: string): React.ReactNode => { - return list.map( - (item): React.ReactElement => { - const Comp = this.editor.components[item.pluginKey]; - return ( -
- -
- ); - } - ); - }; - - 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 ( -
- {this.renderTabPanels(tabList, height)} - {this.renderPanels(panelList, height)} -
- ); - } -} diff --git a/packages/editor/src/skeleton/layouts/TopArea/index.scss b/packages/editor/src/skeleton/layouts/TopArea/index.scss deleted file mode 100644 index c0a132f84..000000000 --- a/packages/editor/src/skeleton/layouts/TopArea/index.scss +++ /dev/null @@ -1,30 +0,0 @@ -.lowcode-top-area { - 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; - } -} diff --git a/packages/editor/src/skeleton/layouts/TopArea/index.tsx b/packages/editor/src/skeleton/layouts/TopArea/index.tsx deleted file mode 100644 index d6b33453c..000000000 --- a/packages/editor/src/skeleton/layouts/TopArea/index.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import React, { PureComponent } from 'react'; -import { Grid } from '@alifd/next'; -import TopPlugin from '../../components/TopPlugin'; -import './index.scss'; -import Editor from '../../../framework/index'; -import { PluginConfig } from '../../../framework/definitions'; -import AreaManager from '../../../framework/areaManager'; - -const { Row, Col } = Grid; - -export interface TopAreaProps { - editor: Editor; -} - -export default class TopArea extends PureComponent { - static displayName = 'LowcodeTopArea'; - - private areaManager: AreaManager; - - private editor: Editor; - - constructor(props) { - super(props); - this.editor = props.editor; - this.areaManager = new AreaManager(props.editor, 'topArea'); - } - - 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(); - } - }; - - renderPluginList = (list: PluginConfig[] = []): React.ReactElement[] => { - return list.map( - (item, idx): React.ReactElement => { - const isDivider = item.type === 'Divider'; - return ( - - {!isDivider && ( - - )} - - ); - } - ); - }; - - 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 currList = align === 'right' ? rightList : leftList; - if (currList.length === 0 || currList[currList.length - 1].type === 'Divider') return; - } - if (align === 'right') { - rightList.push(item); - } else { - leftList.push(item); - } - }); - return ( -
-
- {this.renderPluginList(leftList)} -
-
- {this.renderPluginList(rightList)} -
-
- ); - } -} diff --git a/packages/editor/src/skeleton/locale/en-US.js b/packages/editor/src/skeleton/locale/en-US.js deleted file mode 100644 index 936701e33..000000000 --- a/packages/editor/src/skeleton/locale/en-US.js +++ /dev/null @@ -1,10 +0,0 @@ -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' -}; diff --git a/packages/editor/src/skeleton/locale/ja-JP.js b/packages/editor/src/skeleton/locale/ja-JP.js deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/locale/ja-JP.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/locale/zh-CN.js b/packages/editor/src/skeleton/locale/zh-CN.js deleted file mode 100644 index efe4ea898..000000000 --- a/packages/editor/src/skeleton/locale/zh-CN.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - loading: '加载中...', - rejectRedirect: '开发中,已阻止发生跳转', - expand: '展开', - fold: '收起', - pageNotExist: '当前访问地址不存在', - enterFromAppCenter: '请从应用中心入口重新进入', - noPermission: '抱歉,您暂无开发权限', - getPermission: '请移步应用中心申请开发权限, 或联系 {owners} 开通权限' -}; diff --git a/packages/editor/src/skeleton/locale/zh-TW.js b/packages/editor/src/skeleton/locale/zh-TW.js deleted file mode 100644 index ff8b4c563..000000000 --- a/packages/editor/src/skeleton/locale/zh-TW.js +++ /dev/null @@ -1 +0,0 @@ -export default {}; diff --git a/packages/editor/src/skeleton/skeleton.tsx b/packages/editor/src/skeleton/skeleton.tsx deleted file mode 100644 index eababc5f7..000000000 --- a/packages/editor/src/skeleton/skeleton.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import React, { PureComponent } from 'react'; - -import { HashRouter as Router, Route } from 'react-router-dom'; -import { Loading, ConfigProvider } from '@alifd/next'; - -import Editor from '../framework/editor'; -import { EditorConfig, Utils, PluginComponents } from '../framework/definitions'; -import { comboEditorConfig, parseSearch } from '../framework/utils'; - -import defaultConfig from './config/skeleton'; -import skeletonUtils from './config/utils'; - -import TopArea from './layouts/TopArea'; -import LeftArea from './layouts/LeftArea'; -import CenterArea from './layouts/CenterArea'; -import RightArea from './layouts/RightArea'; - -import './global.scss'; - -let renderIdx = 0; - -export interface SkeletonProps { - components: PluginComponents; - config: EditorConfig; - utils: Utils; -} - -export interface SkeletonState { - initReady: boolean; - skeletonKey: string; - __hasError?: boolean; -} - -export default class Skeleton extends PureComponent { - static displayName = 'LowcodeEditorSkeleton'; - - static getDerivedStateFromError() { - return { - __hasError: true - }; - } - - private editor: Editor; - - constructor(props) { - super(props); - - this.state = { - initReady: false, - skeletonKey: `skeleton${renderIdx}` - }; - - this.init(); - } - - componentWillUnmount() { - this.editor && this.editor.destroy(); - } - - componentDidCatch(err) { - console.error(err); - } - - init = (isReset: boolean = false): void => { - if (this.editor) { - this.editor.destroy(); - } - const { utils, config, components } = this.props; - const editor = (this.editor = new Editor(comboEditorConfig(defaultConfig, config), components, { - ...skeletonUtils, - ...utils - })); - window.__ctx = { - editor, - appHelper: editor - }; - editor.once('editor.reset', () => { - this.setState({ - initReady: false - }); - editor.emit('editor.beforeReset'); - this.init(true); - }); - - this.editor.init().then(() => { - this.setState( - { - initReady: true, - //刷新IDE时生成新的skeletonKey保证插件生命周期重新执行 - skeletonKey: isReset ? `skeleton${++renderIdx}` : this.state.skeletonKey - }, - () => { - editor.emit('editor.ready'); - isReset && editor.emit('ide.afterReset'); - } - ); - }); - }; - - render() { - const { initReady, skeletonKey, __hasError } = this.state; - if (__hasError || !this.editor) { - return 'error'; - } - - return ( - - { - const { location, history, match } = props; - location.query = parseSearch(location.search); - this.editor.set('location', location); - this.editor.set('history', history); - this.editor.set('match', match); - console.log('&&&&&&&&&&'); - return ( - - -
- -
- - - - -
-
-
-
- ); - }} - /> -
- ); - } -} diff --git a/packages/material-parser/package.json b/packages/material-parser/package.json index 23a22f626..c14336985 100644 --- a/packages/material-parser/package.json +++ b/packages/material-parser/package.json @@ -42,6 +42,7 @@ }, "license": "MIT", "dependencies": { + "@ali/lowcode-editor-core": "^0.0.1", "@babel/generator": "^7.8.4", "@babel/parser": "^7.8.4", "@babel/traverse": "^7.8.4", diff --git a/packages/editor-framework/README.md b/packages/plugin-components-pane/README.md similarity index 100% rename from packages/editor-framework/README.md rename to packages/plugin-components-pane/README.md diff --git a/packages/plugin-components-pane/abc.json b/packages/plugin-components-pane/abc.json new file mode 100644 index 000000000..dce1f92ed --- /dev/null +++ b/packages/plugin-components-pane/abc.json @@ -0,0 +1,4 @@ +{ + "type": "ice-scripts", + "builder": "@ali/builder-ice-scripts" +} diff --git a/packages/plugin-components-pane/build.json b/packages/plugin-components-pane/build.json new file mode 100644 index 000000000..77627cdf9 --- /dev/null +++ b/packages/plugin-components-pane/build.json @@ -0,0 +1,9 @@ +{ + "plugins": [ + "build-plugin-component", + "build-plugin-fusion", + ["build-plugin-moment-locales", { + "locales": ["zh-cn"] + }] + ] +} \ No newline at end of file diff --git a/packages/plugin-components-pane/package.json b/packages/plugin-components-pane/package.json new file mode 100644 index 000000000..b7aefb40e --- /dev/null +++ b/packages/plugin-components-pane/package.json @@ -0,0 +1,59 @@ +{ + "name": "@ali/lowcode-plugin-components-pane", + "version": "0.0.1", + "description": "alibaba lowcode editor component-list plugin", + "files": [ + "demo/", + "es/", + "lib/", + "build/" + ], + "main": "lib/index.tsx", + "module": "es/index.js", + "stylePath": "style.js", + "scripts": { + "start": "build-scripts start", + "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}\" " + }, + "keywords": [ + "lowcode", + "editor" + ], + "author": "xiayang.xy", + "dependencies": { + "@ali/iceluna-addon-component-list": "^1.0.11", + "@ali/iceluna-comp-material-show": "^1.0.10", + "@ali/iceluna-sdk": "^1.0.6-beta.6", + "@alifd/next": "^1.19.19", + "prop-types": "^15.5.8", + "react": "^16.8.1", + "react-dom": "^16.8.1", + "react-router-dom": "^5.1.2" + }, + "peerDependencies": { + "@ali/lowcode-editor-core": "0.0.1" + }, + "devDependencies": { + "@alib/build-scripts": "^0.1.3", + "@alifd/next": "1.x", + "@ice/spec": "^0.1.1", + "@types/lodash": "^4.14.149", + "@types/react": "^16.9.13", + "@types/react-dom": "^16.9.4", + "build-plugin-component": "^0.2.7-1", + "build-plugin-fusion": "^0.1.0", + "build-plugin-moment-locales": "^0.1.0", + "eslint": "^6.0.1", + "prettier": "^1.19.1", + "react": "^16.8.0", + "react-dom": "^16.8.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/ice-lab/react-materials/tree/master/scaffolds/ice-ts" + }, + "homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-engine-skeleton@0.0.1/build/index.html" +} diff --git a/packages/plugin-components-pane/src/index.scss b/packages/plugin-components-pane/src/index.scss new file mode 100644 index 000000000..73206cc27 --- /dev/null +++ b/packages/plugin-components-pane/src/index.scss @@ -0,0 +1,25 @@ +.lowcode-component-list { + height: 100%; + display: flex; + flex-flow: column; + .title { + height: 40px; + line-height: 39px; + border-bottom: 1px solid $color-line1-1; + padding: 0 8px; + position: relative; + } + .search { + display: block; + width: auto; + margin: 8px 8px 0; + } + .select { + display: block; + margin: 8px 8px 4px; + } + .components-show { + flex: 1 1 0%; + overflow: hidden; + } +} diff --git a/packages/plugin-components-pane/src/index.tsx b/packages/plugin-components-pane/src/index.tsx new file mode 100644 index 000000000..fc88400eb --- /dev/null +++ b/packages/plugin-components-pane/src/index.tsx @@ -0,0 +1,189 @@ +import React, { PureComponent } from 'react'; +import { Icon, Search, Select } from '@alifd/next'; +import MaterialShow from '@ali/iceluna-comp-material-show'; +import { PluginProps } from '@ali/lowcode-editor-core/lib/definitions'; + +import './index.scss'; + +export interface LibrayInfo { + label: string; + value: string; +} + +export interface IState { + loading: boolean; + libs: LibrayInfo[]; + searchKey: string; + currentLib: string; + componentList: object[]; +} + +export default class ComponentListPlugin extends PureComponent< + PluginProps, + IState +> { + static displayName = 'LowcodeComponentListPlugin'; + + constructor(props) { + super(props); + this.state = { + loading: false, + libs: [ + { + label: '全部', + value: 'all', + }, + ], + searchKey: '', + currentLib: 'all', + componentList: [], + }; + } + + componentDidMount(): void { + const { editor } = this.props; + if (editor.assets) { + this.initComponentList(); + } else { + editor.once('editor.ready', this.initComponentList); + } + } + + transformMaterial = (componentList): any => { + 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), + }; + }), + }; + }), + }; + }); + }; + + initComponentList = (): void => { + const { editor } = this.props; + const assets = editor.assets || {}; + const list: string[] = []; + const libs: LibrayInfo[] = []; + Object.values(assets.packages).forEach((item): void => { + list.push(item.library); + libs.push({ + label: item.title, + value: item.library, + }); + }); + + if (list.length > 1) { + libs.unshift({ + label: '全部', + value: list.join(','), + }); + } + + const componentList = this.transformMaterial(assets.componentList); + + this.setState({ + libs, + componentList, + currentLib: libs[0] && libs[0].value, + }); + + 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, + ); + } + }, + }); + }; + + searchAction = (value: string): void => { + this.setState({ + searchKey: value, + }); + }; + + filterMaterial = (): any => { + const { searchKey, currentLib, componentList } = this.state; + const libs = currentLib.split(','); + return (componentList || []) + .map(cate => { + return { + ...cate, + items: (cate.items || []).filter(item => { + let libFlag = libs.some(lib => lib == item.library); + + let keyFlag = true; + if (searchKey) { + keyFlag = + `${item.name || ''} ${item.title || ''}` + .toLowerCase() + .indexOf(searchKey.trim().toLowerCase()) >= 0; + } else { + keyFlag = item.is_show === undefined || !!(item.is_show == 1); + } + return libFlag && keyFlag; + }), + }; + }) + .filter(cate => { + return cate.items && cate.items.length > 0; + }); + }; + + render(): React.ReactNode { + const { libs, loading, currentLib } = this.state; + return ( +
+
+ + 组件库 +
+ +