mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2025-12-11 18:42:56 +00:00
feat: add getDOMNode api definition in node module
This commit is contained in:
parent
9228a2baa7
commit
964833128b
@ -645,3 +645,14 @@ setConditionalVisible(): void;
|
||||
```
|
||||
|
||||
**@since v1.1.0**
|
||||
|
||||
### getDOMNode
|
||||
获取节点实例对应的 dom 节点
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 获取节点实例对应的 dom 节点
|
||||
*/
|
||||
getDOMNode(): HTMLElement;
|
||||
|
||||
```
|
||||
@ -15,6 +15,7 @@ const jestConfig = {
|
||||
// testMatch: ['**/document-model.test.ts'],
|
||||
// testMatch: ['**/prop.test.ts'],
|
||||
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
|
||||
// testMatch: ['**/document/node/node.add.test.ts'],
|
||||
transformIgnorePatterns: [
|
||||
`/node_modules/(?!${esModules})/`,
|
||||
],
|
||||
|
||||
@ -71,6 +71,8 @@ export interface IDesigner {
|
||||
|
||||
get editor(): IPublicModelEditor;
|
||||
|
||||
get detecting(): Detecting;
|
||||
|
||||
createScroller(scrollable: IPublicTypeScrollable): IPublicModelScroller;
|
||||
|
||||
/**
|
||||
|
||||
@ -17,6 +17,7 @@ function generateSessionId(nodes: INode[]) {
|
||||
}
|
||||
|
||||
export interface ISettingTopEntry extends ISettingEntry {
|
||||
purge(): void;
|
||||
}
|
||||
|
||||
export class SettingTopEntry implements ISettingTopEntry {
|
||||
|
||||
@ -87,6 +87,8 @@ export interface IDocumentModel extends Omit< IPublicModelDocumentModel<
|
||||
|
||||
get active(): boolean;
|
||||
|
||||
get nodesMap(): Map<string, INode>;
|
||||
|
||||
/**
|
||||
* 根据 id 获取节点
|
||||
*/
|
||||
@ -114,6 +116,12 @@ export interface IDocumentModel extends Omit< IPublicModelDocumentModel<
|
||||
onChangeNodeVisible(fn: (node: INode, visible: boolean) => void): IPublicTypeDisposable;
|
||||
|
||||
addWillPurge(node: INode): void;
|
||||
|
||||
removeWillPurge(node: INode): void;
|
||||
|
||||
getComponentMeta(componentName: string): IComponentMeta;
|
||||
|
||||
insertNodes(parent: INode, thing: INode[] | IPublicTypeNodeData[], at?: number | null, copy?: boolean): INode[];
|
||||
}
|
||||
|
||||
export class DocumentModel implements IDocumentModel {
|
||||
@ -379,7 +387,7 @@ export class DocumentModel implements IDocumentModel {
|
||||
* 根据 schema 创建一个节点
|
||||
*/
|
||||
@action
|
||||
createNode<T extends INode = INode, C = undefined>(data: GetDataType<C, T>, checkId: boolean = true): T {
|
||||
createNode<T extends INode = INode, C = undefined>(data: GetDataType<C, T>): T {
|
||||
let schema: any;
|
||||
if (isDOMText(data) || isJSExpression(data)) {
|
||||
schema = {
|
||||
@ -410,7 +418,7 @@ export class DocumentModel implements IDocumentModel {
|
||||
}
|
||||
}
|
||||
if (!node) {
|
||||
node = new Node(this, schema, { checkId });
|
||||
node = new Node(this, schema);
|
||||
// will add
|
||||
// todo: this.activeNodes?.push(node);
|
||||
}
|
||||
@ -429,7 +437,7 @@ export class DocumentModel implements IDocumentModel {
|
||||
/**
|
||||
* 插入一个节点
|
||||
*/
|
||||
insertNode(parent: INode, thing: INode | IPublicTypeNodeData, at?: number | null, copy?: boolean): INode {
|
||||
insertNode(parent: INode, thing: INode | IPublicTypeNodeData, at?: number | null, copy?: boolean): INode | null {
|
||||
return insertChild(parent, thing, at, copy);
|
||||
}
|
||||
|
||||
@ -445,7 +453,7 @@ export class DocumentModel implements IDocumentModel {
|
||||
*/
|
||||
removeNode(idOrNode: string | INode) {
|
||||
let id: string;
|
||||
let node: INode | null;
|
||||
let node: INode | null = null;
|
||||
if (typeof idOrNode === 'string') {
|
||||
id = idOrNode;
|
||||
node = this.getNode(id);
|
||||
@ -859,7 +867,7 @@ export class DocumentModel implements IDocumentModel {
|
||||
onReady(fn: Function) {
|
||||
this.designer.editor.eventBus.on('document-open', fn);
|
||||
return () => {
|
||||
this.designer.editor.removeListener('document-open', fn);
|
||||
this.designer.editor.eventBus.off('document-open', fn);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,18 +1,30 @@
|
||||
import { obx, computed, makeObservable } from '@alilc/lowcode-editor-core';
|
||||
import { uniqueId } from '@alilc/lowcode-utils';
|
||||
import { IPublicTypeTitleContent, IPublicModelExclusiveGroup } from '@alilc/lowcode-types';
|
||||
import { Node } from './node';
|
||||
import { INode } from './node';
|
||||
import { intl } from '../../locale';
|
||||
|
||||
export interface IExclusiveGroup extends IPublicModelExclusiveGroup<INode> {
|
||||
readonly name: string;
|
||||
|
||||
remove(node: INode): void;
|
||||
|
||||
add(node: INode): void;
|
||||
|
||||
isVisible(node: INode): boolean;
|
||||
}
|
||||
|
||||
// modals assoc x-hide value, initial: check is Modal, yes will put it in modals, cross levels
|
||||
// if-else-if assoc conditionGroup value, should be the same level,
|
||||
// and siblings, need renderEngine support
|
||||
export class ExclusiveGroup implements IPublicModelExclusiveGroup {
|
||||
export class ExclusiveGroup implements IExclusiveGroup {
|
||||
readonly isExclusiveGroup = true;
|
||||
|
||||
readonly id = uniqueId('exclusive');
|
||||
|
||||
@obx.shallow readonly children: Node[] = [];
|
||||
readonly title: IPublicTypeTitleContent;
|
||||
|
||||
@obx.shallow readonly children: INode[] = [];
|
||||
|
||||
@obx private visibleIndex = 0;
|
||||
|
||||
@ -28,11 +40,11 @@ export class ExclusiveGroup implements IPublicModelExclusiveGroup {
|
||||
return this.children.length;
|
||||
}
|
||||
|
||||
@computed get visibleNode(): Node {
|
||||
@computed get visibleNode(): INode {
|
||||
return this.children[this.visibleIndex];
|
||||
}
|
||||
|
||||
@computed get firstNode(): Node {
|
||||
@computed get firstNode(): INode {
|
||||
return this.children[0]!;
|
||||
}
|
||||
|
||||
@ -40,8 +52,16 @@ export class ExclusiveGroup implements IPublicModelExclusiveGroup {
|
||||
return this.firstNode.index;
|
||||
}
|
||||
|
||||
add(node: Node) {
|
||||
if (node.nextSibling && node.nextSibling.conditionGroup === this) {
|
||||
constructor(readonly name: string, title?: IPublicTypeTitleContent) {
|
||||
makeObservable(this);
|
||||
this.title = title || {
|
||||
type: 'i18n',
|
||||
intl: intl('Condition Group'),
|
||||
};
|
||||
}
|
||||
|
||||
add(node: INode) {
|
||||
if (node.nextSibling && node.nextSibling.conditionGroup?.id === this.id) {
|
||||
const i = this.children.indexOf(node.nextSibling);
|
||||
this.children.splice(i, 0, node);
|
||||
} else {
|
||||
@ -49,7 +69,7 @@ export class ExclusiveGroup implements IPublicModelExclusiveGroup {
|
||||
}
|
||||
}
|
||||
|
||||
remove(node: Node) {
|
||||
remove(node: INode) {
|
||||
const i = this.children.indexOf(node);
|
||||
if (i > -1) {
|
||||
this.children.splice(i, 1);
|
||||
@ -61,27 +81,17 @@ export class ExclusiveGroup implements IPublicModelExclusiveGroup {
|
||||
}
|
||||
}
|
||||
|
||||
setVisible(node: Node) {
|
||||
setVisible(node: INode) {
|
||||
const i = this.children.indexOf(node);
|
||||
if (i > -1) {
|
||||
this.visibleIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
isVisible(node: Node) {
|
||||
isVisible(node: INode) {
|
||||
const i = this.children.indexOf(node);
|
||||
return i === this.visibleIndex;
|
||||
}
|
||||
|
||||
readonly title: IPublicTypeTitleContent;
|
||||
|
||||
constructor(readonly name: string, title?: IPublicTypeTitleContent) {
|
||||
makeObservable(this);
|
||||
this.title = title || {
|
||||
type: 'i18n',
|
||||
intl: intl('Condition Group'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function isExclusiveGroup(obj: any): obj is ExclusiveGroup {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { obx, computed, globalContext, makeObservable, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
|
||||
import { Node, INode } from './node';
|
||||
import { IPublicTypeNodeData, IPublicModelNodeChildren, IPublicEnumTransformStage } from '@alilc/lowcode-types';
|
||||
import { IPublicTypeNodeData, IPublicModelNodeChildren, IPublicEnumTransformStage, IPublicTypeDisposable } from '@alilc/lowcode-types';
|
||||
import { shallowEqual, compatStage, isNodeSchema } from '@alilc/lowcode-utils';
|
||||
import { foreachReverse } from '../../utils/tree';
|
||||
import { NodeRemoveOptions } from '../../types';
|
||||
@ -18,6 +18,8 @@ export interface INodeChildren extends Omit<IPublicModelNodeChildren<INode>,
|
||||
> {
|
||||
get owner(): INode;
|
||||
|
||||
get length(): number;
|
||||
|
||||
unlinkChild(node: INode): void;
|
||||
|
||||
/**
|
||||
@ -58,6 +60,8 @@ export interface INodeChildren extends Omit<IPublicModelNodeChildren<INode>,
|
||||
|
||||
internalInitParent(): void;
|
||||
|
||||
onChange(fn: (info?: IOnChangeOptions) => void): IPublicTypeDisposable;
|
||||
|
||||
/** overriding methods end */
|
||||
}
|
||||
export class NodeChildren implements INodeChildren {
|
||||
@ -478,7 +482,7 @@ export class NodeChildren implements INodeChildren {
|
||||
}
|
||||
}
|
||||
|
||||
onChange(fn: (info?: IOnChangeOptions) => void): () => void {
|
||||
onChange(fn: (info?: IOnChangeOptions) => void): IPublicTypeDisposable {
|
||||
this.emitter.on('change', fn);
|
||||
return () => {
|
||||
this.emitter.removeListener('change', fn);
|
||||
|
||||
@ -18,14 +18,14 @@ import {
|
||||
IPublicTypeDisposable,
|
||||
IBaseModelNode,
|
||||
} from '@alilc/lowcode-types';
|
||||
import { compatStage, isDOMText, isJSExpression, isNode } from '@alilc/lowcode-utils';
|
||||
import { ISettingTopEntry, SettingTopEntry } from '@alilc/lowcode-designer';
|
||||
import { compatStage, isDOMText, isJSExpression, isNode, isNodeSchema } from '@alilc/lowcode-utils';
|
||||
import { ISettingTopEntry } from '@alilc/lowcode-designer';
|
||||
import { Props, getConvertedExtraKey, IProps } from './props/props';
|
||||
import { DocumentModel, IDocumentModel } from '../document-model';
|
||||
import { IDocumentModel } from '../document-model';
|
||||
import { NodeChildren, INodeChildren } from './node-children';
|
||||
import { IProp, Prop } from './props/prop';
|
||||
import { ComponentMeta, IComponentMeta } from '../../component-meta';
|
||||
import { ExclusiveGroup, isExclusiveGroup } from './exclusive-group';
|
||||
import { IComponentMeta } from '../../component-meta';
|
||||
import { ExclusiveGroup, IExclusiveGroup, isExclusiveGroup } from './exclusive-group';
|
||||
import { includeSlot, removeSlot } from '../../utils/slot';
|
||||
import { foreachReverse } from '../../utils/tree';
|
||||
import { NodeRemoveOptions, EDITOR_EVENT } from '../../types';
|
||||
@ -42,14 +42,11 @@ export interface INode extends Omit<IBaseModelNode<
|
||||
INode,
|
||||
INodeChildren,
|
||||
IComponentMeta,
|
||||
ISettingTopEntry
|
||||
ISettingTopEntry,
|
||||
IProps,
|
||||
IProp,
|
||||
IExclusiveGroup
|
||||
>,
|
||||
'slots' |
|
||||
'slotFor' |
|
||||
'props' |
|
||||
'getProp' |
|
||||
'getExtraProp' |
|
||||
'replaceChild' |
|
||||
'isRoot' |
|
||||
'isPage' |
|
||||
'isComponent' |
|
||||
@ -64,29 +61,21 @@ export interface INode extends Omit<IBaseModelNode<
|
||||
'exportSchema' |
|
||||
'visible' |
|
||||
'importSchema' |
|
||||
'isEmptyNode' |
|
||||
// 内外实现有差异
|
||||
'isContainer' |
|
||||
'isEmpty'
|
||||
> {
|
||||
get slots(): INode[];
|
||||
|
||||
/**
|
||||
* 关联属性
|
||||
*/
|
||||
get slotFor(): IProp | null;
|
||||
|
||||
get props(): IProps;
|
||||
isNode: boolean;
|
||||
|
||||
get componentMeta(): IComponentMeta;
|
||||
|
||||
get settingEntry(): SettingTopEntry;
|
||||
get settingEntry(): ISettingTopEntry;
|
||||
|
||||
get isPurged(): boolean;
|
||||
|
||||
setVisible(flag: boolean): void;
|
||||
get index(): number | undefined;
|
||||
|
||||
getVisible(): boolean;
|
||||
get isPurging(): boolean;
|
||||
|
||||
/**
|
||||
* 内部方法,请勿使用
|
||||
@ -100,7 +89,7 @@ export interface INode extends Omit<IBaseModelNode<
|
||||
|
||||
internalPurgeStart(): void;
|
||||
|
||||
unlinkSlot(slotNode: Node): void;
|
||||
unlinkSlot(slotNode: INode): void;
|
||||
|
||||
/**
|
||||
* 导出 schema
|
||||
@ -117,15 +106,9 @@ export interface INode extends Omit<IBaseModelNode<
|
||||
|
||||
onVisibleChange(func: (flag: boolean) => any): () => void;
|
||||
|
||||
getProp(path: string, createIfNone?: boolean): IProp | null;
|
||||
|
||||
getExtraProp(key: string, createIfNone?: boolean): IProp | null;
|
||||
|
||||
replaceChild(node: INode, data: any): INode;
|
||||
|
||||
getSuitablePlace(node: INode, ref: any): any;
|
||||
|
||||
onChildrenChange(fn: (param?: { type: string; node: INode }) => void): IPublicTypeDisposable;
|
||||
onChildrenChange(fn: (param?: { type: string; node: INode }) => void): IPublicTypeDisposable | undefined;
|
||||
|
||||
onPropChange(func: (info: IPublicTypePropChangeOptions) => void): IPublicTypeDisposable;
|
||||
|
||||
@ -153,13 +136,17 @@ export interface INode extends Omit<IBaseModelNode<
|
||||
options?: NodeRemoveOptions,
|
||||
): void;
|
||||
|
||||
didDropIn(dragment: Node): void;
|
||||
didDropIn(dragment: INode): void;
|
||||
|
||||
didDropOut(dragment: Node): void;
|
||||
|
||||
get isPurging(): boolean;
|
||||
didDropOut(dragment: INode): void;
|
||||
|
||||
purge(): void;
|
||||
|
||||
removeSlot(slotNode: INode): boolean;
|
||||
|
||||
setVisible(flag: boolean): void;
|
||||
|
||||
getVisible(): boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,7 +309,11 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
return !!this._isRGLContainer;
|
||||
}
|
||||
|
||||
private _slotFor?: IProp | null = null;
|
||||
get isEmptyNode() {
|
||||
return this.isEmpty();
|
||||
}
|
||||
|
||||
private _slotFor?: IProp | null | undefined = null;
|
||||
|
||||
@obx.shallow _slots: INode[] = [];
|
||||
|
||||
@ -331,10 +322,10 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
@obx.ref private _conditionGroup: IPublicModelExclusiveGroup | null = null;
|
||||
@obx.ref private _conditionGroup: IExclusiveGroup | null = null;
|
||||
|
||||
/* istanbul ignore next */
|
||||
get conditionGroup(): IPublicModelExclusiveGroup | null {
|
||||
get conditionGroup(): IExclusiveGroup | null {
|
||||
return this._conditionGroup;
|
||||
}
|
||||
|
||||
@ -518,7 +509,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
this.document.addWillPurge(this);
|
||||
}
|
||||
|
||||
didDropIn(dragment: Node) {
|
||||
didDropIn(dragment: INode) {
|
||||
const { callbacks } = this.componentMeta.advanced;
|
||||
if (callbacks?.onNodeAdd) {
|
||||
const cbThis = this.internalToShellNode();
|
||||
@ -529,7 +520,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
}
|
||||
}
|
||||
|
||||
didDropOut(dragment: Node) {
|
||||
didDropOut(dragment: INode) {
|
||||
const { callbacks } = this.componentMeta.advanced;
|
||||
if (callbacks?.onNodeRemove) {
|
||||
const cbThis = this.internalToShellNode();
|
||||
@ -590,7 +581,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
/**
|
||||
* 关联属性
|
||||
*/
|
||||
get slotFor(): IProp | null {
|
||||
get slotFor(): IProp | null | undefined {
|
||||
return this._slotFor;
|
||||
}
|
||||
|
||||
@ -610,10 +601,10 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
});
|
||||
}
|
||||
if (this.isSlot()) {
|
||||
this.parent.removeSlot(this, purge);
|
||||
this.parent.children.internalDelete(this, purge, useMutator, { suppressRemoveEvent: true });
|
||||
this.parent.removeSlot(this);
|
||||
this.parent.children?.internalDelete(this, purge, useMutator, { suppressRemoveEvent: true });
|
||||
} else {
|
||||
this.parent.children.internalDelete(this, purge, useMutator, { suppressRemoveEvent: true });
|
||||
this.parent.children?.internalDelete(this, purge, useMutator, { suppressRemoveEvent: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -653,7 +644,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
/**
|
||||
* 节点组件描述
|
||||
*/
|
||||
@computed get componentMeta(): ComponentMeta {
|
||||
@computed get componentMeta(): IComponentMeta {
|
||||
return this.document.getComponentMeta(this.componentName);
|
||||
}
|
||||
|
||||
@ -670,6 +661,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
|
||||
/* istanbul ignore next */
|
||||
setConditionGroup(grp: IPublicModelExclusiveGroup | string | null) {
|
||||
let _grp: IExclusiveGroup | null = null;
|
||||
if (!grp) {
|
||||
this.getExtraProp('conditionGroup', false)?.remove();
|
||||
if (this._conditionGroup) {
|
||||
@ -680,20 +672,20 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
}
|
||||
if (!isExclusiveGroup(grp)) {
|
||||
if (this.prevSibling?.conditionGroup?.name === grp) {
|
||||
grp = this.prevSibling.conditionGroup;
|
||||
_grp = this.prevSibling.conditionGroup;
|
||||
} else if (this.nextSibling?.conditionGroup?.name === grp) {
|
||||
grp = this.nextSibling.conditionGroup;
|
||||
} else {
|
||||
grp = new ExclusiveGroup(grp);
|
||||
_grp = this.nextSibling.conditionGroup;
|
||||
} else if (typeof grp === 'string') {
|
||||
_grp = new ExclusiveGroup(grp);
|
||||
}
|
||||
}
|
||||
if (this._conditionGroup !== grp) {
|
||||
this.getExtraProp('conditionGroup', true)?.setValue(grp.name);
|
||||
if (_grp && this._conditionGroup !== _grp) {
|
||||
this.getExtraProp('conditionGroup', true)?.setValue(_grp.name);
|
||||
if (this._conditionGroup) {
|
||||
this._conditionGroup.remove(this);
|
||||
}
|
||||
this._conditionGroup = grp;
|
||||
grp.add(this);
|
||||
this._conditionGroup = _grp;
|
||||
_grp?.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -749,13 +741,17 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
* @param {INode} node
|
||||
* @param {object} data
|
||||
*/
|
||||
replaceChild(node: INode, data: any): INode {
|
||||
replaceChild(node: INode, data: any): INode | null {
|
||||
if (this.children?.has(node)) {
|
||||
const selected = this.document.selection.has(node.id);
|
||||
|
||||
delete data.id;
|
||||
const newNode = this.document.createNode(data);
|
||||
|
||||
if (!isNode(newNode)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
this.insertBefore(newNode, node, false);
|
||||
node.remove(false);
|
||||
|
||||
@ -838,39 +834,45 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
/**
|
||||
* 获取节点在父容器中的索引
|
||||
*/
|
||||
@computed get index(): number {
|
||||
@computed get index(): number | undefined {
|
||||
if (!this.parent) {
|
||||
return -1;
|
||||
}
|
||||
return this.parent.children.indexOf(this);
|
||||
return this.parent.children?.indexOf(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个兄弟节点
|
||||
*/
|
||||
get nextSibling(): INode | null {
|
||||
get nextSibling(): INode | null | undefined {
|
||||
if (!this.parent) {
|
||||
return null;
|
||||
}
|
||||
const { index } = this;
|
||||
if (typeof index !== 'number') {
|
||||
return null;
|
||||
}
|
||||
if (index < 0) {
|
||||
return null;
|
||||
}
|
||||
return this.parent.children.get(index + 1);
|
||||
return this.parent.children?.get(index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上一个兄弟节点
|
||||
*/
|
||||
get prevSibling(): INode | null {
|
||||
get prevSibling(): INode | null | undefined {
|
||||
if (!this.parent) {
|
||||
return null;
|
||||
}
|
||||
const { index } = this;
|
||||
if (typeof index !== 'number') {
|
||||
return null;
|
||||
}
|
||||
if (index < 1) {
|
||||
return null;
|
||||
}
|
||||
return this.parent.children.get(index - 1);
|
||||
return this.parent.children?.get(index - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -889,7 +891,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
if (this.isSlot()) {
|
||||
foreachReverse(
|
||||
this.children!,
|
||||
(subNode: Node) => {
|
||||
(subNode: INode) => {
|
||||
subNode.remove(true, true);
|
||||
},
|
||||
(iterable, idx) => (iterable as NodeChildren).get(idx),
|
||||
@ -954,7 +956,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
...this.document.designer.transformProps(_extras_, this, stage),
|
||||
};
|
||||
|
||||
if (this.isParental() && this.children.size > 0 && !options.bypassChildren) {
|
||||
if (this.isParental() && this.children && this.children.size > 0 && !options.bypassChildren) {
|
||||
schema.children = this.children.export(stage);
|
||||
}
|
||||
|
||||
@ -971,7 +973,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
/**
|
||||
* 获取特定深度的父亲节点
|
||||
*/
|
||||
getZLevelTop(zLevel: number): Node | null {
|
||||
getZLevelTop(zLevel: number): INode | null {
|
||||
return getZLevelTop(this, zLevel);
|
||||
}
|
||||
|
||||
@ -983,11 +985,11 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
* 2 thisNode before or after otherNode
|
||||
* 0 thisNode same as otherNode
|
||||
*/
|
||||
comparePosition(otherNode: Node): PositionNO {
|
||||
comparePosition(otherNode: INode): PositionNO {
|
||||
return comparePosition(this, otherNode);
|
||||
}
|
||||
|
||||
unlinkSlot(slotNode: Node) {
|
||||
unlinkSlot(slotNode: INode) {
|
||||
const i = this._slots.indexOf(slotNode);
|
||||
if (i < 0) {
|
||||
return false;
|
||||
@ -998,7 +1000,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
/**
|
||||
* 删除一个Slot节点
|
||||
*/
|
||||
removeSlot(slotNode: Node): boolean {
|
||||
removeSlot(slotNode: INode): boolean {
|
||||
// if (purge) {
|
||||
// // should set parent null
|
||||
// slotNode?.internalSetParent(null, false);
|
||||
@ -1039,7 +1041,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
* 删除一个节点
|
||||
* @param node
|
||||
*/
|
||||
removeChild(node: Node) {
|
||||
removeChild(node: INode) {
|
||||
this.children?.delete(node);
|
||||
}
|
||||
|
||||
@ -1090,7 +1092,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
return this.componentName;
|
||||
}
|
||||
|
||||
insert(node: Node, ref?: INode, useMutator = true) {
|
||||
insert(node: INode, ref?: INode, useMutator = true) {
|
||||
this.insertAfter(node, ref, useMutator);
|
||||
}
|
||||
|
||||
@ -1101,7 +1103,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
|
||||
insertAfter(node: any, ref?: INode, useMutator = true) {
|
||||
const nodeInstance = ensureNode(node, this.document);
|
||||
this.children?.internalInsert(nodeInstance, ref ? ref.index + 1 : null, useMutator);
|
||||
this.children?.internalInsert(nodeInstance, ref ? (ref.index || 0) + 1 : null, useMutator);
|
||||
}
|
||||
|
||||
getParent() {
|
||||
@ -1128,7 +1130,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
return this.props;
|
||||
}
|
||||
|
||||
onChildrenChange(fn: (param?: { type: string; node: INode }) => void): IPublicTypeDisposable {
|
||||
onChildrenChange(fn: (param?: { type: string; node: INode }) => void): IPublicTypeDisposable | undefined {
|
||||
const wrappedFunc = wrapWithEventSwitch(fn);
|
||||
return this.children?.onChange(wrappedFunc);
|
||||
}
|
||||
@ -1330,7 +1332,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
|
||||
}
|
||||
}
|
||||
|
||||
function ensureNode(node: any, document: DocumentModel): Node {
|
||||
function ensureNode(node: any, document: IDocumentModel): INode {
|
||||
let nodeInstance = node;
|
||||
if (!isNode(node)) {
|
||||
if (node.getComponentName) {
|
||||
@ -1443,20 +1445,24 @@ export function insertChild(
|
||||
thing: INode | IPublicTypeNodeData,
|
||||
at?: number | null,
|
||||
copy?: boolean,
|
||||
): INode {
|
||||
let node: INode;
|
||||
if (isNode(thing) && (copy || thing.isSlot())) {
|
||||
thing = thing.export(IPublicEnumTransformStage.Clone);
|
||||
}
|
||||
if (isNode(thing)) {
|
||||
): INode | null {
|
||||
let node: INode | null | RootNode | undefined;
|
||||
let nodeSchema: IPublicTypeNodeSchema;
|
||||
if (isNode<INode>(thing) && (copy || thing.isSlot())) {
|
||||
nodeSchema = thing.export(IPublicEnumTransformStage.Clone);
|
||||
node = container.document?.createNode(nodeSchema);
|
||||
} else if (isNode<INode>(thing)) {
|
||||
node = thing;
|
||||
} else {
|
||||
node = container.document.createNode(thing);
|
||||
} else if (isNodeSchema(thing)) {
|
||||
node = container.document?.createNode(thing);
|
||||
}
|
||||
|
||||
container.children.insert(node, at);
|
||||
if (isNode<INode>(node)) {
|
||||
container.children?.insert(node, at);
|
||||
return node;
|
||||
}
|
||||
|
||||
return node;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function insertChildren(
|
||||
|
||||
@ -11,14 +11,14 @@ export const UNSET = Symbol.for('unset');
|
||||
// eslint-disable-next-line no-redeclare
|
||||
export type UNSET = typeof UNSET;
|
||||
|
||||
export interface IProp extends Omit<IPublicModelProp, 'exportSchema' | 'node' | 'slotNode' > {
|
||||
export interface IProp extends Omit<IPublicModelProp<
|
||||
INode
|
||||
>, 'exportSchema' | 'node' > {
|
||||
|
||||
readonly props: IProps;
|
||||
|
||||
readonly owner: INode;
|
||||
|
||||
get slotNode(): INode | null;
|
||||
|
||||
delete(prop: Prop): void;
|
||||
|
||||
export(stage: IPublicEnumTransformStage): IPublicTypeCompositeValue;
|
||||
@ -26,6 +26,10 @@ export interface IProp extends Omit<IPublicModelProp, 'exportSchema' | 'node' |
|
||||
getNode(): INode;
|
||||
|
||||
getAsString(): string;
|
||||
|
||||
unset(): void;
|
||||
|
||||
get value(): IPublicTypeCompositeValue | UNSET;
|
||||
}
|
||||
|
||||
export type ValueTypes = 'unset' | 'literal' | 'map' | 'list' | 'expression' | 'slot';
|
||||
|
||||
@ -33,8 +33,6 @@ export interface IPropParent {
|
||||
get path(): string[];
|
||||
|
||||
delete(prop: Prop): void;
|
||||
|
||||
query(path: string, createIfNone: boolean): Prop | null;
|
||||
}
|
||||
|
||||
export interface IProps extends Omit<IBaseModelProps<IProp>, | 'getExtraProp' | 'getExtraPropValue' | 'setExtraPropValue' | 'node'> {
|
||||
@ -52,6 +50,12 @@ export interface IProps extends Omit<IBaseModelProps<IProp>, | 'getExtraProp' |
|
||||
};
|
||||
|
||||
merge(value: IPublicTypePropsMap, extras?: IPublicTypePropsMap): void;
|
||||
|
||||
purge(): void;
|
||||
|
||||
query(path: string, createIfNone: boolean): Prop | null;
|
||||
|
||||
import(value?: IPublicTypePropsMap | IPublicTypePropsList | null, extras?: ExtrasObject): void;
|
||||
}
|
||||
|
||||
export class Props implements IProps, IPropParent {
|
||||
|
||||
@ -23,7 +23,7 @@ describe('document-model 测试', () => {
|
||||
|
||||
it('empty schema', () => {
|
||||
const doc = new DocumentModel(project);
|
||||
expect(doc.rootNode.id).toBe('root');
|
||||
expect(doc.rootNode?.id).toBe('root');
|
||||
expect(doc.currentRoot).toBe(doc.rootNode);
|
||||
expect(doc.root).toBe(doc.rootNode);
|
||||
expect(doc.modalNode).toBeUndefined();
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import set from 'lodash/set';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import '../../fixtures/window';
|
||||
import { Project } from '../../../src/project/project';
|
||||
import { Node } from '../../../src/document/node/node';
|
||||
import { Project, IProject } from '../../../src/project/project';
|
||||
import { Node, INode } from '../../../src/document/node/node';
|
||||
import { Designer } from '../../../src/designer/designer';
|
||||
import formSchema from '../../fixtures/schema/form';
|
||||
import { getIdsFromSchema, getNodeFromSchemaById } from '../../utils';
|
||||
@ -37,7 +37,7 @@ beforeAll(() => {
|
||||
|
||||
describe('schema 生成节点模型测试', () => {
|
||||
describe('block ❌ | component ❌ | slot ❌', () => {
|
||||
let project: Project;
|
||||
let project: IProject;
|
||||
beforeEach(() => {
|
||||
project = new Project(designer, {
|
||||
componentsTree: [
|
||||
@ -52,12 +52,12 @@ describe('schema 生成节点模型测试', () => {
|
||||
it('基本的节点模型初始化,模型导出', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const expectedNodeCnt = ids.length;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
expect(nodesMap?.size).toBe(expectedNodeCnt);
|
||||
ids.forEach(id => {
|
||||
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
expect(nodesMap?.get(id)?.componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
|
||||
});
|
||||
|
||||
const pageNode = currentDocument?.getNode('page');
|
||||
@ -76,18 +76,18 @@ describe('schema 生成节点模型测试', () => {
|
||||
it('基本的节点模型初始化,节点深度', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
const getNode = currentDocument?.getNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('page');
|
||||
const rootHeaderNode = getNode('node_k1ow3cba');
|
||||
const rootContentNode = getNode('node_k1ow3cbb');
|
||||
const rootFooterNode = getNode('node_k1ow3cbc');
|
||||
const formNode = getNode('form');
|
||||
const cardNode = getNode('node_k1ow3cbj');
|
||||
const cardContentNode = getNode('node_k1ow3cbk');
|
||||
const columnsLayoutNode = getNode('node_k1ow3cbw');
|
||||
const columnNode = getNode('node_k1ow3cbx');
|
||||
const textFieldNode = getNode('node_k1ow3cbz');
|
||||
const pageNode = getNode?.('page');
|
||||
const rootHeaderNode = getNode?.('node_k1ow3cba');
|
||||
const rootContentNode = getNode?.('node_k1ow3cbb');
|
||||
const rootFooterNode = getNode?.('node_k1ow3cbc');
|
||||
const formNode = getNode?.('form');
|
||||
const cardNode = getNode?.('node_k1ow3cbj');
|
||||
const cardContentNode = getNode?.('node_k1ow3cbk');
|
||||
const columnsLayoutNode = getNode?.('node_k1ow3cbw');
|
||||
const columnNode = getNode?.('node_k1ow3cbx');
|
||||
const textFieldNode = getNode?.('node_k1ow3cbz');
|
||||
|
||||
expect(pageNode?.zLevel).toBe(0);
|
||||
expect(rootHeaderNode?.zLevel).toBe(1);
|
||||
@ -131,7 +131,7 @@ describe('schema 生成节点模型测试', () => {
|
||||
const textFieldNode = getNode('node_k1ow3cbz');
|
||||
|
||||
expect(pageNode?.index).toBe(-1);
|
||||
expect(pageNode?.children.toString()).toBe('[object Array]');
|
||||
expect(pageNode?.children?.toString()).toBe('[object Array]');
|
||||
expect(pageNode?.children?.get(1)).toBe(rootContentNode);
|
||||
expect(pageNode?.getChildren()?.get(1)).toBe(rootContentNode);
|
||||
expect(pageNode?.getNode()).toBe(pageNode);
|
||||
@ -162,20 +162,20 @@ describe('schema 生成节点模型测试', () => {
|
||||
it('基本的节点模型初始化,节点新建、删除等事件', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const getNode = currentDocument.getNode.bind(currentDocument);
|
||||
const createNode = currentDocument.createNode.bind(currentDocument);
|
||||
const getNode = currentDocument?.getNode.bind(currentDocument);
|
||||
const createNode = currentDocument?.createNode.bind(currentDocument);
|
||||
|
||||
const pageNode = getNode('page');
|
||||
const pageNode = getNode?.('page');
|
||||
const nodeCreateHandler = jest.fn();
|
||||
const offCreate = currentDocument?.onNodeCreate(nodeCreateHandler);
|
||||
|
||||
const node = createNode({
|
||||
const node = createNode?.({
|
||||
componentName: 'TextInput',
|
||||
props: {
|
||||
propA: 'haha',
|
||||
},
|
||||
});
|
||||
currentDocument?.insertNode(pageNode, node);
|
||||
pageNode && node && currentDocument?.insertNode(pageNode, node);
|
||||
|
||||
expect(nodeCreateHandler).toHaveBeenCalledTimes(1);
|
||||
expect(nodeCreateHandler.mock.calls[0][0]).toBe(node);
|
||||
@ -184,7 +184,7 @@ describe('schema 生成节点模型测试', () => {
|
||||
|
||||
const nodeDestroyHandler = jest.fn();
|
||||
const offDestroy = currentDocument?.onNodeDestroy(nodeDestroyHandler);
|
||||
node.remove();
|
||||
node?.remove();
|
||||
expect(nodeDestroyHandler).toHaveBeenCalledTimes(1);
|
||||
expect(nodeDestroyHandler.mock.calls[0][0]).toBe(node);
|
||||
expect(nodeDestroyHandler.mock.calls[0][0].componentName).toBe('TextInput');
|
||||
@ -290,9 +290,9 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode && currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
@ -300,11 +300,11 @@ describe('schema 生成节点模型测试', () => {
|
||||
propB: 3,
|
||||
},
|
||||
}, 0);
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children.length).toBe(4);
|
||||
const insertedNode = formNode.children.get(0);
|
||||
expect(insertedNode.componentName).toBe('TextInput');
|
||||
expect(insertedNode.propsData).toEqual({
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode?.children?.length).toBe(4);
|
||||
const insertedNode = formNode?.children?.get(0);
|
||||
expect(insertedNode?.componentName).toBe('TextInput');
|
||||
expect(insertedNode?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
@ -316,9 +316,9 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode && currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
@ -326,11 +326,11 @@ describe('schema 生成节点模型测试', () => {
|
||||
propB: 3,
|
||||
},
|
||||
}, 1);
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(formNode.children.length).toBe(4);
|
||||
const insertedNode = formNode.children.get(1);
|
||||
expect(insertedNode.componentName).toBe('TextInput');
|
||||
expect(insertedNode.propsData).toEqual({
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode?.children?.length).toBe(4);
|
||||
const insertedNode = formNode?.children?.get(1);
|
||||
expect(insertedNode?.componentName).toBe('TextInput');
|
||||
expect(insertedNode?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
@ -342,8 +342,8 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as INode;
|
||||
currentDocument?.insertNode(formNode, {
|
||||
componentName: 'ParentNode',
|
||||
props: {
|
||||
@ -367,8 +367,8 @@ describe('schema 生成节点模型测试', () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(nodesMap.size).toBe(ids.length + 3);
|
||||
expect(formNode.children.length).toBe(4);
|
||||
expect(nodesMap?.size).toBe(ids.length + 3);
|
||||
expect(formNode.children?.length).toBe(4);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('ParentNode');
|
||||
expect(formNode.children?.get(3)?.children?.get(0)?.componentName).toBe('SubNode');
|
||||
expect(formNode.children?.get(3)?.children?.get(1)?.componentName).toBe('SubNode2');
|
||||
@ -378,9 +378,9 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode && currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
@ -388,17 +388,17 @@ describe('schema 生成节点模型测试', () => {
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput');
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it.skip('场景一:插入 NodeSchema,id 与现有 schema 里的 id 重复,但关闭了 id 检测器', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
currentDocument?.insertNode(formNode, {
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
formNode && currentDocument?.insertNode(formNode, {
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id1',
|
||||
props: {
|
||||
@ -406,16 +406,16 @@ describe('schema 生成节点模型测试', () => {
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
expect(nodesMap.get('nodeschema-id1').componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(nodesMap?.get('nodeschema-id1')?.componentName).toBe('TextInput');
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景二:插入 Node 实例', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form');
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form');
|
||||
const inputNode = currentDocument?.createNode({
|
||||
componentName: 'TextInput',
|
||||
id: 'nodeschema-id2',
|
||||
@ -424,22 +424,22 @@ describe('schema 生成节点模型测试', () => {
|
||||
propB: 3,
|
||||
},
|
||||
});
|
||||
currentDocument?.insertNode(formNode, inputNode);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('TextInput');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
formNode && currentDocument?.insertNode(formNode, inputNode);
|
||||
expect(formNode?.children?.get(3)?.componentName).toBe('TextInput');
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
});
|
||||
|
||||
it('场景三:插入 JSExpression', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as Node;
|
||||
currentDocument?.insertNode(formNode, {
|
||||
type: 'JSExpression',
|
||||
value: 'just a expression',
|
||||
});
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
|
||||
// expect(formNode.children?.get(3)?.children).toEqual({
|
||||
// type: 'JSExpression',
|
||||
@ -450,10 +450,10 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as Node;
|
||||
currentDocument?.insertNode(formNode, 'just a string');
|
||||
expect(nodesMap.size).toBe(ids.length + 1);
|
||||
expect(nodesMap?.size).toBe(ids.length + 1);
|
||||
expect(formNode.children?.get(3)?.componentName).toBe('Leaf');
|
||||
// expect(formNode.children?.get(3)?.children).toBe('just a string');
|
||||
});
|
||||
@ -473,8 +473,8 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as Node;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
currentDocument?.insertNodes(formNode, [
|
||||
@ -493,17 +493,17 @@ describe('schema 生成节点模型测试', () => {
|
||||
},
|
||||
},
|
||||
], 1);
|
||||
expect(nodesMap.size).toBe(ids.length + 2);
|
||||
expect(nodesMap?.size).toBe(ids.length + 2);
|
||||
expect(formNode.children?.length).toBe(5);
|
||||
const insertedNode1 = formNode.children.get(1);
|
||||
const insertedNode2 = formNode.children.get(2);
|
||||
expect(insertedNode1.componentName).toBe('TextInput');
|
||||
expect(insertedNode1.propsData).toEqual({
|
||||
const insertedNode1 = formNode.children?.get(1);
|
||||
const insertedNode2 = formNode.children?.get(2);
|
||||
expect(insertedNode1?.componentName).toBe('TextInput');
|
||||
expect(insertedNode1?.propsData).toEqual({
|
||||
propA: 'haha2',
|
||||
propB: 3,
|
||||
});
|
||||
expect(insertedNode2.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2.propsData).toEqual({
|
||||
expect(insertedNode2?.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
@ -513,8 +513,8 @@ describe('schema 生成节点模型测试', () => {
|
||||
expect(project).toBeTruthy();
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const formNode = nodesMap.get('form') as Node;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const formNode = nodesMap?.get('form') as INode;
|
||||
const formNode2 = currentDocument?.getNode('form');
|
||||
expect(formNode).toEqual(formNode2);
|
||||
const createdNode1 = currentDocument?.createNode({
|
||||
@ -532,17 +532,17 @@ describe('schema 生成节点模型测试', () => {
|
||||
},
|
||||
});
|
||||
currentDocument?.insertNodes(formNode, [createdNode1, createdNode2], 1);
|
||||
expect(nodesMap.size).toBe(ids.length + 2);
|
||||
expect(nodesMap?.size).toBe(ids.length + 2);
|
||||
expect(formNode.children?.length).toBe(5);
|
||||
const insertedNode1 = formNode.children.get(1);
|
||||
const insertedNode2 = formNode.children.get(2);
|
||||
expect(insertedNode1.componentName).toBe('TextInput');
|
||||
expect(insertedNode1.propsData).toEqual({
|
||||
const insertedNode1 = formNode.children?.get(1);
|
||||
const insertedNode2 = formNode.children?.get(2);
|
||||
expect(insertedNode1?.componentName).toBe('TextInput');
|
||||
expect(insertedNode1?.propsData).toEqual({
|
||||
propA: 'haha2',
|
||||
propB: 3,
|
||||
});
|
||||
expect(insertedNode2.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2.propsData).toEqual({
|
||||
expect(insertedNode2?.componentName).toBe('TextInput2');
|
||||
expect(insertedNode2?.propsData).toEqual({
|
||||
propA: 'haha',
|
||||
propB: 3,
|
||||
});
|
||||
@ -561,13 +561,13 @@ describe('schema 生成节点模型测试', () => {
|
||||
project.open();
|
||||
expect(project).toBeTruthy();
|
||||
const { currentDocument } = project;
|
||||
const { nodesMap } = currentDocument;
|
||||
const nodesMap = currentDocument?.nodesMap;
|
||||
const ids = getIdsFromSchema(formSchema);
|
||||
// 目前每个 slot 会新增(1 + children.length)个节点
|
||||
const expectedNodeCnt = ids.length + 2;
|
||||
expect(nodesMap.size).toBe(expectedNodeCnt);
|
||||
expect(nodesMap?.size).toBe(expectedNodeCnt);
|
||||
// PageHeader
|
||||
expect(nodesMap.get('node_k1ow3cbd').slots).toHaveLength(1);
|
||||
expect(nodesMap?.get('node_k1ow3cbd')?.slots).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
42
packages/shell/src/model/condition-group.ts
Normal file
42
packages/shell/src/model/condition-group.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { IExclusiveGroup } from '@alilc/lowcode-designer';
|
||||
import { IPublicModelExclusiveGroup, IPublicModelNode } from '@alilc/lowcode-types';
|
||||
import { conditionGroupSymbol, nodeSymbol } from '../symbols';
|
||||
import { Node } from './node';
|
||||
|
||||
export class ConditionGroup implements IPublicModelExclusiveGroup {
|
||||
private [conditionGroupSymbol]: IExclusiveGroup | null;
|
||||
|
||||
constructor(conditionGroup: IExclusiveGroup | null) {
|
||||
this[conditionGroupSymbol] = conditionGroup;
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this[conditionGroupSymbol]?.id;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this[conditionGroupSymbol]?.title;
|
||||
}
|
||||
|
||||
get firstNode() {
|
||||
return Node.create(this[conditionGroupSymbol]?.firstNode);
|
||||
}
|
||||
|
||||
setVisible(node: IPublicModelNode) {
|
||||
this[conditionGroupSymbol]?.setVisible((node as any)[nodeSymbol] ? (node as any)[nodeSymbol] : node);
|
||||
}
|
||||
|
||||
static create(conditionGroup: IExclusiveGroup | null) {
|
||||
if (!conditionGroup) {
|
||||
return null;
|
||||
}
|
||||
// @ts-ignore
|
||||
if (conditionGroup[conditionGroupSymbol]) {
|
||||
return (conditionGroup as any)[conditionGroupSymbol];
|
||||
}
|
||||
const shellConditionGroup = new ConditionGroup(conditionGroup);
|
||||
// @ts-ignore
|
||||
shellConditionGroup[conditionGroupSymbol] = shellConditionGroup;
|
||||
return shellConditionGroup;
|
||||
}
|
||||
}
|
||||
@ -27,6 +27,7 @@ import { ComponentMeta as ShellComponentMeta } from './component-meta';
|
||||
import { SettingTopEntry as ShellSettingTopEntry } from './setting-top-entry';
|
||||
import { documentSymbol, nodeSymbol } from '../symbols';
|
||||
import { ReactElement } from 'react';
|
||||
import { ConditionGroup } from './condition-group';
|
||||
|
||||
const shellNodeSymbol = Symbol('shellNodeSymbol');
|
||||
|
||||
@ -289,7 +290,7 @@ export class Node implements IPublicModelNode {
|
||||
/**
|
||||
* 当前节点为插槽节点时,返回节点对应的属性实例
|
||||
*/
|
||||
get slotFor(): IPublicModelProp | null {
|
||||
get slotFor(): IPublicModelProp | null | undefined {
|
||||
return ShellProp.create(this[nodeSymbol].slotFor);
|
||||
}
|
||||
|
||||
@ -349,7 +350,6 @@ export class Node implements IPublicModelNode {
|
||||
|
||||
/**
|
||||
* 获取节点实例对应的 dom 节点
|
||||
* @deprecated
|
||||
*/
|
||||
getDOMNode() {
|
||||
return (this[nodeSymbol] as any).getDOMNode();
|
||||
@ -362,9 +362,9 @@ export class Node implements IPublicModelNode {
|
||||
* @param sorter
|
||||
*/
|
||||
mergeChildren(
|
||||
remover: (node: Node, idx: number) => boolean,
|
||||
adder: (children: Node[]) => any,
|
||||
sorter: (firstNode: Node, secondNode: Node) => number,
|
||||
remover: (node: IPublicModelNode, idx: number) => boolean,
|
||||
adder: (children: IPublicModelNode[]) => any,
|
||||
sorter: (firstNode: IPublicModelNode, secondNode: IPublicModelNode) => number,
|
||||
): any {
|
||||
return this.children?.mergeChildren(remover, adder, sorter);
|
||||
}
|
||||
@ -641,7 +641,7 @@ export class Node implements IPublicModelNode {
|
||||
* @since v1.1.0
|
||||
*/
|
||||
get conditionGroup(): IPublicModelExclusiveGroup | null {
|
||||
return this[nodeSymbol].conditionGroup;
|
||||
return ConditionGroup.create(this[nodeSymbol].conditionGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -33,3 +33,4 @@ export const resourceTypeSymbol = Symbol('resourceType');
|
||||
export const resourceSymbol = Symbol('resource');
|
||||
export const clipboardSymbol = Symbol('clipboard');
|
||||
export const configSymbol = Symbol('configSymbol');
|
||||
export const conditionGroupSymbol = Symbol('conditionGroup');
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { IPublicModelNode } from '..';
|
||||
import { IPublicModelNode, IPublicTypeTitleContent } from '..';
|
||||
|
||||
export interface IPublicModelExclusiveGroup {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
get firstNode(): IPublicModelNode;
|
||||
export interface IPublicModelExclusiveGroup<
|
||||
Node = IPublicModelNode,
|
||||
> {
|
||||
readonly id: string | undefined;
|
||||
readonly title: IPublicTypeTitleContent | undefined;
|
||||
get firstNode(): Node | null;
|
||||
setVisible(node: Node): void;
|
||||
}
|
||||
|
||||
@ -8,7 +8,10 @@ export interface IBaseModelNode<
|
||||
Node = IPublicModelNode,
|
||||
NodeChildren = IPublicModelNodeChildren,
|
||||
ComponentMeta = IPublicModelComponentMeta,
|
||||
SettingTopEntry = IPublicModelSettingTopEntry
|
||||
SettingTopEntry = IPublicModelSettingTopEntry,
|
||||
Props = IPublicModelProps,
|
||||
Prop = IPublicModelProp,
|
||||
ExclusiveGroup = IPublicModelExclusiveGroup
|
||||
> {
|
||||
|
||||
/**
|
||||
@ -167,7 +170,7 @@ export interface IBaseModelNode<
|
||||
* 下标
|
||||
* index
|
||||
*/
|
||||
get index(): number;
|
||||
get index(): number | undefined;
|
||||
|
||||
/**
|
||||
* 图标
|
||||
@ -203,13 +206,13 @@ export interface IBaseModelNode<
|
||||
* 获取当前节点的前一个兄弟节点
|
||||
* get previous sibling of this node
|
||||
*/
|
||||
get prevSibling(): Node | null;
|
||||
get prevSibling(): Node | null | undefined;
|
||||
|
||||
/**
|
||||
* 获取当前节点的后一个兄弟节点
|
||||
* get next sibling of this node
|
||||
*/
|
||||
get nextSibling(): Node | null;
|
||||
get nextSibling(): Node | null | undefined;
|
||||
|
||||
/**
|
||||
* 获取当前节点的父亲节点
|
||||
@ -233,13 +236,13 @@ export interface IBaseModelNode<
|
||||
* 当前节点为插槽节点时,返回节点对应的属性实例
|
||||
* return coresponding prop when this node is a slot node
|
||||
*/
|
||||
get slotFor(): IPublicModelProp | null;
|
||||
get slotFor(): Prop | null | undefined;
|
||||
|
||||
/**
|
||||
* 返回节点的属性集
|
||||
* get props
|
||||
*/
|
||||
get props(): IPublicModelProps | null;
|
||||
get props(): Props | null;
|
||||
|
||||
/**
|
||||
* 返回节点的属性集
|
||||
@ -250,7 +253,7 @@ export interface IBaseModelNode<
|
||||
/**
|
||||
* get conditionGroup
|
||||
*/
|
||||
get conditionGroup(): IPublicModelExclusiveGroup | null;
|
||||
get conditionGroup(): ExclusiveGroup | null;
|
||||
|
||||
/**
|
||||
* 获取符合搭建协议 - 节点 schema 结构
|
||||
@ -295,7 +298,7 @@ export interface IBaseModelNode<
|
||||
* get prop by path
|
||||
* @param path 属性路径,支持 a / a.b / a.0 等格式
|
||||
*/
|
||||
getProp(path: string, createIfNone: boolean): IPublicModelProp | null;
|
||||
getProp(path: string, createIfNone: boolean): Prop | null;
|
||||
|
||||
/**
|
||||
* 获取指定 path 的属性模型实例值
|
||||
@ -313,7 +316,7 @@ export interface IBaseModelNode<
|
||||
* @param path 属性路径,支持 a / a.b / a.0 等格式
|
||||
* @param createIfNone 当没有属性的时候,是否创建一个属性
|
||||
*/
|
||||
getExtraProp(path: string, createIfNone?: boolean): IPublicModelProp | null;
|
||||
getExtraProp(path: string, createIfNone?: boolean): Prop | null;
|
||||
|
||||
/**
|
||||
* 获取指定 path 的属性模型实例,
|
||||
@ -481,6 +484,11 @@ export interface IBaseModelNode<
|
||||
* @since v1.1.0
|
||||
*/
|
||||
setConditionalVisible(): void;
|
||||
|
||||
/**
|
||||
* 获取节点实例对应的 dom 节点
|
||||
*/
|
||||
getDOMNode(): HTMLElement;
|
||||
}
|
||||
|
||||
export interface IPublicModelNode extends IBaseModelNode<IPublicModelDocumentModel, IPublicModelNode> {}
|
||||
@ -2,7 +2,9 @@ import { IPublicEnumTransformStage } from '../enum';
|
||||
import { IPublicTypeCompositeValue } from '../type';
|
||||
import { IPublicModelNode } from './';
|
||||
|
||||
export interface IPublicModelProp {
|
||||
export interface IPublicModelProp<
|
||||
Node = IPublicModelNode
|
||||
> {
|
||||
|
||||
/**
|
||||
* id
|
||||
@ -25,14 +27,14 @@ export interface IPublicModelProp {
|
||||
* 返回所属的节点实例
|
||||
* get node instance, which this prop belongs to
|
||||
*/
|
||||
get node(): IPublicModelNode | null;
|
||||
get node(): Node | null;
|
||||
|
||||
/**
|
||||
* 当本 prop 代表一个 Slot 时,返回对应的 slotNode
|
||||
* return the slot node (only if the current prop represents a slot)
|
||||
* @since v1.1.0
|
||||
*/
|
||||
get slotNode(): IPublicModelNode | undefined | null;
|
||||
get slotNode(): Node | undefined | null;
|
||||
|
||||
/**
|
||||
* 是否是 Prop , 固定返回 true
|
||||
|
||||
@ -195,8 +195,8 @@ export interface IPublicTypeCallbacks {
|
||||
onChildMoveHook?: (childNode: IPublicModelNode, currentNode: IPublicModelNode) => boolean;
|
||||
|
||||
// events
|
||||
onNodeRemove?: (removedNode: IPublicModelNode, currentNode: IPublicModelNode) => void;
|
||||
onNodeAdd?: (addedNode: IPublicModelNode, currentNode: IPublicModelNode) => void;
|
||||
onNodeRemove?: (removedNode: IPublicModelNode | null, currentNode: IPublicModelNode | null) => void;
|
||||
onNodeAdd?: (addedNode: IPublicModelNode | null, currentNode: IPublicModelNode | null) => void;
|
||||
onSubtreeModified?: (currentNode: IPublicModelNode, options: any) => void;
|
||||
onResize?: (
|
||||
e: MouseEvent & {
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export function isDOMText(data: any): boolean {
|
||||
export function isDOMText(data: any): data is string {
|
||||
return typeof data === 'string';
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { IPublicTypeNodeSchema } from '@alilc/lowcode-types';
|
||||
|
||||
export function isNodeSchema(data: any): data is IPublicTypeNodeSchema {
|
||||
return data && data.componentName;
|
||||
return data && data.componentName && !data.isNode;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user