fix: convertI18nObject

fix: initialChildren 传入 settingTopEntry 参数
This commit is contained in:
力皓 2020-10-16 17:31:02 +08:00
parent 4f16d363a8
commit 66d43f2103
26 changed files with 1258 additions and 39 deletions

View File

@ -42,7 +42,7 @@
"xima": "^0.2.15"
},
"engines": {
"node": ">=10.0.0"
"node": ">=14.0.0"
},
"tnpm": {
"mode": "yarn",

View File

@ -12,5 +12,6 @@ module.exports = {
'no-prototype-builtins': 1,
'no-useless-constructor': 1,
'no-empty-function': 1,
'@typescript-eslint/member-ordering': 0,
}
}

View File

@ -2,6 +2,7 @@
"plugins": [
[
"build-plugin-component"
]
],
"./build.plugin.js"
]
}

View File

@ -0,0 +1,7 @@
module.exports = ({ onGetJestConfig }) => {
// console.log('== test ==');
onGetJestConfig((jestConfig) => {
// console.log(jestConfig);
return jestConfig;
});
};

View File

@ -0,0 +1,26 @@
// jest.config.js
const { pathsToModuleNameMapper } = require('ts-jest/utils');
// In the following statement, replace `./tsconfig` with the path to your `tsconfig` file
// which contains the path mapping (ie the `compilerOptions.paths` option):
const esModules = ['@recore/obx-react'].join('|');
// console.log('>>> compilerOptions', compilerOptions);
// console.log('>>> compilerOptions', pathsToModuleNameMapper(compilerOptions.paths));
module.exports = {
/* transform: {
'^.+\\.[jt]sx?$': 'babel-jest',
// '^.+\\.(ts|tsx)$': 'ts-jest',
// '^.+\\.(js|jsx)$': 'babel-jest',
}, */
// testMatch: ['(/tests?/.*(test))\\.[jt]s$'],
transformIgnorePatterns: [
`/node_modules/(?!${esModules})/`,
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
collectCoverage: false,
collectCoverageFrom: [
'src/**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/vendor/**',
],
};

View File

@ -10,7 +10,7 @@
],
"scripts": {
"build": "build-scripts build --skip-demo",
"test": "jest -c jest.config.js"
"test": "build-scripts test"
},
"license": "MIT",
"dependencies": {

View File

@ -31,6 +31,10 @@ function combineTransducer(transducer, arr, context) {
}
export class Transducer {
setterTransducer: any;
context: any;
constructor(context, config) {
let { setter } = config;

View File

@ -115,6 +115,7 @@ export class NodeChildren {
}
this.children.splice(i, 1);
}
/**
*
*/
@ -122,18 +123,21 @@ export class NodeChildren {
if (node.isParental()) {
foreachReverse(node.children, (subNode: Node) => {
subNode.remove(useMutator, purge);
});
}, (iterable, idx) => (iterable as NodeChildren).get(idx));
foreachReverse(node.slots, (slotNode: Node) => {
slotNode.remove(useMutator, purge);
}, (iterable, idx) => (iterable as [])[idx]);
}
if (purge) {
// should set parent null
node.internalSetParent(null, useMutator);
try {
node.purge(useMutator);
node.purge();
} catch (err) {
console.error(err);
}
}
const document = node.document;
const { document } = node;
document.unlinkNode(node);
document.selection.remove(node.id);
document.destroyNode(node);

View File

@ -162,18 +162,19 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this.props = new Props(this, {
children: isDOMText(children) || isJSExpression(children) ? children : '',
});
this.settingEntry = this.document.designer.createSettingEntry([this]);
} else {
// 这里 props 被初始化两次,一次 new一次 importnew 的实例需要给 propsReducer 的钩子去使用,
// import 是为了使用钩子返回的值,并非完全幂等的操作,部分行为执行两次会有 bug
// 所以在 props 里会对 new / import 做一些区别化的解析
this.props = new Props(this, props, extras);
this.settingEntry = this.document.designer.createSettingEntry([this]);
this._children = new NodeChildren(this as ParentalNode, this.initialChildren(children));
this._children.internalInitParent();
this.props.import(this.upgradeProps(this.initProps(props || {})), this.upgradeProps(extras || {}));
this.setupAutoruns();
}
this.settingEntry = this.document.designer.createSettingEntry([this]);
this.emitter = new EventEmitter();
}
@ -317,6 +318,7 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
if (this.parent) {
if (this.isSlot()) {
this.parent.removeSlot(this, purge);
this.parent.children.delete(this, purge, useMutator);
} else {
this.parent.children.delete(this, purge, useMutator);
}
@ -677,13 +679,13 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
* Slot节点
*/
removeSlot(slotNode: Node, purge = false): boolean {
if (purge) {
// should set parent null
slotNode?.internalSetParent(null, false);
slotNode?.purge();
}
this.document.unlinkNode(slotNode);
this.document.selection.remove(slotNode.id);
// if (purge) {
// // should set parent null
// slotNode?.internalSetParent(null, false);
// slotNode?.purge();
// }
// this.document.unlinkNode(slotNode);
// this.document.selection.remove(slotNode.id);
const i = this._slots.indexOf(slotNode);
if (i < 0) {
return false;

View File

@ -6,6 +6,7 @@ import { valueToSource } from './value-to-source';
import { Props } from './props';
import { SlotNode, Node } from '../node';
import { TransformStage } from '../transform-stage';
import { includesSlot } from '../../../utils/slot';
export const UNSET = Symbol.for('unset');
export type UNSET = typeof UNSET;
@ -231,7 +232,7 @@ export class Prop implements IPropParent {
} else if (Array.isArray(val)) {
this._type = 'list';
} else if (isPlainObject(val)) {
if (isJSSlot(val) && this.options.propsMode !== 'init') {
if (isJSSlot(val) && this.options.skipSetSlot !== true) {
this.setAsSlot(val);
return;
}
@ -293,9 +294,11 @@ export class Prop implements IPropParent {
this._slotNode.import(slotSchema);
} else {
const { owner } = this.props;
this._slotNode = owner.document.createNode<SlotNode>(slotSchema);
owner.addSlot(this._slotNode);
this._slotNode.internalSetSlotFor(this);
if (!includesSlot(owner, data.name)) {
this._slotNode = owner.document.createNode<SlotNode>(slotSchema);
owner.addSlot(this._slotNode);
this._slotNode.internalSetSlotFor(this);
}
}
this.dispose();
}

View File

@ -59,9 +59,9 @@ export class Props implements IPropParent {
constructor(readonly owner: Node, value?: PropsMap | PropsList | null, extras?: object) {
if (Array.isArray(value)) {
this.type = 'list';
this.items = value.map(item => new Prop(this, item.value, item.name, item.spread, { propsMode: 'init' }));
this.items = value.map(item => new Prop(this, item.value, item.name, item.spread, { skipSetSlot: true }));
} else if (value != null) {
this.items = Object.keys(value).map(key => new Prop(this, value[key], key, false, { propsMode: 'init' }));
this.items = Object.keys(value).map(key => new Prop(this, value[key], key, false, { skipSetSlot: true }));
}
if (extras) {
Object.keys(extras).forEach(key => {
@ -241,8 +241,8 @@ export class Props implements IPropParent {
/**
*
*/
add(value: CompositeValue | null, key?: string | number, spread = false): Prop {
const prop = new Prop(this, value, key, spread);
add(value: CompositeValue | null, key?: string | number, spread = false, options: any = {}): Prop {
const prop = new Prop(this, value, key, spread, options);
this.items.push(prop);
return prop;
}

View File

@ -164,7 +164,7 @@ export class Project {
}
createDocument(data?: RootSchema): DocumentModel {
const doc = new DocumentModel(this, data);
const doc = new DocumentModel(this, data || this?.data?.componentsTree?.[0]);
this.documents.push(doc);
this.documentsMap.set(doc.id, doc);
return doc;

View File

@ -0,0 +1,8 @@
import { Node } from '../document/node/node';
export function includesSlot(node: Node, slotName: string | undefined): boolean {
const { slots = [] } = node;
return slots.some(slot => {
return slotName && slotName === slot?.getExtraProp('name')?.getAsString();
});
}

View File

@ -1,7 +1,14 @@
import { NodeChildren } from '../document/node/node-children';
export function foreachReverse(arr: NodeChildren, fn: Function, context: any = {}) {
type IterableArray = NodeChildren | any[];
export function foreachReverse(
arr: IterableArray,
action: (item: any) => void,
getter: (arr: IterableArray, index: number) => any,
context: any = {},
) {
for (let i = arr.length - 1; i >= 0; i--) {
fn.call(context, arr.get(i));
action.call(context, getter(arr, i));
}
}

View File

@ -0,0 +1,10 @@
export class DocumentModel {
a = 1;
c = {};
constructor() {
console.log('xxxxxxxxxxxxxxxxxxxx');
const b = { x: { y: 2 } };
const c: number = 2;
this.a = b?.x?.y;
}
}

View File

@ -0,0 +1,9 @@
export class Node2 {
a = 1;
c = {};
constructor() {
const b = { x: { y: 2 } };
const c: number = 2;
this.a = b?.x?.y;
}
}

View File

@ -0,0 +1,17 @@
import '../../fixtures/window';
console.log('window.matchMedia', window.matchMedia);
window.matchMedia('width=600px');
import { DocumentModel } from '../../../src/document/document-model';
// const { DocumentModel } = require('../../../src/document/document-model');
// const { Node } = require('../__mocks__/node');
describe('basic utility', () => {
test.only('delegateMethod - useOriginMethodName', () => {
const node = new DocumentModel({}, {
componentName: 'Component',
});
console.log(node);
expect(1).toBe(1);
});
});

View File

@ -0,0 +1,28 @@
import '../../fixtures/window';
import { DocumentModel } from '../../../src/document/document-model';
import { Node } from '../../../src/document/node/node';
// import { Node2 } from './__mocks__/node';
jest.mock('../../../src/document/document-model', () => {
return {
DocumentModel: jest.fn().mockImplementation(() => {
return {
project: {
designer: { createSettingEntry() {}, transformProps() {} },
getSchema() {},
},
nextId() {},
};
}),
};
});
describe('basic utility', () => {
test('delegateMethod - useOriginMethodName', () => {
const dm = new DocumentModel({} as any, {} as any);
console.log(dm.nextId);
const node = new Node(dm, { componentName: 'Leaf' });
console.log(node);
expect(1).toBe(1);
});
});

View File

@ -0,0 +1,968 @@
export default {
componentName: 'Page',
id: 'node_k1ow3cb9',
props: {
extensions: {
启用页头: true,
},
pageStyle: {
backgroundColor: '#f2f3f5',
},
containerStyle: {},
className: 'page_kgaqfbm4',
templateVersion: '1.0.0',
},
lifeCycles: {
constructor: {
type: 'js',
compiled:
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
source:
"function constructor() {\nvar module = { exports: {} };\nvar _this = this;\nthis.__initMethods__(module.exports, module);\nObject.keys(module.exports).forEach(function(item) {\n if(typeof module.exports[item] === 'function'){\n _this[item] = module.exports[item];\n }\n});\n\n}",
},
},
condition: true,
css:
'body{background-color:#f2f3f5}.card_kgaqfbm5 {\n margin-bottom: 12px;\n}.card_kgaqfbm6 {\n margin-bottom: 12px;\n}.button_kgaqfbm7 {\n margin-right: 16px;\n width: 80px\n}.button_kgaqfbm8 {\n width: 80px;\n}.div_kgaqfbm9 {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
methods: {
__initMethods__: {
type: 'js',
source: 'function (exports, module) { /*set actions code here*/ }',
compiled: 'function (exports, module) { /*set actions code here*/ }',
},
},
dataSource: {
offline: [],
globalConfig: {
fit: {
compiled: '',
source: '',
type: 'js',
error: {},
},
},
online: [],
sync: true,
list: [],
},
children: [
{
componentName: 'RootHeader',
id: 'node_k1ow3cba',
props: {},
condition: true,
children: [
{
componentName: 'PageHeader',
id: 'node_k1ow3cbd',
props: {
extraContent: '',
__slot__extraContent: false,
__slot__action: false,
title: {
// type: 'JSSlot',
value: [
{
componentName: 'Text',
id: 'node_k1ow3cbf',
props: {
showTitle: false,
behavior: 'NORMAL',
content: {
use: 'zh_CN',
en_US: 'Title',
zh_CN: '个人信息',
type: 'i18n',
},
__style__: {},
fieldId: 'text_k1ow3h1j',
maxLine: 0,
},
condition: true,
},
],
},
content: '',
__slot__logo: false,
__slot__crumb: false,
crumb: '',
tab: '',
logo: '',
action: '',
__slot__tab: false,
__style__: {},
__slot__content: false,
fieldId: 'pageHeader_k1ow3h1i',
subTitle: false,
},
condition: true,
},
],
},
{
componentName: 'RootContent',
id: 'node_k1ow3cbb',
props: {
contentBgColor: 'transparent',
contentPadding: '0',
contentMargin: '20',
},
condition: true,
children: [
{
componentName: 'Form',
id: 'node_k1ow3cbq',
props: {
size: 'medium',
labelAlign: 'top',
autoValidate: true,
scrollToFirstError: true,
autoUnmount: true,
behavior: 'NORMAL',
dataSource: {
type: 'variable',
variable: 'state.formData',
},
__style__: {},
fieldId: 'form',
fieldOptions: {},
},
condition: true,
children: [
{
componentName: 'Card',
id: 'node_k1ow3cbj',
props: {
__slot__title: false,
subTitle: {
use: 'zh_CN',
en_US: '',
zh_CN: '',
type: 'i18n',
},
__slot__subTitle: false,
extra: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
className: 'card_kgaqfbm5',
title: {
use: 'zh_CN',
en_US: 'Title',
zh_CN: '基本信息',
type: 'i18n',
},
__slot__extra: false,
showHeadDivider: true,
__style__: ':root {\n margin-bottom: 12px;\n}',
showTitleBullet: true,
contentHeight: '',
fieldId: 'card_k1ow3h1l',
dividerNoInset: false,
},
condition: true,
children: [
{
componentName: 'CardContent',
id: 'node_k1ow3cbk',
props: {},
condition: true,
children: [
{
componentName: 'ColumnsLayout',
id: 'node_k1ow3cbw',
props: {
layout: '6:6',
columnGap: '20',
rowGap: 0,
__style__: {},
fieldId: 'columns_k1ow3h1v',
},
condition: true,
children: [
{
componentName: 'Column',
id: 'node_k1ow3cbx',
props: {
colSpan: '',
__style__: {},
fieldId: 'column_k1p1bnjm',
},
condition: true,
children: [
{
componentName: 'TextField',
id: 'node_k1ow3cbz',
props: {
fieldName: 'name',
hasClear: false,
autoFocus: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
trim: false,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please input',
zh_CN: '请输入',
type: 'i18n',
},
state: '',
behavior: 'NORMAL',
value: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
addonBefore: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
validation: [
{
type: 'required',
},
],
hasLimitHint: false,
cutString: false,
__style__: {},
fieldId: 'textField_k1ow3h1w',
htmlType: 'input',
autoHeight: false,
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'TextField',
zh_CN: '姓名',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
rows: 4,
addonAfter: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
wrapperColOffset: 0,
size: 'medium',
labelAlign: 'top',
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
},
condition: true,
},
{
componentName: 'TextField',
id: 'node_k1ow3cc1',
props: {
fieldName: 'englishName',
hasClear: false,
autoFocus: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
trim: false,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please input',
zh_CN: '请输入',
type: 'i18n',
},
state: '',
behavior: 'NORMAL',
value: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
addonBefore: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
validation: [],
hasLimitHint: false,
cutString: false,
__style__: {},
fieldId: 'textField_k1ow3h1y',
htmlType: 'input',
autoHeight: false,
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'TextField',
zh_CN: '英文名',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
rows: 4,
addonAfter: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
wrapperColOffset: 0,
size: 'medium',
labelAlign: 'top',
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
},
condition: true,
},
{
componentName: 'TextField',
id: 'node_k1ow3cc3',
props: {
fieldName: 'jobTitle',
hasClear: false,
autoFocus: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
trim: false,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please input',
zh_CN: '请输入',
type: 'i18n',
},
state: '',
behavior: 'NORMAL',
value: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
addonBefore: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
validation: [],
hasLimitHint: false,
cutString: false,
__style__: {},
fieldId: 'textField_k1ow3h20',
htmlType: 'input',
autoHeight: false,
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'TextField',
zh_CN: '职位',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
rows: 4,
addonAfter: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
wrapperColOffset: 0,
size: 'medium',
labelAlign: 'top',
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
},
condition: true,
},
],
},
{
componentName: 'Column',
id: 'node_k1ow3cby',
props: {
colSpan: '',
__style__: {},
fieldId: 'column_k1p1bnjn',
},
condition: true,
children: [
{
componentName: 'TextField',
id: 'node_k1ow3cc2',
props: {
fieldName: 'nickName',
hasClear: false,
autoFocus: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
trim: false,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please input',
zh_CN: '请输入',
type: 'i18n',
},
state: '',
behavior: 'NORMAL',
value: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
addonBefore: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
validation: [],
hasLimitHint: false,
cutString: false,
__style__: {},
fieldId: 'textField_k1ow3h1z',
htmlType: 'input',
autoHeight: false,
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'TextField',
zh_CN: '花名',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
rows: 4,
addonAfter: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
wrapperColOffset: 0,
size: 'medium',
labelAlign: 'top',
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
},
condition: true,
},
{
componentName: 'SelectField',
id: 'node_k1ow3cc0',
props: {
fieldName: 'gender',
hasClear: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
mode: 'single',
showSearch: false,
autoWidth: true,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please select',
zh_CN: '请选择',
type: 'i18n',
},
hasBorder: true,
behavior: 'NORMAL',
value: '',
validation: [
{
type: 'required',
},
],
__style__: {},
fieldId: 'select_k1ow3h1x',
notFoundContent: {
use: 'zh_CN',
type: 'i18n',
},
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'SelectField',
zh_CN: '性别',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
wrapperColOffset: 0,
hasSelectAll: false,
hasArrow: true,
size: 'medium',
labelAlign: 'top',
filterLocal: true,
dataSource: [
{
defaultChecked: false,
text: {
en_US: 'Option 1',
zh_CN: '男',
type: 'i18n',
__sid__: 'param_k1owc4tb',
},
__sid__: 'serial_k1owc4t1',
value: 'M',
sid: 'opt_k1owc4t2',
},
{
defaultChecked: false,
text: {
en_US: 'Option 2',
zh_CN: '女',
type: 'i18n',
__sid__: 'param_k1owc4tf',
},
__sid__: 'serial_k1owc4t2',
value: 'F',
sid: 'opt_k1owc4t3',
},
],
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
searchDelay: 300,
},
condition: true,
},
],
},
],
},
],
},
],
},
{
componentName: 'Card',
id: 'node_k1ow3cbl',
props: {
__slot__title: false,
subTitle: {
use: 'zh_CN',
en_US: '',
zh_CN: '',
type: 'i18n',
},
__slot__subTitle: false,
extra: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
className: 'card_kgaqfbm6',
title: {
use: 'zh_CN',
en_US: 'Title',
zh_CN: '部门信息',
type: 'i18n',
},
__slot__extra: false,
showHeadDivider: true,
__style__: ':root {\n margin-bottom: 12px;\n}',
showTitleBullet: true,
contentHeight: '',
fieldId: 'card_k1ow3h1m',
dividerNoInset: false,
},
condition: true,
children: [
{
componentName: 'CardContent',
id: 'node_k1ow3cbm',
props: {},
condition: true,
children: [
{
componentName: 'TextField',
id: 'node_k1ow3cc4',
props: {
fieldName: 'department',
hasClear: false,
autoFocus: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
trim: false,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please input',
zh_CN: '请输入',
type: 'i18n',
},
state: '',
behavior: 'NORMAL',
value: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
addonBefore: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
validation: [],
hasLimitHint: false,
cutString: false,
__style__: {},
fieldId: 'textField_k1ow3h21',
htmlType: 'input',
autoHeight: false,
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'TextField',
zh_CN: '所属部门',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
rows: 4,
addonAfter: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
wrapperColOffset: 0,
size: 'medium',
labelAlign: 'top',
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
},
condition: true,
},
{
componentName: 'ColumnsLayout',
id: 'node_k1ow3cc5',
props: {
layout: '6:6',
columnGap: '20',
rowGap: 0,
__style__: {},
fieldId: 'columns_k1ow3h22',
},
condition: true,
children: [
{
componentName: 'Column',
id: 'node_k1ow3cc6',
props: {
colSpan: '',
__style__: {},
fieldId: 'column_k1p1bnjo',
},
condition: true,
children: [
{
componentName: 'TextField',
id: 'node_k1ow3cc8',
props: {
fieldName: 'leader',
hasClear: false,
autoFocus: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
trim: false,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please input',
zh_CN: '请输入',
type: 'i18n',
},
state: '',
behavior: 'NORMAL',
value: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
addonBefore: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
validation: [],
hasLimitHint: false,
cutString: false,
__style__: {},
fieldId: 'textField_k1ow3h23',
htmlType: 'input',
autoHeight: false,
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'TextField',
zh_CN: '主管',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
rows: 4,
addonAfter: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
wrapperColOffset: 0,
size: 'medium',
labelAlign: 'top',
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
},
condition: true,
},
],
},
{
componentName: 'Column',
id: 'node_k1ow3cc7',
props: {
colSpan: '',
__style__: {},
fieldId: 'column_k1p1bnjp',
},
condition: true,
children: [
{
componentName: 'TextField',
id: 'node_k1ow3cc9',
props: {
fieldName: 'hrg',
hasClear: false,
autoFocus: false,
tips: {
en_US: '',
zh_CN: '',
type: 'i18n',
},
trim: false,
labelTextAlign: 'right',
placeholder: {
use: 'zh_CN',
en_US: 'please input',
zh_CN: '请输入',
type: 'i18n',
},
state: '',
behavior: 'NORMAL',
value: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
addonBefore: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
validation: [],
hasLimitHint: false,
cutString: false,
__style__: {},
fieldId: 'textField_k1ow3h24',
htmlType: 'input',
autoHeight: false,
labelColOffset: 0,
label: {
use: 'zh_CN',
en_US: 'TextField',
zh_CN: 'HRG',
type: 'i18n',
},
__category__: 'form',
labelColSpan: 4,
wrapperColSpan: 0,
rows: 4,
addonAfter: {
use: 'zh_CN',
zh_CN: '',
type: 'i18n',
},
wrapperColOffset: 0,
size: 'medium',
labelAlign: 'top',
__useMediator: 'value',
labelTipsTypes: 'none',
labelTipsIcon: '',
labelTipsText: {
type: 'i18n',
use: 'zh_CN',
en_US: null,
zh_CN: '',
},
},
condition: true,
},
],
},
],
},
],
},
],
},
{
componentName: 'Div',
id: 'node_k1ow3cbo',
props: {
className: 'div_kgaqfbm9',
behavior: 'NORMAL',
__style__:
':root {\n display: flex;\n align-items: flex-start;\n justify-content: center;\n background: #fff;\n padding: 20px 0;\n}',
events: {},
fieldId: 'div_k1ow3h1o',
useFieldIdAsDomId: false,
customClassName: '',
},
condition: true,
children: [
{
componentName: 'Button',
id: 'node_k1ow3cbn',
props: {
triggerEventsWhenLoading: false,
onClick: {
rawType: 'events',
type: 'JSExpression',
value: 'this.utils.legaoBuiltin.execEventFlow.bind(this, [this.submit])',
events: [
{
name: 'submit',
id: 'submit',
params: {},
type: 'actionRef',
uuid: '1570966253282_0',
},
],
},
size: 'medium',
baseIcon: '',
otherIcon: '',
className: 'button_kgaqfbm7',
type: 'primary',
behavior: 'NORMAL',
loading: false,
content: {
use: 'zh_CN',
en_US: 'Button',
zh_CN: '提交',
type: 'i18n',
},
__style__: ':root {\n margin-right: 16px;\n width: 80px\n}',
fieldId: 'button_k1ow3h1n',
},
condition: true,
},
{
componentName: 'Button',
id: 'node_k1ow3cbp',
props: {
triggerEventsWhenLoading: false,
size: 'medium',
baseIcon: '',
otherIcon: '',
className: 'button_kgaqfbm8',
type: 'normal',
behavior: 'NORMAL',
loading: false,
content: {
use: 'zh_CN',
en_US: 'Button',
zh_CN: '取消',
type: 'i18n',
},
__style__: ':root {\n width: 80px;\n}',
fieldId: 'button_k1ow3h1p',
},
condition: true,
},
],
},
],
},
],
},
{
componentName: 'RootFooter',
id: 'node_k1ow3cbc',
props: {},
condition: true,
},
],
};

View File

@ -0,0 +1,18 @@
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn().mockImplementation(query => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(), // deprecated
removeListener: jest.fn(), // deprecated
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
Object.defineProperty(window, 'React', {
writable: true,
value: {},
});

View File

@ -0,0 +1,86 @@
import '../fixtures/window';
import { Project } from '../../src/project/project';
// import { Node } from '../../../src/document/node/node';
import { Designer } from '../../src/designer/designer';
import formSchema from '../fixtures/schema/form';
import { getIdsFromSchema, getNodeFromSchemaById } from '../utils';
const mockCreateSettingEntry = jest.fn();
jest.mock('../../src/designer/designer', () => {
return {
Designer: jest.fn().mockImplementation(() => {
return {
getComponentMeta() {
return {
getMetadata() {
return { experimental: null };
},
};
},
transformProps(props) { return props; },
createSettingEntry: mockCreateSettingEntry,
postEvent() {},
};
}),
};
});
let designer = null;
beforeAll(() => {
designer = new Designer({});
});
describe('schema 渲染测试', () => {
it('最简单的例子,练手用', () => {
const project = new Project(designer, {
componentsTree: [{
componentName: 'Page',
id: 'page_id',
props: {
name: 'haha',
},
children: [{
componentName: 'Div',
id: 'div_id',
props: {
name: 'div from haha',
},
}],
}],
});
project.open();
expect(project).toBeTruthy();
const { currentDocument } = project;
const { nodesMap } = currentDocument;
// console.log(project.currentDocument.nodesMap.get('div_id').props.items);
expect(nodesMap.has('page_id')).toBeTruthy;
expect(nodesMap.has('div_id')).toBeTruthy;
expect(mockCreateSettingEntry).toBeCalledTimes(2);
// console.log(currentDocument.export(3));
});
it.only('普通场景,无 block / component无 slot', () => {
const project = new Project(designer, {
componentsTree: [
formSchema,
],
});
project.open();
expect(project).toBeTruthy();
const { currentDocument } = project;
const { nodesMap } = currentDocument;
const ids = getIdsFromSchema(formSchema);
ids.forEach(id => {
expect(nodesMap.get(id).componentName).toBe(getNodeFromSchemaById(formSchema, id).componentName);
});
// console.log(nodesMap.get('node_k1ow3cb9').componentName, getNodeFromSchemaById(formSchema, 'node_k1ow3cb9').componentName)
console.log(nodesMap.size);
// expect(nodesMap.has('page_id')).toBeTruthy;
// expect(nodesMap.has('div_id')).toBeTruthy;
// expect(mockCreateSettingEntry).toBeCalledTimes(2);
// console.log(currentDocument.export(3));
});
it('普通场景,无 block / component有 slot', () => {});
it('普通场景,无 block / component有 slot', () => {});
});

View File

@ -0,0 +1,28 @@
export function getIdsFromSchema(schema, ids = []) {
if (!schema) return ids;
const { componentName, id, children } = schema;
if (componentName) {
ids.push(id);
}
if (Array.isArray(children) && children.length > 0) {
children.forEach(node => getIdsFromSchema(node, ids));
}
return ids;
}
export function getNodeFromSchemaById(schema, _id) {
if (!schema) return null;
const { id, children } = schema;
let retNode = null;
if (_id === id) return schema;
if (Array.isArray(children) && children.length > 0) {
children.some(node => {
retNode = getNodeFromSchemaById(node, _id);
if (retNode) {
return true;
}
return false;
});
}
return retNode;
}

View File

@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "lib"
},
"include": [
"./src/"
]
}

View File

@ -125,7 +125,7 @@ designer.addPropsReducer((props, node) => {
!isI18NObject(ov) &&
!isJSExpression(ov) &&
!isVariable(ov)) {
newProps[item.name] = v;
newProps[item.name] = convertToI18NObject(v);
}
} catch (e) {
if (hasOwnProperty(props, item.name)) {
@ -133,7 +133,7 @@ designer.addPropsReducer((props, node) => {
}
}
if (newProps[item.name] && !node.props.has(item.name)) {
node.props.add(newProps[item.name], item.name);
node.props.add(newProps[item.name], item.name, false, { skipSetSlot: true });
}
});
}

View File

@ -54,5 +54,5 @@
"publishConfig": {
"registry": "http://registry.npm.alibaba-inc.com"
},
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.12.1-3/build/index.html"
"homepage": "https://unpkg.alibaba-inc.com/@ali/lowcode-react-renderer@0.13.1-1/build/index.html"
}

View File

@ -1,4 +1,5 @@
import { isI18NObject } from './is-object';
export function isUseI18NSetter(prototype: any, propName: string) {
const configure = prototype?.options?.configure;
@ -10,6 +11,7 @@ export function isUseI18NSetter(prototype: any, propName: string) {
return false;
}
export function convertToI18NObject(v: string, locale: string = 'zh_CN') {
export function convertToI18NObject(v: string | object, locale: string = 'zh_CN') {
if (isI18NObject(v)) return v;
return { type: 'i18n', use: locale, [locale]: v };
}