mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-13 09:41:57 +00:00
complete trunk polyfill
This commit is contained in:
parent
4531562f79
commit
0430bfd219
@ -100,7 +100,7 @@ class Toolbar extends Component<{ observed: OffsetObserver }> {
|
||||
// FIXME: need this?
|
||||
return;
|
||||
}
|
||||
if (important && (typeof condition === 'function' ? condition(node) : condition !== false)) {
|
||||
if (important && (typeof condition === 'function' ? condition(node) !== false : condition !== false)) {
|
||||
actions.push(createAction(content, name, node));
|
||||
}
|
||||
});
|
||||
@ -120,7 +120,7 @@ function createAction(content: ReactNode | ComponentType<any> | ActionContentObj
|
||||
return createElement(content, { key, node });
|
||||
}
|
||||
if (isActionContentObject(content)) {
|
||||
const { action, description, icon } = content;
|
||||
const { action, title, icon } = content;
|
||||
return (
|
||||
<div
|
||||
key={key}
|
||||
@ -130,7 +130,7 @@ function createAction(content: ReactNode | ComponentType<any> | ActionContentObj
|
||||
}}
|
||||
>
|
||||
{icon && createIcon(icon)}
|
||||
<EmbedTip>{description}</EmbedTip>
|
||||
<EmbedTip>{title}</EmbedTip>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -31,18 +31,18 @@ import clipboard from '../designer/clipboard';
|
||||
export interface LibraryItem {
|
||||
package: string;
|
||||
library: string;
|
||||
urls: Asset;
|
||||
urls?: Asset;
|
||||
}
|
||||
|
||||
export interface BuiltinSimulatorProps {
|
||||
// 从 documentModel 上获取
|
||||
// suspended?: boolean;
|
||||
designMode?: 'live' | 'design' | 'mock' | 'extend' | 'border' | 'preview';
|
||||
designMode?: 'live' | 'design' | 'preview' | 'extend' | 'border';
|
||||
device?: 'mobile' | 'iphone' | string;
|
||||
deviceClassName?: string;
|
||||
simulatorUrl?: Asset;
|
||||
environment?: Asset;
|
||||
library?: LibraryItem[];
|
||||
simulatorUrl?: Asset;
|
||||
theme?: Asset;
|
||||
componentsAsset?: Asset;
|
||||
[key: string]: any;
|
||||
@ -79,8 +79,6 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
readonly designer = this.document.designer;
|
||||
|
||||
@computed get device(): string | undefined {
|
||||
// 根据 device 不同来做画布外框样式变化 渲染时可选择不同组件
|
||||
// renderer 依赖
|
||||
return this.get('device') || 'default';
|
||||
}
|
||||
|
||||
@ -88,7 +86,7 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
return this.get('deviceClassName');
|
||||
}
|
||||
|
||||
@computed get designMode(): 'live' | 'design' | 'extend' | 'border' | 'preview' {
|
||||
@computed get designMode(): 'live' | 'design' | 'preview' {
|
||||
// renderer 依赖
|
||||
// TODO: 需要根据 design mode 不同切换鼠标响应情况
|
||||
return this.get('designMode') || 'design';
|
||||
@ -180,7 +178,9 @@ export class BuiltinSimulatorHost implements ISimulatorHost<BuiltinSimulatorProp
|
||||
if (library) {
|
||||
library.forEach((item) => {
|
||||
this.libraryMap[item.package] = item.library;
|
||||
libraryAsset.push(item.urls);
|
||||
if (item.urls) {
|
||||
libraryAsset.push(item.urls);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -32,44 +32,8 @@ function ensureAList(list?: string | string[]): string[] | null {
|
||||
return list;
|
||||
}
|
||||
|
||||
function npmToURI(npm: {
|
||||
package: string;
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
destructuring?: boolean;
|
||||
main?: string;
|
||||
version: string;
|
||||
}): string {
|
||||
const pkg = [];
|
||||
if (npm.package) {
|
||||
pkg.push(npm.package);
|
||||
}
|
||||
if (npm.main) {
|
||||
if (npm.main[0] === '/') {
|
||||
pkg.push(npm.main.slice(1));
|
||||
} else if (npm.main.slice(0, 2) === './') {
|
||||
pkg.push(npm.main.slice(2));
|
||||
} else {
|
||||
pkg.push(npm.main);
|
||||
}
|
||||
}
|
||||
|
||||
let uri = pkg.join('/');
|
||||
uri += `:${npm.destructuring && npm.exportName ? npm.exportName : 'default'}`;
|
||||
|
||||
if (npm.subName) {
|
||||
uri += `.${npm.subName}`;
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
export class ComponentMeta {
|
||||
readonly isComponentMeta = true;
|
||||
private _uri?: string;
|
||||
get uri(): string {
|
||||
return this._uri!;
|
||||
}
|
||||
private _npm?: NpmInfo;
|
||||
get npm() {
|
||||
return this._npm;
|
||||
@ -125,23 +89,27 @@ export class ComponentMeta {
|
||||
this.parseMetadata(metadata);
|
||||
}
|
||||
|
||||
setNpm(info: NpmInfo) {
|
||||
if (!this._npm) {
|
||||
this._npm = info;
|
||||
}
|
||||
}
|
||||
|
||||
private parseMetadata(metadta: ComponentMetadata) {
|
||||
const { componentName, uri, npm } = metadta;
|
||||
const { componentName, npm } = metadta;
|
||||
this._npm = npm;
|
||||
this._uri = uri || (npm ? npmToURI(npm) : componentName);
|
||||
this._componentName = componentName;
|
||||
|
||||
metadta.uri = this._uri;
|
||||
// 额外转换逻辑
|
||||
this._transformedMetadata = this.transformMetadata(metadta);
|
||||
|
||||
const title = this._transformedMetadata.title;
|
||||
if (title && typeof title === 'string') {
|
||||
this._title = {
|
||||
if (title) {
|
||||
this._title = typeof title === 'string' ? {
|
||||
type: 'i18n',
|
||||
'en-US': this.componentName,
|
||||
'zh-CN': title,
|
||||
};
|
||||
} : title;
|
||||
}
|
||||
|
||||
const { configure = {} } = this._transformedMetadata;
|
||||
@ -181,12 +149,14 @@ export class ComponentMeta {
|
||||
|
||||
@computed get availableActions() {
|
||||
let { disableBehaviors, actions } = this._transformedMetadata?.configure.component || {};
|
||||
const disabled = ensureAList(disableBehaviors) || (this.isRootComponent() ? ['copy', 'remove'] : null);
|
||||
actions = builtinComponentActions.concat(this.designer.getGlobalComponentActions() || [], actions || []);
|
||||
if (!disableBehaviors && this.isRootComponent()) {
|
||||
disableBehaviors = ['copy', 'remove'];
|
||||
}
|
||||
if (disableBehaviors) {
|
||||
return actions.filter(action => disableBehaviors!.indexOf(action.name) < 0);
|
||||
|
||||
if (disabled) {
|
||||
if (disabled.includes('*')) {
|
||||
return actions.filter((action) => action.condition === 'always');
|
||||
}
|
||||
return actions.filter((action) => disabled.indexOf(action.name) < 0);
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
@ -216,6 +186,10 @@ export class ComponentMeta {
|
||||
}
|
||||
}
|
||||
|
||||
export function isComponentMeta(obj: any): obj is ComponentMeta {
|
||||
return obj && obj.isComponentMeta;
|
||||
}
|
||||
|
||||
function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMetadata {
|
||||
if (metadata.configure) {
|
||||
if (Array.isArray(metadata.configure)) {
|
||||
@ -235,7 +209,7 @@ function preprocessMetadata(metadata: ComponentMetadata): TransformedComponentMe
|
||||
};
|
||||
}
|
||||
|
||||
registerMetadataTransducer(metadata => {
|
||||
registerMetadataTransducer((metadata) => {
|
||||
const { configure, componentName } = metadata;
|
||||
const { component = {} } = configure;
|
||||
if (!component.nestingRule) {
|
||||
@ -280,7 +254,7 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
name: 'remove',
|
||||
content: {
|
||||
icon: IconRemove,
|
||||
description: intl('remove'),
|
||||
title: intl('remove'),
|
||||
action(node: Node) {
|
||||
node.remove();
|
||||
},
|
||||
@ -291,7 +265,7 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
name: 'copy',
|
||||
content: {
|
||||
icon: IconClone,
|
||||
description: intl('copy'),
|
||||
title: intl('copy'),
|
||||
action(node: Node) {
|
||||
// node.remove();
|
||||
},
|
||||
@ -299,3 +273,13 @@ const builtinComponentActions: ComponentAction[] = [
|
||||
important: true,
|
||||
},
|
||||
];
|
||||
|
||||
export function removeBuiltinComponentAction(name: string) {
|
||||
const i = builtinComponentActions.findIndex(action => action.name === name);
|
||||
if (i > -1) {
|
||||
builtinComponentActions.splice(i, 1);
|
||||
}
|
||||
}
|
||||
export function addBuiltinComponentAction(action: ComponentAction) {
|
||||
builtinComponentActions.push(action);
|
||||
}
|
||||
|
||||
@ -306,24 +306,27 @@ export class Designer {
|
||||
private _lostComponentMetasMap = new Map<string, ComponentMeta>();
|
||||
|
||||
private buildComponentMetasMap(metas: ComponentMetadata[]) {
|
||||
metas.forEach((data) => {
|
||||
const key = data.componentName;
|
||||
let meta = this._componentMetasMap.get(key);
|
||||
metas.forEach((data) => this.createComponentMeta(data));
|
||||
}
|
||||
|
||||
createComponentMeta(data: ComponentMetadata): ComponentMeta {
|
||||
const key = data.componentName;
|
||||
let meta = this._componentMetasMap.get(key);
|
||||
if (meta) {
|
||||
meta.setMetadata(data);
|
||||
} else {
|
||||
meta = this._lostComponentMetasMap.get(key);
|
||||
|
||||
if (meta) {
|
||||
meta.setMetadata(data);
|
||||
this._lostComponentMetasMap.delete(key);
|
||||
} else {
|
||||
meta = this._lostComponentMetasMap.get(key);
|
||||
|
||||
if (meta) {
|
||||
meta.setMetadata(data);
|
||||
this._lostComponentMetasMap.delete(key);
|
||||
} else {
|
||||
meta = new ComponentMeta(this, data);
|
||||
}
|
||||
|
||||
this._componentMetasMap.set(key, meta);
|
||||
meta = new ComponentMeta(this, data);
|
||||
}
|
||||
});
|
||||
|
||||
this._componentMetasMap.set(key, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
getGlobalComponentActions(): ComponentAction[] | null {
|
||||
|
||||
@ -7,8 +7,18 @@ export type RegisteredSetter = {
|
||||
component: CustomView;
|
||||
defaultProps?: object;
|
||||
title?: TitleContent;
|
||||
/**
|
||||
* for MixedSetter to check this setter if available
|
||||
*/
|
||||
condition?: (field: any) => boolean;
|
||||
/**
|
||||
* for MixedSetter to manual change to this setter
|
||||
*/
|
||||
initialValue?: any | ((field: any) => any);
|
||||
};
|
||||
const settersMap = new Map<string, RegisteredSetter>();
|
||||
const settersMap = new Map<string, RegisteredSetter & {
|
||||
type: string;
|
||||
}>();
|
||||
export function registerSetter(
|
||||
typeOrMaps: string | { [key: string]: CustomView | RegisteredSetter },
|
||||
setter?: CustomView | RegisteredSetter,
|
||||
@ -25,15 +35,19 @@ export function registerSetter(
|
||||
if (isCustomView(setter)) {
|
||||
setter = {
|
||||
component: setter,
|
||||
// todo: intl
|
||||
title: (setter as any).displayName || (setter as any).name || 'CustomSetter',
|
||||
};
|
||||
}
|
||||
settersMap.set(typeOrMaps, setter);
|
||||
settersMap.set(typeOrMaps, { type: typeOrMaps, ...setter });
|
||||
}
|
||||
|
||||
export function getSetter(type: string): RegisteredSetter | null {
|
||||
return settersMap.get(type) || null;
|
||||
}
|
||||
export function getSettersMap() {
|
||||
return settersMap;
|
||||
}
|
||||
|
||||
export function createSetterContent(setter: any, props: object): ReactNode {
|
||||
if (typeof setter === 'string') {
|
||||
|
||||
@ -1,10 +1,29 @@
|
||||
import { TransformedComponentMetadata } from '../types';
|
||||
|
||||
export type MetadataTransducer = (prev: TransformedComponentMetadata) => TransformedComponentMetadata;
|
||||
export interface MetadataTransducer {
|
||||
(prev: TransformedComponentMetadata): TransformedComponentMetadata;
|
||||
/**
|
||||
* 0 - 9 system
|
||||
* 10 - 99 builtin-plugin
|
||||
* 100 - app & plugin
|
||||
*/
|
||||
level?: number;
|
||||
/**
|
||||
* use to replace TODO
|
||||
*/
|
||||
id?: string;
|
||||
}
|
||||
const metadataTransducers: MetadataTransducer[] = [];
|
||||
|
||||
export function registerMetadataTransducer(transducer: MetadataTransducer) {
|
||||
metadataTransducers.push(transducer);
|
||||
export function registerMetadataTransducer(transducer: MetadataTransducer, level: number = 100, id?: string) {
|
||||
transducer.level = level;
|
||||
transducer.id = id;
|
||||
const i = metadataTransducers.findIndex(item => item.level != null && item.level > level);
|
||||
if (i < 0) {
|
||||
metadataTransducers.push(transducer);
|
||||
} else {
|
||||
metadataTransducers.splice(i, 0, transducer);
|
||||
}
|
||||
}
|
||||
|
||||
export function getRegisteredMetadataTransducers(): MetadataTransducer[] {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { TitleContent } from './title';
|
||||
import { SetterType } from './setter-config';
|
||||
import { SetterType, DynamicSetter } from './setter-config';
|
||||
|
||||
|
||||
export interface FieldExtraProps {
|
||||
@ -18,6 +18,14 @@ export interface FieldExtraProps {
|
||||
* @default undefined
|
||||
*/
|
||||
condition?: (field: any) => boolean;
|
||||
/**
|
||||
* autorun when something change
|
||||
*/
|
||||
autorun?: (field: any) => void;
|
||||
/**
|
||||
* is this field is a virtual field that not save to schema
|
||||
*/
|
||||
virtual?: (field: any) => boolean;
|
||||
/**
|
||||
* default collapsed when display accordion
|
||||
*/
|
||||
@ -46,7 +54,7 @@ export interface FieldConfig extends FieldExtraProps {
|
||||
/**
|
||||
* the field body contains when .type = 'field'
|
||||
*/
|
||||
setter?: SetterType;
|
||||
setter?: SetterType | DynamicSetter;
|
||||
/**
|
||||
* the setting items which group body contains when .type = 'group'
|
||||
*/
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { ReactNode, ComponentType, ReactElement } from 'react';
|
||||
import { IconType } from './icon';
|
||||
import { TipContent } from './tip';
|
||||
import { TitleContent } from './title';
|
||||
import { PropConfig } from './prop-config';
|
||||
import { NpmInfo } from './npm';
|
||||
import { FieldConfig } from './field-config';
|
||||
import { NodeSchema } from './schema';
|
||||
|
||||
export type NestingFilter = (which: any, my: any) => boolean;
|
||||
export type NestingFilter = (testNode: any, currentNode: any) => boolean;
|
||||
export interface NestingRule {
|
||||
// 子级白名单
|
||||
childWhitelist?: string[] | string | RegExp | NestingFilter;
|
||||
@ -26,12 +27,46 @@ export interface ComponentConfigure {
|
||||
isNullNode?: boolean;
|
||||
descriptor?: string;
|
||||
nestingRule?: NestingRule;
|
||||
|
||||
rectSelector?: string;
|
||||
// copy,move,delete
|
||||
disableBehaviors?: string[];
|
||||
// copy,move,delete | *
|
||||
disableBehaviors?: string[] | string;
|
||||
actions?: ComponentAction[];
|
||||
}
|
||||
|
||||
export interface Snippet {
|
||||
screenshot: string;
|
||||
label: string;
|
||||
schema: NodeSchema;
|
||||
}
|
||||
|
||||
export interface Experimental {
|
||||
context?: { [contextInfoName: string]: any };
|
||||
snippets?: Snippet[];
|
||||
view?: ComponentType<any>;
|
||||
transducers?: any; // ? should support
|
||||
callbacks?: Callbacks;
|
||||
|
||||
// 样式 及 位置,handle上必须有明确的标识以便事件路由判断,或者主动设置事件独占模式
|
||||
// NWSE 是交给引擎计算放置位置,ReactElement 必须自己控制初始位置
|
||||
getResizingHandlers?: (
|
||||
currentNode: any,
|
||||
) =>
|
||||
| Array<{
|
||||
type: 'N' | 'W' | 'S' | 'E' | 'NW' | 'NE' | 'SE' | 'SW';
|
||||
content?: ReactElement;
|
||||
propTarget?: string;
|
||||
appearOn?: 'mouse-enter' | 'mouse-hover' | 'selected' | 'always';
|
||||
}>
|
||||
| ReactElement[];
|
||||
// hover时 控制柄高亮
|
||||
// mousedown 时请求独占
|
||||
// dragstart 请求 通用 resizing 控制
|
||||
// 请求 hud 显示
|
||||
// drag 时 计算 并 设置效果
|
||||
// 更新控制柄位置
|
||||
}
|
||||
|
||||
export interface Configure {
|
||||
props?: FieldConfig[];
|
||||
styles?: object;
|
||||
@ -43,9 +78,9 @@ export interface ActionContentObject {
|
||||
// 图标
|
||||
icon?: IconType;
|
||||
// 描述
|
||||
description?: TipContent;
|
||||
title?: TipContent;
|
||||
// 执行动作
|
||||
action?: (node: any) => void;
|
||||
action?: (currentNode: any) => void;
|
||||
}
|
||||
|
||||
export interface ComponentAction {
|
||||
@ -55,8 +90,8 @@ export interface ComponentAction {
|
||||
content: string | ReactNode | ActionContentObject;
|
||||
// 子集
|
||||
items?: ComponentAction[];
|
||||
// 不显示
|
||||
condition?: boolean | ((node: any) => boolean);
|
||||
// 显示与否,always: 无法禁用
|
||||
condition?: boolean | ((currentNode: any) => boolean) | 'always';
|
||||
// 显示在工具条上
|
||||
important?: boolean;
|
||||
}
|
||||
@ -87,8 +122,52 @@ export interface ComponentMetadata {
|
||||
npm?: NpmInfo;
|
||||
props?: PropConfig[];
|
||||
configure?: FieldConfig[] | Configure;
|
||||
experimental?: Experimental;
|
||||
}
|
||||
|
||||
export interface TransformedComponentMetadata extends ComponentMetadata {
|
||||
configure: Configure & { combined?: FieldConfig[] };
|
||||
}
|
||||
|
||||
// handleResizing
|
||||
|
||||
// hooks & events
|
||||
export interface Callbacks {
|
||||
// hooks
|
||||
onMouseDownHook?: (e: MouseEvent, currentNode: any) => any;
|
||||
onDblClickHook?: (e: MouseEvent, currentNode: any) => any;
|
||||
onClickHook?: (e: MouseEvent, currentNode: any) => any;
|
||||
onLocateHook?: (e: any, currentNode: any) => any;
|
||||
onAcceptHook?: (currentNode: any, locationData: any) => any;
|
||||
onMoveHook?: (currentNode: any) => boolean; // thinkof 限制性拖拽
|
||||
onChildMoveHook?: (childNode: any, currentNode: any) => boolean;
|
||||
|
||||
// events
|
||||
onNodeRemove?: (removedNode: any, currentNode: any) => void;
|
||||
onNodeAdd?: (addedNode: any, currentNode: any) => void;
|
||||
onSubtreeModified?: (currentNode: any, options: any) => void;
|
||||
onResize?: (
|
||||
e: MouseEvent & {
|
||||
trigger: string;
|
||||
deltaX?: number;
|
||||
deltaY?: number;
|
||||
},
|
||||
currentNode: any,
|
||||
) => void;
|
||||
onResizeStart?: (
|
||||
e: MouseEvent & {
|
||||
trigger: string;
|
||||
deltaX?: number;
|
||||
deltaY?: number;
|
||||
},
|
||||
currentNode: any,
|
||||
) => void;
|
||||
onResizeEnd?: (
|
||||
e: MouseEvent & {
|
||||
trigger: string;
|
||||
deltaX?: number;
|
||||
deltaY?: number;
|
||||
},
|
||||
currentNode: any,
|
||||
) => void;
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
export interface NpmInfo {
|
||||
componentName?: string;
|
||||
package: string;
|
||||
version: string;
|
||||
version?: string;
|
||||
destructuring?: boolean;
|
||||
exportName?: string;
|
||||
subName?: string;
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { isReactComponent } from '../utils';
|
||||
import { ComponentType, ReactElement, isValidElement } from 'react';
|
||||
import { TitleContent } from './title';
|
||||
|
||||
export type CustomView = ReactElement | ComponentType<any>;
|
||||
|
||||
export type DynamicProps = (field: any) => object;
|
||||
export type DynamicSetter = (field: any) => string | SetterConfig | CustomView;
|
||||
|
||||
export interface SetterConfig {
|
||||
/**
|
||||
@ -17,12 +19,16 @@ export interface SetterConfig {
|
||||
children?: any;
|
||||
isRequired?: boolean;
|
||||
initialValue?: any | ((field: any) => any);
|
||||
/* for MixedSetter */
|
||||
title?: TitleContent;
|
||||
// for MixedSetter check this is available
|
||||
condition?: (field: any) => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* if *string* passed must be a registered Setter Name, future support blockSchema
|
||||
*/
|
||||
export type SetterType = SetterConfig | string | CustomView;
|
||||
export type SetterType = SetterConfig | SetterConfig[] | string | CustomView;
|
||||
|
||||
|
||||
export function isSetterConfig(obj: any): obj is SetterConfig {
|
||||
@ -32,3 +38,7 @@ export function isSetterConfig(obj: any): obj is SetterConfig {
|
||||
export function isCustomView(obj: any): obj is CustomView {
|
||||
return obj && (isValidElement(obj) || isReactComponent(obj));
|
||||
}
|
||||
|
||||
export function isDynamicSetter(obj: any): obj is DynamicSetter {
|
||||
return obj && typeof obj === 'function' && !obj.displayName;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import { IconType } from './icon';
|
||||
export interface TitleConfig {
|
||||
label?: I18nData | ReactNode;
|
||||
tip?: TipContent;
|
||||
docUrl?: string;
|
||||
icon?: IconType;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@ -15,7 +15,9 @@ export interface JSExpression {
|
||||
|
||||
export interface JSSlot {
|
||||
type: 'JSSlot';
|
||||
value: NodeSchema;
|
||||
// 函数的入参
|
||||
params?: string[];
|
||||
value: NodeSchema[];
|
||||
}
|
||||
|
||||
// JSON 基本类型
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
export interface AssetItem {
|
||||
type: AssetType;
|
||||
content?: string | null;
|
||||
device?: string;
|
||||
level?: AssetLevel;
|
||||
id?: string;
|
||||
}
|
||||
@ -68,6 +69,14 @@ export function assetBundle(assets?: Asset | AssetList | null, level?: AssetLeve
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
urls: "view.js,view2 <device selector>, view3 <device selector>",
|
||||
urls: [
|
||||
"view.js",
|
||||
"view.js *",
|
||||
"view1.js mobile|pc",
|
||||
"view2.js <device selector>"
|
||||
]*/
|
||||
export function assetItem(type: AssetType, content?: string | null, level?: AssetLevel, id?: string): AssetItem | null {
|
||||
if (!content) {
|
||||
return null;
|
||||
|
||||
@ -20,50 +20,36 @@ export default class DesignerPlugin extends PureComponent<PluginProps, DesignerP
|
||||
library: null,
|
||||
};
|
||||
|
||||
private _lifeState = 0;
|
||||
private _mounted = true;
|
||||
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
const { editor } = this.props;
|
||||
const assets = editor.get('assets');
|
||||
|
||||
if (assets) {
|
||||
this.setupAssets(assets);
|
||||
} else {
|
||||
editor.once('assets.loaded', this.setupAssets);
|
||||
}
|
||||
this._lifeState = 1;
|
||||
this.setupAssets();
|
||||
}
|
||||
|
||||
setupAssets = (assets: any) => {
|
||||
if (this._lifeState < 0) {
|
||||
private async setupAssets() {
|
||||
const { editor } = this.props;
|
||||
const assets = await editor.onceGot('assets');
|
||||
if (!this._mounted) {
|
||||
return;
|
||||
}
|
||||
const { components, packages } = assets;
|
||||
const state = {
|
||||
componentMetadatas: components ? Object.values(components) : [],
|
||||
componentMetadatas: components ? components.filter(item => item.type === 'ComponentMetadata') : [],
|
||||
library: packages ? Object.values(packages) : [],
|
||||
};
|
||||
if (this._lifeState === 0) {
|
||||
this.state = state;
|
||||
} else {
|
||||
this.setState(state);
|
||||
}
|
||||
this.setState(state);
|
||||
};
|
||||
|
||||
componentWillUnmount() {
|
||||
this._lifeState = -1;
|
||||
this._mounted = false;
|
||||
}
|
||||
|
||||
handleDesignerMount = (designer: Designer): void => {
|
||||
private handleDesignerMount = (designer: Designer): void => {
|
||||
const { editor } = this.props;
|
||||
editor.set(Designer, designer);
|
||||
editor.emit('designer.ready', designer);
|
||||
const schema = editor.get('schema');
|
||||
if (schema) {
|
||||
designer.project.open(schema);
|
||||
}
|
||||
editor.on('schema.loaded', (schema) => {
|
||||
editor.onGot('schema', (schema) => {
|
||||
designer.project.open(schema);
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import { uniqueId } from '@ali/lowcode-globals';
|
||||
import { uniqueId, DynamicSetter, isDynamicSetter } from '@ali/lowcode-globals';
|
||||
import { ComponentMeta, Node, Designer, Selection } from '@ali/lowcode-designer';
|
||||
import { TitleContent, FieldExtraProps, SetterType, CustomView, FieldConfig, isCustomView } from '@ali/lowcode-globals';
|
||||
import { getTreeMaster } from '@ali/lowcode-plugin-outline-pane';
|
||||
@ -73,7 +73,16 @@ export class SettingField implements SettingTarget {
|
||||
readonly title: TitleContent;
|
||||
readonly editor: any;
|
||||
readonly extraProps: FieldExtraProps;
|
||||
readonly setter?: SetterType;
|
||||
private _setter?: SetterType | DynamicSetter;
|
||||
get setter(): SetterType | null {
|
||||
if (!this._setter) {
|
||||
return null;
|
||||
}
|
||||
if (isDynamicSetter(this._setter)) {
|
||||
return this._setter(this);
|
||||
}
|
||||
return this._setter;
|
||||
}
|
||||
readonly isSame: boolean;
|
||||
readonly isMulti: boolean;
|
||||
readonly isOne: boolean;
|
||||
@ -107,7 +116,7 @@ export class SettingField implements SettingTarget {
|
||||
this._name = name;
|
||||
// make this reactive
|
||||
this.title = title || (typeof name === 'number' ? `项目 ${name}` : name);
|
||||
this.setter = setter;
|
||||
this._setter = setter;
|
||||
this.extraProps = {
|
||||
...rest,
|
||||
...extraProps,
|
||||
|
||||
241
packages/plugin-settings-pane/src/setters/mixed-setter/index.tsx
Normal file
241
packages/plugin-settings-pane/src/setters/mixed-setter/index.tsx
Normal file
@ -0,0 +1,241 @@
|
||||
import React, { PureComponent, Component } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Dropdown, Button, Menu, Icon } from '@alifd/next';
|
||||
import { getSetter, getSettersMap, SetterConfig, computed, obx, CustomView, DynamicProps, DynamicSetter, TitleContent, isSetterConfig, Title, createSetterContent } from '@ali/lowcode-globals';
|
||||
import { SettingField } from 'plugin-settings-pane/src/main';
|
||||
|
||||
import './index.scss';
|
||||
|
||||
export interface SetterItem {
|
||||
name: string;
|
||||
title: TitleContent;
|
||||
setter: string | DynamicSetter | CustomView;
|
||||
props?: object | DynamicProps;
|
||||
condition?: (field: SettingField) => boolean;
|
||||
initialValue?: (field: SettingField) => any;
|
||||
}
|
||||
|
||||
function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | DynamicSetter>): SetterItem[] {
|
||||
if (!setters) {
|
||||
const normalized: SetterItem[] = [];
|
||||
getSettersMap().forEach((setter, name) => {
|
||||
if (name === 'MixedSetter') {
|
||||
return;
|
||||
}
|
||||
normalized.push({
|
||||
name,
|
||||
title: setter.title || name,
|
||||
setter: name,
|
||||
condition: setter.condition,
|
||||
initialValue: setter.initialValue,
|
||||
});
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
const names: string[] = [];
|
||||
function generateName(n: string) {
|
||||
let idx = 1;
|
||||
let got = n;
|
||||
while (names.indexOf(got) > -1) {
|
||||
got = `${n}:${idx++}`;
|
||||
}
|
||||
names.push(got);
|
||||
return got;
|
||||
}
|
||||
return setters.map(setter => {
|
||||
const config: any = {
|
||||
setter,
|
||||
};
|
||||
if (isSetterConfig(setter)) {
|
||||
config.setter = setter.componentName;
|
||||
config.props = setter.props;
|
||||
config.condition = setter.condition;
|
||||
config.initialValue = setter.initialValue;
|
||||
config.title = setter.title;
|
||||
}
|
||||
if (typeof config.setter === 'string') {
|
||||
config.name = config.setter;
|
||||
names.push(config.name);
|
||||
const info = getSetter(config.setter);
|
||||
if (!config.title) {
|
||||
config.title = info?.title || config.setter;
|
||||
}
|
||||
if (!config.condition) {
|
||||
config.condition = info?.condition;
|
||||
}
|
||||
if (!config.initialValue) {
|
||||
config.initialValue = info?.initialValue;
|
||||
}
|
||||
} else {
|
||||
config.name = generateName((config.setter as any).displayName || (config.setter as any).name || 'CustomSetter');
|
||||
if (!config.title) {
|
||||
config.title = config.name;
|
||||
}
|
||||
}
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
export default class MixedSetter extends Component<{
|
||||
field: SettingField;
|
||||
setters?: Array<string | SetterConfig | CustomView | DynamicSetter>;
|
||||
onSetterChange: (field: SettingField, name: string) => void;
|
||||
}> {
|
||||
private setters = nomalizeSetters(this.props.setters);
|
||||
@obx.ref private used?: string;
|
||||
@computed private getCurrentSetter() {
|
||||
const { field } = this.props;
|
||||
if (this.used != null) {
|
||||
const selected = this.used;
|
||||
if (selected.condition) {
|
||||
if (selected.condition(field)) {
|
||||
return selected;
|
||||
}
|
||||
} else {
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
return this.setters.find(item => {
|
||||
if (!item.condition) {
|
||||
return true;
|
||||
}
|
||||
return item.condition(field);
|
||||
});
|
||||
}
|
||||
|
||||
private checkIsBlockField() {
|
||||
if (this.shell) {
|
||||
const setter = this.shell.lastElementChild!.firstElementChild;
|
||||
if (setter && setter.classList.contains('lc-block-setter')) {
|
||||
this.shell.classList.add('lc-block-setter');
|
||||
} else {
|
||||
this.shell.classList.remove('lc-block-field');
|
||||
}
|
||||
}
|
||||
}
|
||||
componentDidUpdate() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
componentDidMount() {
|
||||
this.checkIsBlockField();
|
||||
}
|
||||
|
||||
private useSetter: (id: string) => {
|
||||
const { field, onChange } = this.props;
|
||||
const newValue = setter.initialValue?.(field);
|
||||
this.used = setter;
|
||||
onChange && onChange(newValue);
|
||||
}
|
||||
render() {
|
||||
const {
|
||||
style = {},
|
||||
className,
|
||||
types = [],
|
||||
defaultType,
|
||||
...restProps
|
||||
} = this.props;
|
||||
this.typeMap = {};
|
||||
let realTypes: any[] = [];
|
||||
types.forEach( (el: { name: any; props: any; }) => {
|
||||
const { name, props } = el;
|
||||
const Setter = getSetter(name);
|
||||
if (Setter) {
|
||||
this.typeMap[name] = {
|
||||
label: name,
|
||||
component: Setter.component,
|
||||
props,
|
||||
}
|
||||
}
|
||||
realTypes.push(name);
|
||||
})
|
||||
let moreBtnNode = null;
|
||||
//如果只有2种,且有变量表达式,则直接展示变量按钮
|
||||
if (realTypes.length > 1) {
|
||||
let isTwoType = !!(realTypes.length === 2 && ~realTypes.indexOf('ExpressionSetter'));
|
||||
let btnProps = {
|
||||
size: 'small',
|
||||
text: true,
|
||||
style: {
|
||||
position: 'absolute',
|
||||
left: '100%',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
margin: 'auto 0 auto 8px',
|
||||
padding: 0,
|
||||
width: 16,
|
||||
height: 16,
|
||||
lineHeight: '16px',
|
||||
textAlign: 'center'
|
||||
}
|
||||
};
|
||||
if (isTwoType) {
|
||||
btnProps.onClick = this.changeType.bind(this, realTypes.indexOf(this.state.type) ? realTypes[0] : realTypes[1]);
|
||||
}
|
||||
// 未匹配的 null 值,显示 NullValue 空值
|
||||
// 未匹配的 其它 值,显示 InvalidValue 非法值
|
||||
let triggerNode = (
|
||||
<Button {...btnProps} size={isTwoType ? 'large' : 'small'}>
|
||||
<Icon type={isTwoType ? 'edit' : 'ellipsis'} />
|
||||
</Button>
|
||||
);
|
||||
if (isTwoType) {
|
||||
moreBtnNode = triggerNode;
|
||||
} else {
|
||||
let MenuItems: {} | null | undefined = [];
|
||||
realTypes.map(type => {
|
||||
if (this.typeMap[type]) {
|
||||
MenuItems.push(<Menu.Item key={type}></Menu.Item>);
|
||||
} else {
|
||||
console.error(
|
||||
this.i18n('typeError', {
|
||||
type
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
let MenuNode = (
|
||||
<Menu
|
||||
selectMode="single"
|
||||
hasSelectedIcon={false}
|
||||
selectedKeys={this.used}
|
||||
onItemClick={this.useSetter}
|
||||
>
|
||||
{this.setters.map((setter) => {
|
||||
return <Menu.Item key={setter.name}>
|
||||
<Title title={setter.title} />
|
||||
</Menu.Item>
|
||||
})}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
moreBtnNode = (
|
||||
<Dropdown trigger={triggerNode} triggerType="click">
|
||||
<Menu
|
||||
selectMode="single"
|
||||
hasSelectedIcon={false}
|
||||
selectedKeys={this.used}
|
||||
onItemClick={this.useSetter}
|
||||
>
|
||||
{this.setters.map((setter) => {
|
||||
return <Menu.Item key={setter.name}>
|
||||
<Title title={setter.title} />
|
||||
</Menu.Item>
|
||||
})}
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
}
|
||||
let TargetNode = this.typeMap[this.state.type]?.component || 'div';
|
||||
let targetProps = this.typeMap[this.state.type]?.props || {};
|
||||
let tarStyle = { position: 'relative', ...style };
|
||||
let classes = classNames(className, 'lowcode-setter-mixin');
|
||||
|
||||
return (
|
||||
<div style={tarStyle} className={classes} >
|
||||
{createSetterContent()}
|
||||
{moreBtnNode}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,10 @@ class SettingFieldView extends Component<{ field: SettingField }> {
|
||||
const { field } = this.props;
|
||||
const { setter } = field;
|
||||
let setterProps: object | DynamicProps = {};
|
||||
if (Array.isArray(setter)) {
|
||||
this.setterType = 'MixedSetter';
|
||||
// setterProps =
|
||||
}
|
||||
if (isSetterConfig(setter)) {
|
||||
this.setterType = setter.componentName;
|
||||
if (setter.props) {
|
||||
|
||||
@ -3,7 +3,7 @@ import parseProps from './parse-props';
|
||||
import addonCombine from './addon-combine';
|
||||
|
||||
// parseProps
|
||||
registerMetadataTransducer(parseProps);
|
||||
registerMetadataTransducer(parseProps, 10, 'parse-props');
|
||||
|
||||
// addon/platform custom
|
||||
registerMetadataTransducer(addonCombine);
|
||||
registerMetadataTransducer(addonCombine, 11, 'combine-props');
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { PureComponent, createElement as reactCreateElement } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Debug from 'debug';
|
||||
import Div from '@ali/iceluna-comp-div';
|
||||
@ -41,7 +41,6 @@ export default class BaseEngine extends PureComponent {
|
||||
messages: PropTypes.object,
|
||||
__appHelper: PropTypes.object,
|
||||
__components: PropTypes.object,
|
||||
__componentsMap: PropTypes.object,
|
||||
__ctx: PropTypes.object,
|
||||
__schema: PropTypes.object,
|
||||
};
|
||||
@ -199,7 +198,7 @@ export default class BaseEngine extends PureComponent {
|
||||
// idx 若为循环渲染的循环Index
|
||||
__createVirtualDom = (schema, self, parentInfo, idx) => {
|
||||
if (!schema) return null;
|
||||
const { __appHelper: appHelper, __components: components = {}, __componentsMap: componentsMap = {} } =
|
||||
const { __appHelper: appHelper, __components: components = {} } =
|
||||
this.props || {};
|
||||
const { engine } = this.context || {};
|
||||
if (isJSExpression(schema)) {
|
||||
@ -277,13 +276,12 @@ export default class BaseEngine extends PureComponent {
|
||||
__schema: schema,
|
||||
__appHelper: appHelper,
|
||||
__components: components,
|
||||
__componentsMap: componentsMap,
|
||||
}
|
||||
: {};
|
||||
if (engine && engine.props.designMode) {
|
||||
otherProps.__designMode = engine.props.designMode;
|
||||
}
|
||||
const componentInfo = componentsMap[schema.componentName] || {};
|
||||
const componentInfo = {};
|
||||
const props = this.__parseProps(schema.props, self, '', {
|
||||
schema,
|
||||
Comp,
|
||||
@ -314,9 +312,12 @@ export default class BaseEngine extends PureComponent {
|
||||
} else if (typeof idx === 'number' && !props.key) {
|
||||
props.key = idx;
|
||||
}
|
||||
const renderComp = (props) => (
|
||||
<Comp {...props}>
|
||||
{(!isFileSchema(schema) &&
|
||||
const createElement = engine.props.customCreateElement || reactCreateElement;
|
||||
const renderComp = (props) => {
|
||||
return createElement(
|
||||
Comp,
|
||||
props,
|
||||
(!isFileSchema(schema) &&
|
||||
!!schema.children &&
|
||||
this.__createVirtualDom(
|
||||
isJSExpression(schema.children) ? parseExpression(schema.children, self) : schema.children,
|
||||
@ -326,9 +327,9 @@ export default class BaseEngine extends PureComponent {
|
||||
Comp,
|
||||
},
|
||||
)) ||
|
||||
null}
|
||||
</Comp>
|
||||
);
|
||||
null,
|
||||
);
|
||||
};
|
||||
//设计模式下的特殊处理
|
||||
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
|
||||
//对于overlay,dialog等组件为了使其在设计模式下显示,外层需要增加一个div容器
|
||||
|
||||
@ -27,17 +27,16 @@ export default class Engine extends PureComponent {
|
||||
static propTypes = {
|
||||
appHelper: PropTypes.object,
|
||||
components: PropTypes.object,
|
||||
componentsMap: PropTypes.object,
|
||||
designMode: PropTypes.string,
|
||||
suspended: PropTypes.bool,
|
||||
schema: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
|
||||
onCompGetRef: PropTypes.func,
|
||||
onCompGetCtx: PropTypes.func,
|
||||
customCreateElement: PropTypes.func,
|
||||
};
|
||||
static defaultProps = {
|
||||
appHelper: null,
|
||||
components: {},
|
||||
componentsMap: {},
|
||||
designMode: '',
|
||||
suspended: false,
|
||||
schema: {},
|
||||
@ -87,7 +86,7 @@ export default class Engine extends PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { schema, designMode, appHelper, components, componentsMap } = this.props;
|
||||
const { schema, designMode, appHelper, components } = this.props;
|
||||
if (isEmpty(schema)) {
|
||||
return null;
|
||||
}
|
||||
@ -103,7 +102,6 @@ export default class Engine extends PureComponent {
|
||||
value={{
|
||||
appHelper,
|
||||
components: allComponents,
|
||||
componentsMap,
|
||||
engine: this,
|
||||
}}
|
||||
>
|
||||
@ -112,7 +110,6 @@ export default class Engine extends PureComponent {
|
||||
ref={this.__getRef}
|
||||
__appHelper={appHelper}
|
||||
__components={allComponents}
|
||||
__componentsMap={componentsMap}
|
||||
__schema={schema}
|
||||
__designMode={designMode}
|
||||
{...this.props}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import LowCodeRenderer from '@ali/lowcode-react-renderer';
|
||||
import { ReactInstance, Fragment, Component } from 'react';
|
||||
import { ReactInstance, Fragment, Component, createElement } from 'react';
|
||||
import { observer } from '@recore/obx-react';
|
||||
import { SimulatorRenderer } from './renderer';
|
||||
import './renderer.less';
|
||||
@ -48,9 +48,11 @@ class Renderer extends Component<{ renderer: SimulatorRenderer }> {
|
||||
appHelper={renderer.context}
|
||||
// context={renderer.context}
|
||||
designMode={renderer.designMode}
|
||||
componentsMap={renderer.componentsMap}
|
||||
suspended={renderer.suspended}
|
||||
self={renderer.scope}
|
||||
customCreateElement={(Component, props, children) => {
|
||||
return createElement(Component, props, children);
|
||||
}}
|
||||
onCompGetRef={(schema: any, ref: ReactInstance | null) => {
|
||||
renderer.mountInstance(schema.id, ref);
|
||||
}}
|
||||
|
||||
@ -5,7 +5,7 @@ import MixinSetter from './mixin-setter';
|
||||
import ColorSetter from './color-setter';
|
||||
import JsonSetter from './json-setter';
|
||||
import EventsSetter from './events-setter';
|
||||
import StyleSetter from './style-setter';
|
||||
// import StyleSetter from './style-setter';
|
||||
|
||||
export const StringSetter = {
|
||||
component: Input,
|
||||
@ -29,7 +29,7 @@ export const DateYearSetter = DatePicker.YearPicker;
|
||||
export const DateMonthSetter = DatePicker.MonthPicker;
|
||||
export const DateRangeSetter = DatePicker.RangePicker;
|
||||
|
||||
export { ExpressionSetter, MixinSetter, EventsSetter, StyleSetter }
|
||||
export { ExpressionSetter, MixinSetter, EventsSetter }
|
||||
|
||||
// todo:
|
||||
export const ClassNameSetter = () => {
|
||||
@ -50,7 +50,7 @@ const builtinSetters = {
|
||||
DateMonthSetter,
|
||||
DateRangeSetter,
|
||||
EventsSetter,
|
||||
StyleSetter,
|
||||
// StyleSetter,
|
||||
ColorSetter,
|
||||
JsonSetter,
|
||||
};
|
||||
|
||||
@ -22,6 +22,8 @@ miniapp view.miniapp.xxx
|
||||
view.<device>.xxx
|
||||
通配 view.xxx
|
||||
|
||||
universal
|
||||
|
||||
规则 2
|
||||
urls: "view.js,view2 <device selector>, view3 <device selector>",
|
||||
urls: [
|
||||
@ -66,3 +68,15 @@ load simulator resources
|
||||
|
||||
|
||||
simulator 中加载资源,根据 componentsMap 构建组件查询字典,
|
||||
|
||||
|
||||
获取 view 相关的样式、脚本
|
||||
获取 proto 相关的样式
|
||||
在 simulator 中也加载一次
|
||||
|
||||
1. meta 信息构造
|
||||
2. components 字典构造, proto.getView 或者 通过 npm 信息查询
|
||||
3.
|
||||
|
||||
|
||||
componentMeta 段描述的信息,如果包含 x-prototype-urls ,那么这个 meta 信息都可以丢掉
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"entry": {
|
||||
"index": "src/demo.ts"
|
||||
"index": "src/demo/index.ts"
|
||||
},
|
||||
"vendor": false,
|
||||
"devServer": {
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
<script src="https://g.alicdn.com/mylib/moment/2.24.0/min/moment.min.js"></script>
|
||||
<link rel="stylesheet" href="https://alifd.alicdn.com/npm/@alifd/next/1.11.6/next.min.css" />
|
||||
<script src="https://unpkg.alibaba-inc.com/@alifd/next@1.18.17/dist/next.min.js"></script>
|
||||
<script src="https://g.alicdn.com/vision/visualengine-utils/4.3.1/engine-utils.js"></script>
|
||||
<!-- lowcode engine globals -->
|
||||
<link rel="stylesheet" href="https://dev.g.alicdn.com/ali-lowcode/ali-lowcode-engine/0.9.0/globals.css" />
|
||||
</head>
|
||||
|
||||
@ -365,197 +365,87 @@
|
||||
"version": "1.22.0"
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
"x-prototypes": [
|
||||
{
|
||||
"componentName": "AliVcDiv",
|
||||
"npm": {
|
||||
"package": "@ali/vc-div",
|
||||
"library": "AliVcDiv",
|
||||
"version": "1.0.1",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/proto.a264564.js"]
|
||||
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-div/1.0.1/proto.a264564.js"]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcPage",
|
||||
"npm": {
|
||||
"package": "@ali/vc-page",
|
||||
"library": "AliVcPage",
|
||||
"version": "1.0.5",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-page/1.0.5/proto.899e4b1.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-page/1.0.5/proto.bfe05a5.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcDeep",
|
||||
"npm": {
|
||||
"package": "@ali/vc-deep",
|
||||
"library": "AliVcDeep",
|
||||
"version": "2.0.11",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/proto.15be45f.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-deep/2.0.11/proto.039dc00.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcShell",
|
||||
"npm": {
|
||||
"package": "@ali/vc-shell",
|
||||
"library": "AliVcShell",
|
||||
"version": "1.5.6",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.6/proto.70bac75.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-shell/1.5.6/proto.8d105cf.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcSlot",
|
||||
"npm": {
|
||||
"package": "@ali/vc-slot",
|
||||
"library": "AliVcSlot",
|
||||
"version": "2.0.1",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/proto.0e43387.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-slot/2.0.1/proto.9e01f34.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcText",
|
||||
"npm": {
|
||||
"package": "@ali/vc-text",
|
||||
"library": "AliVcText",
|
||||
"version": "4.0.1",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.1/proto.595bd91.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-text/4.0.1/proto.90c4998.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcLink",
|
||||
"npm": {
|
||||
"package": "@ali/vc-link",
|
||||
"library": "AliVcLink",
|
||||
"version": "5.1.1",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/proto.4828821.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link/5.1.1/proto.91e063a.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcLinkBlock",
|
||||
"npm": {
|
||||
"package": "@ali/vc-link-block",
|
||||
"library": "AliVcLinkBlock",
|
||||
"version": "5.1.0",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/proto.4e9a9d2.js"]
|
||||
"urls": ["https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-link-block/5.1.0/proto.4e9a9d2.js"]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcChartColumn",
|
||||
"npm": {
|
||||
"package": "@ali/vc-chart-column",
|
||||
"library": "AliVcChartColumn",
|
||||
"version": "3.0.5",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-column/3.0.5/proto.5973a33.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-column/3.0.5/proto.df847e6.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcChartLine",
|
||||
"npm": {
|
||||
"package": "@ali/vc-chart-line",
|
||||
"library": "AliVcChartLine",
|
||||
"version": "3.0.4",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-line/3.0.4/proto.11e8b2c.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-line/3.0.4/proto.bbc1a73.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcChartPie",
|
||||
"npm": {
|
||||
"package": "@ali/vc-chart-pie",
|
||||
"library": "AliVcChartPie",
|
||||
"version": "3.0.2",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-pie/3.0.2/proto.81a7751.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-pie/3.0.2/proto.ce342e4.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcChartRadar",
|
||||
"npm": {
|
||||
"package": "@ali/vc-chart-radar",
|
||||
"library": "AliVcChartRadar",
|
||||
"version": "3.0.2",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-radar/3.0.2/proto.81a7751.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-chart-radar/3.0.2/proto.638100d.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliVcMarkdown",
|
||||
"npm": {
|
||||
"package": "@ali/vc-markdown",
|
||||
"library": "AliVcMarkdown",
|
||||
"version": "2.0.0",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/proto.3f91095.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/vc-markdown/2.0.0/proto.1bed9e5.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"componentName": "AliAcAmdpHomeConsumer",
|
||||
"npm": {
|
||||
"package": "@ali/ac-amdp-home-consumer",
|
||||
"library": "AliAcAmdpHomeConsumer",
|
||||
"version": "1.0.11",
|
||||
"destructuring": false
|
||||
},
|
||||
"props": [],
|
||||
"x-prototype-urls": [
|
||||
"urls": [
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/ac-amdp-home-consumer/1.0.11/proto.f3f24d5.css",
|
||||
"https://g.alicdn.com/legao-comp/web_bundle_0724/@ali/ac-amdp-home-consumer/1.0.11/proto.f12090f.js"
|
||||
]
|
||||
},
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"componentName": "AliReactJsonView",
|
||||
"npm": {
|
||||
@ -1,75 +1,63 @@
|
||||
import lg from '@ali/vu-logger';
|
||||
import { camelCase, find, findIndex, upperFirst } from 'lodash';
|
||||
import { ComponentClass, ReactElement, ComponentType } from 'react';
|
||||
|
||||
import { UnknownComponent } from '../../ui/placeholders';
|
||||
import Trunk, { IComponentBundle } from './trunk';
|
||||
import { ComponentClass, ComponentType } from 'react';
|
||||
import Prototype from './prototype';
|
||||
import { ComponentMeta } from '@ali/lowcode-designer';
|
||||
import { designer } from '../editor';
|
||||
|
||||
function basename(name: string) {
|
||||
return name ? (/[^\/]+$/.exec(name) || [''])[0] : '';
|
||||
}
|
||||
|
||||
function getCamelName(name: string) {
|
||||
const words = basename(name).replace(/^((vc)-)?(.+)/, '$3').split('-');
|
||||
const words = basename(name)
|
||||
.replace(/^((vc)-)?(.+)/, '$3')
|
||||
.split('-');
|
||||
return words.reduce((s, word) => s + word[0].toUpperCase() + word.substring(1), '');
|
||||
}
|
||||
|
||||
export declare interface IComponentProto {
|
||||
export interface ComponentProtoBundle {
|
||||
// @ali/vc-xxx
|
||||
name: string;
|
||||
module: Prototype;
|
||||
componentName?: string;
|
||||
category?: string;
|
||||
module: Prototype | Prototype[];
|
||||
}
|
||||
|
||||
export interface ComponentViewBundle {
|
||||
// @ali/vc-xxx
|
||||
name: string;
|
||||
// alias to property name
|
||||
componentName?: string;
|
||||
category?: string;
|
||||
module: ComponentType<any>;
|
||||
}
|
||||
|
||||
export default class Bundle {
|
||||
public static createPrototype = Prototype.create;
|
||||
public static addGlobalPropsReducer = Prototype.addGlobalPropsReducer;
|
||||
public static addGlobalPropsConfigure = Prototype.addGlobalPropsConfigure;
|
||||
public static addGlobalExtraActions = Prototype.addGlobalExtraActions;
|
||||
public static addGlobalNodeCanDragConfig = Prototype.addGlobalNodeCanDragConfig;
|
||||
public static removeGlobalPropsConfigure = Prototype.removeGlobalPropsConfigure;
|
||||
public static overridePropsConfigure = Prototype.overridePropsConfigure;
|
||||
public static create = function createBundle(
|
||||
components: Array<IComponentProto[] | IComponentProto>,
|
||||
views: IComponentBundle[], nameSpace: string) {
|
||||
return new Bundle(components, views, nameSpace);
|
||||
};
|
||||
static createPrototype = Prototype.create;
|
||||
static addGlobalPropsReducer = Prototype.addGlobalPropsReducer;
|
||||
static addGlobalPropsConfigure = Prototype.addGlobalPropsConfigure;
|
||||
static addGlobalExtraActions = Prototype.addGlobalExtraActions;
|
||||
static removeGlobalPropsConfigure = Prototype.removeGlobalPropsConfigure;
|
||||
static overridePropsConfigure = Prototype.overridePropsConfigure;
|
||||
static create(protos: ComponentProtoBundle[], views?: ComponentViewBundle[]) {
|
||||
return new Bundle(protos, views);
|
||||
}
|
||||
|
||||
/**
|
||||
* if all components are packed in a single package
|
||||
* the compositeBundle property shall be true
|
||||
*/
|
||||
public compositeBundle: boolean = false;
|
||||
private viewsMap: { [componentName: string]: ComponentType } = {};
|
||||
private registry: { [componentName: string]: Prototype } = {};
|
||||
private prototypeList: Prototype[] = [];
|
||||
|
||||
private trunk: Trunk;
|
||||
private nameSpace: string;
|
||||
private registry: { [name: string]: Prototype };
|
||||
private registryById: { [id: string]: Prototype };
|
||||
private prototypeList: Prototype[];
|
||||
|
||||
private viewMap: { [viewName: string]: ComponentClass } = {};
|
||||
private viewList: ComponentClass[] = [];
|
||||
|
||||
constructor(
|
||||
componentPrototypes: Array<IComponentProto | IComponentProto[]>,
|
||||
views: IComponentBundle[],
|
||||
nameSpace: string,
|
||||
) {
|
||||
this.nameSpace = nameSpace || '';
|
||||
this.registry = {};
|
||||
this.registryById = {};
|
||||
this.prototypeList = [];
|
||||
this.trunk = new Trunk();
|
||||
|
||||
if (Array.isArray(views)) {
|
||||
constructor(protos?: ComponentProtoBundle[], views?: ComponentViewBundle[]) {
|
||||
// 注册 prototypeView 视图
|
||||
if (views && Array.isArray(views)) {
|
||||
this.recursivelyRegisterViews(views);
|
||||
}
|
||||
componentPrototypes.forEach((item: IComponentProto) => {
|
||||
protos?.forEach((item) => {
|
||||
const prototype = item.module;
|
||||
if (prototype instanceof Prototype) {
|
||||
this.revisePrototype(item, prototype);
|
||||
const matchedView = this.viewMap[item.componentName || prototype.getComponentName()] || null;
|
||||
const componentName = item.componentName || prototype.getComponentName()!;
|
||||
const matchedView = this.viewsMap[componentName] || null;
|
||||
if (!prototype.getView() && matchedView) {
|
||||
prototype.setView(matchedView);
|
||||
}
|
||||
@ -78,137 +66,46 @@ export default class Bundle {
|
||||
this.recursivelyRegisterPrototypes(prototype, item);
|
||||
}
|
||||
});
|
||||
this.prototypeList.forEach((p, idx) => {
|
||||
if (!p.getView()) {
|
||||
p.setView(UnknownComponent);
|
||||
}
|
||||
});
|
||||
// invoke prototype mocker while the prototype does not exist
|
||||
Object.keys(this.viewMap).forEach((viewName) => {
|
||||
if (!find(this.prototypeList, (proto) => proto.getComponentName() === viewName)) {
|
||||
const mockedPrototype = this.trunk.mockComponentPrototype(this.viewMap[viewName]);
|
||||
if (mockedPrototype) {
|
||||
if (!mockedPrototype.getPackageName()) {
|
||||
mockedPrototype.setPackageName((this.viewMap[viewName] as any)._packageName_);
|
||||
}
|
||||
this.registry[viewName] = mockedPrototype;
|
||||
this.registryById[mockedPrototype.getId()] = mockedPrototype;
|
||||
this.prototypeList.push(mockedPrototype);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public addComponentBundle(bundles: Array<IComponentProto | IComponentBundle>): void;
|
||||
public addComponentBundle(bundles: any) {
|
||||
/**
|
||||
* Normal Component bundle: [ Prototype, PrototypeView ]
|
||||
* Component without Prototype.js: [ View ]
|
||||
*/
|
||||
if (bundles.length >= 2) {
|
||||
const prototype = bundles[0];
|
||||
const prototypeView = bundles[1];
|
||||
prototype.setView(prototypeView);
|
||||
this.registerPrototype(prototype);
|
||||
} else if (bundles.length === 1) {
|
||||
// Mock a Prototype for DIY Component load from async build
|
||||
const proto = this.trunk.mockComponentPrototype(bundles[0]);
|
||||
if (!proto) {
|
||||
return;
|
||||
}
|
||||
if (!proto.getView()) {
|
||||
proto.setView(bundles[0]);
|
||||
}
|
||||
this.registerPrototype(proto);
|
||||
getFromMeta(componentName: string): Prototype {
|
||||
if (this.registry[componentName]) {
|
||||
return this.registry[componentName];
|
||||
}
|
||||
const meta = designer.getComponentMeta(componentName);
|
||||
const prototype = Prototype.create(meta);
|
||||
this.prototypeList.push(prototype);
|
||||
this.registry[componentName] = prototype;
|
||||
return prototype;
|
||||
}
|
||||
|
||||
public removeComponentBundle(componentName: string) {
|
||||
const cIndex = findIndex(this.prototypeList, (proto) => proto.getComponentName() === componentName);
|
||||
const id = this.prototypeList[cIndex].getId();
|
||||
delete this.registryById[id];
|
||||
removeComponentBundle(componentName: string) {
|
||||
const cIndex = this.prototypeList.findIndex((proto) => proto.getComponentName() === componentName);
|
||||
delete this.registry[componentName];
|
||||
this.prototypeList.splice(cIndex, 1);
|
||||
}
|
||||
|
||||
public getNamespace() {
|
||||
return this.nameSpace;
|
||||
}
|
||||
|
||||
public getList() {
|
||||
getList() {
|
||||
return this.prototypeList;
|
||||
}
|
||||
|
||||
public get(componentName: string) {
|
||||
get(componentName: string) {
|
||||
return this.registry[componentName];
|
||||
}
|
||||
|
||||
public getById(id: string) {
|
||||
return this.registryById[id];
|
||||
}
|
||||
|
||||
public isCompositeBundle() {
|
||||
return this.isCompositeBundle;
|
||||
}
|
||||
|
||||
public filter(fn: (item: Prototype) => boolean) {
|
||||
this.prototypeList = this.prototypeList.filter((item) => {
|
||||
if (fn(item) === false) {
|
||||
if (this.registry[item.getComponentName()] === item) {
|
||||
delete this.registry[item.getComponentName()];
|
||||
}
|
||||
if (this.registryById[item.getId()] === item) {
|
||||
delete this.registryById[item.getId()];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public replacePrototype(componentName: string, cp: Prototype) {
|
||||
replacePrototype(componentName: string, cp: Prototype) {
|
||||
const view: any = this.get(componentName).getView();
|
||||
this.removeComponentBundle(componentName);
|
||||
this.registry[cp.getComponentName()] = cp;
|
||||
this.registryById[cp.getId()] = cp;
|
||||
this.registry[cp.getComponentName()!] = cp;
|
||||
this.prototypeList.push(cp);
|
||||
cp.setView(view);
|
||||
}
|
||||
|
||||
private recursivelyRegisterPrototypes(list: any[], cp: IComponentProto) {
|
||||
const propList: IComponentProto[] = list;
|
||||
propList.forEach((proto: IComponentProto, index: number) => {
|
||||
if (Array.isArray(proto)) {
|
||||
this.recursivelyRegisterPrototypes(proto, cp);
|
||||
return;
|
||||
}
|
||||
if (proto instanceof Prototype) {
|
||||
if (!proto.getView() && this.viewMap[proto.getComponentName()]) {
|
||||
proto.setView(this.viewMap[proto.getComponentName()]);
|
||||
}
|
||||
if (index === 0 && cp.componentName) {
|
||||
proto.setComponentName(cp.componentName);
|
||||
}
|
||||
if (cp.name && !proto.getPackageName()) {
|
||||
proto.setPackageName(cp.name);
|
||||
}
|
||||
this.registerPrototype(proto);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* register View
|
||||
* @param list ViewList
|
||||
* @param viewName
|
||||
*/
|
||||
private recursivelyRegisterViews(list: IComponentBundle[], viewName?: string): void;
|
||||
private recursivelyRegisterViews(list: any[], viewName?: string): void {
|
||||
list.forEach((item: any) => {
|
||||
if (Array.isArray(item.module)) {
|
||||
return this.recursivelyRegisterViews(item.module, item.name);
|
||||
} else if (Array.isArray(item)) {
|
||||
this.compositeBundle = true;
|
||||
return this.recursivelyRegisterViews(item, viewName);
|
||||
}
|
||||
let viewDetail: ComponentClass;
|
||||
@ -220,44 +117,51 @@ export default class Bundle {
|
||||
if (!viewDetail.displayName) {
|
||||
lg.log('ERROR_NO_PROTOTYPE_VIEW');
|
||||
lg.error('WARNING: the package without displayName is', item);
|
||||
viewDetail.displayName = upperFirst(
|
||||
// mock componentName from packageName
|
||||
camelCase((viewName || item.name).split('-').slice(1).join('-')),
|
||||
);
|
||||
viewDetail.displayName = getCamelName(viewName || item.name);
|
||||
}
|
||||
(viewDetail as any)._packageName_ = viewName || item.name;
|
||||
this.viewMap[viewDetail.displayName] = viewDetail;
|
||||
this.viewList.push(viewDetail);
|
||||
this.viewsMap[viewDetail.displayName] = viewDetail;
|
||||
});
|
||||
}
|
||||
|
||||
private revisePrototype(item: IComponentProto, prototype: Prototype) {
|
||||
const name = item.name || item.componentName;
|
||||
private recursivelyRegisterPrototypes(list: any[], cp: ComponentProtoBundle) {
|
||||
const propList: ComponentProtoBundle[] = list;
|
||||
propList.forEach((proto: ComponentProtoBundle, index: number) => {
|
||||
if (Array.isArray(proto)) {
|
||||
this.recursivelyRegisterPrototypes(proto, cp);
|
||||
return;
|
||||
}
|
||||
if (proto instanceof Prototype) {
|
||||
const componentName = proto.getComponentName()!;
|
||||
if (!proto.getView() && this.viewsMap[componentName]) {
|
||||
proto.setView(this.viewsMap[componentName]);
|
||||
}
|
||||
if (cp.name && !proto.getPackageName()) {
|
||||
proto.setPackageName(cp.name);
|
||||
}
|
||||
this.registerPrototype(proto);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private revisePrototype(item: ComponentProtoBundle, prototype: Prototype) {
|
||||
if (item.category) {
|
||||
prototype.setCategory(item.category);
|
||||
}
|
||||
if (item.name && !prototype.getPackageName()) {
|
||||
prototype.setPackageName(item.name);
|
||||
}
|
||||
if (item.componentName) {
|
||||
prototype.setComponentName(item.componentName);
|
||||
}
|
||||
if (!prototype.getComponentName()) {
|
||||
prototype.setComponentName(getCamelName(name));
|
||||
}
|
||||
}
|
||||
|
||||
private registerPrototype(prototype: Prototype) {
|
||||
if (this.registry[prototype.getComponentName()]) {
|
||||
lg.warn('WARN: override prototype', prototype, prototype.getComponentName());
|
||||
const idx = findIndex(this.prototypeList, (proto) =>
|
||||
prototype.getComponentName() === proto.getComponentName());
|
||||
const componentName = prototype.getComponentName()!;
|
||||
if (this.registry[componentName]) {
|
||||
lg.warn('WARN: override prototype', prototype, componentName);
|
||||
const idx = this.prototypeList.findIndex((proto) => componentName === proto.getComponentName());
|
||||
this.prototypeList[idx] = prototype;
|
||||
delete this.registryById[prototype.getId()];
|
||||
} else {
|
||||
this.prototypeList.push(prototype);
|
||||
}
|
||||
this.registry[prototype.getComponentName()] = prototype;
|
||||
this.registryById[prototype.getId()] = prototype;
|
||||
this.registry[componentName] = prototype;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,299 +1,47 @@
|
||||
import lg from '@ali/vu-logger';
|
||||
import { ReactElement, ComponentType } from 'react';
|
||||
import { EventEmitter } from 'events';
|
||||
import { ComponentClass } from 'react';
|
||||
|
||||
import { registerSetter } from '@ali/lowcode-globals';
|
||||
import Bundle from './bundle';
|
||||
import Prototype, { setPackages } from './prototype';
|
||||
import Bus from '../bus';
|
||||
|
||||
interface IComponentInfo {
|
||||
image?: string;
|
||||
description?: string;
|
||||
componentDetail?: string;
|
||||
newVersion?: string;
|
||||
}
|
||||
|
||||
interface IComponentLoader {
|
||||
load: (packageName: string, packageVersion: string, filePath?: string) => Promise<IComponentBundle>;
|
||||
}
|
||||
|
||||
interface IComponentPrototypeMocker {
|
||||
mockPrototype: (bundle: ComponentClass) => Prototype;
|
||||
}
|
||||
|
||||
interface IComponentBundle {
|
||||
// @ali/vc-xxx
|
||||
name: string;
|
||||
// alias to property name
|
||||
componentName?: string;
|
||||
category?: string;
|
||||
module: ComponentClass;
|
||||
}
|
||||
|
||||
interface IComponentBundleLoadingConfig {
|
||||
isDIYComponent?: boolean;
|
||||
// need to emit 'trunk_change' event
|
||||
isSilence?: boolean;
|
||||
isNpmComponent?: boolean;
|
||||
}
|
||||
|
||||
interface IComponentBundleConfigListItem {
|
||||
name: string;
|
||||
version: string;
|
||||
path?: string;
|
||||
// ac component in LeGao
|
||||
// FIXME: remove this logic out of Trunk in the future
|
||||
isDIYComponent?: boolean;
|
||||
// install comp directly from npm
|
||||
isNpmComponent?: boolean;
|
||||
}
|
||||
|
||||
interface IBeforeLoad extends IComponentBundleLoadingConfig {
|
||||
name: string;
|
||||
version: string;
|
||||
path: string;
|
||||
}
|
||||
|
||||
type beforeLoadFn = (loadingConfig: IBeforeLoad) => IBeforeLoad;
|
||||
type afterLoadFn = (bundle: IComponentBundle, loadingConfig: IBeforeLoad) => IComponentBundle;
|
||||
|
||||
class Trunk {
|
||||
private trunk: any[];
|
||||
private list: any[];
|
||||
private emitter: EventEmitter;
|
||||
private componentBundleLoader: IComponentLoader;
|
||||
private componentPrototypeMocker: IComponentPrototypeMocker;
|
||||
|
||||
private beforeLoad: beforeLoadFn;
|
||||
private afterLoad: afterLoadFn;
|
||||
|
||||
constructor() {
|
||||
this.trunk = [];
|
||||
this.emitter = new EventEmitter();
|
||||
this.componentBundleLoader = null;
|
||||
}
|
||||
export class Trunk {
|
||||
private trunk: any[] = [];
|
||||
private emitter: EventEmitter = new EventEmitter();
|
||||
private metaBundle = new Bundle();
|
||||
|
||||
isReady() {
|
||||
return this.getList().length > 0;
|
||||
}
|
||||
|
||||
addBundle(bundle: Bundle, bundleOptions: {
|
||||
before?: (bundle: Bundle) => Promise<Bundle>;
|
||||
after?: (bundle: Bundle) => any;
|
||||
} = {}) {
|
||||
// filter exsits
|
||||
bundle.filter((item) => {
|
||||
const componentName = item.getComponentName();
|
||||
if (componentName && this.getPrototype(componentName)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (bundleOptions.before) {
|
||||
bundleOptions.before.call(this, bundle).then((processedBundle: Bundle) => {
|
||||
this.trunk.push(processedBundle);
|
||||
this.emitter.emit('trunkchange');
|
||||
});
|
||||
} else {
|
||||
this.trunk.push(bundle);
|
||||
this.emitter.emit('trunkchange');
|
||||
}
|
||||
if (bundleOptions.after) {
|
||||
bundleOptions.after.call(this, bundle);
|
||||
}
|
||||
addBundle(bundle: Bundle) {
|
||||
this.trunk.push(bundle);
|
||||
this.emitter.emit('trunkchange');
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册组件信息加载器
|
||||
*/
|
||||
registerComponentBundleLoader(loader: IComponentLoader) {
|
||||
// warn replacement method
|
||||
this.componentBundleLoader = loader;
|
||||
getBundle(): Bundle {
|
||||
console.warn('Trunk.getBundle is deprecated');
|
||||
return this.trunk[0];
|
||||
}
|
||||
|
||||
registerComponentPrototypeMocker() {
|
||||
console.warn('Trunk.registerComponentPrototypeMocker is deprecated');
|
||||
getList(): any[] {
|
||||
return this.trunk.reduceRight((prev, cur) => prev.concat(cur.getList()), []);
|
||||
}
|
||||
|
||||
getBundle(nameSpace?: string): Bundle {
|
||||
console.warn('Trunk.getTrunk is deprecated');
|
||||
if (!nameSpace) {
|
||||
return this.trunk[0];
|
||||
}
|
||||
return find(this.trunk, (b: any) => b.getNamespace() === nameSpace);
|
||||
}
|
||||
|
||||
public getList(): any[] {
|
||||
return this.list || this.trunk.reduceRight((prev, cur) => prev.concat(cur.getList()), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 列出所有组件列表
|
||||
*
|
||||
*/
|
||||
listByCategory() {
|
||||
console.warn('Trunk.listByCategory is deprecated');
|
||||
const categories: any[] = [];
|
||||
const categoryMap: any = {};
|
||||
const categoryItems: any[] = [];
|
||||
const defaultCategory = {
|
||||
items: categoryItems,
|
||||
name: '*',
|
||||
};
|
||||
categories.push(defaultCategory);
|
||||
categoryMap['*'] = defaultCategory;
|
||||
this.getList().forEach((prototype) => {
|
||||
const cat = prototype.getCategory();
|
||||
if (!cat) {
|
||||
return;
|
||||
}
|
||||
if (!categoryMap.hasOwnProperty(cat)) {
|
||||
const categoryMapItems: any[] = [];
|
||||
categoryMap[cat] = {
|
||||
items: categoryMapItems,
|
||||
name: cat,
|
||||
};
|
||||
categories.push(categoryMap[cat]);
|
||||
}
|
||||
categoryMap[cat].items.push(prototype);
|
||||
});
|
||||
return categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取仓库
|
||||
*
|
||||
* @returns {Array}
|
||||
*/
|
||||
getTrunk() {
|
||||
// legao-design 中有用
|
||||
console.warn('Trunk.getTrunk is deprecated');
|
||||
return this.trunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 componentName 查找对应的 prototype
|
||||
*
|
||||
* @param {string} componentName
|
||||
* @returns {Prototype}
|
||||
*/
|
||||
getPrototype(componentName: string) {
|
||||
if (!componentName) {
|
||||
lg.error('ERROR: no component name found while get Prototype');
|
||||
return null;
|
||||
}
|
||||
const name = componentName.split('.');
|
||||
const namespace = name.length > 1 ? name[0] : '';
|
||||
let i = this.trunk.length;
|
||||
let bundle;
|
||||
let ns;
|
||||
let prototype;
|
||||
while (i-- > 0) {
|
||||
bundle = this.trunk[i];
|
||||
ns = bundle.getNamespace();
|
||||
if (ns === '' || namespace === ns) {
|
||||
prototype = bundle.get(componentName);
|
||||
if (prototype) {
|
||||
return prototype;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getPrototypeById(id: string) {
|
||||
getPrototype(name: string) {
|
||||
let i = this.trunk.length;
|
||||
let bundle;
|
||||
let prototype;
|
||||
while (i-- > 0) {
|
||||
bundle = this.trunk[i];
|
||||
prototype = bundle.getById(id);
|
||||
prototype = bundle.get(name);
|
||||
if (prototype) {
|
||||
return prototype;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return this.metaBundle.getFromMeta(name);
|
||||
}
|
||||
|
||||
public getPrototypeView(componentName: string) {
|
||||
const prototype = this.getPrototype(componentName);
|
||||
return prototype ? prototype.getView() : null;
|
||||
}
|
||||
|
||||
public loadComponentBundleList(componentBundleList: IComponentBundleConfigListItem[]) {
|
||||
Promise.all(componentBundleList.map((componentBundle) => {
|
||||
const { name, version, path, ...bundleContextInfo } = componentBundle;
|
||||
return this.loadComponentBundle(name, version, path, {
|
||||
...bundleContextInfo,
|
||||
isDIYComponent: componentBundle.isDIYComponent,
|
||||
isSilence: true,
|
||||
});
|
||||
})).then((results) => {
|
||||
results.forEach((r: any) => this.getBundle().addComponentBundle(r));
|
||||
this.emitter.emit('trunkchange');
|
||||
});
|
||||
}
|
||||
|
||||
public loadComponentBundle(
|
||||
name: string,
|
||||
version: string,
|
||||
path?: string,
|
||||
options?: IComponentBundleLoadingConfig) {
|
||||
const bundleList: IComponentBundle[] = [];
|
||||
return new Promise((resolve: any, reject: any) => {
|
||||
if (options && options.isDIYComponent) {
|
||||
let result: IBeforeLoad = { name, version, path, ...options };
|
||||
if (isFunction(this.beforeLoad)) {
|
||||
result = this.beforeLoad(result);
|
||||
}
|
||||
return this.componentBundleLoader.load(result.name, result.version, result.path)
|
||||
.then((b: IComponentBundle) => {
|
||||
if (isFunction(this.afterLoad)) {
|
||||
this.afterLoad(b, { name, path, version, ...options });
|
||||
}
|
||||
if (!options.isSilence) {
|
||||
this.getBundle().addComponentBundle([b]);
|
||||
this.emitter.emit('trunkchange');
|
||||
}
|
||||
resolve([b]);
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
Bus.emit('ve.error.networkError', e);
|
||||
reject(e);
|
||||
});
|
||||
} else {
|
||||
this.componentBundleLoader.load(name, version, 'build/prototype.js')
|
||||
.then((b: IComponentBundle) => {
|
||||
bundleList.push(b);
|
||||
return this.componentBundleLoader.load(name, version, 'build/prototypeView.js');
|
||||
})
|
||||
.then((b: IComponentBundle) => {
|
||||
bundleList.push(b);
|
||||
if (!options.isSilence) {
|
||||
this.getBundle().addComponentBundle(bundleList);
|
||||
this.emitter.emit('trunkchange');
|
||||
}
|
||||
resolve(bundleList);
|
||||
})
|
||||
.catch((e: Error) => {
|
||||
Bus.emit('ve.error.networkError', e);
|
||||
reject(e);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
removeComponentBundle(name: string) {
|
||||
this.getBundle().removeComponentBundle(name);
|
||||
this.emitter.emit('trunkchange');
|
||||
}
|
||||
|
||||
beforeLoadBundle(fn: beforeLoadFn) {
|
||||
this.beforeLoad = fn;
|
||||
}
|
||||
|
||||
afterLoadBundle(fn: afterLoadFn) {
|
||||
this.afterLoad = fn;
|
||||
getPrototypeView(componentName: string) {
|
||||
return this.getPrototype(componentName)?.getView();
|
||||
}
|
||||
|
||||
onTrunkChange(func: () => any) {
|
||||
@ -303,8 +51,25 @@ class Trunk {
|
||||
};
|
||||
}
|
||||
|
||||
setPackages(packages: Array<{ package: string; library: object | string }>) {
|
||||
setPackages(packages);
|
||||
registerSetter(type: string, setter: ReactElement | ComponentType<any>) {
|
||||
console.warn('Trunk.registerSetter is deprecated');
|
||||
registerSetter(type, setter);
|
||||
}
|
||||
|
||||
beforeLoadBundle() {
|
||||
console.warn('Trunk.beforeLoadBundle is deprecated');
|
||||
}
|
||||
|
||||
afterLoadBundle() {
|
||||
console.warn('Trunk.afterLoadBundle is deprecated');
|
||||
}
|
||||
|
||||
registerComponentPrototypeMocker() {
|
||||
console.warn('Trunk.registerComponentPrototypeMocker is deprecated');
|
||||
}
|
||||
|
||||
setPackages() {
|
||||
console.warn('Trunk.setPackages is deprecated');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,118 +1,20 @@
|
||||
/**
|
||||
* 拒绝
|
||||
*/
|
||||
export type REJECTED = 0 | false;
|
||||
/**
|
||||
* 限制性的
|
||||
*/
|
||||
export type LIMITED = 2;
|
||||
/**
|
||||
* 允许
|
||||
*/
|
||||
export type ALLOWED = true | 4;
|
||||
import { ComponentType, ReactElement, isValidElement, ComponentClass } from 'react';
|
||||
import { isI18nData } from '@ali/lowcode-globals';
|
||||
|
||||
export type HandleState = REJECTED | ALLOWED | LIMITED;
|
||||
|
||||
/*
|
||||
* model.editing:(dbclick) 父级优先(捕获过程)
|
||||
* asCode(gotocode) 默认行为 select - option
|
||||
* asRichText (运行值)
|
||||
* asPlainText (运行值) 仅包含
|
||||
* null|undefined 不响应(默认值)
|
||||
* false 禁用 阻止继续捕获
|
||||
* handle-function
|
||||
*
|
||||
* ## 检查与控制 handle
|
||||
*
|
||||
* model.shouldRemoveChild: HandleState | (my, child) => HandleState 移除子节点时(触发时),return false,拒绝移除
|
||||
* model.shouldMoveChild: HandleState | (my, child) => HandleState 移动子节点, return false: 拒绝移动; return 0: 不得改变嵌套关系
|
||||
* model.shouldRemove: HandleState | (my) => HandleState
|
||||
* model.shouldMove: HandleState | (my) => HandleState return false, 拒绝移动 return 0; 不得改变嵌套关系
|
||||
*
|
||||
* ## 类型嵌套检查 (白名单机制)
|
||||
*
|
||||
* 自定义 locate
|
||||
* model.locate: (my, transferData, mouseEvent?) => Location | null, 用于非 node 节点任意数据的定位
|
||||
*
|
||||
* test-RegExp: /^tagName./
|
||||
* test-List: 'tagName,tagName2' | ['tagName', 'tagName2']
|
||||
* test-Func: (target, my) => boolean
|
||||
* Tester: RegExp | Pattern | Func
|
||||
*
|
||||
* component.accept
|
||||
* accept: '@CHILD',从子节点寻找一个容器,针对 slot,比如 TabsLayout,ColumnsLayout, 大纲树误定位则错误信息透出,拒绝投入
|
||||
* accept: false|null 表示不是一个容器,是一个端点,比如input,option
|
||||
* accept: true 表示ok,无任何限制,比如 div,
|
||||
* accept: Tester, 表示限定接受,作为filter条件,比如 select,不接受的主视图跳过定位,大纲树定位进去后红线提示
|
||||
* model.nesting 多级过滤,错误信息透出 (nextTick异步检查),拒绝投入
|
||||
* null | undefined | false | true 未设置 | 无意义值,不作拦截
|
||||
* Tester
|
||||
* model.dropTarget
|
||||
* Tester // 实时约束
|
||||
* {
|
||||
* highlight?: Tester | boolean, // 高亮,默认false,设为true时根据 parent | ancestor 取值
|
||||
* parent?: Tester, // 实时约束,主视图限制定位,大纲树定位进去时红线提示
|
||||
* ancestor?: Tester, // 异步检查,上文检查, 设置此值时,parent 可不设置
|
||||
* }
|
||||
* '@ROOT' 只能放根节点,不高亮,异步检查
|
||||
* null | undefined | boolean 未设置|无意义值,不作拦截,不高亮
|
||||
*
|
||||
* 所有拒绝投放的,在结束时均会检查,并抖动提示原因
|
||||
*
|
||||
*
|
||||
* 1. 分栏容器嵌套栏/UL 嵌套 li 子嵌套约束
|
||||
* 2. Form 嵌套 Button, Input 后裔嵌套约束
|
||||
* 3. 数据实体 拖入 可接受目标,比如变量拖入富文本编辑器(@千緖)
|
||||
* 4. Li 拖拽时高亮所有 UL,根据Li设置的 dropTargetRules 目标规则筛选节点,取并集区域
|
||||
* 5. 能弹出提示
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
export interface BehaviorControl {
|
||||
handleMove?: HandleState | ((my: ElementNode) => HandleState);
|
||||
handleRemove?: HandleState | ((my: ElementNode) => HandleState);
|
||||
handleChildMove?: HandleState | ((my: ElementNode, child: INode) => HandleState);
|
||||
handleChildRemove?: HandleState | ((my: ElementNode, child: INode) => HandleState);
|
||||
}
|
||||
|
||||
export const AT_CHILD = Symbol.for('@CHILD');
|
||||
export const AT_ROOT = Symbol.for('@ROOT');
|
||||
export type AT_ROOT = typeof AT_ROOT;
|
||||
export type AT_CHILD = typeof AT_CHILD;
|
||||
|
||||
export type AcceptFunc = (
|
||||
my: ElementNode,
|
||||
e: LocateEvent | KeyboardEvent | MouseEvent,
|
||||
) => LocationData | INodeParent | AT_CHILD | null;
|
||||
|
||||
// should appear couple
|
||||
export interface AcceptControl {
|
||||
/**
|
||||
* MouseEvent: drag a entiy from browser out
|
||||
* KeyboardEvent: paste a entiy
|
||||
* LocateEvent: drag a entiy from pane
|
||||
*/
|
||||
handleLocate?: AcceptFunc | AT_CHILD;
|
||||
handleAccept?: (my: ElementNode, locationData: LocationData) => void;
|
||||
}
|
||||
|
||||
export interface ContentEditable {
|
||||
propTarget: string;
|
||||
selector?: string;
|
||||
}
|
||||
type Field = any;
|
||||
|
||||
export enum DISPLAY_TYPE {
|
||||
NONE = 'none',
|
||||
NONE = 'none', // => condition'plain'
|
||||
PLAIN = 'plain',
|
||||
INLINE = 'inline',
|
||||
BLOCK = 'block',
|
||||
ACCORDION = 'accordion',
|
||||
TAB = 'tab',
|
||||
TAB = 'tab', // => 'accordion'
|
||||
ENTRY = 'entry',
|
||||
}
|
||||
|
||||
export interface IPropConfig {
|
||||
// from vision 5.4
|
||||
export interface OldPropConfig {
|
||||
/**
|
||||
* composite share the namespace
|
||||
* group just be tie up together
|
||||
@ -121,32 +23,24 @@ export interface IPropConfig {
|
||||
/**
|
||||
* when type is composite or group
|
||||
*/
|
||||
items?: IPropConfig[]; // => items
|
||||
items?: OldPropConfig[]; // => items
|
||||
/**
|
||||
* property name: the field key in props of schema
|
||||
*/
|
||||
name: string; // =>
|
||||
title?: string; // =>
|
||||
tip?: {
|
||||
// =>
|
||||
title?: string;
|
||||
content?: string;
|
||||
url?: string;
|
||||
};
|
||||
initialValue?: any; // => ?
|
||||
defaultValue?: any; // =>
|
||||
defaultValue?: any; // => extraProps.defaultValue
|
||||
initialValue?: any | ((value: any, defaultValue: any) => any); // => extraProps.initialValue
|
||||
initial?: (value: any, defaultValue: any) => any // => extraProps.initialValue
|
||||
|
||||
display?: DISPLAY_TYPE; // => fieldExtraProps
|
||||
fieldStyle?: DISPLAY_TYPE; // => fieldExtraProps
|
||||
setter?: ComponentClass | ISetterConfig[] | string | SetterGetter; // =>
|
||||
/**
|
||||
* if a prop is dynamicProp, every-time while rendering setting field
|
||||
* - getValue() will not include value of getHotValue()
|
||||
* - getValue() will trigger accessor() to calculate a new value
|
||||
* this prop usually work out when we need to generate prop value
|
||||
* from node of current page
|
||||
*/
|
||||
isDynamicProp?: boolean;
|
||||
supportVariable?: boolean; // => use MixinSetter
|
||||
supportVariable?: boolean; // => use MixedSetter
|
||||
/**
|
||||
* the prop should be collapsed while display value is accordion
|
||||
*/
|
||||
@ -165,75 +59,377 @@ export interface IPropConfig {
|
||||
* will not export data to schema
|
||||
*/
|
||||
ignore?: boolean | ReturnBooleanFunction; // => ?virtualProp ? thinkof global transform
|
||||
/**
|
||||
* if a prop is declared as virtual, it will not be saved in
|
||||
* schema props, instead it will be saved into context field
|
||||
*/
|
||||
virtual?: boolean | ReturnBooleanFunction; // =>?virtualProp
|
||||
hidden?: boolean | ReturnBooleanFunction; // => condition
|
||||
/**
|
||||
* if a prop is a lifeCycle function
|
||||
*/
|
||||
lifeCycle?: boolean; // =>?
|
||||
destroy?: () => any; // => x
|
||||
initial?(this: Prop, value: any, initialValue: any): any;
|
||||
|
||||
/**
|
||||
* when use getValue(), accessor shall be called as initializer
|
||||
*/
|
||||
accessor?(this: Prop): any; // => getValue
|
||||
accessor?(this: Field, value: any): any; // => getValue
|
||||
/**
|
||||
* when current prop value mutate, the mutator function shall be called
|
||||
*/
|
||||
mutator?( // => setValue
|
||||
this: Prop,
|
||||
this: Field,
|
||||
value: any,
|
||||
/*
|
||||
hotValue: any, // => x
|
||||
preValue: any, // => x
|
||||
preHotValue: any, // => x
|
||||
*/
|
||||
): void;
|
||||
/**
|
||||
* other values' change will trigger sync function here
|
||||
*/
|
||||
sync?(this: Prop, value: any): void; // => ? autorun
|
||||
/**
|
||||
* transform runtime data between view and setter
|
||||
* @param toHotValue hot value for the setter
|
||||
* @param toViewValue static value for the view
|
||||
*/
|
||||
transformer?( // =>?
|
||||
toHotValue: (data: any) => any,
|
||||
toViewValue: (str: string) => any,
|
||||
): any;
|
||||
sync?(this: Field, value: any): void; // => autorun
|
||||
/**
|
||||
* user click var to change current field to
|
||||
* variable setting field
|
||||
*/
|
||||
useVariableChange?(data: { isUseVariable: boolean }): any; // => ?
|
||||
useVariableChange?(this: Field, data: { isUseVariable: boolean }): any; // => as MixinSetter param
|
||||
|
||||
slotName?: string;
|
||||
slotTitle?: string;
|
||||
initialChildren?: any; // schema
|
||||
allowTextInput: boolean;
|
||||
}
|
||||
|
||||
export interface SettingFieldConfig {
|
||||
type?: 'field';
|
||||
title?: string;
|
||||
name: string;
|
||||
setter: ComponentClass | ISetterConfig[] | string | SetterGetter;
|
||||
extraProps?: {
|
||||
[key: string]: any;
|
||||
// from vision 5.4
|
||||
export interface OldPrototypeConfig {
|
||||
packageName: string; // => npm.package
|
||||
/**
|
||||
* category display in the component pane
|
||||
* component will be hidden while the value is: null
|
||||
*/
|
||||
category: string; // => tags
|
||||
componentName: string; // =>
|
||||
docUrl?: string; // =>
|
||||
defaultProps?: any; // => ?
|
||||
/**
|
||||
* extra actions on the outline of current selected node
|
||||
* by default we have: remove / clone
|
||||
*/
|
||||
extraActions?: Array<ComponentType<any> | ReactElement> | (() => ReactElement); // => configure.component.actions
|
||||
title?: string; // =>
|
||||
icon?: ComponentType<any> | ReactElement; // =>
|
||||
view: ComponentType; // => ?
|
||||
initialChildren?: (props: any) => any[]; // => snippets
|
||||
|
||||
/**
|
||||
* Props configurations of node
|
||||
*/
|
||||
configure: OldPropConfig[]; // => configure.props
|
||||
snippets?: any[]; // => snippets
|
||||
transducers?: any; // => ?
|
||||
/**
|
||||
* Selector expression rectangle of a node, it is usually a querySelector string
|
||||
* @example '.classname > div'
|
||||
*/
|
||||
rectSelector?: string; // => configure.component.rectSelector
|
||||
context?: {
|
||||
// => ?
|
||||
[contextInfoName: string]: any;
|
||||
};
|
||||
|
||||
isContainer?: boolean; // => configure.component.isContainer
|
||||
isModal?: boolean; // => configure.component.isModal
|
||||
isFloating?: boolean; // => configure.component.isFloating
|
||||
descriptor?: string; // => configure.component.descriptor
|
||||
|
||||
// alias to canDragging
|
||||
canDraging?: boolean; // => onDrag
|
||||
canDragging?: boolean; // => ?
|
||||
|
||||
canOperating?: boolean; // => disabledActions
|
||||
canSelecting?: boolean;
|
||||
canContain?: (dragment: Node) => boolean; // => nestingRule
|
||||
|
||||
canDropTo?: ((container: Node) => boolean) | string | string[]; // => nestingRule
|
||||
canDropto?: (container: Node) => boolean; // => nestingRule
|
||||
|
||||
canDropIn?: ((dragment: Node) => boolean) | string | string[]; // => nestingRule
|
||||
canDroping?: (dragment: Node) => boolean; // => nestingRule
|
||||
|
||||
didDropOut?: (dragment: any, container: any) => void; // => hooks
|
||||
didDropIn?: (dragment: any, container: any) => void; // => hooks
|
||||
|
||||
/**
|
||||
* when sub-node of the current node changed
|
||||
* including: sub-node insert / remove
|
||||
*/
|
||||
subtreeModified?(this: Node): any; // => ? hooks
|
||||
|
||||
// => ?
|
||||
canResizing?: ((dragment: any, triggerDirection: string) => boolean) | boolean;
|
||||
onResizeStart?: (e: MouseEvent, triggerDirection: string, dragment: Node) => void;
|
||||
onResize?: (e: MouseEvent, triggerDirection: string, dragment: Node, moveX: number, moveY: number) => void;
|
||||
onResizeEnd?: (e: MouseEvent, triggerDirection: string, dragment: Node) => void;
|
||||
}
|
||||
|
||||
export interface SettingGroupConfig {
|
||||
type: 'group';
|
||||
title?: string;
|
||||
items: Array<SettingGroupConfig | SettingFieldConfig>;
|
||||
extraProps?: {
|
||||
[key: string]: any;
|
||||
export interface ISetterConfig {
|
||||
setter?: ComponentClass;
|
||||
// use value to decide whether this setter is available
|
||||
condition?: (value: any) => boolean;
|
||||
}
|
||||
|
||||
type SetterGetter = (this: Field, value: any) => ComponentClass;
|
||||
|
||||
type ReturnBooleanFunction = (this: Field, value: any) => boolean;
|
||||
|
||||
export function upgradePropConfig(config: OldPropConfig) {
|
||||
const {
|
||||
type,
|
||||
name,
|
||||
title,
|
||||
tip,
|
||||
slotName,
|
||||
slotTitle,
|
||||
initialChildren,
|
||||
allowTextInput,
|
||||
initialValue,
|
||||
defaultValue,
|
||||
display,
|
||||
fieldStyle,
|
||||
collapse,
|
||||
collapsed,
|
||||
fieldCollapsed,
|
||||
hidden,
|
||||
disabled,
|
||||
items,
|
||||
ignore,
|
||||
initial,
|
||||
sync,
|
||||
accessor,
|
||||
mutator,
|
||||
setter,
|
||||
useVariableChange,
|
||||
supportVariable,
|
||||
} = config;
|
||||
|
||||
const extraProps: any = {};
|
||||
const newConfig: any = {
|
||||
type: type === 'group' ? 'group' : 'field',
|
||||
name,
|
||||
title,
|
||||
extraProps,
|
||||
};
|
||||
|
||||
if (tip) {
|
||||
if (typeof title !== 'object' || isI18nData(title) || isValidElement(title)) {
|
||||
newConfig.title = {
|
||||
title,
|
||||
tip: tip.content,
|
||||
docUrl: tip.url
|
||||
};
|
||||
} else {
|
||||
newConfig.title = {
|
||||
...(title as any),
|
||||
tip: tip.content,
|
||||
docUrl: tip.url
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (display || fieldStyle) {
|
||||
extraProps.display = display || fieldStyle;
|
||||
if (extraProps.display === DISPLAY_TYPE.TAB) {
|
||||
extraProps.display = DISPLAY_TYPE.ACCORDION;
|
||||
}
|
||||
}
|
||||
|
||||
if (collapse || collapsed || fieldCollapsed) {
|
||||
extraProps.defaultCollapsed = true;
|
||||
}
|
||||
function isDisabled(field: Field) {
|
||||
if (typeof disabled === 'function') {
|
||||
return disabled.call(field, field.getValue()) === true;
|
||||
}
|
||||
return disabled === true;
|
||||
}
|
||||
function isHidden(field: Field) {
|
||||
if (typeof hidden === 'function') {
|
||||
return hidden.call(field, field.getValue()) === true;
|
||||
}
|
||||
return hidden === true;
|
||||
}
|
||||
if (extraProps.display === DISPLAY_TYPE.NONE) {
|
||||
extraProps.display = undefined;
|
||||
extraProps.condition = () => false;
|
||||
} else if (hidden != null || disabled != null) {
|
||||
extraProps.condition = (field: Field) => !(isHidden(field) || isDisabled(field));
|
||||
}
|
||||
if (ignore != null || disabled != null) {
|
||||
extraProps.virtual = (field: Field) => {
|
||||
if (isDisabled(field)) { return true; }
|
||||
|
||||
if (typeof ignore === 'function') {
|
||||
return ignore.call(field, field.getValue()) === true;
|
||||
}
|
||||
return ignore === true;
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'group') {
|
||||
newConfig.items = items ? upgradeConfigure(items) : [];
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
if (slotName) {
|
||||
newConfig.name = slotName;
|
||||
if (!newConfig.title && slotTitle) {
|
||||
newConfig.title = slotTitle;
|
||||
}
|
||||
const slotSetter = {
|
||||
componentName: 'SlotSetter',
|
||||
initialValue: () => ({
|
||||
type: 'JSSlot',
|
||||
// params:
|
||||
value: initialChildren
|
||||
}),
|
||||
}
|
||||
if (allowTextInput === false) {
|
||||
newConfig.setter = slotSetter;
|
||||
} else {
|
||||
newConfig.setter = [{
|
||||
componentName: 'StringSetter',
|
||||
initialValue,
|
||||
}, slotSetter];
|
||||
}
|
||||
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
if (defaultValue !== undefined) {
|
||||
extraProps.defaultValue = defaultValue;
|
||||
} else if (typeof initialValue !== 'function') {
|
||||
extraProps.defaultValue = initialValue;
|
||||
}
|
||||
|
||||
const initialFn = initial || initialValue;
|
||||
extraProps.initialValue = (field: Field, defaultValue?: any) => {
|
||||
if (defaultValue === undefined) {
|
||||
defaultValue = extraProps.defaultValue;
|
||||
}
|
||||
|
||||
if (typeof initialFn === 'function') {
|
||||
return initialFn(null, defaultValue);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
extraProps.autorun = (field: Field) => {
|
||||
const value = sync.call(field, field.getValue());
|
||||
if (value !== undefined) {
|
||||
field.setValue(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (accessor) {
|
||||
extraProps.getValue = (field: Field, fieldValue: any) => {
|
||||
return accessor.call(field, fieldValue);
|
||||
};
|
||||
}
|
||||
if (mutator) {
|
||||
extraProps.setValue = (field: Field, value: any) => {
|
||||
mutator.call(field, value);
|
||||
};
|
||||
}
|
||||
|
||||
let primarySetter: any;
|
||||
if (type === 'composite') {
|
||||
const objItems = items ? upgradeConfigure(items) : [];
|
||||
primarySetter = {
|
||||
componentName: 'ObjectSetter',
|
||||
props: {
|
||||
config: {
|
||||
items: objItems,
|
||||
},
|
||||
},
|
||||
initialValue: (field: Field) => {
|
||||
// FIXME: read from objItems
|
||||
return extraProps.initialValue(field, {});
|
||||
},
|
||||
};
|
||||
} else if (setter) {
|
||||
if (Array.isArray(setter)) {
|
||||
primarySetter = setter.map(({ setter, condition }) => {
|
||||
return {
|
||||
componentName: setter,
|
||||
condition: condition ? (field: Field) => {
|
||||
return condition.call(field, field.getValue());
|
||||
} : null,
|
||||
};
|
||||
});
|
||||
} else {
|
||||
primarySetter = setter;
|
||||
}
|
||||
}
|
||||
if (supportVariable) {
|
||||
if (primarySetter) {
|
||||
const setters = Array.isArray(primarySetter) ? primarySetter.concat('ExpressionSetter') : [primarySetter, 'ExpressionSetter'];
|
||||
primarySetter = {
|
||||
componentName: 'MixedSetter',
|
||||
setters,
|
||||
onSetterChange: (field: Field, name: string) => {
|
||||
if (useVariableChange) {
|
||||
useVariableChange.call(field, { isUseVariable: name === 'ExpressionSetter' });
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
primarySetter = 'ExpressionSetter';
|
||||
}
|
||||
}
|
||||
newConfig.setter = primarySetter;
|
||||
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
export function upgradeConfigure(items: OldPropConfig[]) {
|
||||
const configure = [];
|
||||
let ignoreSlotName: any = null;
|
||||
return items.forEach((config) => {
|
||||
if (config.slotName) {
|
||||
ignoreSlotName = config.slotName;
|
||||
} else if (ignoreSlotName) {
|
||||
if (config.name === ignoreSlotName) {
|
||||
ignoreSlotName = null;
|
||||
return;
|
||||
}
|
||||
ignoreSlotName = null;
|
||||
}
|
||||
configure.push(upgradePropConfig(config));
|
||||
});
|
||||
}
|
||||
|
||||
export function upgradeActions(actions?: Array<ComponentType<any> | ReactElement> | (() => ReactElement)) {
|
||||
if (!actions) {
|
||||
return null;
|
||||
}
|
||||
if (!Array.isArray(actions)) {
|
||||
actions = [actions];
|
||||
}
|
||||
return actions.map((content) => {
|
||||
const type: any = isValidElement(content) ? content.type : content;
|
||||
if (typeof content === 'function') {
|
||||
const fn = content as (() => ReactElement);
|
||||
content = (({ node }: any) => {
|
||||
fn.call(node);
|
||||
}) as any;
|
||||
}
|
||||
return {
|
||||
name: type.displayName || type.name || 'anonymous',
|
||||
content,
|
||||
important: true,
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 升级
|
||||
*/
|
||||
function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
const {
|
||||
componentName,
|
||||
docUrl,
|
||||
@ -242,11 +438,11 @@ function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
packageName,
|
||||
category,
|
||||
extraActions,
|
||||
view,
|
||||
configure,
|
||||
defaultProps,
|
||||
initialChildren,
|
||||
snippets,
|
||||
view,
|
||||
configure,
|
||||
transducers,
|
||||
isContainer,
|
||||
rectSelector,
|
||||
@ -260,16 +456,18 @@ function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
canDropto,
|
||||
canDropIn,
|
||||
canDroping,
|
||||
// handles
|
||||
canDraging, canDragging, // handleDragging
|
||||
canResizing, // handleResizing
|
||||
|
||||
// hooks
|
||||
canDraging, canDragging, // handleDragging
|
||||
// events
|
||||
didDropOut, // onNodeRemove
|
||||
didDropIn, // onNodeAdd
|
||||
subtreeModified, // onSubtreeModified
|
||||
|
||||
canResizing, // resizing
|
||||
onResizeStart, // onResizeStart
|
||||
onResize, // onResize
|
||||
onResizeEnd, // onResizeEnd
|
||||
subtreeModified, // onSubtreeModified
|
||||
} = oldConfig;
|
||||
|
||||
|
||||
@ -279,7 +477,7 @@ function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
icon,
|
||||
docUrl,
|
||||
devMode: 'procode',
|
||||
}
|
||||
};
|
||||
|
||||
if (category) {
|
||||
meta.tags = [category];
|
||||
@ -303,13 +501,7 @@ function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
component.disableBehaviors = '*';
|
||||
}
|
||||
if (extraActions) {
|
||||
component.actions = extraActions.map((content) => {
|
||||
return {
|
||||
name: content.displayName || content.name || 'anonymous',
|
||||
content,
|
||||
important: true,
|
||||
};
|
||||
});
|
||||
component.actions = upgradeActions(extraActions);
|
||||
}
|
||||
const nestingRule: any = {};
|
||||
if (canContain) {
|
||||
@ -323,97 +515,98 @@ function upgradeMetadata(oldConfig: OldPrototypeConfig) {
|
||||
}
|
||||
component.nestingRule = nestingRule;
|
||||
|
||||
if (canDragging || canDraging) {
|
||||
// hooks|handle
|
||||
}
|
||||
|
||||
// 未考虑清楚的,放在实验性段落
|
||||
const experimental: any = {};
|
||||
if (context) {
|
||||
// for prototype.getContextInfo
|
||||
experimental.context = context;
|
||||
}
|
||||
if (snippets) {
|
||||
experimental.snippets = snippets;
|
||||
}
|
||||
if (defaultProps || initialChildren) {
|
||||
const snippet = {
|
||||
screenshot: icon,
|
||||
label: title,
|
||||
schema: {
|
||||
componentName,
|
||||
props: defaultProps,
|
||||
children: initialChildren,
|
||||
},
|
||||
};
|
||||
if (experimental.snippets) {
|
||||
experimental.snippets.push(snippet);
|
||||
} else {
|
||||
experimental.snippets = [snippet];
|
||||
}
|
||||
}
|
||||
if (view) {
|
||||
experimental.view = view;
|
||||
}
|
||||
if (transducers) {
|
||||
// Array<{ toStatic, toNative }>
|
||||
// ? only twice
|
||||
experimental.transducers = transducers;
|
||||
}
|
||||
if (canResizing) {
|
||||
// TODO: enhance
|
||||
experimental.getResizingHandlers = (currentNode: any) => {
|
||||
const directs = ['n', 'w', 's', 'e'];
|
||||
if (canResizing === true) {
|
||||
return directs;
|
||||
}
|
||||
return directs.filter((d) => canResizing(currentNode, d));
|
||||
};
|
||||
}
|
||||
|
||||
const props = {};
|
||||
const styles = {};
|
||||
const events = {};
|
||||
meta.configure = { props, component, styles, events, experimental };
|
||||
const callbacks: any = {};
|
||||
if (canDragging != null || canDraging != null) {
|
||||
let v = true;
|
||||
if (canDragging === false || canDraging === false) {
|
||||
v = false;
|
||||
}
|
||||
callbacks.onMoveHook = () => v;
|
||||
}
|
||||
if (didDropIn) {
|
||||
callbacks.onNodeAdd = didDropIn;
|
||||
}
|
||||
if (didDropOut) {
|
||||
callbacks.onNodeRemove = didDropOut;
|
||||
}
|
||||
if (subtreeModified) {
|
||||
callbacks.onSubtreeModified = (...args: any[]) => {
|
||||
// FIXME! args not correct
|
||||
subtreeModified.apply(args[0], args as any);
|
||||
};
|
||||
}
|
||||
if (onResize) {
|
||||
callbacks.onResize = (e: any, currentNode: any) => {
|
||||
// todo: what is trigger?
|
||||
const { trigger, deltaX, deltaY } = e;
|
||||
onResize(e, trigger, currentNode, deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
if (onResizeStart) {
|
||||
callbacks.onResizeStart = (e: any, currentNode: any) => {
|
||||
// todo: what is trigger?
|
||||
const { trigger } = e;
|
||||
onResizeStart(e, trigger, currentNode);
|
||||
}
|
||||
}
|
||||
if (onResizeEnd) {
|
||||
callbacks.onResizeEnd = (e: any, currentNode: any) => {
|
||||
// todo: what is trigger?
|
||||
const { trigger } = e;
|
||||
onResizeEnd(e, trigger, currentNode);
|
||||
}
|
||||
}
|
||||
|
||||
experimental.callbacks = callbacks;
|
||||
|
||||
const props = upgradeConfigure(configure || []);
|
||||
meta.configure = { props, component };
|
||||
meta.experimental = experimental;
|
||||
return meta;
|
||||
}
|
||||
|
||||
export interface OldPrototypeConfig {
|
||||
packageName: string; // => npm.package
|
||||
/**
|
||||
* category display in the component pane
|
||||
* component will be hidden while the value is: null
|
||||
*/
|
||||
category: string; // => tags
|
||||
componentName: string; // =>
|
||||
docUrl?: string; // =>
|
||||
defaultProps?: any; // => ?
|
||||
/**
|
||||
* extra actions on the outline of current selected node
|
||||
* by default we have: remove / clone
|
||||
*/
|
||||
extraActions?: Component[]; // => configure.component.actions
|
||||
title?: string; // =>
|
||||
icon?: Component; // =>
|
||||
view: Component; // => ?
|
||||
initialChildren?: (props: any) => any[]; // => snippets
|
||||
|
||||
/**
|
||||
* Props configurations of node
|
||||
*/
|
||||
configure: IPropConfig[]; // => configure.props
|
||||
snippets?: ISnippet[]; // => snippets
|
||||
transducers?: any; // => ?
|
||||
/**
|
||||
* Selector expression rectangle of a node, it is usually a querySelector string
|
||||
* @example '.classname > div'
|
||||
*/
|
||||
rectSelector?: string; // => configure.component.rectSelector
|
||||
context?: {
|
||||
// => ?
|
||||
[contextInfoName: string]: any;
|
||||
};
|
||||
|
||||
isContainer?: boolean; // => configure.component.isContainer
|
||||
isModal?: boolean; // => configure.component.isModal
|
||||
isFloating?: boolean; // => configure.component.isFloating
|
||||
descriptor?: string; // => configure.component.descriptor
|
||||
|
||||
/**
|
||||
* enable slot-mode
|
||||
* @see https://yuque.antfin-inc.com/legao/solutions/atgtdl
|
||||
*/
|
||||
hasSlot?: boolean; // => ?
|
||||
|
||||
// alias to canDragging
|
||||
canDraging?: boolean; // => onDrag
|
||||
canDragging?: boolean; // => ?
|
||||
|
||||
canOperating?: boolean; // => disabledActions
|
||||
canSelecting?: boolean;
|
||||
canContain?: (dragment: Node) => boolean; // => nestingRule
|
||||
|
||||
canDropTo?: ((container: Node) => boolean) | string | string[]; // => nestingRule
|
||||
canDropto?: (container: Node) => boolean; // => nestingRule
|
||||
|
||||
canDropIn?: ((dragment: Node) => boolean) | string | string[]; // => nestingRule
|
||||
canDroping?: (dragment: Node) => boolean; // => nestingRule
|
||||
|
||||
didDropOut?: (container: any | Prototype, dragment: any) => boolean; // => hooks
|
||||
didDropIn?: (container: any | Prototype, dragment: any) => boolean; // => hooks
|
||||
|
||||
// => ?
|
||||
canResizing?: ((dragment: Node, triggerDirection: string) => boolean) | boolean;
|
||||
onResizeStart?: (e: MouseEvent, triggerDirection: string, dragment: Node) => void;
|
||||
onResize?: (e: MouseEvent, triggerDirection: string, dragment: Node, moveX: number, moveY: number) => void;
|
||||
onResizeEnd?: (e: MouseEvent, triggerDirection: string, dragment: Node) => void;
|
||||
|
||||
/**
|
||||
* when sub-node of the current node changed
|
||||
* including: sub-node insert / remove
|
||||
*/
|
||||
subtreeModified?(this: Node): any; // => ? hooks
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ import { EventEmitter } from 'events';
|
||||
/**
|
||||
* Bus class as an EventEmitter
|
||||
*/
|
||||
class Bus {
|
||||
export class Bus {
|
||||
private emitter = new EventEmitter();
|
||||
|
||||
getEmitter() {
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
import { init } from './vision'; // VisualEngine
|
||||
import { editor } from './editor';
|
||||
|
||||
init();
|
||||
|
||||
load();
|
||||
|
||||
async function load() {
|
||||
const assets = await editor.utils.get('./assets.json');
|
||||
editor.set('assets', assets);
|
||||
editor.emit('assets.loaded', assets);
|
||||
|
||||
const schema = await editor.utils.get('./schema.json');
|
||||
editor.set('schema', schema);
|
||||
editor.emit('schema.loaded', schema);
|
||||
}
|
||||
55
packages/vision-polyfill/src/demo/index.ts
Normal file
55
packages/vision-polyfill/src/demo/index.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import Engine from '../vision'; // VisualEngine
|
||||
import { editor } from '../editor';
|
||||
import loadUrls from './loader';
|
||||
|
||||
Engine.init();
|
||||
|
||||
load();
|
||||
|
||||
async function load() {
|
||||
await loadAssets();
|
||||
|
||||
loadSchema();
|
||||
}
|
||||
|
||||
const externals = [
|
||||
'react',
|
||||
'react-dom',
|
||||
'prop-types',
|
||||
'react-router',
|
||||
'react-router-dom',
|
||||
'@ali/recore',
|
||||
];
|
||||
async function loadAssets() {
|
||||
const assets = await editor.utils.get('./legao-assets.json');
|
||||
// Trunk.setPackages(assets.packages);
|
||||
|
||||
if (assets.packages) {
|
||||
assets.packages.forEach((item: any) => {
|
||||
if (item.package.indexOf('@ali/vc-') === 0 && item.urls) {
|
||||
item.urls = item.urls.filter((url: string) => {
|
||||
return url.indexOf('view.mobile') < 0
|
||||
});
|
||||
} else if (item.package && externals.indexOf(item.package) > -1) {
|
||||
item.urls = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (assets['x-prototypes']) {
|
||||
const tasks: Array<Promise<any>> = [];
|
||||
assets['x-prototypes'].forEach((pkg: any) => {
|
||||
tasks.push(loadUrls(pkg?.urls));
|
||||
});
|
||||
await Promise.all(tasks);
|
||||
|
||||
// proccess snippets
|
||||
}
|
||||
|
||||
editor.set('assets', assets);
|
||||
}
|
||||
|
||||
async function loadSchema() {
|
||||
const schema = await editor.utils.get('./schema.json');
|
||||
editor.set('schema', schema);
|
||||
}
|
||||
171
packages/vision-polyfill/src/demo/loader.js
Normal file
171
packages/vision-polyfill/src/demo/loader.js
Normal file
@ -0,0 +1,171 @@
|
||||
function getStylePoint(id, level) {
|
||||
if (stylePointTable[id]) {
|
||||
return stylePointTable[id];
|
||||
}
|
||||
|
||||
const base = getBasePoint();
|
||||
|
||||
if (id === 'base') {
|
||||
return base;
|
||||
}
|
||||
|
||||
const point = new StylePoint(id, level || 2000);
|
||||
if (level >= base.level) {
|
||||
let prev = base;
|
||||
let next = prev.next;
|
||||
while (next && level >= next.level) {
|
||||
prev = next;
|
||||
next = prev.next;
|
||||
}
|
||||
prev.next = point;
|
||||
point.prev = prev;
|
||||
if (next) {
|
||||
point.next = next;
|
||||
next.prev = point;
|
||||
}
|
||||
} else {
|
||||
let next = base;
|
||||
let prev = next.prev;
|
||||
while (prev && level < prev.level) {
|
||||
next = prev;
|
||||
prev = next.prev;
|
||||
}
|
||||
next.prev = point;
|
||||
point.next = next;
|
||||
if (prev) {
|
||||
point.prev = prev;
|
||||
prev.next = point;
|
||||
}
|
||||
}
|
||||
point.insert();
|
||||
stylePointTable[id] = point;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
const stylePointTable = {};
|
||||
|
||||
function getBasePoint() {
|
||||
if (!stylePointTable.base) {
|
||||
stylePointTable.base = new StylePoint('base', 1000);
|
||||
stylePointTable.base.insert();
|
||||
}
|
||||
return stylePointTable.base;
|
||||
}
|
||||
|
||||
class StylePoint {
|
||||
constructor(id, level, placeholder) {
|
||||
this.lastContent = null;
|
||||
this.lastUrl = null;
|
||||
this.next = null;
|
||||
this.prev = null;
|
||||
this.id = id;
|
||||
this.level = level;
|
||||
if (placeholder) {
|
||||
this.placeholder = placeholder;
|
||||
} else {
|
||||
this.placeholder = document.createTextNode('');
|
||||
}
|
||||
}
|
||||
|
||||
insert() {
|
||||
if (this.next) {
|
||||
document.head.insertBefore(this.placeholder, this.next.placeholder);
|
||||
} else if (this.prev) {
|
||||
document.head.insertBefore(this.placeholder, this.prev.placeholder.nextSibling);
|
||||
} else {
|
||||
document.head.appendChild(this.placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
applyText(content) {
|
||||
if (this.lastContent === content) {
|
||||
return;
|
||||
}
|
||||
this.lastContent = content;
|
||||
this.lastUrl = undefined;
|
||||
const element = document.createElement('style');
|
||||
element.setAttribute('type', 'text/css');
|
||||
element.setAttribute('data-for', this.id);
|
||||
element.appendChild(document.createTextNode(content));
|
||||
document.head.insertBefore(element, this.placeholder);
|
||||
document.head.removeChild(this.placeholder);
|
||||
this.placeholder = element;
|
||||
}
|
||||
|
||||
applyUrl(url) {
|
||||
if (this.lastUrl === url) {
|
||||
return;
|
||||
}
|
||||
this.lastContent = undefined;
|
||||
this.lastUrl = url;
|
||||
const element = document.createElement('link');
|
||||
element.href = url;
|
||||
element.rel = 'stylesheet';
|
||||
element.setAttribute('data-for', this.id);
|
||||
document.head.insertBefore(element, this.placeholder);
|
||||
document.head.removeChild(this.placeholder);
|
||||
this.placeholder = element;
|
||||
}
|
||||
}
|
||||
|
||||
function loadCSS(url) {
|
||||
getStylePoint(url).applyUrl(url);
|
||||
}
|
||||
|
||||
function isCSSUrl(url) {
|
||||
return /\.css$/.test(url);
|
||||
}
|
||||
|
||||
function loadScript(url) {
|
||||
const node = document.createElement('script');
|
||||
|
||||
// node.setAttribute('crossorigin', 'anonymous');
|
||||
|
||||
node.onload = onload;
|
||||
node.onerror = onload;
|
||||
|
||||
const i = {};
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
i.resolve = resolve;
|
||||
i.reject = reject;
|
||||
});
|
||||
|
||||
function onload(e) {
|
||||
node.onload = null;
|
||||
node.onerror = null;
|
||||
if (e.type === 'load') {
|
||||
i.resolve();
|
||||
} else {
|
||||
i.reject();
|
||||
}
|
||||
// document.head.removeChild(node);
|
||||
// node = null;
|
||||
}
|
||||
|
||||
// node.async = true;
|
||||
node.src = url;
|
||||
|
||||
document.head.appendChild(node);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
export default function loadUrls(urls) {
|
||||
if (!urls || urls.length < 1) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let promise = null;
|
||||
urls.forEach((url) => {
|
||||
if (isCSSUrl(url)) {
|
||||
loadCSS(url);
|
||||
} else if (!promise) {
|
||||
promise = loadScript(url);
|
||||
} else {
|
||||
promise = promise.then(() => loadScript(url));
|
||||
}
|
||||
});
|
||||
|
||||
return promise || Promise.resolve();
|
||||
}
|
||||
@ -1,11 +1,11 @@
|
||||
import { globalContext } from '@ali/lowcode-globals';
|
||||
import Editor from '@ali/lowcode-editor-core';
|
||||
import { Designer } from '@ali/lowcode-designer';
|
||||
import { registerSetters } from '@ali/lowcode-setters';
|
||||
import OutlinePane from '@ali/lowcode-plugin-outline-pane';
|
||||
import SettingsPane from '@ali/lowcode-plugin-settings-pane';
|
||||
import DesignerView from '@ali/lowcode-plugin-designer';
|
||||
import { registerSetters } from '@ali/lowcode-setters';
|
||||
import { Skeleton } from './skeleton/skeleton';
|
||||
import { Designer } from 'designer/src/designer';
|
||||
import { globalContext } from '@ali/lowcode-globals';
|
||||
|
||||
registerSetters();
|
||||
|
||||
@ -41,11 +41,3 @@ skeleton.leftArea.add({
|
||||
area: 'leftFixedArea',
|
||||
},
|
||||
});
|
||||
|
||||
// editor-core
|
||||
// 1. di 实现
|
||||
// 2. general bus: pub/sub
|
||||
// editor-skeleton/workbench 视图实现
|
||||
// 1. skeleton 区域划分 panes
|
||||
// provide fixed left pane
|
||||
// provide float left pane
|
||||
|
||||
@ -6,12 +6,15 @@ import { createElement } from 'react';
|
||||
import { VE_EVENTS as EVENTS, VE_HOOKS as HOOKS } from './const';
|
||||
import Bus from './bus';
|
||||
import Symbols from './symbols';
|
||||
import { editor, skeleton } from './editor';
|
||||
import { skeleton } from './editor';
|
||||
import { VisionWorkbench } from './skeleton/workbench';
|
||||
import Panes from './panes';
|
||||
import Exchange from './exchange';
|
||||
import VisualEngineContext from './context';
|
||||
import VisualManager from './base/visualManager';
|
||||
import Trunk from './bundle/trunk';
|
||||
import Prototype from './bundle/prototype';
|
||||
import Bundle from './bundle/bundle';
|
||||
|
||||
function init(container?: Element) {
|
||||
if (!container) {
|
||||
@ -40,7 +43,7 @@ const modules = {
|
||||
|
||||
const context = new VisualEngineContext();
|
||||
|
||||
export {
|
||||
const VisualEngine = {
|
||||
/**
|
||||
* VE.Popup
|
||||
*/
|
||||
@ -68,9 +71,14 @@ export {
|
||||
ui,
|
||||
Panes,
|
||||
modules,
|
||||
Trunk,
|
||||
Prototype,
|
||||
Bundle,
|
||||
};
|
||||
|
||||
export default VisualEngine;
|
||||
|
||||
(window as any).VisualEngine = VisualEngine;
|
||||
/*
|
||||
console.log(
|
||||
`%cLowcodeEngine %cv${VERSION}`,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user