can change props

This commit is contained in:
kangwei 2020-03-06 00:39:09 +08:00
parent 9c52978702
commit 38f7c6d5cc
10 changed files with 232 additions and 56 deletions

View File

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

View File

@ -228,15 +228,25 @@ export default class Designer {
}
@obx.val private _componentTypesMap = new Map<string, ComponentType>();
private _lostComponentTypesMap = new Map<string, ComponentType>();
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 } {

View File

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

View File

@ -29,7 +29,6 @@ export default class PropStash implements IPropParent {
}
}
if (pending.length > 0) {
debugger;
untracked(() => {
for (const item of pending) {
write(item);

View File

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

View File

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

View File

@ -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 (
<div className="lc-settings-navigator">
{this.main.componentType ? this.main.componentType.icon : <Icon type="ellipsis" />}
<span></span>
{this.main.componentType!.icon || <Icon type="ellipsis" size="small" />}
<span>
{this.main.componentType!.title} x {this.main.nodes.length}
</span>
</div>
);
}
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(<Breadcrumb.Item {...props}>{node.title}</Breadcrumb.Item>);
node = node.parent;
}
return (
<div className="lc-settings-navigator">
{this.main.componentType ? this.main.componentType.icon : <Icon type="ellipsis" />}
<Breadcrumb>
<Breadcrumb.Item></Breadcrumb.Item>
<Breadcrumb.Item></Breadcrumb.Item>
<Breadcrumb.Item></Breadcrumb.Item>
</Breadcrumb>
{this.main.componentType!.icon || <Icon type="ellipsis" size="small" />}
<Breadcrumb className="lc-settings-node-breadcrumb">{items}</Breadcrumb>
</div>
);
}
@ -49,12 +64,24 @@ export default class SettingsPane extends Component {
render() {
if (this.main.isNone) {
// 未选中节点,提示选中 或者 显示根节点设置
return <div className="lc-settings-pane"></div>;
return (
<div className="lc-settings-pane">
<div className="lc-settings-notice">
<p></p>
</div>
</div>
);
}
if (!this.main.isSame) {
// todo: future support 获取设置项交集编辑
return <div className="lc-settings-pane"></div>;
return (
<div className="lc-settings-pane">
<div className="lc-settings-notice">
<p></p>
</div>
</div>
);
}
const { items } = this.main;
@ -79,7 +106,7 @@ export default class SettingsPane extends Component {
>
{(items as SettingField[]).map(field => (
<Tab.Item className="lc-settings-tab-item" title={<Title title={field.title} />} key={field.name}>
<SettingsTab target={field} />
<SettingsTab target={field} key={field.id} />
</Tab.Item>
))}
</Tab>
@ -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 };

View File

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

View File

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

View File

@ -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 {