add some assets

This commit is contained in:
kangwei 2020-03-16 03:05:21 +08:00
parent b9272d6bab
commit a44120df48
11 changed files with 1290 additions and 95 deletions

View File

@ -4,7 +4,11 @@ import { SimulatorHost } from './host';
import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '../utils/asset'; import { AssetLevel, AssetLevels, AssetList, isAssetBundle, isAssetItem, AssetType, assetItem } from '../utils/asset';
import { isCSSUrl } from '../../../utils/is-css-url'; import { isCSSUrl } from '../../../utils/is-css-url';
export function createSimulator(host: SimulatorHost, iframe: HTMLIFrameElement, vendors: AssetList = []): Promise<SimulatorRenderer> { export function createSimulator(
host: SimulatorHost,
iframe: HTMLIFrameElement,
vendors: AssetList = [],
): Promise<SimulatorRenderer> {
const win: any = iframe.contentWindow; const win: any = iframe.contentWindow;
const doc = iframe.contentDocument!; const doc = iframe.contentDocument!;
@ -12,7 +16,7 @@ export function createSimulator(host: SimulatorHost, iframe: HTMLIFrameElement,
const styles: any = {}; const styles: any = {};
const scripts: any = {}; const scripts: any = {};
AssetLevels.forEach((lv) => { AssetLevels.forEach(lv => {
styles[lv] = []; styles[lv] = [];
scripts[lv] = []; scripts[lv] = [];
}); });
@ -36,9 +40,9 @@ export function createSimulator(host: SimulatorHost, iframe: HTMLIFrameElement,
asset = assetItem(isCSSUrl(asset) ? AssetType.CSSUrl : AssetType.JSUrl, asset, level)!; asset = assetItem(isCSSUrl(asset) ? AssetType.CSSUrl : AssetType.JSUrl, asset, level)!;
} }
const id = asset.id ? ` data-id="${asset.id}"` : ''; const id = asset.id ? ` data-id="${asset.id}"` : '';
const lv = asset.level || level || AssetLevel.BaseDepends; const lv = asset.level || level || AssetLevel.Environment;
if (asset.type === AssetType.JSUrl) { if (asset.type === AssetType.JSUrl) {
(scripts[lv] || scripts[AssetLevel.App]).push(`<script src="${asset.content}"${id}></script>`) (scripts[lv] || scripts[AssetLevel.App]).push(`<script src="${asset.content}"${id}></script>`);
} else if (asset.type === AssetType.JSText) { } else if (asset.type === AssetType.JSText) {
(scripts[lv] || scripts[AssetLevel.App]).push(`<script${id}>${asset.content}</script>`); (scripts[lv] || scripts[AssetLevel.App]).push(`<script${id}>${asset.content}</script>`);
} else if (asset.type === AssetType.CSSUrl) { } else if (asset.type === AssetType.CSSUrl) {
@ -51,12 +55,16 @@ export function createSimulator(host: SimulatorHost, iframe: HTMLIFrameElement,
parseAssetList(vendors); parseAssetList(vendors);
const styleFrags = Object.keys(styles).map(key => { const styleFrags = Object.keys(styles)
return styles[key].join('\n') + `<meta level="${key}" />`; .map(key => {
}).join(''); return styles[key].join('\n') + `<meta level="${key}" />`;
const scriptFrags = Object.keys(scripts).map(key => { })
return scripts[key].join('\n'); .join('');
}).join(''); const scriptFrags = Object.keys(scripts)
.map(key => {
return scripts[key].join('\n');
})
.join('');
doc.open(); doc.open();
doc.write(`<!doctype html><html><head><meta charset="utf-8"/> doc.write(`<!doctype html><html><head><meta charset="utf-8"/>

View File

@ -6,7 +6,7 @@ import { SimulatorRenderer } from '../renderer/renderer';
import Node, { NodeParent, isNodeParent, isNode, contains } from '../../../designer/document/node/node'; import Node, { NodeParent, isNodeParent, isNode, contains } from '../../../designer/document/node/node';
import DocumentModel from '../../../designer/document/document-model'; import DocumentModel from '../../../designer/document/document-model';
import ResourceConsumer from './resource-consumer'; import ResourceConsumer from './resource-consumer';
import { AssetLevel, Asset, assetBundle, assetItem, AssetType } from '../utils/asset'; import { AssetLevel, Asset, AssetList, assetBundle, assetItem, AssetType } from '../utils/asset';
import { import {
DragObjectType, DragObjectType,
isShaken, isShaken,
@ -34,6 +34,12 @@ import { ReactInstance } from 'react';
import { isRootNode } from '../../../designer/document/node/root-node'; import { isRootNode } from '../../../designer/document/node/root-node';
import { parseProps } from '../utils/parse-props'; import { parseProps } from '../utils/parse-props';
export interface LibraryItem {
package: string;
library: string;
urls: Asset;
}
export interface SimulatorProps { export interface SimulatorProps {
// 从 documentModel 上获取 // 从 documentModel 上获取
// suspended?: boolean; // suspended?: boolean;
@ -41,8 +47,9 @@ export interface SimulatorProps {
device?: 'mobile' | 'iphone' | string; device?: 'mobile' | 'iphone' | string;
deviceClassName?: string; deviceClassName?: string;
simulatorUrl?: Asset; simulatorUrl?: Asset;
dependsAsset?: Asset; environment?: Asset;
themesAsset?: Asset; library?: LibraryItem[];
theme?: Asset;
componentsAsset?: Asset; componentsAsset?: Asset;
[key: string]: any; [key: string]: any;
} }
@ -59,14 +66,13 @@ const defaultSimulatorUrl = (() => {
return urls; return urls;
})(); })();
const defaultDepends = [ const defaultEnvironment = [
// https://g.alicdn.com/mylib/??react/16.11.0/umd/react.production.min.js,react-dom/16.8.6/umd/react-dom.production.min.js,prop-types/15.7.2/prop-types.min.js // https://g.alicdn.com/mylib/??react/16.11.0/umd/react.production.min.js,react-dom/16.8.6/umd/react-dom.production.min.js,prop-types/15.7.2/prop-types.min.js
assetItem(AssetType.JSText, 'window.React=parent.React;window.ReactDOM=parent.ReactDOM;', undefined, 'react'), assetItem(AssetType.JSText, 'window.React=parent.React;window.ReactDOM=parent.ReactDOM;', undefined, 'react'),
assetItem( assetItem(
AssetType.JSText, AssetType.JSText,
'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;', 'window.PropTypes=parent.PropTypes;React.PropTypes=parent.PropTypes; window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = window.parent.__REACT_DEVTOOLS_GLOBAL_HOOK__;',
), ),
// assetItem(AssetType.JSUrl, 'https://g.alicdn.com/mylib/@ali/recore/1.5.7/umd/recore.min.js'),
assetItem(AssetType.JSUrl, '/statics/lowcode-renderer.js'), assetItem(AssetType.JSUrl, '/statics/lowcode-renderer.js'),
]; ];
@ -97,8 +103,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
return this.get('componentsAsset'); return this.get('componentsAsset');
} }
@computed get themesAsset(): Asset | undefined { @computed get theme(): Asset | undefined {
return this.get('themesAsset'); return this.get('theme');
} }
@computed get componentsMap() { @computed get componentsMap() {
@ -166,6 +172,8 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
return {}; return {};
}); });
readonly libraryMap: { [key: string]: string } = {};
async mountContentFrame(iframe: HTMLIFrameElement | null) { async mountContentFrame(iframe: HTMLIFrameElement | null) {
if (!iframe) { if (!iframe) {
return; return;
@ -173,11 +181,22 @@ export class SimulatorHost implements ISimulator<SimulatorProps> {
this._contentWindow = iframe.contentWindow!; this._contentWindow = iframe.contentWindow!;
const library = this.get('library') as LibraryItem[];
const libraryAsset: AssetList = [];
if (library) {
library.forEach(item => {
this.libraryMap[item.package] = item.library;
libraryAsset.push(item.urls);
});
}
const vendors = [ const vendors = [
// required & use once // required & use once
assetBundle(this.get('dependsAsset') || defaultDepends, AssetLevel.BaseDepends), assetBundle(this.get('environment') || defaultEnvironment, AssetLevel.Environment),
// required & use once
assetBundle(libraryAsset, AssetLevel.Library),
// required & TODO: think of update // required & TODO: think of update
assetBundle(this.themesAsset, AssetLevel.Theme), assetBundle(this.theme, AssetLevel.Theme),
// required & use once // required & use once
assetBundle(this.get('simulatorUrl') || defaultSimulatorUrl, AssetLevel.Runtime), assetBundle(this.get('simulatorUrl') || defaultSimulatorUrl, AssetLevel.Runtime),
]; ];

View File

@ -40,11 +40,11 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
} }
render() { render() {
const { renderer } = this.props; const { renderer } = this.props;
const { components, schemas } = LowCodeRenderer.others; // const { components, schemas } = LowCodeRenderer.others;
return ( return (
<LowCodeRenderer <LowCodeRenderer
schema={renderer.schema} schema={renderer.schema}
components={components /*renderer.components*/} components={renderer.components}
appHelper={renderer.context} appHelper={renderer.context}
// context={renderer.context} // context={renderer.context}
designMode={renderer.designMode} designMode={renderer.designMode}

View File

@ -27,8 +27,12 @@ export class SimulatorRenderer {
// sync schema // sync schema
this._schema = host.document.schema; this._schema = host.document.schema;
this._componentsMap = host.designer.componentsMap; // todo: split with others, not all should recompute
this.buildComponents(); if (this._libraryMap !== host.libraryMap || this._componentsMap !== host.designer.componentsMap) {
this._libraryMap = host.libraryMap || {};
this._componentsMap = host.designer.componentsMap;
this.buildComponents();
}
// sync designMode // sync designMode
@ -64,8 +68,9 @@ export class SimulatorRenderer {
@computed get schema(): any { @computed get schema(): any {
return this._schema; return this._schema;
} }
private _libraryMap: { [key: string]: string } = {};
private buildComponents() { private buildComponents() {
this._components = buildComponents(this._componentsMap); this._components = buildComponents(this._libraryMap, this._componentsMap);
} }
@obx.ref private _components: any = {}; @obx.ref private _components: any = {};
@computed get components(): object { @computed get components(): object {
@ -280,7 +285,7 @@ function getSubComponent(library: any, paths: string[]) {
return component; return component;
} }
function findComponent(componentName: string, npm?: NpmInfo) { function findComponent(libraryMap: LibraryMap, componentName: string, npm?: NpmInfo) {
if (!npm) { if (!npm) {
return accessLibrary(componentName); return accessLibrary(componentName);
} }
@ -290,21 +295,26 @@ function findComponent(componentName: string, npm?: NpmInfo) {
// export { exportName as componentName } from package // export { exportName as componentName } from package
// if exportName == null exportName === componentName; // if exportName == null exportName === componentName;
// const componentName = exportName.subName, if exportName empty subName donot use // const componentName = exportName.subName, if exportName empty subName donot use
const libraryName = npm.exportName || npm.componentName || componentName; const exportName = npm.exportName || npm.componentName || componentName;
const libraryName = libraryMap[npm.package] || exportName;
const library = accessLibrary(libraryName); const library = accessLibrary(libraryName);
const paths = npm.exportName && npm.subName ? npm.subName.split('.') : []; const paths = npm.exportName && npm.subName ? npm.subName.split('.') : [];
if (npm.destructuring) { if (npm.destructuring) {
paths.unshift(libraryName); paths.unshift(exportName);
} else if (isESModule(library)) { } else if (isESModule(library)) {
paths.unshift('default'); paths.unshift('default');
} }
return getSubComponent(library, paths); return getSubComponent(library, paths);
} }
function buildComponents(componentsMap: { [componentName: string]: NpmInfo }) { export interface LibraryMap {
[key: string]: string;
}
function buildComponents(libraryMap: LibraryMap, componentsMap: { [componentName: string]: NpmInfo }) {
const components: any = {}; const components: any = {};
Object.keys(componentsMap).forEach(componentName => { Object.keys(componentsMap).forEach(componentName => {
components[componentName] = findComponent(componentName, componentsMap[componentName]); components[componentName] = findComponent(libraryMap, componentName, componentsMap[componentName]);
}); });
return components; return components;
} }

View File

@ -6,11 +6,11 @@ export interface AssetItem {
} }
export enum AssetLevel { export enum AssetLevel {
// 基础依赖库 // 环境依赖库 比如 react, react-dom
BaseDepends = 1, Environment = 1,
// 基础组件库 // 基础类库,比如 lodash deep fusion antd
BaseComponents = 2, Library = 2,
// 主题 // 主题
Theme = 3, Theme = 3,
// 运行时 // 运行时
Runtime = 4, Runtime = 4,
@ -21,8 +21,8 @@ export enum AssetLevel {
} }
export const AssetLevels = [ export const AssetLevels = [
AssetLevel.BaseDepends, AssetLevel.Environment,
AssetLevel.BaseComponents, AssetLevel.Library,
AssetLevel.Theme, AssetLevel.Theme,
AssetLevel.Runtime, AssetLevel.Runtime,
AssetLevel.Components, AssetLevel.Components,

View File

@ -1,10 +1,20 @@
import { load, evaluate } from './script'; import { load, evaluate } from './script';
import StylePoint from './style'; import StylePoint from './style';
import { Asset, AssetLevel, AssetLevels, AssetType, AssetList, isAssetBundle, isAssetItem, assetItem, AssetItem } from './asset'; import {
Asset,
AssetLevel,
AssetLevels,
AssetType,
AssetList,
isAssetBundle,
isAssetItem,
assetItem,
AssetItem,
} from './asset';
import { isCSSUrl } from '../../../utils/is-css-url'; import { isCSSUrl } from '../../../utils/is-css-url';
function parseAssetList(scripts: any, styles: any, assets: AssetList, level?: AssetLevel) { function parseAssetList(scripts: any, styles: any, assets: AssetList, level?: AssetLevel) {
for (let asset of assets) { for (const asset of assets) {
parseAsset(scripts, styles, asset, level); parseAsset(scripts, styles, asset, level);
} }
} }
@ -36,7 +46,7 @@ function parseAsset(scripts: any, styles: any, asset: Asset | undefined | null,
let lv = asset.level || level; let lv = asset.level || level;
if (!lv || AssetLevel[lv] == null) { if (!lv || AssetLevel[lv] == null) {
lv = AssetLevel.App lv = AssetLevel.App;
} }
asset.level = lv; asset.level = lv;
@ -51,19 +61,19 @@ export class AssetLoader {
async load(asset: Asset) { async load(asset: Asset) {
const styles: any = {}; const styles: any = {};
const scripts: any = {}; const scripts: any = {};
AssetLevels.forEach((lv) => { AssetLevels.forEach(lv => {
styles[lv] = []; styles[lv] = [];
scripts[lv] = []; scripts[lv] = [];
}); });
parseAsset(scripts, styles, asset); parseAsset(scripts, styles, asset);
const styleQueue: AssetItem[] = styles[AssetLevel.BaseDepends].concat( const styleQueue: AssetItem[] = styles[AssetLevel.Environment].concat(
styles[AssetLevel.BaseComponents], styles[AssetLevel.Library],
styles[AssetLevel.Theme], styles[AssetLevel.Theme],
styles[AssetLevel.Runtime], styles[AssetLevel.Runtime],
styles[AssetLevel.App], styles[AssetLevel.App],
); );
const scriptQueue: AssetItem[] = scripts[AssetLevel.BaseDepends].concat( const scriptQueue: AssetItem[] = scripts[AssetLevel.Environment].concat(
scripts[AssetLevel.BaseComponents], scripts[AssetLevel.Library],
scripts[AssetLevel.Theme], scripts[AssetLevel.Theme],
scripts[AssetLevel.Runtime], scripts[AssetLevel.Runtime],
scripts[AssetLevel.App], scripts[AssetLevel.App],
@ -71,9 +81,7 @@ export class AssetLoader {
await Promise.all( await Promise.all(
styleQueue.map(({ content, level, type, id }) => this.loadStyle(content, level!, type === AssetType.CSSUrl, id)), styleQueue.map(({ content, level, type, id }) => this.loadStyle(content, level!, type === AssetType.CSSUrl, id)),
); );
await Promise.all( await Promise.all(scriptQueue.map(({ content, type }) => this.loadScript(content, type === AssetType.JSUrl)));
scriptQueue.map(({ content, type }) => this.loadScript(content, type === AssetType.JSUrl)),
);
} }
private stylePoints = new Map<string, StylePoint>(); private stylePoints = new Map<string, StylePoint>();

View File

@ -330,7 +330,7 @@ export default class Designer {
return meta; return meta;
} }
get componentsMap(): { [key: string]: NpmInfo } { @computed get componentsMap(): { [key: string]: NpmInfo } {
const maps: any = {}; const maps: any = {};
this._componentMetasMap.forEach((config, key) => { this._componentMetasMap.forEach((config, key) => {
maps[key] = config.metadata.npm; maps[key] = config.metadata.npm;

View File

@ -325,7 +325,7 @@ export default class Node {
*/ */
export(serialize = false): NodeSchema { export(serialize = false): NodeSchema {
const baseSchema: any = { const baseSchema: any = {
componentName: this.componentName, componentName: this.componentName === 'Leaf' ? 'Fragment' : this.componentName,
}; };
if (serialize) { if (serialize) {
@ -334,10 +334,11 @@ export default class Node {
if (!isNodeParent(this)) { if (!isNodeParent(this)) {
baseSchema.children = this.props.get('children')?.export(serialize); baseSchema.children = this.props.get('children')?.export(serialize);
return baseSchema; // FIXME!
return baseSchema.children;
} }
const { props, extras } = this.props.export(serialize) || {}; const { props = {}, extras } = this.props.export(serialize) || {};
const schema: any = { const schema: any = {
...baseSchema, ...baseSchema,
props, props,

View File

@ -33,22 +33,24 @@ export default {
propType: 'string' propType: 'string'
} }
], ],
configure: { },
props: [ 'Button.Group': {
{ componentName: 'Button.Group',
name: 'type', title: '按钮组',
setter: { devMode: 'proCode',
componentName: 'Input' npm: {
} package: '@alifd/next',
}, version: '1.19.18',
{ destructuring: true,
name: 'children', exportName: 'Button',
setter: { subName: 'Group'
componentName: 'Input' },
} props: [
} {
] name: 'size',
} propType: 'string'
}
],
}, },
Input: { Input: {
componentName: 'Input', componentName: 'Input',
@ -66,16 +68,91 @@ export default {
propType: 'string' propType: 'string'
} }
], ],
configure: { },
props: [ Form: {
{ componentName: 'Form',
name: 'placeholder', title: '表单容器',
setter: { devMode: 'proCode',
componentName: 'Input' npm: {
} package: '@alifd/next',
} version: '1.19.18',
] destructuring: true,
} exportName: 'Form'
},
props: [
{
name: 'device',
propType: 'string'
}
],
},
'Form.Item': {
componentName: 'Form.Item',
title: '表单项',
devMode: 'proCode',
npm: {
package: '@alifd/next',
version: '1.19.18',
destructuring: true,
exportName: 'Form',
subName: 'Item'
},
props: [
{
name: 'label',
propType: 'string',
},
{
name: 'device',
propType: 'string'
}
],
},
NumberPicker: {
componentName: 'NumberPicker',
title: '数字输入',
devMode: 'proCode',
npm: {
package: '@alifd/next',
version: '1.19.18',
destructuring: true,
exportName: 'NumberPicker',
},
props: [
{
name: 'size',
propType: 'string',
},
{
name: 'defaultValue',
propType: 'number'
}
],
},
Select: {
componentName: 'Select',
title: '下拉',
devMode: 'proCode',
npm: {
package: '@alifd/next',
version: '1.19.18',
destructuring: true,
exportName: 'Select',
},
props: [
{
name: 'size',
propType: 'string',
},
{
name: 'defaultValue',
propType: 'number'
},
{
name: 'placeholder',
propType: 'string'
}
],
} }
}, },
componentList: [ componentList: [

View File

@ -6,6 +6,8 @@ import { PluginConfig } from '../../framework/definitions';
// @ts-ignore // @ts-ignore
import Designer from '../../../../designer'; import Designer from '../../../../designer';
import assets from '../../config/assets';
import './index.scss'; import './index.scss';
export interface PluginProps { export interface PluginProps {
@ -53,7 +55,7 @@ const SCHEMA = {
}, },
children: [ children: [
{ {
componentName: 'FormItem', componentName: 'Form.Item',
props: { props: {
label: '姓名:', label: '姓名:',
name: 'name', name: 'name',
@ -73,7 +75,7 @@ const SCHEMA = {
] ]
}, },
{ {
componentName: 'FormItem', componentName: 'Form.Item',
props: { props: {
label: '年龄:', label: '年龄:',
name: 'age', name: 'age',
@ -90,7 +92,7 @@ const SCHEMA = {
] ]
}, },
{ {
componentName: 'FormItem', componentName: 'Form.Item',
props: { props: {
label: '职业:', label: '职业:',
name: 'profession' name: 'profession'
@ -126,7 +128,7 @@ const SCHEMA = {
}, },
children: [ children: [
{ {
componentName: 'ButtonGroup', componentName: 'Button.Group',
props: {}, props: {},
children: [ children: [
{ {
@ -179,21 +181,9 @@ export default class DesignerPlugin extends PureComponent<PluginProps> {
className="lowcode-plugin-designer" className="lowcode-plugin-designer"
defaultSchema={SCHEMA as any} defaultSchema={SCHEMA as any}
eventPipe={editor as any} eventPipe={editor as any}
componentsDescription={Object.values(assets.components) as any}
simulatorProps={{ simulatorProps={{
componentsAsset: [ library: Object.values(assets.packages),
{
type: 'jsUrl',
content: 'https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js',
id: 'next',
level: 2
},
{
type: 'cssUrl',
content: 'https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.css',
id: 'next',
level: 2
}
]
}} }}
/> />
); );

File diff suppressed because it is too large Load Diff