This commit is contained in:
kangwei 2020-04-20 16:54:19 +08:00
commit a57730f254
15 changed files with 226 additions and 199 deletions

View File

@ -20,10 +20,9 @@ import {
getRectTarget, getRectTarget,
Rect, Rect,
CanvasPoint, CanvasPoint,
hotkey,
} from '../designer'; } from '../designer';
import { parseProps } from './utils/parse-props'; 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 { ComponentMetadata } from '@ali/lowcode-globals';
import { BuiltinSimulatorRenderer } from './renderer'; import { BuiltinSimulatorRenderer } from './renderer';
import clipboard from '../designer/clipboard'; import clipboard from '../designer/clipboard';

View File

@ -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);

View File

@ -1,7 +1,7 @@
import './builtin-hotkey';
export * from './designer'; export * from './designer';
export * from './designer-view'; export * from './designer-view';
export * from './dragon'; export * from './dragon';
export * from './hotkey';
export * from './hovering'; export * from './hovering';
export * from './location'; export * from './location';
export * from './offset-observer'; export * from './offset-observer';

View File

@ -471,12 +471,31 @@ export class Node {
isEmpty(): boolean { isEmpty(): boolean {
return this.children ? this.children.isEmpty() : true; 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() { getStatus() {
return 'default'; return 'default';
} }
/**
* @deprecated
*/
setStatus() { setStatus() {
} }
/**
* @deprecated
*/
getDOMNode() { getDOMNode() {
const instance = this.document.simulator?.getComponentInstances(this)?.[0]; const instance = this.document.simulator?.getComponentInstances(this)?.[0];
if (!instance) { if (!instance) {
@ -484,18 +503,13 @@ export class Node {
} }
return this.document.simulator?.findDOMNodes(instance)?.[0]; return this.document.simulator?.findDOMNodes(instance)?.[0];
} }
getChildren() { /**
return this.children; * @deprecated
} */
getPage() { getPage() {
console.warn('getPage is deprecated, use document instead');
return this.document; 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 { export interface NodeParent extends Node {

View File

@ -25,7 +25,7 @@ export interface HooksFuncs {
[idx: number]: (msg: string, handler: (...args: []) => void) => void; [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 type ClassType = Function | (new (...args: any[]) => any);
export interface GetOptions { export interface GetOptions {
forceNew?: boolean; forceNew?: boolean;

View File

@ -602,3 +602,6 @@ export class Hotkey {
} }
} }
} }
export const hotkey = new Hotkey();
hotkey.mount(window);

View File

@ -14,10 +14,11 @@
"test:snapshot": "ava --update-snapshots" "test:snapshot": "ava --update-snapshots"
}, },
"dependencies": { "dependencies": {
"@ali/lowcode-designer": "^0.9.3", "@ali/lowcode-designer": "^0.9.2",
"@ali/lowcode-editor-core": "^0.8.6", "@ali/lowcode-editor-core": "^0.8.5",
"@ali/lowcode-globals": "^0.9.3", "@ali/lowcode-globals": "^0.9.2",
"@ali/lowcode-plugin-outline-pane": "^0.8.9", "@ali/lowcode-plugin-outline-pane": "^0.8.8",
"@ali/ve-stage-box": "^4.0.0",
"@alifd/next": "^1.19.16", "@alifd/next": "^1.19.16",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"react": "^16" "react": "^16"

View File

@ -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<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>
);
}
}
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>
);
}
}

View File

@ -3,50 +3,22 @@ import classNames from 'classnames';
import { Icon } from '@alifd/next'; import { Icon } from '@alifd/next';
import { Title, TitleContent } from '@ali/lowcode-globals'; import { Title, TitleContent } from '@ali/lowcode-globals';
import './index.less'; import './index.less';
import { CommonField, PopupField } from './fields';
export interface FieldProps { export interface FieldProps {
className?: string; className?: string;
// span // span
title?: TitleContent | null; title?: TitleContent | null;
type?: string;
} }
export class Field extends Component<FieldProps> { 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() { render() {
const { className, children, title } = this.props; const { type, ...rest } = this.props;
if (type === 'popup') {
return ( return <PopupField {...rest} />;
<div ref={shell => (this.shell = shell)} className={classNames('lc-field lc-inline-field', className)}> }
{title && ( return <CommonField {...rest} />;
<div className="lc-field-head">
<div className="lc-field-title">
<Title title={title} />
</div>
</div>
)}
<div className="lc-field-body">{children}</div>
</div>
);
} }
} }

View File

@ -28,12 +28,15 @@ export class PopupPipe {
}, },
show: (target: Element, actionKey?: string) => { show: (target: Element, actionKey?: string) => {
this.currentId = id; this.currentId = id;
this.popup({ this.popup(
...props, {
actionKey, ...props,
content: sendContent, actionKey,
title: sendTitle, content: sendContent,
}, target); title: sendTitle,
},
target,
);
}, },
}; };
} }
@ -121,7 +124,7 @@ export class PopupContent extends PureComponent<{ safeId?: string }> {
safeNode={id} safeNode={id}
visible={visible} visible={visible}
style={{ width }} style={{ width }}
onVisibleChange={visible => { onVisibleChange={(visible) => {
if (avoidLaterHidden) { if (avoidLaterHidden) {
return; return;
} }
@ -136,7 +139,11 @@ export class PopupContent extends PureComponent<{ safeId?: string }> {
shouldUpdatePosition shouldUpdatePosition
> >
<div className="lc-ballon-title">{title}</div> <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> </Balloon>
); );
} }

View File

@ -7,7 +7,7 @@ import {
shallowIntl, shallowIntl,
isSetterConfig, isSetterConfig,
createSetterContent, createSetterContent,
shallowEqual shallowEqual,
} from '@ali/lowcode-globals'; } from '@ali/lowcode-globals';
import { SettingField, isSettingField, SettingTarget } from './main'; import { SettingField, isSettingField, SettingTarget } from './main';
import { Field, FieldGroup } from './field'; import { Field, FieldGroup } from './field';
@ -38,7 +38,7 @@ class SettingFieldView extends Component<{ field: SettingField }> {
} else if (setter) { } else if (setter) {
this.setterType = setter; this.setterType = setter;
} }
let firstRun: boolean = true; let firstRun = true;
this.dispose = field.onEffect(() => { this.dispose = field.onEffect(() => {
const state: any = {}; const state: any = {};
const { extraProps } = field; const { extraProps } = field;
@ -137,7 +137,7 @@ class SettingGroupView extends Component<{ field: SettingField }> {
super(props); super(props);
const { field } = this.props; const { field } = this.props;
const { condition } = field.extraProps; const { condition } = field.extraProps;
let firstRun: boolean = true; let firstRun = true;
this.dispose = field.onEffect(() => { this.dispose = field.onEffect(() => {
const state: any = {}; const state: any = {};
state.visible = field.isOne && typeof condition === 'function' ? !condition(field) : true; state.visible = field.isOne && typeof condition === 'function' ? !condition(field) : true;
@ -204,7 +204,7 @@ export default class SettingsPane extends Component<{ target: SettingTarget }> {
super(props); super(props);
const { target } = this.props; const { target } = this.props;
let firstRun: boolean = true; let firstRun = true;
this.dispose = target.onEffect(() => { this.dispose = target.onEffect(() => {
const state = { const state = {
items: target.items.slice(), items: target.items.slice(),

View File

@ -30,6 +30,7 @@
"@ali/vu-logger": "^1.0.7", "@ali/vu-logger": "^1.0.7",
"@ali/vu-style-sheet": "^2.4.0", "@ali/vu-style-sheet": "^2.4.0",
"@alifd/next": "^1.19.12", "@alifd/next": "^1.19.12",
"@ali/ve-stage-box": "^4.0.0",
"@alife/theme-lowcode-dark": "^0.1.0", "@alife/theme-lowcode-dark": "^0.1.0",
"@alife/theme-lowcode-light": "^0.1.0", "@alife/theme-lowcode-light": "^0.1.0",
"react": "^16.8.1", "react": "^16.8.1",

View File

@ -18,16 +18,24 @@ const VEOldAPIs = {
* Core UI Components * Core UI Components
*/ */
ui: { ui: {
// FIELD_TYPE_MAP
Field: { Field: {
SettingField, // SettingField,
Stage, // Stage,
CaptionField, // CaptionField,
PopupField, // PopupField,
EntryField, // EntryField,
// AccordionField,
// BlockField,
// InlineField,
// PlainField
AccordionField, AccordionField,
BlockField,
InlineField, InlineField,
PlainField BlockField,
CaptionField, // 不支持 variableSwitcher 版的 BlockField
PlainField, // 不渲染 title 的 InlineField
EntryField,
PopupField,
}, },
Icon: Icons, Icon: Icons,
Icons, Icons,

View File

@ -149,11 +149,11 @@ export class Skeleton {
return; return;
} }
Object.keys(plugins).forEach((area) => { Object.keys(plugins).forEach((area) => {
plugins[area].forEach(item => { plugins[area].forEach((item) => {
const { pluginKey, type, props = {}, pluginProps } = item; const { pluginKey, type, props = {}, pluginProps } = item;
const config: any = { const config: any = {
area, area,
type: "Widget", type: 'Widget',
name: pluginKey, name: pluginKey,
contentProps: pluginProps, contentProps: pluginProps,
}; };
@ -181,7 +181,7 @@ export class Skeleton {
} }
this.add(config); this.add(config);
}); });
}) });
} }
postEvent(event: SkeletonEvents, ...args: any[]) { postEvent(event: SkeletonEvents, ...args: any[]) {
@ -218,9 +218,9 @@ export class Skeleton {
createContainer( createContainer(
name: string, name: string,
handle: (item: any) => any, handle: (item: any) => any,
exclusive: boolean = false, exclusive = false,
checkVisible: () => boolean = () => true, checkVisible: () => boolean = () => true,
defaultSetCurrent: boolean = false, defaultSetCurrent = false,
) { ) {
const container = new WidgetContainer(name, handle, exclusive, checkVisible, defaultSetCurrent); const container = new WidgetContainer(name, handle, exclusive, checkVisible, defaultSetCurrent);
this.containers.set(name, container); this.containers.set(name, container);
@ -231,7 +231,7 @@ export class Skeleton {
const { content, ...restConfig } = config; const { content, ...restConfig } = config;
if (content) { if (content) {
if (typeof content === 'object' && !isValidElement(content)) { if (typeof content === 'object' && !isValidElement(content)) {
Object.keys(content).forEach(key => { Object.keys(content).forEach((key) => {
if (/props$/i.test(key) && restConfig[key]) { if (/props$/i.test(key) && restConfig[key]) {
restConfig[key] = { restConfig[key] = {
...restConfig[key], ...restConfig[key],
@ -247,17 +247,24 @@ export class Skeleton {
} }
const { area } = restConfig; const { area } = restConfig;
switch (area) { switch (area) {
case 'leftArea': case 'left': case 'leftArea':
case 'left':
return this.leftArea.add(restConfig as any); return this.leftArea.add(restConfig as any);
case 'rightArea': case 'right': case 'rightArea':
case 'right':
return this.rightArea.add(restConfig as any); return this.rightArea.add(restConfig as any);
case 'topArea': case 'top': case 'topArea':
case 'top':
return this.topArea.add(restConfig as any); return this.topArea.add(restConfig as any);
case 'toolbar': case 'toolbar':
return this.toolbar.add(restConfig as any); 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); return this.mainArea.add(restConfig as any);
case 'bottomArea': case 'bottom': case 'bottomArea':
case 'bottom':
return this.bottomArea.add(restConfig as any); return this.bottomArea.add(restConfig as any);
case 'leftFixedArea': case 'leftFixedArea':
return this.leftFixedArea.add(restConfig as any); return this.leftFixedArea.add(restConfig as any);

View File

@ -3,6 +3,7 @@ import Popup from '@ali/ve-popups';
import Icons from '@ali/ve-icons'; import Icons from '@ali/ve-icons';
import { render } from 'react-dom'; import { render } from 'react-dom';
import I18nUtil from '@ali/ve-i18n-util'; import I18nUtil from '@ali/ve-i18n-util';
import { hotkey as Hotkey } from '@ali/lowcode-globals';
import { createElement } from 'react'; import { createElement } from 'react';
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './const'; import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './const';
import Bus from './bus'; import Bus from './bus';
@ -68,6 +69,7 @@ const VisualEngine = {
*/ */
utils, utils,
I18nUtil, I18nUtil,
Hotkey,
Env, Env,
/* pub/sub 集线器 */ /* pub/sub 集线器 */
Bus, Bus,