mirror of
https://github.com/smartxworks/sunmao-ui.git
synced 2024-11-27 08:39:59 +08:00
Merge branch 'develop' of github.com:webzard-io/sunmao-ui into fix/codeeditor
This commit is contained in:
commit
03aad6c651
@ -1,19 +1,47 @@
|
||||
import { AppModel } from '../../src/AppModel/AppModel';
|
||||
import { ComponentId } from '../../src/AppModel/IAppModel';
|
||||
import { registry } from '../services';
|
||||
import { AppSchema } from './mock';
|
||||
import { AppSchema, PasteComponentWithChildrenSchema } from './mock';
|
||||
import { genOperation } from '../../src/operations';
|
||||
|
||||
describe('Component operations', ()=> {
|
||||
it('Change component id', ()=> {
|
||||
const appModel = new AppModel(AppSchema.spec.components, registry);
|
||||
describe('Component operations', () => {
|
||||
it('Change component id', () => {
|
||||
const appModel = new AppModel(AppSchema.spec.components, registry);
|
||||
|
||||
expect(appModel.getComponentById('text1' as ComponentId).id).toBe('text1');
|
||||
const operation = genOperation(registry, 'modifyComponentId', {
|
||||
componentId: 'text1',
|
||||
newId: 'text2',
|
||||
});
|
||||
operation.do(appModel);
|
||||
expect(appModel.getComponentById('text2' as ComponentId).id).toBe('text2');
|
||||
})
|
||||
})
|
||||
expect(appModel.getComponentById('text1' as ComponentId)?.id).toBe('text1');
|
||||
const operation = genOperation(registry, 'modifyComponentId', {
|
||||
componentId: 'text1',
|
||||
newId: 'text2',
|
||||
});
|
||||
operation.do(appModel);
|
||||
expect(appModel.getComponentById('text2' as ComponentId)?.id).toBe('text2');
|
||||
});
|
||||
|
||||
it(`Paste component with children and the children's slot trait is correct`, () => {
|
||||
const appModel = new AppModel(
|
||||
PasteComponentWithChildrenSchema.spec.components,
|
||||
registry
|
||||
);
|
||||
|
||||
const pasteComponent = [
|
||||
appModel.getComponentById('stack5' as ComponentId)!.toSchema(),
|
||||
appModel.getComponentById('text6' as ComponentId)!.toSchema(),
|
||||
];
|
||||
const operation = genOperation(registry, 'pasteComponent', {
|
||||
parentId: 'stack3',
|
||||
slot: 'content',
|
||||
component: new AppModel(pasteComponent, registry).getComponentById(
|
||||
'stack5' as ComponentId
|
||||
)!,
|
||||
copyTimes: 0,
|
||||
});
|
||||
operation.do(appModel);
|
||||
expect(appModel.getComponentById('text6_copy0' as ComponentId)?.parent?.id).toBe(
|
||||
'stack5_copy0'
|
||||
);
|
||||
expect(
|
||||
appModel.getComponentById('text6_copy0' as ComponentId)?.traits[0].rawProperties
|
||||
.container.id
|
||||
).toBe('stack5_copy0');
|
||||
});
|
||||
});
|
||||
|
@ -15,3 +15,71 @@ export const AppSchema: Application = {
|
||||
],
|
||||
},
|
||||
};
|
||||
export const PasteComponentWithChildrenSchema: Application = {
|
||||
version: 'sunmao/v1',
|
||||
kind: 'Application',
|
||||
metadata: {
|
||||
name: 'some App',
|
||||
},
|
||||
spec: {
|
||||
components: [
|
||||
{
|
||||
id: 'stack3',
|
||||
type: 'core/v1/stack',
|
||||
properties: {
|
||||
spacing: 12,
|
||||
direction: 'horizontal',
|
||||
align: 'auto',
|
||||
wrap: false,
|
||||
justify: 'flex-start',
|
||||
},
|
||||
traits: [],
|
||||
},
|
||||
{
|
||||
id: 'stack5',
|
||||
type: 'core/v1/stack',
|
||||
properties: {
|
||||
spacing: 12,
|
||||
direction: 'horizontal',
|
||||
align: 'auto',
|
||||
wrap: false,
|
||||
justify: 'flex-start',
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'stack3',
|
||||
slot: 'content',
|
||||
},
|
||||
ifCondition: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'text6',
|
||||
type: 'core/v1/text',
|
||||
properties: {
|
||||
value: {
|
||||
raw: 'text',
|
||||
format: 'plain',
|
||||
},
|
||||
},
|
||||
traits: [
|
||||
{
|
||||
type: 'core/v1/slot',
|
||||
properties: {
|
||||
container: {
|
||||
id: 'stack5',
|
||||
slot: 'content',
|
||||
},
|
||||
ifCondition: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
@ -7,11 +7,13 @@ import {
|
||||
ComponentType,
|
||||
IAppModel,
|
||||
IComponentModel,
|
||||
IFieldModel,
|
||||
ModuleId,
|
||||
SlotName,
|
||||
} from './IAppModel';
|
||||
import { genComponent } from './utils';
|
||||
import mitt from 'mitt';
|
||||
import { forEach } from 'lodash';
|
||||
|
||||
export class AppModel implements IAppModel {
|
||||
topComponents: IComponentModel[] = [];
|
||||
@ -143,8 +145,35 @@ export class AppModel implements IAppModel {
|
||||
});
|
||||
}
|
||||
}
|
||||
this.topComponents.forEach(parent => {
|
||||
traverse(parent);
|
||||
if (this.topComponents.length) {
|
||||
this.topComponents.forEach(parent => {
|
||||
traverse(parent);
|
||||
});
|
||||
} else {
|
||||
// When all the components have slot trait, there is no topComponents.
|
||||
// Just iterate them in order.
|
||||
Object.values(this.componentMap).forEach(c => {
|
||||
cb(c);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
traverseAllFields(cb: (f: IFieldModel) => void) {
|
||||
function traverseField(field: IFieldModel) {
|
||||
cb(field);
|
||||
const value = field.getValue();
|
||||
if (typeof value === 'object') {
|
||||
forEach(value, childField => {
|
||||
traverseField(childField);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.traverseTree(c => {
|
||||
traverseField(c.properties);
|
||||
c.traits.forEach(t => {
|
||||
traverseField(t.properties);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -68,18 +68,11 @@ export class ComponentModel implements IComponentModel {
|
||||
this.type = schema.type as ComponentType;
|
||||
this.spec = this.registry.getComponentByType(this.type) as any;
|
||||
|
||||
this.traits = schema.traits.map(
|
||||
t => new TraitModel(t, this.registry, this.appModel, this)
|
||||
);
|
||||
this.traits = schema.traits.map(t => new TraitModel(t, this.registry, this));
|
||||
this.genStateExample();
|
||||
this.parentId = this._slotTrait?.rawProperties.container.id;
|
||||
this.parentSlot = this._slotTrait?.rawProperties.container.slot;
|
||||
this.properties = new FieldModel(
|
||||
schema.properties,
|
||||
this.spec.spec.properties,
|
||||
this.appModel,
|
||||
this
|
||||
);
|
||||
this.properties = new FieldModel(schema.properties, this, this.spec.spec.properties);
|
||||
}
|
||||
|
||||
get slots() {
|
||||
@ -174,7 +167,7 @@ export class ComponentModel implements IComponentModel {
|
||||
|
||||
addTrait(traitType: TraitType, properties: Record<string, unknown>): ITraitModel {
|
||||
const traitSchema = genTrait(traitType, properties);
|
||||
const trait = new TraitModel(traitSchema, this.registry, this.appModel, this);
|
||||
const trait = new TraitModel(traitSchema, this.registry, this);
|
||||
this.traits.push(trait);
|
||||
this._isDirty = true;
|
||||
this.genStateExample();
|
||||
@ -243,7 +236,7 @@ export class ComponentModel implements IComponentModel {
|
||||
}
|
||||
this._isDirty = true;
|
||||
this.appModel.changeComponentMapId(oldId, newId);
|
||||
this.appModel.emitter.emit('idChange', { oldId, newId });
|
||||
this.appModel.traverseAllFields(field => field.changeReferenceId(oldId, newId));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -7,14 +7,12 @@ import { flattenDeep, isArray, isObject } from 'lodash';
|
||||
import { isExpression } from '../validator/utils';
|
||||
import {
|
||||
ComponentId,
|
||||
IAppModel,
|
||||
IComponentModel,
|
||||
ITraitModel,
|
||||
IFieldModel,
|
||||
ModuleId,
|
||||
RefInfo,
|
||||
ASTNode,
|
||||
AppModelEventType,
|
||||
} from './IAppModel';
|
||||
import escodegen from 'escodegen';
|
||||
import { JSONSchema7 } from 'json-schema';
|
||||
@ -38,13 +36,11 @@ export class FieldModel implements IFieldModel {
|
||||
|
||||
constructor(
|
||||
value: unknown,
|
||||
public spec?: JSONSchema7 & CustomOptions,
|
||||
private appModel?: IAppModel,
|
||||
private componentModel?: IComponentModel,
|
||||
public spec?: JSONSchema7 & CustomOptions,
|
||||
private traitModel?: ITraitModel
|
||||
) {
|
||||
this.update(value);
|
||||
this.appModel?.emitter.on('idChange', this.onReferenceIdChange.bind(this));
|
||||
}
|
||||
|
||||
get rawValue() {
|
||||
@ -81,11 +77,10 @@ export class FieldModel implements IFieldModel {
|
||||
} else {
|
||||
newValue = new FieldModel(
|
||||
value[key],
|
||||
this.componentModel,
|
||||
(this.spec?.properties?.[key] || this.spec?.items) as
|
||||
| (JSONSchema7 & CustomOptions)
|
||||
| undefined,
|
||||
this.appModel,
|
||||
this.componentModel,
|
||||
this.traitModel
|
||||
);
|
||||
}
|
||||
@ -255,7 +250,7 @@ export class FieldModel implements IFieldModel {
|
||||
return path.slice(1).join('.');
|
||||
}
|
||||
|
||||
private onReferenceIdChange({ oldId, newId }: AppModelEventType['idChange']) {
|
||||
changeReferenceId(oldId: ComponentId, newId: ComponentId) {
|
||||
if (!this.componentModel) {
|
||||
return;
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ export interface IAppModel {
|
||||
changeComponentMapId(oldId: ComponentId, newId: ComponentId): void;
|
||||
_bindComponentToModel(component: IComponentModel): void;
|
||||
traverseTree(cb: (c: IComponentModel) => void): void;
|
||||
traverseAllFields(cb: (f: IFieldModel) => void): void;
|
||||
}
|
||||
|
||||
export interface IModuleModel {
|
||||
@ -145,4 +146,5 @@ export interface IFieldModel {
|
||||
traverse: (cb: (f: IFieldModel, key: string) => void) => void;
|
||||
// ids of used components in the expression
|
||||
refComponentInfos: Record<ComponentId | ModuleId, RefInfo>;
|
||||
changeReferenceId(oldId: ComponentId, newId: ComponentId): void;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import {
|
||||
ITraitModel,
|
||||
IFieldModel,
|
||||
TraitId,
|
||||
IAppModel,
|
||||
} from './IAppModel';
|
||||
import { FieldModel } from './FieldModel';
|
||||
import { genTrait } from './utils';
|
||||
@ -24,7 +23,6 @@ export class TraitModel implements ITraitModel {
|
||||
constructor(
|
||||
trait: TraitSchema,
|
||||
private registry: RegistryInterface,
|
||||
private appModel: IAppModel,
|
||||
public parent: IComponentModel
|
||||
) {
|
||||
this.schema = trait;
|
||||
@ -35,9 +33,8 @@ export class TraitModel implements ITraitModel {
|
||||
|
||||
this.properties = new FieldModel(
|
||||
trait.properties,
|
||||
this.spec.spec.properties,
|
||||
this.appModel,
|
||||
this.parent,
|
||||
this.spec.spec.properties,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ export class EditorStore {
|
||||
setComponents: action,
|
||||
setHoverComponentId: action,
|
||||
setDragOverComponentId: action,
|
||||
setHoverComponentId: action,
|
||||
});
|
||||
|
||||
this.eventBus.on('selectComponent', id => {
|
||||
|
Loading…
Reference in New Issue
Block a user