From 38f7c6d5ccfa46e5dbceb9ed89f4b004bf6183f0 Mon Sep 17 00:00:00 2001 From: kangwei Date: Fri, 6 Mar 2020 00:39:09 +0800 Subject: [PATCH] can change props --- .../designer/src/designer/component-type.ts | 28 ++---- packages/designer/src/designer/designer.ts | 28 +++++- .../src/designer/document/node/node.ts | 13 ++- .../document/node/props/prop-stash.ts | 1 - .../src/designer/document/node/props/prop.ts | 28 ++++++ .../designer/src/designer/helper/hovering.ts | 6 ++ packages/plugin-settings-pane/src/index.tsx | 56 ++++++++--- packages/plugin-settings-pane/src/main.ts | 98 +++++++++++++++---- .../plugin-settings-pane/src/settings-tab.tsx | 4 +- packages/plugin-settings-pane/src/style.less | 26 +++++ 10 files changed, 232 insertions(+), 56 deletions(-) diff --git a/packages/designer/src/designer/component-type.ts b/packages/designer/src/designer/component-type.ts index 9d61bbfa4..52665d801 100644 --- a/packages/designer/src/designer/component-type.ts +++ b/packages/designer/src/designer/component-type.ts @@ -147,18 +147,17 @@ export class ComponentType { name: '#props', title: "属性", items: [{ - name: 'title', - title: '标题', + name: 'label', + title: '标签', setter: 'StringSetter' }, { - name: 'description', - title: '描述', - setter: { - componentName: 'StringSetter', - props: { - multiline: true, - } - } + name: 'name', + title: '名称', + setter: 'StringSetter' + }, { + name: 'size', + title: '大小', + setter: 'StringSetter' }] }, { name: '#styles', @@ -188,12 +187,6 @@ export class ComponentType { name: '#data', title: "数据", items: [] - }, { - name: '#a', - title: "数据1", - }, { - name: '#b', - title: "数据2", }]; } @@ -201,7 +194,7 @@ export class ComponentType { private childWhitelist?: string[] | null; get title() { - return this._spec.title; + return this._spec.title || this.componentName; } get icon() { @@ -261,7 +254,6 @@ export class ComponentType { return this._spec; } - checkNestingUp(my: Node | NodeData, parent: NodeParent) { if (this.parentWhitelist) { return this.parentWhitelist.includes(parent.componentName); diff --git a/packages/designer/src/designer/designer.ts b/packages/designer/src/designer/designer.ts index b00672864..a24f29128 100644 --- a/packages/designer/src/designer/designer.ts +++ b/packages/designer/src/designer/designer.ts @@ -228,15 +228,25 @@ export default class Designer { } @obx.val private _componentTypesMap = new Map(); + private _lostComponentTypesMap = new Map(); private buildComponentTypesMap(specs: ComponentDescription[]) { specs.forEach(spec => { const key = spec.componentName; - const had = this._componentTypesMap.get(key); - if (had) { - had.spec = spec; + let cType = this._componentTypesMap.get(key); + if (cType) { + cType.spec = spec; } else { - this._componentTypesMap.set(key, new ComponentType(spec)); + cType = this._lostComponentTypesMap.get(key); + + if (cType) { + cType.spec = spec; + this._lostComponentTypesMap.delete(key); + } else { + cType = new ComponentType(spec); + } + + this._componentTypesMap.set(key, cType); } }); } @@ -246,9 +256,17 @@ export default class Designer { return this._componentTypesMap.get(componentName)!; } - return new ComponentType({ + if (this._lostComponentTypesMap.has(componentName)) { + return this._lostComponentTypesMap.get(componentName)!; + } + + const cType = new ComponentType({ componentName, }); + + this._lostComponentTypesMap.set(componentName, cType); + + return cType; } get componentsMap(): { [key: string]: ComponentDescription } { diff --git a/packages/designer/src/designer/document/node/node.ts b/packages/designer/src/designer/document/node/node.ts index ad7492c2c..a18e555c8 100644 --- a/packages/designer/src/designer/document/node/node.ts +++ b/packages/designer/src/designer/document/node/node.ts @@ -98,7 +98,7 @@ export default class Node { return v; } } - return this.componentName; + return this.componentType.title; } get isSlotRoot(): boolean { @@ -173,6 +173,17 @@ export default class Node { this.document.selection.select(this.id); } + /** + * 悬停高亮 + */ + hover(flag: boolean = true) { + if (flag) { + this.document.designer.hovering.hover(this); + } else { + this.document.designer.hovering.unhover(this); + } + } + /** * 节点组件类 */ diff --git a/packages/designer/src/designer/document/node/props/prop-stash.ts b/packages/designer/src/designer/document/node/props/prop-stash.ts index 836b2f45b..4c233717f 100644 --- a/packages/designer/src/designer/document/node/props/prop-stash.ts +++ b/packages/designer/src/designer/document/node/props/prop-stash.ts @@ -29,7 +29,6 @@ export default class PropStash implements IPropParent { } } if (pending.length > 0) { - debugger; untracked(() => { for (const item of pending) { write(item); diff --git a/packages/designer/src/designer/document/node/props/prop.ts b/packages/designer/src/designer/document/node/props/prop.ts index 3a87ec9f4..639e42090 100644 --- a/packages/designer/src/designer/document/node/props/prop.ts +++ b/packages/designer/src/designer/document/node/props/prop.ts @@ -77,6 +77,22 @@ export default class Prop implements IPropParent { return null; } + /** + * 获得表达式值 + */ + @computed get code() { + if (isJSExpression(this.value)) { + return this.value.value; + } + if (this.type === 'slot') { + return JSON.stringify(this._slotNode!.export(false)); + } + return JSON.stringify(this.value); + } + set code(val) { + + } + @computed getAsString(): string { if (this.type === 'literal') { return this._value ? String(this._value) : ''; @@ -167,6 +183,17 @@ export default class Prop implements IPropParent { return this._type === 'unset'; } + isEqual(otherProp: Prop | null): boolean { + if (!otherProp) { + return this.isUnset(); + } + if (otherProp.type !== this.type) { + return false; + } + // 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot' + return this.code === otherProp.code; + } + /** * 值是否是带类型的 JS * 比如 JSExpresion | JSSlot 等值 @@ -278,6 +305,7 @@ export default class Prop implements IPropParent { get(path: string): Prop; get(path: string, stash = true) { const type = this._type; + // todo: support list get if (type !== 'map' && type !== 'unset' && !stash) { return null; } diff --git a/packages/designer/src/designer/helper/hovering.ts b/packages/designer/src/designer/helper/hovering.ts index 215435c76..a4f62abda 100644 --- a/packages/designer/src/designer/helper/hovering.ts +++ b/packages/designer/src/designer/helper/hovering.ts @@ -24,6 +24,12 @@ export default class Hovering { this._current = node; } + unhover(node: Node) { + if (this._current === node) { + this._current = null; + } + } + leave(document: DocumentModel) { if (this.current && this.current.document === document) { this._current = null; diff --git a/packages/plugin-settings-pane/src/index.tsx b/packages/plugin-settings-pane/src/index.tsx index 155f06daf..06f8d5e86 100644 --- a/packages/plugin-settings-pane/src/index.tsx +++ b/packages/plugin-settings-pane/src/index.tsx @@ -4,6 +4,7 @@ import { SettingsMain, SettingField, isSettingField } from './main'; import './style.less'; import Title from './title'; import SettingsTab, { registerSetter, createSetterContent, getSetter, createSettingFieldView } from './settings-tab'; +import Node from '../../designer/src/designer/document/node/node'; export default class SettingsPane extends Component { private main: SettingsMain; @@ -28,20 +29,34 @@ export default class SettingsPane extends Component { if (this.main.isMulti) { return (
- {this.main.componentType ? this.main.componentType.icon : } - 多个节点 + {this.main.componentType!.icon || } + + {this.main.componentType!.title} x {this.main.nodes.length} +
); } + let node: Node | null = this.main.nodes[0]!; + const items = []; + let l = 4; + while (l-- > 0 && node) { + const props = + l === 3 + ? {} + : { + onMouseOver: hoverNode.bind(null, node, true), + onMouseOut: hoverNode.bind(null, node, false), + onClick: selectNode.bind(null, node), + }; + items.unshift({node.title}); + node = node.parent; + } + return (
- {this.main.componentType ? this.main.componentType.icon : } - - 页面 - 容器 - 输入框 - + {this.main.componentType!.icon || } + {items}
); } @@ -49,12 +64,24 @@ export default class SettingsPane extends Component { render() { if (this.main.isNone) { // 未选中节点,提示选中 或者 显示根节点设置 - return
请选中节点
; + return ( +
+
+

请在左侧画布选中节点

+
+
+ ); } if (!this.main.isSame) { // todo: future support 获取设置项交集编辑 - return
选中同一类型节点编辑
; + return ( +
+
+

请选中同一类型节点编辑

+
+
+ ); } const { items } = this.main; @@ -79,7 +106,7 @@ export default class SettingsPane extends Component { > {(items as SettingField[]).map(field => ( } key={field.name}> - + ))} @@ -88,4 +115,11 @@ export default class SettingsPane extends Component { } } +function hoverNode(node: Node, flag: boolean) { + node.hover(flag); +} +function selectNode(node: Node) { + node.select(); +} + export { registerSetter, createSetterContent, getSetter, createSettingFieldView }; diff --git a/packages/plugin-settings-pane/src/main.ts b/packages/plugin-settings-pane/src/main.ts index fdc2dda7a..3ab9766d9 100644 --- a/packages/plugin-settings-pane/src/main.ts +++ b/packages/plugin-settings-pane/src/main.ts @@ -43,24 +43,28 @@ export interface SettingTarget { readonly designer?: Designer; + readonly path: string[]; + /** * 响应式自动运行 */ onEffect(action: () => void): () => void; + // 获取属性值 + getPropValue(propName: string): any; + + // 设置属性值 + setPropValue(path: string, value: any): void; + /* // 所有属性值数据 readonly props: object; - // 获取属性值 - getPropValue(propName: string): any; // 设置多个属性值,替换原有值 setProps(data: object): void; // 设置多个属性值,和原有值合并 mergeProps(data: object): void; // 绑定属性值发生变化时 onPropsChange(fn: () => void): () => void; - // 设置属性值 - setPropValue(path: string, value: any) {} */ } @@ -149,8 +153,9 @@ export class SettingField implements SettingTarget { readonly nodes: Node[]; readonly componentType: ComponentType | null; readonly designer: Designer; + readonly path: string[]; - constructor(readonly parent: SettingTarget, private config: FieldConfig) { + constructor(readonly parent: SettingTarget, config: FieldConfig) { const { type, title, name, items, setter, extraProps, ...rest } = config; if (type == null) { @@ -184,6 +189,10 @@ export class SettingField implements SettingTarget { this.isOne = parent.isOne; this.isNone = parent.isNone; this.designer = parent.designer!; + this.path = parent.path.slice(); + if (this.type === 'field') { + this.path.push(this.name); + } // initial items if (this.type === 'group' && items) { @@ -210,36 +219,44 @@ export class SettingField implements SettingTarget { this._items = []; } - get isSameValue() { - return true; - } - get items() { return this._items; } - get prop(): SettingTargetProp | void { - if (this.type === 'field') { + // ====== 当前属性读写 ===== - } - return; + get isSameValue(): boolean { + // todo: + return true; } getValue(): any { - return null; + return this.parent.getPropValue(this.name); } setValue(val: any) { + this.parent.setPropValue(this.name, val); + } + // 设置属性值 + setPropValue(propName: string, value: any) { + const path = this.type === 'field' ? `${this.name}.${propName}` : propName; + this.parent.setPropValue(path, value); + } + + // 获取属性值 + getPropValue(propName: string): any { + const path = this.type === 'field' ? `${this.name}.${propName}` : propName; + return this.parent.getPropValue(path); } // 添加 // addItem(config: FieldConfig): SettingField {} // 删除 - deleteItem() {} + // deleteItem() {} // 移动 - insertItem(item: SettingField, index?: number) {} - remove() {} + // insertItem(item: SettingField, index?: number) {} + // remove() {} purge() { this.disposeItems(); @@ -250,8 +267,6 @@ export function isSettingField(obj: any): obj is SettingField { return obj && obj.isSettingField; } -export class SettingTargetProp {} - export class SettingsMain implements SettingTarget { private emitter = new EventEmitter(); @@ -260,6 +275,7 @@ export class SettingsMain implements SettingTarget { private _sessionId = ''; private _componentType: ComponentType | null = null; private _isSame: boolean = true; + readonly path = []; get nodes(): Node[] { return this._nodes; @@ -345,6 +361,47 @@ export class SettingsMain implements SettingTarget { return this.onNodesChange(action); } + /** + * 获取属性值 + */ + getPropValue(propName: string): any { + if (!this.isSame) { + return null; + } + const first = this.nodes[0].getProp(propName)!; + let l = this.nodes.length; + while (l-- > 1) { + const next = this.nodes[l].getProp(propName, false); + if (!first.isEqual(next)) { + return null; + } + } + return first.value; + } + + /** + * 设置属性值 + */ + setPropValue(propName: string, value: any) { + this.nodes.forEach(node => { + node.setPropValue(propName, value); + }); + } + + // 设置多个属性值,替换原有值 + setProps(data: object) { + this.nodes.forEach(node => { + node.setProps(data as any); + }); + } + + // 设置多个属性值,和原有值合并 + mergeProps(data: object) { + this.nodes.forEach(node => { + node.mergeProps(data as any); + }); + } + private setup(nodes: Node[]) { this._nodes = nodes; @@ -394,6 +451,9 @@ export class SettingsMain implements SettingTarget { if (theSame) { this._isSame = true; this._componentType = type; + } else { + this._isSame = false; + this._componentType = null; } } diff --git a/packages/plugin-settings-pane/src/settings-tab.tsx b/packages/plugin-settings-pane/src/settings-tab.tsx index 8582563b1..a32f89144 100644 --- a/packages/plugin-settings-pane/src/settings-tab.tsx +++ b/packages/plugin-settings-pane/src/settings-tab.tsx @@ -60,7 +60,7 @@ class SettingFieldView extends Component<{ field: SettingField }> { render() { const { field } = this.props; - const { setter, title, extraProps, isSameValue } = field; + const { setter, title, extraProps } = field; const { defaultValue } = extraProps; const { visible, value } = this.state; // reaction point @@ -77,9 +77,11 @@ class SettingFieldView extends Component<{ field: SettingField }> { if (defaultValue != null && !('defaultValue' in props)) { props.defaultValue = defaultValue; } + /* if (!('placeholder' in props) && !isSameValue) { props.placeholder = '多种值'; } + */ // todo: error handling diff --git a/packages/plugin-settings-pane/src/style.less b/packages/plugin-settings-pane/src/style.less index 7bc94f5a1..d5bc918cf 100644 --- a/packages/plugin-settings-pane/src/style.less +++ b/packages/plugin-settings-pane/src/style.less @@ -34,12 +34,38 @@ .lc-settings-pane { position: relative; + .lc-settings-notice { + text-align: center; + font-size: 12px; + font-family: PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica,Arial,sans-serif; + color: var(--color-text ,rgba(0,0,0,.6)); + margin: 50px 15px 0; + overflow: hidden; + padding: 15px 0; + } + .lc-settings-navigator { height: 30px; display: flex; align-items: center; padding-left: 5px; border-bottom: 1px solid var(--color-line-normal); + .lc-settings-node-breadcrumb { + margin-left: 5px; + .next-breadcrumb { + display: inline-flex; + align-items: stretch; + height: 24px; + } + .next-breadcrumb-item { + display: inline-flex; + align-items: center; + cursor: default; + &:not(:last-child):hover { + cursor: pointer; + } + } + } } .lc-settings-body {