diff --git a/packages/designer/src/builtin-simulator/host.ts b/packages/designer/src/builtin-simulator/host.ts index 543031853..18fc15a83 100644 --- a/packages/designer/src/builtin-simulator/host.ts +++ b/packages/designer/src/builtin-simulator/host.ts @@ -20,10 +20,9 @@ import { getRectTarget, Rect, CanvasPoint, - hotkey, } from '../designer'; import { parseProps } from './utils/parse-props'; -import { isElement } from '@ali/lowcode-globals'; +import { isElement, hotkey } from '@ali/lowcode-globals'; import { ComponentMetadata } from '@ali/lowcode-globals'; import { BuiltinSimulatorRenderer } from './renderer'; import clipboard from '../designer/clipboard'; diff --git a/packages/designer/src/designer/hotkey.ts b/packages/designer/src/designer/hotkey.ts deleted file mode 100644 index a77c64a9e..000000000 --- a/packages/designer/src/designer/hotkey.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { Hotkey, isFormEvent } from '@ali/lowcode-globals'; -import { focusing } from './focusing'; -import { insertChildren } from '../document'; -import clipboard from './clipboard'; - -export const hotkey = new Hotkey(); - -// hotkey binding -hotkey.bind(['backspace', 'del'], (e: KeyboardEvent) => { - const doc = focusing.focusDesigner?.currentDocument; - if (isFormEvent(e) || !doc) { - return; - } - e.preventDefault(); - - const sel = doc.selection; - const topItems = sel.getTopNodes(); - // TODO: check can remove - topItems.forEach(node => { - doc.removeNode(node); - }); - sel.clear(); -}); - -hotkey.bind('escape', (e: KeyboardEvent) => { - // const currentFocus = focusing.current; - const sel = focusing.focusDesigner?.currentDocument?.selection; - if (isFormEvent(e) || !sel) { - return; - } - e.preventDefault(); - - sel.clear(); - // currentFocus.esc(); -}); - -// command + c copy command + x cut -hotkey.bind(['command+c', 'ctrl+c', 'command+x', 'ctrl+x'], (e, action) => { - const doc = focusing.focusDesigner?.currentDocument; - if (isFormEvent(e) || !doc) { - return; - } - e.preventDefault(); - - /* - const doc = getCurrentDocument(); - if (isFormEvent(e) || !doc || !(focusing.id === 'outline' || focusing.id === 'canvas')) { - return; - } - e.preventDefault(); - */ - - const selected = doc.selection.getTopNodes(true); - if (!selected || selected.length < 1) return; - - const componentsMap = {}; - const componentsTree = selected.map(item => item.export(false)); - - const data = { type: 'nodeSchema', componentsMap, componentsTree }; - - clipboard.setData(data); - /* - const cutMode = action.indexOf('x') > 0; - if (cutMode) { - const parentNode = selected.getParent(); - parentNode.select(); - selected.remove(); - } - */ -}); - -// command + v paste -hotkey.bind(['command+v', 'ctrl+v'], (e) => { - const designer = focusing.focusDesigner; - const doc = designer?.currentDocument; - if (isFormEvent(e) || !designer || !doc) { - return; - } - clipboard.waitPasteData(e, ({ componentsTree }) => { - if (componentsTree) { - const { target, index } = designer.getSuitableInsertion() || {}; - if (!target) { - return; - } - const nodes = insertChildren(target, componentsTree, index); - if (nodes) { - doc.selection.selectAll(nodes.map(o => o.id)); - setTimeout(() => designer.activeTracker.track(nodes[0]), 10); - } - } - }); -}); - - -// command + z undo -hotkey.bind(['command+z', 'ctrl+z'], (e) => { - const his = focusing.focusDesigner?.currentHistory; - if (isFormEvent(e) || !his) { - return; - } - - e.preventDefault(); - his.back(); -}); - -// command + shift + z redo -hotkey.bind(['command+y', 'ctrl+y', 'command+shift+z'], (e) => { - const his = focusing.focusDesigner?.currentHistory; - if (isFormEvent(e) || !his) { - return; - } - e.preventDefault(); - - his.forward(); -}); - -hotkey.mount(window); diff --git a/packages/designer/src/designer/index.ts b/packages/designer/src/designer/index.ts index f96859ae2..a928b3d49 100644 --- a/packages/designer/src/designer/index.ts +++ b/packages/designer/src/designer/index.ts @@ -1,7 +1,7 @@ +import './builtin-hotkey'; export * from './designer'; export * from './designer-view'; export * from './dragon'; -export * from './hotkey'; export * from './hovering'; export * from './location'; export * from './offset-observer'; diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index 5679c8d33..a146a9a9e 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -471,12 +471,31 @@ export class Node { isEmpty(): boolean { return this.children ? this.children.isEmpty() : true; } + getChildren() { + return this.children; + } + getComponentName() { + return this.componentName; + } + insertBefore(node: Node, ref?: Node) { + this.children?.insert(node, ref ? ref.index : null); + } + + /** + * @deprecated + */ getStatus() { return 'default'; } + /** + * @deprecated + */ setStatus() { } + /** + * @deprecated + */ getDOMNode() { const instance = this.document.simulator?.getComponentInstances(this)?.[0]; if (!instance) { @@ -484,18 +503,13 @@ export class Node { } return this.document.simulator?.findDOMNodes(instance)?.[0]; } - getChildren() { - return this.children; - } + /** + * @deprecated + */ getPage() { + console.warn('getPage is deprecated, use document instead'); return this.document; } - getComponentName() { - return this.componentName; - } - insertBefore(node: Node, ref?: Node) { - this.children?.insert(node, ref ? ref.index : null); - } } export interface NodeParent extends Node { diff --git a/packages/editor-core/src/editor.ts b/packages/editor-core/src/editor.ts index dedb944f7..70f1263ad 100644 --- a/packages/editor-core/src/editor.ts +++ b/packages/editor-core/src/editor.ts @@ -25,7 +25,7 @@ export interface HooksFuncs { [idx: number]: (msg: string, handler: (...args: []) => void) => void; } -export type KeyType = Function | Symbol | string; +export type KeyType = Function | symbol | string; export type ClassType = Function | (new (...args: any[]) => any); export interface GetOptions { forceNew?: boolean; diff --git a/packages/globals/src/utils/hotkey.ts b/packages/globals/src/utils/hotkey.ts index 570e13ebe..293d5179c 100644 --- a/packages/globals/src/utils/hotkey.ts +++ b/packages/globals/src/utils/hotkey.ts @@ -602,3 +602,6 @@ export class Hotkey { } } } + +export const hotkey = new Hotkey(); +hotkey.mount(window); diff --git a/packages/plugin-settings-pane/package.json b/packages/plugin-settings-pane/package.json index 885d14ae4..2a0fab24e 100644 --- a/packages/plugin-settings-pane/package.json +++ b/packages/plugin-settings-pane/package.json @@ -14,10 +14,11 @@ "test:snapshot": "ava --update-snapshots" }, "dependencies": { - "@ali/lowcode-designer": "^0.9.3", - "@ali/lowcode-editor-core": "^0.8.6", - "@ali/lowcode-globals": "^0.9.3", - "@ali/lowcode-plugin-outline-pane": "^0.8.9", + "@ali/lowcode-designer": "^0.9.2", + "@ali/lowcode-editor-core": "^0.8.5", + "@ali/lowcode-globals": "^0.9.2", + "@ali/lowcode-plugin-outline-pane": "^0.8.8", + "@ali/ve-stage-box": "^4.0.0", "@alifd/next": "^1.19.16", "classnames": "^2.2.6", "react": "^16" diff --git a/packages/plugin-settings-pane/src/field/fields.tsx b/packages/plugin-settings-pane/src/field/fields.tsx new file mode 100644 index 000000000..5bbc0116c --- /dev/null +++ b/packages/plugin-settings-pane/src/field/fields.tsx @@ -0,0 +1,130 @@ +import { Component } from 'react'; +import classNames from 'classnames'; +import { Icon } from '@alifd/next'; +import { Title, TitleContent } from '@ali/lowcode-globals'; +import { PopupPipe, PopupContext } from '../popup'; +import './index.less'; + +export interface FieldProps { + className?: string; + // span + title?: TitleContent | null; +} + +export class CommonField extends Component { + private shell: HTMLDivElement | null = null; + + private checkIsBlockField() { + if (this.shell) { + const setter = this.shell.lastElementChild!.firstElementChild; + if (setter && setter.classList.contains('lc-block-setter')) { + this.shell.classList.add('lc-block-field'); + this.shell.classList.remove('lc-inline-field'); + } else { + this.shell.classList.remove('lc-block-field'); + this.shell.classList.add('lc-inline-field'); + } + } + } + componentDidUpdate() { + this.checkIsBlockField(); + } + componentDidMount() { + this.checkIsBlockField(); + } + + render() { + const { className, children, title } = this.props; + return ( +
(this.shell = shell)} className={classNames('lc-field lc-inline-field', className)}> + {title && ( +
+
+ + </div> + </div> + )} + <div className="lc-field-body">{children}</div> + </div> + ); + } +} + +export interface PopupFieldProps extends FieldProps { + width?: number; +} + +export class PopupField extends Component<PopupFieldProps> { + static contextType = PopupContext; + private pipe: any; + + static defaultProps: PopupFieldProps = { + width: 300, + }; + + render() { + const { className, children, title, width } = this.props; + if (!this.pipe) { + this.pipe = (this.context as PopupPipe).create({ width }); + } + + const titleElement = title && ( + <div className="lc-field-title"> + <Title title={title} /> + </div> + ); + + this.pipe.send(<div className="lc-field-body">{children}</div>, titleElement); + + return ( + <div className={classNames('lc-field lc-popup-field', className)}> + {title && ( + <div + className="lc-field-head" + onClick={(e) => { + this.pipe.show((e as any).target); + }} + > + <div className="lc-field-title"> + <Title title={title} /> + </div> + <Icon className="lc-field-icon" type="arrow-left" size="xs" /> + </div> + )} + </div> + ); + } +} + +export type EntryFieldProps = FieldProps; + +export class EntryField extends Component<EntryFieldProps> { + constructor(props: any) { + super(props); + } + + render() { + const { propName, stageName, tip, title, className } = this.props; + const classNameList = classNames('engine-setting-field', 'engine-entry-field', className); + const fieldProps: any = {}; + + if (stageName) { + // 为 stage 切换奠定基础 + fieldProps['data-stage-target'] = stageName; + } + + const innerElements = [ + <span className="engine-field-title" key="field-title"> + {title} + </span>, + renderTip(tip, { propName }), + <Icons name="arrow" className="engine-field-arrow" size="12px" key="engine-field-arrow-icon" />, + ]; + + return ( + <div className={classNameList} {...fieldProps}> + {innerElements} + </div> + ); + } +} diff --git a/packages/plugin-settings-pane/src/field/index.tsx b/packages/plugin-settings-pane/src/field/index.tsx index 4afc13e1a..d51449ebf 100644 --- a/packages/plugin-settings-pane/src/field/index.tsx +++ b/packages/plugin-settings-pane/src/field/index.tsx @@ -3,50 +3,22 @@ import classNames from 'classnames'; import { Icon } from '@alifd/next'; import { Title, TitleContent } from '@ali/lowcode-globals'; import './index.less'; +import { CommonField, PopupField } from './fields'; export interface FieldProps { className?: string; // span title?: TitleContent | null; + type?: string; } export class Field extends Component<FieldProps> { - private shell: HTMLDivElement | null = null; - - private checkIsBlockField() { - if (this.shell) { - const setter = this.shell.lastElementChild!.firstElementChild; - if (setter && setter.classList.contains('lc-block-setter')) { - this.shell.classList.add('lc-block-field'); - this.shell.classList.remove('lc-inline-field'); - } else { - this.shell.classList.remove('lc-block-field'); - this.shell.classList.add('lc-inline-field'); - } - } - } - componentDidUpdate() { - this.checkIsBlockField(); - } - componentDidMount() { - this.checkIsBlockField(); - } - render() { - const { className, children, title } = this.props; - - return ( - <div ref={shell => (this.shell = shell)} className={classNames('lc-field lc-inline-field', className)}> - {title && ( - <div className="lc-field-head"> - <div className="lc-field-title"> - <Title title={title} /> - </div> - </div> - )} - <div className="lc-field-body">{children}</div> - </div> - ); + const { type, ...rest } = this.props; + if (type === 'popup') { + return <PopupField {...rest} />; + } + return <CommonField {...rest} />; } } diff --git a/packages/plugin-settings-pane/src/popup/index.tsx b/packages/plugin-settings-pane/src/popup/index.tsx index 1c13a9b3b..503e986f9 100644 --- a/packages/plugin-settings-pane/src/popup/index.tsx +++ b/packages/plugin-settings-pane/src/popup/index.tsx @@ -28,12 +28,15 @@ export class PopupPipe { }, show: (target: Element, actionKey?: string) => { this.currentId = id; - this.popup({ - ...props, - actionKey, - content: sendContent, - title: sendTitle, - }, target); + this.popup( + { + ...props, + actionKey, + content: sendContent, + title: sendTitle, + }, + target, + ); }, }; } @@ -121,7 +124,7 @@ export class PopupContent extends PureComponent<{ safeId?: string }> { safeNode={id} visible={visible} style={{ width }} - onVisibleChange={visible => { + onVisibleChange={(visible) => { if (avoidLaterHidden) { return; } @@ -136,7 +139,11 @@ export class PopupContent extends PureComponent<{ safeId?: string }> { shouldUpdatePosition > <div className="lc-ballon-title">{title}</div> - <div className="lc-ballon-content"><PopupService actionKey={actionKey} safeId={id}>{content}</PopupService></div> + <div className="lc-ballon-content"> + <PopupService actionKey={actionKey} safeId={id}> + {content} + </PopupService> + </div> </Balloon> ); } diff --git a/packages/plugin-settings-pane/src/settings-pane.tsx b/packages/plugin-settings-pane/src/settings-pane.tsx index 8ff5459c0..8fd482e3a 100644 --- a/packages/plugin-settings-pane/src/settings-pane.tsx +++ b/packages/plugin-settings-pane/src/settings-pane.tsx @@ -7,7 +7,7 @@ import { shallowIntl, isSetterConfig, createSetterContent, - shallowEqual + shallowEqual, } from '@ali/lowcode-globals'; import { SettingField, isSettingField, SettingTarget } from './main'; import { Field, FieldGroup } from './field'; @@ -38,7 +38,7 @@ class SettingFieldView extends Component<{ field: SettingField }> { } else if (setter) { this.setterType = setter; } - let firstRun: boolean = true; + let firstRun = true; this.dispose = field.onEffect(() => { const state: any = {}; const { extraProps } = field; @@ -137,7 +137,7 @@ class SettingGroupView extends Component<{ field: SettingField }> { super(props); const { field } = this.props; const { condition } = field.extraProps; - let firstRun: boolean = true; + let firstRun = true; this.dispose = field.onEffect(() => { const state: any = {}; state.visible = field.isOne && typeof condition === 'function' ? !condition(field) : true; @@ -204,7 +204,7 @@ export default class SettingsPane extends Component<{ target: SettingTarget }> { super(props); const { target } = this.props; - let firstRun: boolean = true; + let firstRun = true; this.dispose = target.onEffect(() => { const state = { items: target.items.slice(), diff --git a/packages/vision-polyfill/package.json b/packages/vision-polyfill/package.json index b5aa88517..294daf12c 100644 --- a/packages/vision-polyfill/package.json +++ b/packages/vision-polyfill/package.json @@ -30,6 +30,7 @@ "@ali/vu-logger": "^1.0.7", "@ali/vu-style-sheet": "^2.4.0", "@alifd/next": "^1.19.12", + "@ali/ve-stage-box": "^4.0.0", "@alife/theme-lowcode-dark": "^0.1.0", "@alife/theme-lowcode-light": "^0.1.0", "react": "^16.8.1", diff --git a/packages/vision-polyfill/src/bak/vision copy.ts b/packages/vision-polyfill/src/bak/vision copy.ts index cd556c087..fad94157b 100644 --- a/packages/vision-polyfill/src/bak/vision copy.ts +++ b/packages/vision-polyfill/src/bak/vision copy.ts @@ -18,16 +18,24 @@ const VEOldAPIs = { * Core UI Components */ ui: { + // FIELD_TYPE_MAP Field: { - SettingField, - Stage, - CaptionField, - PopupField, - EntryField, + // SettingField, + // Stage, + // CaptionField, + // PopupField, + // EntryField, + // AccordionField, + // BlockField, + // InlineField, + // PlainField AccordionField, - BlockField, InlineField, - PlainField + BlockField, + CaptionField, // 不支持 variableSwitcher 版的 BlockField + PlainField, // 不渲染 title 的 InlineField + EntryField, + PopupField, }, Icon: Icons, Icons, diff --git a/packages/vision-polyfill/src/skeleton/skeleton.ts b/packages/vision-polyfill/src/skeleton/skeleton.ts index 6672a4f9d..ebc0996ca 100644 --- a/packages/vision-polyfill/src/skeleton/skeleton.ts +++ b/packages/vision-polyfill/src/skeleton/skeleton.ts @@ -149,11 +149,11 @@ export class Skeleton { return; } Object.keys(plugins).forEach((area) => { - plugins[area].forEach(item => { + plugins[area].forEach((item) => { const { pluginKey, type, props = {}, pluginProps } = item; const config: any = { area, - type: "Widget", + type: 'Widget', name: pluginKey, contentProps: pluginProps, }; @@ -181,7 +181,7 @@ export class Skeleton { } this.add(config); }); - }) + }); } postEvent(event: SkeletonEvents, ...args: any[]) { @@ -218,9 +218,9 @@ export class Skeleton { createContainer( name: string, handle: (item: any) => any, - exclusive: boolean = false, + exclusive = false, checkVisible: () => boolean = () => true, - defaultSetCurrent: boolean = false, + defaultSetCurrent = false, ) { const container = new WidgetContainer(name, handle, exclusive, checkVisible, defaultSetCurrent); this.containers.set(name, container); @@ -231,7 +231,7 @@ export class Skeleton { const { content, ...restConfig } = config; if (content) { if (typeof content === 'object' && !isValidElement(content)) { - Object.keys(content).forEach(key => { + Object.keys(content).forEach((key) => { if (/props$/i.test(key) && restConfig[key]) { restConfig[key] = { ...restConfig[key], @@ -247,17 +247,24 @@ export class Skeleton { } const { area } = restConfig; switch (area) { - case 'leftArea': case 'left': + case 'leftArea': + case 'left': return this.leftArea.add(restConfig as any); - case 'rightArea': case 'right': + case 'rightArea': + case 'right': return this.rightArea.add(restConfig as any); - case 'topArea': case 'top': + case 'topArea': + case 'top': return this.topArea.add(restConfig as any); case 'toolbar': return this.toolbar.add(restConfig as any); - case 'mainArea': case 'main': case 'center': case 'centerArea': + case 'mainArea': + case 'main': + case 'center': + case 'centerArea': return this.mainArea.add(restConfig as any); - case 'bottomArea': case 'bottom': + case 'bottomArea': + case 'bottom': return this.bottomArea.add(restConfig as any); case 'leftFixedArea': return this.leftFixedArea.add(restConfig as any); diff --git a/packages/vision-polyfill/src/vision.ts b/packages/vision-polyfill/src/vision.ts index c7b58cd63..4da902057 100644 --- a/packages/vision-polyfill/src/vision.ts +++ b/packages/vision-polyfill/src/vision.ts @@ -3,6 +3,7 @@ import Popup from '@ali/ve-popups'; import Icons from '@ali/ve-icons'; import { render } from 'react-dom'; import I18nUtil from '@ali/ve-i18n-util'; +import { hotkey as Hotkey } from '@ali/lowcode-globals'; import { createElement } from 'react'; import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './const'; import Bus from './bus'; @@ -68,6 +69,7 @@ const VisualEngine = { */ utils, I18nUtil, + Hotkey, Env, /* pub/sub 集线器 */ Bus,