enhance di

This commit is contained in:
kangwei 2020-04-16 17:50:20 +08:00
parent 5dc04152d5
commit 73e6e22e57
11 changed files with 172 additions and 136 deletions

View File

@ -200,6 +200,7 @@ export class ComponentMeta {
} }
checkNestingUp(my: Node | NodeData, parent: NodeParent) { checkNestingUp(my: Node | NodeData, parent: NodeParent) {
// 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器
if (this.parentWhitelist) { if (this.parentWhitelist) {
return this.parentWhitelist.includes(parent.componentName); return this.parentWhitelist.includes(parent.componentName);
} }
@ -207,6 +208,7 @@ export class ComponentMeta {
} }
checkNestingDown(my: Node, target: Node | NodeSchema) { checkNestingDown(my: Node, target: Node | NodeSchema) {
// 检查父子关系,直接约束型,在画布中拖拽直接掠过目标容器
if (this.childWhitelist) { if (this.childWhitelist) {
return this.childWhitelist.includes(target.componentName); return this.childWhitelist.includes(target.componentName);
} }

View File

@ -138,11 +138,11 @@ export class Designer {
historyDispose(); historyDispose();
historyDispose = undefined; historyDispose = undefined;
} }
this.postEvent('history-change', this.currentHistory); this.postEvent('history.change', this.currentHistory);
if (this.currentHistory) { if (this.currentHistory) {
const currentHistory = this.currentHistory; const currentHistory = this.currentHistory;
historyDispose = currentHistory.onStateChange(() => { historyDispose = currentHistory.onStateChange(() => {
this.postEvent('history-change', currentHistory); this.postEvent('history.change', currentHistory);
}); });
} }
}; };

View File

@ -82,6 +82,58 @@ export default class Editor extends EventEmitter {
instance = this; instance = this;
} }
get<T = undefined, KeyOrType = any>(keyOrType: KeyOrType, opt?: GetOptions): GetReturnType<T, KeyOrType> | undefined {
const x = this.context.get<T, KeyOrType>(keyOrType, opt);
if (x === NOT_FOUND) {
return undefined;
}
return x;
}
has(keyOrType: KeyType): boolean {
return this.context.has(keyOrType);
}
set(key: KeyType, data: any): void {
if (this.context.has(key)) {
this.context.replace(key, data, undefined, true);
} else {
this.context.register(data, key);
}
this.notifyGot(key);
}
onceGot<T = undefined, KeyOrType extends KeyType = any>(keyOrType: KeyOrType): Promise<GetReturnType<T, KeyOrType>> {
const x = this.context.get<T, KeyOrType>(keyOrType);
if (x !== NOT_FOUND) {
return Promise.resolve(x);
}
return new Promise((resolve) => {
this.setWait(keyOrType, resolve, true);
});
}
onGot<T = undefined, KeyOrType extends KeyType = any>(
keyOrType: KeyOrType,
fn: (data: GetReturnType<T, KeyOrType>) => void,
): () => void {
const x = this.context.get<T, KeyOrType>(keyOrType);
if (x !== NOT_FOUND) {
fn(x);
return () => {};
} else {
this.setWait(keyOrType, fn);
return () => {
this.delWait(keyOrType, fn);
};
}
}
register(data: any, key?: KeyType, options?: RegisterOptions): void {
this.context.register(data, key, options);
this.notifyGot(key || data);
}
async init(): Promise<any> { async init(): Promise<any> {
const { hooks, shortCuts = [], lifeCycles } = this.config || {}; const { hooks, shortCuts = [], lifeCycles } = this.config || {};
this.locale = store.get('lowcode-editor-locale') || 'zh-CN'; this.locale = store.get('lowcode-editor-locale') || 'zh-CN';
@ -115,25 +167,31 @@ export default class Editor extends EventEmitter {
} }
} }
get<T = undefined, KeyOrType = any>(keyOrType: KeyOrType, opt?: GetOptions): GetReturnType<T, KeyOrType> | undefined { batchOn(events: string[], lisenter: (...args: any[]) => void): void {
const x = this.context.get<T, KeyOrType>(keyOrType, opt); if (!Array.isArray(events)) {
if (x === NOT_FOUND) { return;
return undefined;
} }
return x; events.forEach((event): void => {
this.on(event, lisenter);
});
} }
has(keyOrType: KeyType): boolean { batchOnce(events: string[], lisenter: (...args: any[]) => void): void {
return this.context.has(keyOrType); if (!Array.isArray(events)) {
return;
}
events.forEach((event): void => {
this.once(event, lisenter);
});
} }
set(key: KeyType, data: any): void { batchOff(events: string[], lisenter: (...args: any[]) => void): void {
if (this.context.has(key)) { if (!Array.isArray(events)) {
this.context.replace(key, data, undefined, true); return;
} else {
this.context.register(data, key);
} }
this.notifyGot(key); events.forEach((event): void => {
this.off(event, lisenter);
});
} }
private waits = new Map< private waits = new Map<
@ -172,62 +230,20 @@ export default class Editor extends EventEmitter {
} }
} }
onceGot<T = undefined, KeyOrType extends KeyType = any>(keyOrType: KeyOrType): Promise<GetReturnType<T, KeyOrType>> { private delWait(key: KeyType, fn: any) {
const x = this.context.get<T, KeyOrType>(keyOrType); const waits = this.waits.get(key);
if (x !== NOT_FOUND) { if (!waits) {
return Promise.resolve(x);
}
return new Promise((resolve) => {
this.setWait(keyOrType, resolve, true);
});
}
onGot<T = undefined, KeyOrType extends KeyType = any>(
keyOrType: KeyOrType,
fn: (data: GetReturnType<T, KeyOrType>) => void,
): () => void {
const x = this.context.get<T, KeyOrType>(keyOrType);
if (x !== NOT_FOUND) {
fn(x);
return () => {};
} else {
this.setWait(keyOrType, fn);
return () => {
};
}
}
register(data: any, key?: KeyType, options?: RegisterOptions): void {
this.context.register(data, key, options);
this.notifyGot(key || data);
}
batchOn(events: string[], lisenter: (...args: any[]) => void): void {
if (!Array.isArray(events)) {
return; return;
} }
events.forEach((event): void => { let i = waits.length;
this.on(event, lisenter); while (i--) {
}); if (waits[i].resolve === fn) {
} waits.splice(i, 1);
}
batchOnce(events: string[], lisenter: (...args: any[]) => void): void {
if (!Array.isArray(events)) {
return;
} }
events.forEach((event): void => { if (waits.length < 1) {
this.once(event, lisenter); this.waits.delete(key);
});
}
batchOff(events: string[], lisenter: (...args: any[]) => void): void {
if (!Array.isArray(events)) {
return;
} }
events.forEach((event): void => {
this.off(event, lisenter);
});
} }
// 销毁hooks中的消息监听 // 销毁hooks中的消息监听

