fix: remove console.log statements, update test, and purge children in NodeChildren class

This commit is contained in:
liujuping 2024-03-06 16:38:19 +08:00 committed by 林熠
parent d7dfde5452
commit 71f9e08cb2
9 changed files with 77 additions and 105 deletions

View File

@ -22,6 +22,7 @@ const jestConfig = {
// testMatch: ['**/selection.test.ts'], // testMatch: ['**/selection.test.ts'],
// testMatch: ['**/plugin/sequencify.test.ts'], // testMatch: ['**/plugin/sequencify.test.ts'],
// testMatch: ['**/builtin-simulator/utils/parse-metadata.test.ts'], // testMatch: ['**/builtin-simulator/utils/parse-metadata.test.ts'],
// testMatch: ['**/setting/setting-top-entry.test.ts'],
transformIgnorePatterns: [ transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`, `/node_modules/(?!${esModules})/`,
], ],

View File

@ -1,4 +1,3 @@
import { ReactNode } from 'react';
import { import {
IPublicTypeTitleContent, IPublicTypeTitleContent,
IPublicTypeSetterType, IPublicTypeSetterType,
@ -7,18 +6,15 @@ import {
IPublicTypeFieldConfig, IPublicTypeFieldConfig,
IPublicTypeCustomView, IPublicTypeCustomView,
IPublicTypeDisposable, IPublicTypeDisposable,
IPublicModelSettingField,
IBaseModelSettingField,
} from '@alilc/lowcode-types'; } from '@alilc/lowcode-types';
import type { import type {
IPublicTypeSetValueOptions, IPublicTypeSetValueOptions,
} from '@alilc/lowcode-types'; } from '@alilc/lowcode-types';
import { Transducer } from './utils'; import { Transducer } from './utils';
import { ISettingPropEntry, SettingPropEntry } from './setting-prop-entry'; import { SettingPropEntry } from './setting-prop-entry';
import { computed, obx, makeObservable, action, untracked, intl } from '@alilc/lowcode-editor-core'; import { computed, obx, makeObservable, action, untracked, intl } from '@alilc/lowcode-editor-core';
import { cloneDeep, isCustomView, isDynamicSetter, isJSExpression } from '@alilc/lowcode-utils'; import { cloneDeep, isCustomView, isDynamicSetter, isJSExpression } from '@alilc/lowcode-utils';
import { ISettingTopEntry } from './setting-top-entry'; import { ISettingTopEntry } from './setting-top-entry';
import { IComponentMeta, INode } from '@alilc/lowcode-designer';
function getSettingFieldCollectorKey(parent: ISettingTopEntry | ISettingField, config: IPublicTypeFieldConfig) { function getSettingFieldCollectorKey(parent: ISettingTopEntry | ISettingField, config: IPublicTypeFieldConfig) {
let cur = parent; let cur = parent;
@ -32,53 +28,9 @@ function getSettingFieldCollectorKey(parent: ISettingTopEntry | ISettingField, c
return path.join('.'); return path.join('.');
} }
export interface ISettingField extends ISettingPropEntry, Omit<IBaseModelSettingField< export interface ISettingField extends SettingField {}
ISettingTopEntry,
ISettingField,
IComponentMeta,
INode
>, 'setValue' | 'key' | 'node'> {
readonly isSettingField: true;
readonly isRequired: boolean; export class SettingField extends SettingPropEntry {
readonly isGroup: boolean;
extraProps: IPublicTypeFieldExtraProps;
get items(): Array<ISettingField | IPublicTypeCustomView>;
get title(): string | ReactNode | undefined;
get setter(): IPublicTypeSetterType | null;
get expanded(): boolean;
get valueState(): number;
setExpanded(value: boolean): void;
purge(): void;
setValue(
val: any,
isHotValue?: boolean,
force?: boolean,
extraOptions?: IPublicTypeSetValueOptions,
): void;
clearValue(): void;
valueChange(options: IPublicTypeSetValueOptions): void;
createField(config: IPublicTypeFieldConfig): ISettingField;
onEffect(action: () => void): IPublicTypeDisposable;
internalToShellField(): IPublicModelSettingField;
}
export class SettingField extends SettingPropEntry implements ISettingField {
readonly isSettingField = true; readonly isSettingField = true;
readonly isRequired: boolean; readonly isRequired: boolean;

View File

@ -1,6 +1,6 @@
import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry, IPublicApiSetters } from '@alilc/lowcode-types'; import { IPublicTypeCustomView, IPublicModelEditor, IPublicModelSettingTopEntry, IPublicApiSetters } from '@alilc/lowcode-types';
import { isCustomView } from '@alilc/lowcode-utils'; import { isCustomView } from '@alilc/lowcode-utils';
import { computed, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; import { computed, IEventBus, createModuleEventBus, obx, makeObservable } from '@alilc/lowcode-editor-core';
import { ISettingEntry } from './setting-entry-type'; import { ISettingEntry } from './setting-entry-type';
import { ISettingField, SettingField } from './setting-field'; import { ISettingField, SettingField } from './setting-field';
import { INode } from '../../document'; import { INode } from '../../document';
@ -14,33 +14,17 @@ function generateSessionId(nodes: INode[]) {
.join(','); .join(',');
} }
export interface ISettingTopEntry extends ISettingEntry, IPublicModelSettingTopEntry< export interface ISettingTopEntry extends SettingTopEntry {}
export class SettingTopEntry implements ISettingEntry, IPublicModelSettingTopEntry<
INode, INode,
ISettingField ISettingField
> { > {
readonly top: ISettingTopEntry;
readonly parent: ISettingTopEntry;
readonly path: never[];
items: Array<ISettingField | IPublicTypeCustomView>;
componentMeta: IComponentMeta | null;
purge(): void;
getExtraPropValue(propName: string): void;
setExtraPropValue(propName: string, value: any): void;
}
export class SettingTopEntry implements ISettingTopEntry {
private emitter: IEventBus = createModuleEventBus('SettingTopEntry'); private emitter: IEventBus = createModuleEventBus('SettingTopEntry');
private _items: Array<SettingField | IPublicTypeCustomView> = []; private _items: Array<ISettingField | IPublicTypeCustomView> = [];
private _componentMeta: IComponentMeta | null = null; private _componentMeta: IComponentMeta | null | undefined = null;
private _isSame = true; private _isSame = true;
@ -75,7 +59,7 @@ export class SettingTopEntry implements ISettingTopEntry {
} }
get isLocked(): boolean { get isLocked(): boolean {
return this.first.isLocked; return this.first?.isLocked ?? false;
} }
/** /**
@ -87,7 +71,11 @@ export class SettingTopEntry implements ISettingTopEntry {
readonly id: string; readonly id: string;
readonly first: INode; @computed get first(): INode | null {
return this._first;
}
@obx.ref _first: INode | null;
readonly designer: IDesigner | undefined; readonly designer: IDesigner | undefined;
@ -96,12 +84,14 @@ export class SettingTopEntry implements ISettingTopEntry {
disposeFunctions: any[] = []; disposeFunctions: any[] = [];
constructor(readonly editor: IPublicModelEditor, readonly nodes: INode[]) { constructor(readonly editor: IPublicModelEditor, readonly nodes: INode[]) {
makeObservable(this);
if (!Array.isArray(nodes) || nodes.length < 1) { if (!Array.isArray(nodes) || nodes.length < 1) {
throw new ReferenceError('nodes should not be empty'); throw new ReferenceError('nodes should not be empty');
} }
this.id = generateSessionId(nodes); this.id = generateSessionId(nodes);
this.first = nodes[0]; this._first = nodes[0];
this.designer = this.first.document?.designer; this.designer = this._first.document?.designer;
this.setters = editor.get('setters') as IPublicApiSetters; this.setters = editor.get('setters') as IPublicApiSetters;
// setups // setups
@ -116,7 +106,7 @@ export class SettingTopEntry implements ISettingTopEntry {
private setupComponentMeta() { private setupComponentMeta() {
// todo: enhance compile a temp configure.compiled // todo: enhance compile a temp configure.compiled
const { first } = this; const { first } = this;
const meta = first.componentMeta; const meta = first?.componentMeta;
const l = this.nodes.length; const l = this.nodes.length;
let theSame = true; let theSame = true;
for (let i = 1; i < l; i++) { for (let i = 1; i < l; i++) {
@ -160,7 +150,7 @@ export class SettingTopEntry implements ISettingTopEntry {
/** /**
* *
*/ */
@computed getValue(): any { getValue(): any {
return this.first?.propsData; return this.first?.propsData;
} }
@ -202,14 +192,14 @@ export class SettingTopEntry implements ISettingTopEntry {
* *
*/ */
getPropValue(propName: string | number): any { getPropValue(propName: string | number): any {
return this.first.getProp(propName.toString(), true)?.getValue(); return this.first?.getProp(propName.toString(), true)?.getValue();
} }
/** /**
* *
*/ */
getExtraPropValue(propName: string) { getExtraPropValue(propName: string) {
return this.first.getExtraProp(propName, false)?.getValue(); return this.first?.getExtraProp(propName, false)?.getValue();
} }
/** /**
@ -244,8 +234,9 @@ export class SettingTopEntry implements ISettingTopEntry {
this.disposeItems(); this.disposeItems();
this._settingFieldMap = {}; this._settingFieldMap = {};
this.emitter.removeAllListeners(); this.emitter.removeAllListeners();
this.disposeFunctions.forEach(f => f()); this.disposeFunctions.forEach(f => f?.());
this.disposeFunctions = []; this.disposeFunctions = [];
this._first = null;
} }
getProp(propName: string | number) { getProp(propName: string | number) {
@ -274,7 +265,7 @@ export class SettingTopEntry implements ISettingTopEntry {
} }
getPage() { getPage() {
return this.first.document; return this.first?.document;
} }
/** /**
@ -292,6 +283,7 @@ export class SettingTopEntry implements ISettingTopEntry {
interface Purgeable { interface Purgeable {
purge(): void; purge(): void;
} }
function isPurgeable(obj: any): obj is Purgeable { function isPurgeable(obj: any): obj is Purgeable {
return obj && obj.purge; return obj && obj.purge;
} }

View File

@ -91,6 +91,7 @@ export class NodeChildren implements Omit<IPublicModelNodeChildren<INode>,
node.import(item); node.import(item);
} else { } else {
node = this.owner.document?.createNode(item); node = this.owner.document?.createNode(item);
child?.purge();
} }
if (node) { if (node) {
@ -98,6 +99,10 @@ export class NodeChildren implements Omit<IPublicModelNodeChildren<INode>,
} }
} }
for (let i = data.length; i < originChildren.length; i++) {
originChildren[i].purge();
}
this.children = children; this.children = children;
this.internalInitParent(); this.internalInitParent();
if (!shallowEqual(children, originChildren)) { if (!shallowEqual(children, originChildren)) {

View File

@ -987,7 +987,7 @@ export class Node<Schema extends IPublicTypeNodeSchema = IPublicTypeNodeSchema>
this.autoruns?.forEach((dispose) => dispose()); this.autoruns?.forEach((dispose) => dispose());
this.props.purge(); this.props.purge();
this.settingEntry?.purge(); this.settingEntry?.purge();
// this.document.destroyNode(this); this.children?.purge();
} }
internalPurgeStart() { internalPurgeStart() {

View File

@ -1,8 +1,9 @@
import '../../fixtures/window'; import '../../fixtures/window';
import { Editor, Setters } from '@alilc/lowcode-editor-core'; import { Editor, Setters, reaction } from '@alilc/lowcode-editor-core';
import { Node } from '../../../src/document/node/node'; import { Node } from '../../../src/document/node/node';
import { Designer } from '../../../src/designer/designer'; import { Designer } from '../../../src/designer/designer';
import settingSchema from '../../fixtures/schema/setting'; import settingSchema from '../../fixtures/schema/setting';
import { SettingTopEntry } from '../../../src/designer/setting/setting-top-entry';
import divMeta from '../../fixtures/component-metadata/div'; import divMeta from '../../fixtures/component-metadata/div';
import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory'; import { shellModelFactory } from '../../../../engine/src/modules/shell-model-factory';
@ -109,6 +110,26 @@ describe('setting-top-entry 测试', () => {
expect(settingEntry.items).toHaveLength(0); expect(settingEntry.items).toHaveLength(0);
}); });
it('should notify when _first is set to null', (done) => {
// 创建一个简单的INode数组用于初始化SettingTopEntry实例
const nodes = [{ id: '1', propsData: {} }, { id: '2', propsData: {} }];
const entry = new SettingTopEntry(editor as any, nodes as any);
// 使用MobX的reaction来观察_first属性的变化
const dispose = reaction(
() => entry.first,
(first) => {
if (first === null) {
dispose(); // 清理reaction监听
done(); // 结束测试
}
}
);
// 执行purge方法期望_first被设置为null触发reaction回调
entry.purge();
});
it('vision 兼容测试', () => { it('vision 兼容测试', () => {
designer.createComponentMeta(divMeta); designer.createComponentMeta(divMeta);
designer.project.open(settingSchema); designer.project.open(settingSchema);

View File

@ -541,8 +541,6 @@ export class Hotkey implements Omit<IPublicApiHotkey, 'bind' | 'callbacks'> {
} }
private handleKeyEvent(e: KeyboardEvent): void { private handleKeyEvent(e: KeyboardEvent): void {
console.log(e);
// debugger;
if (!this.isActivate) { if (!this.isActivate) {
return; return;
} }

View File

@ -1,7 +1,7 @@
import { Node, Designer, Selection, SettingTopEntry } from '@alilc/lowcode-designer'; import { INode, IDesigner, Selection, SettingTopEntry } from '@alilc/lowcode-designer';
import { Editor, obx, computed, makeObservable, action, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core'; import { Editor, obx, computed, makeObservable, action, IEventBus, createModuleEventBus } from '@alilc/lowcode-editor-core';
function generateSessionId(nodes: Node[]) { function generateSessionId(nodes: INode[]) {
return nodes return nodes
.map((node) => node.id) .map((node) => node.id)
.sort() .sort()
@ -29,7 +29,11 @@ export class SettingsMain {
private disposeListener: () => void; private disposeListener: () => void;
private designer?: Designer; private _designer?: IDesigner;
get designer(): IDesigner | undefined {
return this._designer;
}
constructor(readonly editor: Editor) { constructor(readonly editor: Editor) {
makeObservable(this); makeObservable(this);
@ -49,12 +53,12 @@ export class SettingsMain {
this.editor.removeListener('designer.selection.change', setupSelection); this.editor.removeListener('designer.selection.change', setupSelection);
}; };
const designer = await this.editor.onceGot('designer'); const designer = await this.editor.onceGot('designer');
this.designer = designer; this._designer = designer;
setupSelection(designer.currentSelection); setupSelection(designer.currentSelection);
} }
@action @action
private setup(nodes: Node[]) { private setup(nodes: INode[]) {
// check nodes change // check nodes change
const sessionId = generateSessionId(nodes); const sessionId = generateSessionId(nodes);
if (sessionId === this._sessionId) { if (sessionId === this._sessionId) {
@ -66,15 +70,15 @@ export class SettingsMain {
return; return;
} }
if (!this.designer) { if (!this._designer) {
this.designer = nodes[0].document.designer; this._designer = nodes[0].document.designer;
} }
// 当节点只有一个时,复用 node 上挂载的 settingEntry不会产生平行的两个实例这样在整个系统中对 // 当节点只有一个时,复用 node 上挂载的 settingEntry不会产生平行的两个实例这样在整个系统中对
// 某个节点操作的 SettingTopEntry 只有一个实例,后续的 getProp() 也会拿到相同的 SettingField 实例 // 某个节点操作的 SettingTopEntry 只有一个实例,后续的 getProp() 也会拿到相同的 SettingField 实例
if (nodes.length === 1) { if (nodes.length === 1) {
this._settings = nodes[0].settingEntry; this._settings = nodes[0].settingEntry;
} else { } else {
this._settings = this.designer.createSettingEntry(nodes); this._settings = this._designer.createSettingEntry(nodes);
} }
} }

View File

@ -1,14 +1,14 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Tab, Breadcrumb } from '@alifd/next'; import { Tab, Breadcrumb } from '@alifd/next';
import { Title, observer, Editor, obx, globalContext, engineConfig, makeObservable } from '@alilc/lowcode-editor-core'; import { Title, observer, Editor, obx, globalContext, engineConfig, makeObservable } from '@alilc/lowcode-editor-core';
import { Node, SettingField, isSettingField, INode } from '@alilc/lowcode-designer'; import { ISettingField, INode } from '@alilc/lowcode-designer';
import classNames from 'classnames'; import classNames from 'classnames';
import { SettingsMain } from './main'; import { SettingsMain } from './main';
import { SettingsPane } from './settings-pane'; import { SettingsPane } from './settings-pane';
import { StageBox } from '../stage-box'; import { StageBox } from '../stage-box';
import { SkeletonContext } from '../../context'; import { SkeletonContext } from '../../context';
import { intl } from '../../locale'; import { intl } from '../../locale';
import { createIcon } from '@alilc/lowcode-utils'; import { createIcon, isSettingField } from '@alilc/lowcode-utils';
interface ISettingsPrimaryPaneProps { interface ISettingsPrimaryPaneProps {
engineEditor: Editor; engineEditor: Editor;
@ -53,8 +53,7 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
} }
renderBreadcrumb() { renderBreadcrumb() {
const { settings, editor } = this.main; const { settings, editor, designer } = this.main;
// const shouldIgnoreRoot = config.props?.ignoreRoot;
const { shouldIgnoreRoot } = this.state; const { shouldIgnoreRoot } = this.state;
if (!settings) { if (!settings) {
return null; return null;
@ -73,10 +72,9 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
); );
} }
const designer = editor.get('designer');
const current = designer?.currentSelection?.getNodes()?.[0]; const current = designer?.currentSelection?.getNodes()?.[0];
let node: INode | null = settings.first; let node: INode | null = settings.first;
const focusNode = node.document?.focusNode; const focusNode = node?.document?.focusNode;
const items = []; const items = [];
let l = 3; let l = 3;
@ -202,7 +200,7 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
} }
let matched = false; let matched = false;
const tabs = (items as SettingField[]).map((field) => { const tabs = (items as ISettingField[]).map((field) => {
if (this._activeKey === field.name) { if (this._activeKey === field.name) {
matched = true; matched = true;
} }
@ -235,7 +233,7 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
</Tab.Item> </Tab.Item>
); );
}); });
const activeKey = matched ? this._activeKey : (items[0] as SettingField).name; const activeKey = matched ? this._activeKey : (items[0] as ISettingField).name;
const className = classNames('lc-settings-main', { const className = classNames('lc-settings-main', {
'lc-settings-hide-tabs': 'lc-settings-hide-tabs':
@ -261,9 +259,10 @@ export class SettingsPrimaryPane extends Component<ISettingsPrimaryPaneProps, {
} }
} }
function hoverNode(node: Node, flag: boolean) { function hoverNode(node: INode, flag: boolean) {
node.hover(flag); node.hover(flag);
} }
function selectNode(node: Node) {
function selectNode(node: INode) {
node?.select(); node?.select();
} }