diff --git a/packages/demo/build.plugin.js b/packages/demo/build.plugin.js index 50ac34ddd..6c89f05c1 100644 --- a/packages/demo/build.plugin.js +++ b/packages/demo/build.plugin.js @@ -16,7 +16,7 @@ module.exports = ({ onGetWebpackConfig }) => { .plugin('MonacoWebpackPlugin') // 第一项为具体插件,第二项为插件参数 .use(new MonacoWebpackPlugin({ - languages:["javascript","css","json"] + languages:["typescript","css","json"] }), []); config.plugins.delete('hot'); diff --git a/packages/demo/src/vision/index.ts b/packages/demo/src/vision/index.ts index e01b724c6..3ab9ef186 100644 --- a/packages/demo/src/vision/index.ts +++ b/packages/demo/src/vision/index.ts @@ -11,6 +11,7 @@ import PageHistoryPane from '@ali/ve-page-history-pane'; // import I18nPane from '@ali/ve-i18n-pane'; import I18nManagePane from '@ali/ve-i18n-manage-pane'; import ActionPane from '@ali/ve-action-pane'; +import SourceEditor from '@ali/lowcode-plugin-source-editor'; import fetchContext from '@ali/vu-legao-design-fetch-context'; import EventBindDialog from '@ali/lowcode-plugin-event-bind-dialog'; import loadUrls from './loader'; @@ -95,16 +96,43 @@ function initDemoPanes() { type: 'Widget', content: EventBindDialog, }); - skeleton.add({ - area: 'leftArea', - name: 'icon1', - type: 'Dock', - props: { - align: 'bottom', - icon: 'set', - description: '设置' - }, - }); + + // skeleton.add({ + // area: 'left', + // name: 'sourceEditor', + // type: "PanelDock", + // content: SourceEditor, + // props: { + // align: undefined, + // description: "动作面板", + // onDestroy: undefined, + // icon: 'set', + // onInit: undefined + // }, + // panelProps:{ + // height: 300, + // help: undefined, + // hideTitleBar: true, + // maxHeight: 800, + // maxWidth: 1200, + // title: "动作面板", + // width: 600 + // } + + // }); + + // skeleton.add({ + // area: 'leftArea', + // name: 'icon1', + // type: 'PanelDock', + // props: { + // align: 'bottom', + // icon: 'set', + // description: '设置' + // }, + // }); + + skeleton.add({ area: 'leftArea', name: 'icon2', @@ -129,6 +157,8 @@ function initDemoPanes() { children: '发布', }), }); + + skeleton.add({ area: 'topArea', type: 'Dock', @@ -322,6 +352,7 @@ function initActionPane() { enableHeaderTip: true, }; + Panes.add(ActionPane, { props, }); diff --git a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx index 53b051ca5..faa11d4f3 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx +++ b/packages/designer/src/builtin-simulator/bem-tools/border-selecting.tsx @@ -16,6 +16,7 @@ import { SimulatorContext } from '../context'; import { BuiltinSimulatorHost } from '../host'; import { OffsetObserver } from '../../designer'; import { Node } from '../../document'; +import NodeSelector from '../node-selector'; @observer export class BorderSelectingInstance extends Component<{ @@ -101,6 +102,7 @@ class Toolbar extends Component<{ observed: OffsetObserver }> { return (
{actions} +
); } diff --git a/packages/designer/src/builtin-simulator/bem-tools/borders.less b/packages/designer/src/builtin-simulator/bem-tools/borders.less index af21cc622..b25834d18 100644 --- a/packages/designer/src/builtin-simulator/bem-tools/borders.less +++ b/packages/designer/src/builtin-simulator/bem-tools/borders.less @@ -30,7 +30,8 @@ } } - &-action { + &-action, + .ve-icon-button.ve-action-save { box-sizing: border-box; cursor: pointer; height: 20px; diff --git a/packages/designer/src/builtin-simulator/node-selector/index.less b/packages/designer/src/builtin-simulator/node-selector/index.less new file mode 100644 index 000000000..c630e0914 --- /dev/null +++ b/packages/designer/src/builtin-simulator/node-selector/index.less @@ -0,0 +1,82 @@ +@import '~@ali/ve-less-variables/index.less'; + +// 样式直接沿用之前的样式,优化了下命名 +.instance-node-selector { + position: relative; + margin-right: 2px; + color: var(--color-icon-white, @title-bgcolor); + border-radius: @global-border-radius; + margin-right: 2px; + pointer-events: auto; + flex-grow: 0; + flex-shrink: 0; + + svg { + width: 16px; + height: 16px; + margin-right: 5px; + flex-grow: 0; + flex-shrink: 0; + max-width: inherit; + path { + fill: var(--color-icon-white, @title-bgcolor); + } + } + &-current { + background: var(--color-brand, @brand-color-1); + padding: 0 6px; + display: flex; + align-items: center; + height: 20px; + cursor: pointer; + color: var(--color-icon-white, @title-bgcolor); + border-radius: 3px; + + &-title { + padding-right: 6px; + color: var(--color-icon-white, @title-bgcolor); + } + } + &-list { + position: absolute; + left: 0; + right: 0; + opacity: 0; + visibility: hidden; + } + &-node { + margin: 2px 0; + &-content { + padding-left: 6px; + background: #78869a; + display: inline-flex; + border-radius: 3px; + align-items: center; + height: 20px; + color: var(--color-icon-white, @title-bgcolor); + cursor: pointer; + overflow: visible; + } + &-title { + padding-right: 6px; + // margin-left: 5px; + color: var(--color-icon-white, @title-bgcolor); + cursor: pointer; + overflow: visible; + } + &:hover { + opacity: 0.8; + } + } +} + +&:hover { + .instance-node-selector-current { + color: ar(--color-text-reverse, @white-alpha-2); + } + .instance-node-selector-popup { + visibility: visible; + opacity: 1; + transition: 0.2s all ease-in; + } +} diff --git a/packages/designer/src/builtin-simulator/node-selector/index.tsx b/packages/designer/src/builtin-simulator/node-selector/index.tsx new file mode 100644 index 000000000..f63f5c282 --- /dev/null +++ b/packages/designer/src/builtin-simulator/node-selector/index.tsx @@ -0,0 +1,110 @@ +import { Overlay } from '@alifd/next'; +import React from 'react'; +import './index.less'; +import { Title } from '@ali/lowcode-editor-core'; + +import { Node, ParentalNode } from '@ali/lowcode-designer'; + +const { Popup } = Overlay; + +export interface IProps { + node: Node; +} + +export interface IState { + parentNodes: Node[]; +} + +type UnionNode = Node | ParentalNode | null; + +export default class InstanceNodeSelector extends React.Component { + state: IState = { + parentNodes: [], + }; + + componentDidMount() { + const parentNodes = this.getParentNodes(this.props.node); + this.setState({ + parentNodes, + }); + } + + // 获取节点的父级节点(最多获取5层) + getParentNodes = (node: Node) => { + const parentNodes = []; + let currentNode: UnionNode = node; + + while (currentNode && parentNodes.length < 5) { + currentNode = currentNode.getParent(); + if (currentNode) { + parentNodes.push(currentNode); + } + } + return parentNodes; + }; + + onSelect = (node: Node) => () => { + if (node && typeof node.select === 'function') { + node.select(); + } + }; + onMouseOver = (node: Node) => (_: any, flag = true) => { + if (node && typeof node.hover === 'function') { + node.hover(flag); + } + }; + onMouseOut = (node: Node) => (_: any, flag = false) => { + if (node && typeof node.hover === 'function') { + node.hover(flag); + } + }; + renderNodes = (node: Node) => { + const nodes = this.state.parentNodes || []; + const children = nodes.map((node, key) => { + return ( +
+
+ + </div> + </div> + ); + }); + return children; + }; + + render() { + const { node } = this.props; + return ( + <div className="instance-node-selector"> + <Popup + trigger={ + <div className="instance-node-selector-current"> + <Title + className="instance-node-selector-node-title" + title={{ + label: node.title, + icon: node.icon, + }} + /> + </div> + } + triggerType="hover" + > + <div className="instance-node-selector">{this.renderNodes(node)}</div> + </Popup> + </div> + ); + } +} diff --git a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx index 77c9eda0f..182a521b7 100644 --- a/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-primary-pane.tsx @@ -96,6 +96,7 @@ export class SettingsPrimaryPane extends Component<{ editor: Editor }> { return ( <div className="lc-settings-main"> <Tab + key={settings.id} navClassName="lc-settings-tabs" animation={false} excessMode="dropdown" diff --git a/packages/editor-skeleton/src/layouts/workbench.less b/packages/editor-skeleton/src/layouts/workbench.less index 70a07c0ca..14e89355e 100644 --- a/packages/editor-skeleton/src/layouts/workbench.less +++ b/packages/editor-skeleton/src/layouts/workbench.less @@ -272,6 +272,9 @@ body { margin-left: 4px; margin-right: 4px; } + .ve-quick-search-trigger{ + display: flex; + } } } .lc-workbench-body { diff --git a/packages/plugin-source-editor/src/index.scss b/packages/plugin-source-editor/src/index.scss index 02b902227..bd570e930 100644 --- a/packages/plugin-source-editor/src/index.scss +++ b/packages/plugin-source-editor/src/index.scss @@ -19,5 +19,16 @@ height: 100%; } + .full-screen-container{ + position: absolute; + top: 40px; + right: 20px; + cursor: pointer; + img{ + width: 20px; + height: 20px; + } + } + } diff --git a/packages/plugin-source-editor/src/index.tsx b/packages/plugin-source-editor/src/index.tsx index fb957dbb6..0a4aaefdb 100644 --- a/packages/plugin-source-editor/src/index.tsx +++ b/packages/plugin-source-editor/src/index.tsx @@ -1,11 +1,8 @@ import { Component, isValidElement, ReactElement, ReactNode } from 'react'; import { Tab, Search, Input, Button } from '@alifd/next'; -import { Editor } from '@ali/lowcode-editor-core'; +import {Editor} from '@ali/lowcode-editor-core'; import { js_beautify, css_beautify } from 'js-beautify'; import MonacoEditor from 'react-monaco-editor'; - -// import lolizer from './sorceEditorPlugin', - import { Designer } from '@ali/lowcode-designer'; const TAB_KEY = { JS_TAB: 'js_tab', @@ -46,23 +43,33 @@ interface FunctionEventParam { export default class SourceEditor extends Component<{ editor: Editor; - panel?: any }> { - private monocoEditer: any; - private editorCmd: any; + private monocoEditor: Object; + private editorCmd: Object; + private editorRef = React.createRef(); + private editorNode: Object; + private editorParentNode: Object; - state: any = { + state = { isShow: false, tabKey: TAB_KEY.JS_TAB, }; - async componentWillMount() { + componentWillMount() { const { editor } = this.props; editor.on('leftPanel.show', (key: String) => { - if (key === 'sourceEditor' && !this.monocoEditer) { + debugger; + if (key === 'sourceEditor' && !this.monocoEditor) { this.setState({ isShow: true, }); + + + setTimeout(()=>{ + this.editorNode = this.editorRef.current; //记录当前dom节点; + this.editorParentNode = this.editorNode.parentNode; //记录父节点; + console.log(this.editorNode); + },0) } }); @@ -73,57 +80,68 @@ export default class SourceEditor extends Component<{ }); // 定位函数 - editor.on('sourceEditor.focusByFunction',(params:FunctionEventParam)=>{ + editor.on('sourceEditor.focusByFunction', (params: FunctionEventParam) => { this.callEditorEvent('sourceEditor.focusByFunction', params); this.openPluginPannel(); - }) + }); - const designer = await editor.onceGot(Designer); - // let schema = designer.project.getSchema(); - // mock data - let schema = { - componentTree: [ - { - state: { - // 初始state: 选填 对象类型/变量表达式 - btnText: 'submit', // 默认数据值: 选填 变量表达式 - }, - css: 'body {font-size: 12px;} .botton{widht:100px;color:#ff00ff}', //css样式描述: 选填 - lifeCycles: { - //生命周期: 选填 对象类型 - didMount: { - type: 'JSExpression', - value: "function() {\n \t\tconsole.log('did mount');\n\t}", + //editor.once('designer.mount', (designer: Designer) => { + // let schema = designer.project.getSchema(); + // mock data + let schema = { + componentTree: [ + { + state: { + // 初始state: 选填 对象类型/变量表达式 + btnText: 'submit', // 默认数据值: 选填 变量表达式 }, - willUnmount: { - type: 'JSExpression', - value: "function() {\n \t\tconsole.log('will umount');\n\t}", + css: 'body {font-size: 12px;} .botton{widht:100px;color:#ff00ff}', //css样式描述: 选填 + lifeCycles: { + //生命周期: 选填 对象类型 + didMount: { + type: 'JSExpression', + value: "function() {\n \t\tconsole.log('did mount');\n\t}", + }, + willUnmount: { + type: 'JSExpression', + value: "function() {\n \t\tconsole.log('will umount');\n\t}", + }, + }, + methods: { + //自定义方法对象: 选填 对象类型 + getData: { + //自定义方法: 选填 函数类型 + type: 'JSExpression', + value: "function() {\n \t\tconsole.log('testFunc');\n \t}", + }, }, }, - methods: { - //自定义方法对象: 选填 对象类型 - getData: { - //自定义方法: 选填 函数类型 - type: 'JSExpression', - value: "function() {\n \t\tconsole.log('testFunc');\n \t}", - }, - }, - }, - ], - }; + ], + }; + + this.initCode(schema); + //}); + } + + componentDidMount(){ + - this.initCode(schema); } openPluginPannel = () => { - const { panel } = this.props; - if (panel) { - panel.show(); + const { editor } = this.props; + // 判断面板是否处于激活状态 + if (!editor.leftNav || editor.leftNav != 'sourceEditor') { + // 打开面板 + editor.emit('leftNav.change', 'sourceEditor'); } - } + }; - callEditorEvent = (eventName: any, params: any) => { - if (!this.monocoEditer) { + /** + * 执行编辑器事件 + */ + callEditorEvent = (eventName, params) => { + if (!this.monocoEditor) { this.editorCmd = { eventName, params, @@ -131,16 +149,24 @@ export default class SourceEditor extends Component<{ return; } - if (eventName === 'sourceEditor.addFunction') { - this.addFunction(params); - }else if (eventName === 'sourceEditor.focusByFunction'){ - this.focusByFunctionName(params); + if (this.state.selectTab == TAB_KEY.CSS_TAB) { + this.setState({ + selectTab: TAB_KEY.JS_TAB, + }); } - + if (eventName === 'sourceEditor.addFunction') { + setTimeout(() => { + this.addFunction(params); + }, 100); + } else if (eventName === 'sourceEditor.focusByFunction') { + setTimeout(() => { + this.focusByFunctionName(params); + }, 100); + } }; - initCode = (schema: any) => { + initCode = (schema) => { let jsCode = js_beautify(transfrom.schema2Code(schema), { indent_size: 2, indent_empty_lines: true }); let css; @@ -160,19 +186,19 @@ export default class SourceEditor extends Component<{ * @param params */ addFunction(params: FunctionEventParam) { - const count = this.monocoEditer.getModel().getLineCount() || 0; - const range = new (window as any).monaco.Range(count, 1, count, 1); + const count = this.monocoEditor.getModel().getLineCount() || 0; + const range = new monaco.Range(count, 1, count, 1); const functionCode = transfrom.getNewFunctionCode(params.functionName); - this.monocoEditer.executeEdits('log-source', [ + this.monocoEditor.executeEdits('log-source', [ { identifier: 'event_id', range: range, text: functionCode, forceMoveMarkers: true }, ]); setTimeout(() => { - let newPosition = new (window as any).monaco.Position(count + 1, 2); - this.monocoEditer.setPosition(newPosition); - this.monocoEditer.focus(); + let newPosition = new monaco.Position(count + 1, 2); + this.monocoEditor.setPosition(newPosition); + this.monocoEditor.focus(); }, 100); - this.updateCode(this.monocoEditer.getModel().getValue()); + this.updateCode(this.monocoEditor.getModel().getValue()); } /** @@ -181,30 +207,24 @@ export default class SourceEditor extends Component<{ */ focusByFunctionName(params: FunctionEventParam) { const functionName = params.functionName; - const matchedResult = this.monocoEditer + const matchedResult = this.monocoEditor .getModel() .findMatches(`${functionName}\\s*\\([\\s\\S]*\\)[\\s\\S]*\\{`, false, true)[0]; if (matchedResult) { - - setTimeout(()=>{ - this.monocoEditer.revealLineInCenter(matchedResult.range.startLineNumber); - this.monocoEditer.setPosition({ + let monocoEditor = this.monocoEditor; + setTimeout(() => { + monocoEditor.revealLineInCenter(matchedResult.range.startLineNumber); + monocoEditor.setPosition({ column: matchedResult.range.endColumn, lineNumber: matchedResult.range.endLineNumber, }); - - this.monocoEditer.focus(); - },100) + monocoEditor.focus(); + }, 100); } } - editorDidMount = (editor: any, monaco: any) => { + editorDidMount = (editor, monaco) => { console.log('editorDidMount', editor); - this.monocoEditer = editor; - - if (this.editorCmd) { - this.callEditorEvent(this.editorCmd.eventName, this.editorCmd.params); - } // var commandId = editor.addCommand( // 0, @@ -215,39 +235,22 @@ export default class SourceEditor extends Component<{ // '', // ); - // monaco.languages.registerCodeLensProvider('javascript', { - // provideCodeLenses: function(model, token) { - // return { - // lenses: [ - // { - // range: { - // startLineNumber: 1, - // startColumn: 1, - // endLineNumber: 1, - // endColumn: 1, - // }, - // id: 'First Line', - // command: { - // id: commandId, - // title: 'First Line', - // }, - // }, - // ], - // }; - // }, - // resolveCodeLens: function(model, codeLens, token) { - // return codeLens; - // }, - // }); + if (this.state.selectTab == TAB_KEY.JS_TAB) { + this.monocoEditor = editor; + } + + if (this.editorCmd) { + this.callEditorEvent(this.editorCmd.eventName, this.editorCmd.params); + } }; - onTabChange = (key: any) => { + onTabChange = (key) => { this.setState({ selectTab: key, }); }; - updateCode = (newCode: any) => { + updateCode = (newCode) => { const { selectTab } = this.state; if (selectTab === TAB_KEY.JS_TAB) { this.setState({ @@ -270,22 +273,28 @@ export default class SourceEditor extends Component<{ ]; return ( - <div className="source-editor-container"> - <Tab size="small" shape="wrapped" onChange={this.onTabChange}> + <div className="source-editor-container" > + <Tab size="small" shape="wrapped" onChange={this.onTabChange} activeKey={selectTab}> {tabs.map((item) => ( <Tab.Item key={item.key} title={item.tab}> {isShow && ( - <MonacoEditor - value={selectTab == TAB_KEY.JS_TAB ? jsCode : css} - {...defaultEditorOption as any} - {...{ language: selectTab == TAB_KEY.JS_TAB ? 'javascript' : 'css' }} - onChange={(newCode) => this.updateCode(newCode)} - editorDidMount={this.editorDidMount} - /> + <div style={{ height: '100%' }} ref={this.editorRef}> + <MonacoEditor + value={selectTab == TAB_KEY.JS_TAB ? jsCode : css} + {...defaultEditorOption} + {...{ language: selectTab == TAB_KEY.JS_TAB ? 'typescript' : 'css' }} + onChange={(newCode) => this.updateCode(newCode)} + editorDidMount={(editor, monaco) => this.editorDidMount.call(this, editor, monaco)} + /> + </div> )} </Tab.Item> ))} </Tab> + + <div className="full-screen-container" onClick={this.fullScreen}> + <img src="https://gw.alicdn.com/tfs/TB1d7XqE1T2gK0jSZFvXXXnFXXa-200-200.png"></img> + </div> </div> ); } diff --git a/packages/vision-preset/src/editor.ts b/packages/vision-preset/src/editor.ts index 5dac4dfd4..6e6a828d1 100644 --- a/packages/vision-preset/src/editor.ts +++ b/packages/vision-preset/src/editor.ts @@ -136,9 +136,9 @@ skeleton.add({ // }); // 实例节点选择器,线框高亮 -addBuiltinComponentAction({ - name: 'instance-node-selector', - content: InstanceNodeSelector, - important: true, - condition: 'always' -}); +// addBuiltinComponentAction({ +// name: 'instance-node-selector', +// content: InstanceNodeSelector, +// important: true, +// condition: 'always' +// });