View File

@ -6,11 +6,18 @@ import { PropConfig } from './prop-config';
import { NpmInfo } from './npm'; import { NpmInfo } from './npm';
import { FieldConfig } from './field-config'; import { FieldConfig } from './field-config';
export type NestingFilter = (which: any, my: any) => boolean;
export interface NestingRule { export interface NestingRule {
childWhitelist?: string[]; // 子级白名单
parentWhitelist?: string[]; childWhitelist?: string[] | string | RegExp | NestingFilter;
descendantBlacklist?: string[]; // 父级白名单
ancestorWhitelist?: string[]; parentWhitelist?: string[] | string | RegExp | NestingFilter;
// 后裔白名单
descendantWhitelist?: string[] | string | RegExp | NestingFilter;
// 后裔黑名单
descendantBlacklist?: string[] | string | RegExp | NestingFilter;
// 祖先白名单 可用来做区域高亮
ancestorWhitelist?: string[] | string | RegExp | NestingFilter;
} }
export interface ComponentConfigure { export interface ComponentConfigure {

View File

@ -133,19 +133,13 @@ export class OutlineMain implements ISensor, IScrollBoard, IScrollable {
private fixed = false; private fixed = false;
constructor(readonly editor: Editor, at?: string) { constructor(readonly editor: Editor, at?: string) {
let inited = false; let inited = false;
const setup = () => { const setup = async () => {
if (inited) { if (inited) {
return false; return false;
} }
inited = true; inited = true;
const designer = editor.get(Designer); const designer = await editor.onceGot(Designer);
if (designer) { this.setupDesigner(designer);
this.setupDesigner(designer);
} else {
editor.once('designer.mount', (designer: Designer) => {
this.setupDesigner(designer);
});
}
}; };
// FIXME: dirty connect to others // FIXME: dirty connect to others

View File

@ -7,9 +7,11 @@ import './index.scss';
const SamplePreview = ({ editor }: PluginProps) => { const SamplePreview = ({ editor }: PluginProps) => {
const handleClick = () => { const handleClick = () => {
const designer = editor.get(Designer); const designer = editor.get(Designer);
console.info('save schema:', designer.schema); if (designer) {
localStorage.setItem('lce-dev-store', JSON.stringify(designer.schema)); console.info('save schema:', designer.schema);
window.open('./preview.html', 'preview'); localStorage.setItem('lce-dev-store', JSON.stringify(designer.schema));
window.open('./preview.html', 'preview');
}
}; };
return ( return (

View File

@ -339,21 +339,16 @@ export class SettingsMain implements SettingTarget {
} }
}; };
editor.on('designer.selection.change', setupSelection); editor.on('designer.selection.change', setupSelection);
const connectTree = (designer: any) => {
getTreeMaster(designer).onceEnableBuiltin(() => {
this.emitter.emit('outline-visible');
});
}
const designer = editor.get(Designer);
if (designer) {
connectTree(designer);
setupSelection(designer.currentSelection);
} else {
editor.once('designer.mount', connectTree);
}
this.disposeListener = () => { this.disposeListener = () => {
editor.removeListener('designer.selection.change', setupSelection); editor.removeListener('designer.selection.change', setupSelection);
}; };
(async () => {
const designer = await editor.onceGot(Designer);
getTreeMaster(designer).onceEnableBuiltin(() => {
this.emitter.emit('outline-visible');
});
setupSelection(designer.currentSelection);
})();
} }
onEffect(action: () => void): () => void { onEffect(action: () => void): () => void {

View File

@ -30,25 +30,18 @@ export default class UndoRedo extends PureComponent<
}; };
} }
componentDidMount(): void { async componentDidMount() {
const { editor } = this.props; const { editor } = this.props;
editor.on('designer.history-change', this.handleHistoryChange); editor.on('designer.history.change', this.handleHistoryChange);
const designer = editor.get(Designer); const designer = await editor.onceGot(Designer);
if (designer) { this.history = designer.currentHistory;
this.history = designer.currentHistory; this.updateState(this.history?.getState() || 0);
this.updateState(this.history?.getState() || 0);
} else {
editor.once('designer.ready', (): void => {
this.history = editor.get(Designer)?.currentHistory;
this.updateState(this.history?.getState() || 0);
});
}
} }
componentWillUnmount(): void { componentWillUnmount(): void {
const { editor } = this.props; const { editor } = this.props;
editor.off('designer.history-change', this.handleHistoryChange); editor.off('designer.history.change', this.handleHistoryChange);
} }
handleHistoryChange = (history: any): void => { handleHistoryChange = (history: any): void => {
@ -62,7 +55,7 @@ export default class UndoRedo extends PureComponent<
this.history = editor.get(Designer)?.currentHistory; this.history = editor.get(Designer)?.currentHistory;
this.updateState(this.history?.getState() || 0); this.updateState(this.history?.getState() || 0);
editor.on('designer.history-change', (history: any): void => { editor.on('designer.history.change', (history: any): void => {
this.history = history; this.history = history;
this.updateState(this.history?.getState() || 0); this.updateState(this.history?.getState() || 0);
}); });

View File

@ -35,11 +35,11 @@ export type HandleState = REJECTED | ALLOWED | LIMITED;
* model.locate: (my, transferData, mouseEvent?) => Location | null, node * model.locate: (my, transferData, mouseEvent?) => Location | null, node
* *
* test-RegExp: /^tagName./ * test-RegExp: /^tagName./
* test-Pattern: 'tagName,tagName2,Field-*' * test-List: 'tagName,tagName2' | ['tagName', 'tagName2']
* test-Func: (target, my) => boolean * test-Func: (target, my) => boolean
* Tester: RegExp | Pattern | Func | { exclude: Tester, include: Tester } * Tester: RegExp | Pattern | Func
* *
* model.accept * component.accept
* accept: '@CHILD' slot TabsLayoutColumnsLayout, * accept: '@CHILD' slot TabsLayoutColumnsLayout,
* accept: false|null inputoption * accept: false|null inputoption
* accept: true ok div, * accept: true ok div,
@ -66,7 +66,6 @@ export type HandleState = REJECTED | ALLOWED | LIMITED;
* 4. Li ULLi设置的 dropTargetRules * 4. Li ULLi设置的 dropTargetRules
* 5. * 5.
* *
* &
*/ */
@ -94,7 +93,7 @@ export interface AcceptControl {
* KeyboardEvent: paste a entiy * KeyboardEvent: paste a entiy
* LocateEvent: drag a entiy from pane * LocateEvent: drag a entiy from pane
*/ */
accept?: AcceptFunc | AT_CHILD; handleLocate?: AcceptFunc | AT_CHILD;
handleAccept?: (my: ElementNode, locationData: LocationData) => void; handleAccept?: (my: ElementNode, locationData: LocationData) => void;
} }
@ -242,37 +241,35 @@ function upgradeMetadata(oldConfig: OldPrototypeConfig) {
icon, icon,
packageName, packageName,
category, category,
defaultProps,
extraActions, extraActions,
view, view,
initialChildren,
configure, configure,
defaultProps,
initialChildren,
snippets, snippets,
transducers, transducers,
reducers,
isContainer, isContainer,
rectSelector, rectSelector,
isModal, isModal,
isFloating, isFloating,
descriptor, descriptor,
context, context,
hasSlot,
canOperating, canOperating,
canDraging,
canDragging,
canSelecting,
canContain, canContain,
canDropTo, canDropTo,
canDropto, canDropto,
canDropIn, canDropIn,
canDroping, canDroping,
didDropOut, // handles
didDropIn, canDraging, canDragging, // handleDragging
canResizing, canResizing, // handleResizing
onResizeStart, // hooks
onResize, didDropOut, // onNodeRemove
onResizeEnd, didDropIn, // onNodeAdd
subtreeModified, onResizeStart, // onResizeStart
onResize, // onResize
onResizeEnd, // onResizeEnd
subtreeModified, // onSubtreeModified
} = oldConfig; } = oldConfig;
@ -305,13 +302,42 @@ function upgradeMetadata(oldConfig: OldPrototypeConfig) {
if (canOperating === false) { if (canOperating === false) {
component.disableBehaviors = '*'; component.disableBehaviors = '*';
} }
nestingRule if (extraActions) {
disableBehaviors component.actions = extraActions.map((content) => {
actions return {
name: content.displayName || content.name || 'anonymous',
content,
important: true,
};
});
}
const nestingRule: any = {};
if (canContain) {
nestingRule.descendantWhitelist = canContain;
}
if (canDropTo || canDropto) {
nestingRule.parentWhitelist = canDropTo || canDropto;
}
if (canDropIn || canDroping) {
nestingRule.childWhitelist = canDropIn || canDroping;
}
component.nestingRule = nestingRule;
if (canDragging || canDraging) {
// hooks|handle
}
// 未考虑清楚的,放在实验性段落
const experimental: any = {};
if (context) {
// for prototype.getContextInfo
experimental.context = context;
}
const props = {}; const props = {};
const styles = {}; const styles = {};
const events = {}; const events = {};
meta.configure = { props, component, styles, events }; meta.configure = { props, component, styles, events, experimental };
} }
@ -341,7 +367,6 @@ export interface OldPrototypeConfig {
configure: IPropConfig[]; // => configure.props configure: IPropConfig[]; // => configure.props
snippets?: ISnippet[]; // => snippets snippets?: ISnippet[]; // => snippets
transducers?: any; // => ? transducers?: any; // => ?
reducers?: any; // ?
/** /**
* Selector expression rectangle of a node, it is usually a querySelector string * Selector expression rectangle of a node, it is usually a querySelector string
* @example '.classname > div' * @example '.classname > div'

View File

@ -5,7 +5,7 @@ import DesignerView from '@ali/lowcode-plugin-designer';
import { registerSetters } from '@ali/lowcode-setters'; import { registerSetters } from '@ali/lowcode-setters';
import { Skeleton } from './skeleton/skeleton'; import { Skeleton } from './skeleton/skeleton';
import { Designer } from 'designer/src/designer'; import { Designer } from 'designer/src/designer';
import { globalContext } from 'globals/src/di'; import { globalContext } from '@ali/lowcode-globals';
registerSetters(); registerSetters();

View File

@ -17,13 +17,14 @@
--top-area-height: 48px; --top-area-height: 48px;
--toolbar-height: 32px; --toolbar-height: 32px;
--dock-pane-width: 372px; --dock-pane-width: 372px;
--dock-fixed-pane-width: 220px;
} }
@media (min-width: 1860px) { @media (min-width: 1860px) {
:root { :root {
--right-area-width: 400px; --right-area-width: 400px;
--dock-pane-width: 452px; --dock-pane-width: 452px;
--dock-fixed-pane-width: 350px;
} }
} }
@ -188,6 +189,7 @@ body {
.lc-workbench-body { .lc-workbench-body {
flex: 1; flex: 1;
display: flex; display: flex;
min-height: 0;
position: relative; position: relative;
.lc-left-float-pane { .lc-left-float-pane {
position: absolute; position: absolute;
@ -212,7 +214,7 @@ body {
} }
} }
.lc-left-fixed-pane { .lc-left-fixed-pane {
width: var(--dock-pane-width); width: var(--dock-fixed-pane-width);
background-color: var(--color-pane-background); background-color: var(--color-pane-background);
height: 100%; height: 100%;
display: none; display: none;