1026 lines
32 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable no-console */
/* eslint-disable max-len */
/* eslint-disable react/prop-types */
import classnames from 'classnames';
import { create as createDataSourceEngine } from '@alilc/lowcode-datasource-engine/interpret';
import { isI18nData, isJSExpression, isJSFunction, NodeSchema, NodeData, JSONValue, CompositeValue } from '@alilc/lowcode-types';
import adapter from '../adapter';
import divFactory from '../components/Div';
import visualDomFactory from '../components/VisualDom';
import contextFactory from '../context';
import {
forEach,
getValue,
parseData,
parseExpression,
parseThisRequiredExpression,
parseI18n,
isEmpty,
isSchema,
isFileSchema,
transformArrayToMap,
transformStringToFunction,
checkPropTypes,
getI18n,
canAcceptsRef,
getFileCssName,
capitalizeFirstLetter,
DataHelper,
isVariable,
isJSSlot,
} from '../utils';
import { IBaseRendererProps, IInfo, IBaseRenderComponent, IBaseRendererContext, IGeneralConstructor, IRendererAppHelper, DataSource } from '../types';
import { compWrapper } from '../hoc';
import { IComponentConstruct, IComponentHoc, leafWrapper } from '../hoc/leaf';
import logger from '../utils/logger';
import isUseLoop from '../utils/is-use-loop';
export default function baseRendererFactory(): IBaseRenderComponent {
const { BaseRenderer: customBaseRenderer } = adapter.getRenderers();
if (customBaseRenderer) {
return customBaseRenderer;
}
const runtime = adapter.getRuntime();
const Component = runtime.Component as IGeneralConstructor<
IBaseRendererProps,
Record<string, any>,
any
>;
const { createElement } = runtime;
const Div = divFactory();
const VisualDom = visualDomFactory();
const AppContext = contextFactory();
const DESIGN_MODE = {
EXTEND: 'extend',
BORDER: 'border',
PREVIEW: 'preview',
};
const OVERLAY_LIST = ['Dialog', 'Overlay', 'Animate', 'ConfigProvider'];
let scopeIdx = 0;
return class BaseRenderer extends Component {
static displayName = 'base-renderer';
static defaultProps = {
__schema: {},
};
static contextType = AppContext;
__namespace = 'base';
appHelper?: IRendererAppHelper;
__compScopes: Record<string, any> = {};
__instanceMap: Record<string, any> = {};
__dataHelper: any;
__customMethodsList: any[] = [];
dataSourceMap: Record<string, any> = {};
__ref: any;
i18n: any;
getLocale: any;
setLocale: any;
/**
* reference of style element contains schema.css
*
* @type {any}
*/
styleElement: any;
parseExpression: any;
[key: string]: any;
constructor(props: IBaseRendererProps, context: IBaseRendererContext) {
super(props, context);
this.context = context;
this.parseExpression = props?.thisRequiredInJSE ? parseThisRequiredExpression : parseExpression;
this.__beforeInit(props);
this.__init(props);
this.__afterInit(props);
this.__debug(`constructor - ${props?.__schema?.fileName}`);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
__beforeInit(_props: IBaseRendererProps) { }
__init(props: IBaseRendererProps) {
this.appHelper = props.__appHelper;
this.__compScopes = {};
this.__instanceMap = {};
this.__bindCustomMethods(props);
this.__initI18nAPIs(props);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
__afterInit(_props: IBaseRendererProps) { }
static getDerivedStateFromProps(props: IBaseRendererProps, state: any) {
logger.log('getDerivedStateFromProps');
const func = props?.__schema?.lifeCycles?.getDerivedStateFromProps;
if (func) {
if (isJSExpression(func) || isJSFunction(func)) {
const fn = props.thisRequiredInJSE ? parseThisRequiredExpression(func, this) : parseExpression(func, this);
return fn?.(props, state);
}
if (typeof func === 'function') {
// eslint-disable-next-line @typescript-eslint/ban-types
return (func as Function)(props, state);
}
}
return null;
}
async getSnapshotBeforeUpdate(...args: any[]) {
this.__excuteLifeCycleMethod('getSnapshotBeforeUpdate', args);
this.__debug(`getSnapshotBeforeUpdate - ${this.props?.__schema?.fileName}`);
}
async componentDidMount(...args: any[]) {
this.reloadDataSource();
this.__excuteLifeCycleMethod('componentDidMount', args);
this.__debug(`componentDidMount - ${this.props?.__schema?.fileName}`);
}
async componentDidUpdate(...args: any[]) {
this.__excuteLifeCycleMethod('componentDidUpdate', args);
this.__debug(`componentDidUpdate - ${this.props.__schema.fileName}`);
}
async componentWillUnmount(...args: any[]) {
this.__excuteLifeCycleMethod('componentWillUnmount', args);
this.__debug(`componentWillUnmount - ${this.props?.__schema?.fileName}`);
}
async componentDidCatch(...args: any[]) {
this.__excuteLifeCycleMethod('componentDidCatch', args);
console.warn(args);
}
reloadDataSource = () => new Promise((resolve, reject) => {
this.__debug('reload data source');
if (!this.__dataHelper) {
return resolve({});
}
this.__dataHelper.getInitData()
.then((res: any) => {
if (isEmpty(res)) {
this.forceUpdate();
return resolve({});
}
this.setState(res, resolve as () => void);
})
.catch((err: Error) => {
reject(err);
});
});
shouldComponentUpdate() {
if (this.props.getSchemaChangedSymbol?.() && this.props.__container?.rerender) {
this.props.__container?.rerender();
return false;
}
return true;
}
forceUpdate() {
if (this.shouldComponentUpdate()) {
super.forceUpdate();
}
}
/**
* execute method in schema.lifeCycles
* @PRIVATE
*/
__excuteLifeCycleMethod = (method: string, args?: any) => {
const lifeCycleMethods = getValue(this.props.__schema, 'lifeCycles', {});
let fn = lifeCycleMethods[method];
if (fn) {
// TODO: cache
if (isJSExpression(fn) || isJSFunction(fn)) {
fn = this.parseExpression(fn, this);
}
if (typeof fn !== 'function') {
console.error(`生命周期${method}类型不符`, fn);
return;
}
try {
return fn.apply(this, args);
} catch (e) {
console.error(`[${this.props.__schema.componentName}]生命周期${method}出错`, e);
}
}
};
/**
* this method is for legacy purpose only, which used _ prefix instead of __ as private for some historical reasons
* @LEGACY
*/
_getComponentView = (componentName: string) => {
const { __components } = this.props;
if (!__components) {
return;
}
return __components[componentName];
};
__bindCustomMethods = (props: IBaseRendererProps) => {
const { __schema } = props;
const customMethodsList = Object.keys(__schema.methods || {}) || [];
this.__customMethodsList
&& this.__customMethodsList.forEach((item: any) => {
if (!customMethodsList.includes(item)) {
delete this[item];
}
});
this.__customMethodsList = customMethodsList;
forEach(__schema.methods, (val: any, key: string) => {
let value = val;
if (isJSExpression(value) || isJSFunction(value)) {
value = this.parseExpression(value, this);
}
if (typeof value !== 'function') {
console.error(`自定义函数${key}类型不符`, value);
return;
}
this[key] = value.bind(this);
});
};
__generateCtx = (ctx: Record<string, any>) => {
const { pageContext, compContext } = this.context;
const obj = {
page: pageContext,
component: compContext,
...ctx,
};
forEach(obj, (val: any, key: string) => {
this[key] = val;
});
};
__parseData = (data: any, ctx?: Record<string, any>) => {
const { __ctx, thisRequiredInJSE } = this.props;
return parseData(data, ctx || __ctx || this, { thisRequiredInJSE });
};
__initDataSource = (props: IBaseRendererProps) => {
if (!props) {
return;
}
const schema = props.__schema || {};
const defaultDataSource: DataSource = {
list: [],
};
const dataSource = schema.dataSource || defaultDataSource;
// requestHandlersMap 存在才走数据源引擎方案
const useDataSourceEngine = !!(props.__appHelper?.requestHandlersMap);
if (useDataSourceEngine) {
this.__dataHelper = {
updateConfig: (updateDataSource: any) => {
const { dataSourceMap, reloadDataSource } = createDataSourceEngine(
updateDataSource ?? {},
this,
props.__appHelper.requestHandlersMap ? { requestHandlersMap: props.__appHelper.requestHandlersMap } : undefined,
);
this.reloadDataSource = () => new Promise((resolve) => {
this.__debug('reload data source');
reloadDataSource().then(() => {
resolve({});
});
});
return dataSourceMap;
},
};
this.dataSourceMap = this.__dataHelper.updateConfig(dataSource);
} else {
const appHelper = props.__appHelper;
this.__dataHelper = new DataHelper(this, dataSource, appHelper, (config: any) => this.__parseData(config));
this.dataSourceMap = this.__dataHelper.dataSourceMap;
this.reloadDataSource = () => new Promise((resolve, reject) => {
this.__debug('reload data source');
if (!this.__dataHelper) {
return resolve({});
}
this.__dataHelper.getInitData()
.then((res: any) => {
if (isEmpty(res)) {
this.forceUpdate();
return resolve({});
}
this.setState(res, resolve as () => void);
})
.catch((err: Error) => {
reject(err);
});
});
}
};
/**
* init i18n apis
* @PRIVATE
*/
__initI18nAPIs = (props: IBaseRendererProps) => {
this.i18n = (key: string, values = {}) => {
const { locale, messages } = props;
return getI18n(key, values, locale, messages);
};
this.getLocale = () => props.locale;
this.setLocale = (loc: string) => {
const setLocaleFn = this.appHelper?.utils?.i18n?.setLocale;
if (!setLocaleFn || typeof setLocaleFn !== 'function') {
console.warn('initI18nAPIs Failed, i18n only works when appHelper.utils.i18n.setLocale() exists');
return undefined;
}
return setLocaleFn(loc);
};
};
/**
* write props.__schema.css to document as a style element,
* which will be added once and only once.
* @PRIVATE
*/
__writeCss = (props: IBaseRendererProps) => {
const css = getValue(props.__schema, 'css', '');
this.__debug('create this.styleElement with css', css);
let style = this.styleElement;
if (!this.styleElement) {
style = document.createElement('style');
style.type = 'text/css';
style.setAttribute('from', 'style-sheet');
const head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(style);
this.styleElement = style;
this.__debug('this.styleElement is created', this.styleElement);
}
if (style.innerHTML === css) {
return;
}
style.innerHTML = css;
};
__render = () => {
const schema = this.props.__schema;
this.__excuteLifeCycleMethod('render');
this.__writeCss(this.props);
const { engine } = this.context;
if (engine) {
engine.props.onCompGetCtx(schema, this);
// 画布场景才需要每次渲染bind自定义方法
if (engine.props.designMode === 'design') {
this.__bindCustomMethods(this.props);
this.dataSourceMap = this.__dataHelper?.updateConfig(schema.dataSource);
}
}
};
__getRef = (ref: any) => {
const { engine } = this.context;
const { __schema } = this.props;
ref && engine?.props?.onCompGetRef(__schema, ref);
this.__ref = ref;
};
__getSchemaChildren = (schema: NodeSchema | undefined) => {
if (!schema) {
return;
}
if (!schema.props) {
return schema.children;
}
if (!schema.children) {
return schema.props.children;
}
if (!schema.props.children) {
return schema.children;
}
let _children = ([] as NodeData[]).concat(schema.children);
if (Array.isArray(schema.props.children)) {
_children = _children.concat(schema.props.children);
} else {
_children.push(schema.props.children);
}
return _children;
};
__createDom = () => {
const { __schema, __ctx, __components = {} } = this.props;
const scope: any = {};
scope.__proto__ = __ctx || this;
const _children = this.__getSchemaChildren(__schema);
let Comp = __components[__schema.componentName];
if (!Comp) {
this.__debug(`${__schema.componentName} is invalid!`);
}
return this.__createVirtualDom(_children, scope, ({
schema: __schema,
Comp: this.__getHocComp(Comp, __schema, scope),
} as IInfo));
};
/**
* 将模型结构转换成react Element
* @param originalSchema schema
* @param originalScope scope
* @param parentInfo 父组件的信息包含schema和Comp
* @param idx 为循环渲染的循环Index
*/
__createVirtualDom = (originalSchema: NodeData | NodeData[] | undefined, originalScope: any, parentInfo: IInfo, idx: string | number = ''): any => {
if (!originalSchema) {
return null;
}
let scope = originalScope;
let schema = originalSchema;
const { engine } = this.context || {};
if (!engine) {
this.__debug('this.context.engine is invalid!');
return null;
}
try {
const { __appHelper: appHelper, __components: components = {} } = this.props || {};
if (isJSExpression(schema)) {
return this.parseExpression(schema, scope);
}
if (isI18nData(schema)) {
return parseI18n(schema, scope);
}
if (isJSSlot(schema)) {
return this.__createVirtualDom(schema.value, scope, parentInfo);
}
if (typeof schema === 'string') {
return schema;
}
if (typeof schema === 'number' || typeof schema === 'boolean') {
return String(schema);
}
if (Array.isArray(schema)) {
if (schema.length === 1) {
return this.__createVirtualDom(schema[0], scope, parentInfo);
}
return schema.map((item, idy) => this.__createVirtualDom(item, scope, parentInfo, (item as NodeSchema)?.__ctx?.lceKey ? '' : String(idy)));
}
const _children = this.__getSchemaChildren(schema);
// 解析占位组件
if (schema.componentName === 'Fragment' && _children) {
const tarChildren = isJSExpression(_children) ? this.parseExpression(_children, scope) : _children;
return this.__createVirtualDom(tarChildren, scope, parentInfo);
}
if (schema.componentName === 'Text' && typeof schema.props?.text === 'string') {
const text: string = schema.props?.text;
schema = { ...schema };
schema.children = [text];
}
// @ts-expect-error 如果直接转换好了,可以返回
if (schema.$$typeof) {
return schema;
}
if (!isSchema(schema)) {
return null;
}
let Comp = components[schema.componentName] || this.props.__container?.components?.[schema.componentName];
// 容器类组件的上下文通过props传递避免context传递带来的嵌套问题
const otherProps: any = isFileSchema(schema)
? {
__schema: schema,
__appHelper: appHelper,
__components: components,
}
: {};
if (!Comp) {
console.error(`${schema.componentName} component is not found in components list! component list is:`, components || this.props.__container?.components);
return engine.createElement(
engine.getNotFoundComponent(),
{
componentName: schema.componentName,
componentId: schema.id,
},
this.__getSchemaChildrenVirtualDom(schema, scope, Comp),
);
}
if (schema.loop != null) {
const loop = this.__parseData(schema.loop, scope);
const useLoop = isUseLoop(loop, this._designModeIsDesign);
if (useLoop) {
return this.__createLoopVirtualDom(
{
...schema,
loop,
},
scope,
parentInfo,
idx,
);
}
}
const condition = schema.condition == null ? true : this.__parseData(schema.condition, scope);
// DesignMode 为 design 情况下,需要进入 leaf Hoc进行相关事件注册
const displayInHook = engine.props?.designMode === 'design';
if (!condition && !displayInHook) {
return null;
}
let scopeKey = '';
// 判断组件是否需要生成scope且只生成一次挂在this.__compScopes上
if (Comp.generateScope) {
const key = this.parseExpression(schema.props?.key, scope);
if (key) {
// 如果组件自己设置key则使用组件自己的key
scopeKey = key;
} else if (!schema.__ctx) {
// 在生产环境schema没有__ctx上下文需要手动生成一个lceKey
schema.__ctx = {
lceKey: `lce${++scopeIdx}`,
};
scopeKey = schema.__ctx.lceKey;
} else {
// 需要判断循环的情况
scopeKey = schema.__ctx.lceKey + (idx !== undefined ? `_${idx}` : '');
}
if (!this.__compScopes[scopeKey]) {
this.__compScopes[scopeKey] = Comp.generateScope(this, schema);
}
}
// 如果组件有设置scope需要为组件生成一个新的scope上下文
if (scopeKey && this.__compScopes[scopeKey]) {
const compSelf = { ...this.__compScopes[scopeKey] };
compSelf.__proto__ = scope;
scope = compSelf;
}
if (engine.props?.designMode) {
otherProps.__designMode = engine.props.designMode;
}
if (this._designModeIsDesign) {
otherProps.__tag = Math.random();
}
const componentInfo: any = {};
const props: any = this.__getComponentProps(schema, scope, Comp, {
...componentInfo,
props: transformArrayToMap(componentInfo.props, 'name'),
}) || {};
this.componentHoc.forEach((ComponentConstruct: IComponentConstruct) => {
Comp = ComponentConstruct(Comp, {
schema,
componentInfo,
baseRenderer: this,
scope,
});
});
// 对于不可以接收到 ref 的组件需要做特殊处理
if (!canAcceptsRef(Comp)) {
Comp = compWrapper(Comp);
components[schema.componentName] = Comp;
}
otherProps.ref = (ref: any) => {
this.$(props.fieldId || props.ref, ref); // 收集ref
const refProps = props.ref;
if (refProps && typeof refProps === 'string') {
this[refProps] = ref;
}
ref && engine.props?.onCompGetRef(schema, ref);
};
// scope需要传入到组件上
if (scopeKey && this.__compScopes[scopeKey]) {
props.__scope = this.__compScopes[scopeKey];
}
if (schema?.__ctx?.lceKey) {
if (!isFileSchema(schema)) {
engine.props?.onCompGetCtx(schema, scope);
}
props.key = props.key || `${schema.__ctx.lceKey}_${schema.__ctx.idx || 0}_${idx !== undefined ? idx : ''}`;
} else if ((typeof idx === 'number' || typeof idx === 'string') && !props.key) {
// 仅当循环场景走这里
props.key = idx;
}
props.__id = schema.id;
if (!props.key) {
props.key = props.__id;
}
let child = this.__getSchemaChildrenVirtualDom(schema, scope, Comp);
const renderComp = (innerProps: any) => engine.createElement(Comp, innerProps, child);
// 设计模式下的特殊处理
if (engine && [DESIGN_MODE.EXTEND, DESIGN_MODE.BORDER].includes(engine.props.designMode)) {
// 对于overlay,dialog等组件为了使其在设计模式下显示外层需要增加一个div容器
if (OVERLAY_LIST.includes(schema.componentName)) {
const { ref, ...overlayProps } = otherProps;
return createElement(Div, {
ref,
__designMode: engine.props.designMode,
}, renderComp({ ...props, ...overlayProps }));
}
// 虚拟dom显示
if (componentInfo?.parentRule) {
const parentList = componentInfo.parentRule.split(',');
const { schema: parentSchema = { componentName: '' }, Comp: parentComp } = parentInfo;
if (
!parentList.includes(parentSchema.componentName) ||
parentComp !== components[parentSchema.componentName]
) {
props.__componentName = schema.componentName;
Comp = VisualDom;
} else {
// 若虚拟dom在正常的渲染上下文中就不显示设计模式了
props.__disableDesignMode = true;
}
}
}
return renderComp({ ...props, ...otherProps });
} catch (e) {
return engine.createElement(engine.getFaultComponent(), {
error: e,
schema,
self: scope,
parentInfo,
idx,
});
}
};
get componentHoc(): IComponentConstruct[] {
const componentHoc: IComponentHoc[] = [
{
designMode: 'design',
hoc: leafWrapper,
},
];
return componentHoc
.filter((d: IComponentHoc) => {
if (Array.isArray(d.designMode)) {
return d.designMode.includes(this.props.designMode);
}
return d.designMode === this.props.designMode;
})
.map((d: IComponentHoc) => d.hoc);
}
__getSchemaChildrenVirtualDom = (schema: NodeSchema | undefined, scope: any, Comp: any) => {
let _children = this.__getSchemaChildren(schema);
// @todo 补完这里的 Element 定义 @承虎
let children: any = [];
if (/*! isFileSchema(schema) && */_children) {
if (!Array.isArray(_children)) {
_children = [_children];
}
_children.forEach((_child: any) => {
const _childVirtualDom = this.__createVirtualDom(
isJSExpression(_child) ? this.parseExpression(_child, scope) : _child,
scope,
{
schema,
Comp,
},
);
children.push(_childVirtualDom);
});
}
if (children && children.length) {
return children;
}
return null;
};
__getComponentProps = (schema: NodeSchema | undefined, scope: any, Comp: any, componentInfo?: any) => {
if (!schema) {
return {};
}
return this.__parseProps(schema?.props, scope, '', {
schema,
Comp,
componentInfo: {
...(componentInfo || {}),
props: transformArrayToMap((componentInfo || {}).props, 'name'),
},
}) || {};
};
__createLoopVirtualDom = (schema: NodeSchema, scope: any, parentInfo: IInfo, idx: number | string) => {
if (isFileSchema(schema)) {
console.warn('file type not support Loop');
return null;
}
if (!Array.isArray(schema.loop)) return null;
const itemArg = (schema.loopArgs && schema.loopArgs[0]) || 'item';
const indexArg = (schema.loopArgs && schema.loopArgs[1]) || 'index';
const { loop } = schema;
return loop.map((item: JSONValue | CompositeValue, i: number) => {
const loopSelf: any = {
[itemArg]: item,
[indexArg]: i,
};
loopSelf.__proto__ = scope;
return this.__createVirtualDom(
{
...schema,
loop: undefined,
},
loopSelf,
parentInfo,
idx ? `${idx}_${i}` : i,
);
});
};
get _designModeIsDesign() {
const { engine } = this.context || {};
return engine?.props?.designMode === 'design';
}
__parseProps = (originalProps: any, scope: any, path: string, info: IInfo): any => {
let props = originalProps;
const { schema, Comp, componentInfo = {} } = info;
const propInfo = getValue(componentInfo.props, path);
// FIXME! 将这行逻辑外置,解耦,线上环境不要验证参数,调试环境可以有,通过传参自定义
const propType = propInfo?.extra?.propType;
const ignoreParse = schema?.__ignoreParse || [];
const checkProps = (value: any) => {
if (!propType) return value;
return checkPropTypes(value, path, propType, componentInfo.name) ? value : undefined;
};
const parseReactNode = (data: any, params: any) => {
if (isEmpty(params)) {
return checkProps(this.__createVirtualDom(data, scope, ({ schema, Comp } as IInfo)));
}
return checkProps((...argValues: any[]) => {
const args: any = {};
if (Array.isArray(params) && params.length) {
params.forEach((item, idx) => {
if (typeof item === 'string') {
args[item] = argValues[idx];
} else if (item && typeof item === 'object') {
args[item.name] = argValues[idx];
}
});
}
args.__proto__ = scope;
return scope.__createVirtualDom(data, args, { schema, Comp });
});
};
// 判断是否需要解析变量
if (
ignoreParse.some((item: any) => {
if (item instanceof RegExp) {
return item.test(path);
}
return item === path;
})
) {
return checkProps(props);
}
if (isJSExpression(props)) {
props = this.parseExpression(props, scope);
// 只有当变量解析出来为模型结构的时候才会继续解析
if (!isSchema(props) && !isJSSlot(props)) return checkProps(props);
}
const handleLegaoI18n = (innerProps: any) => innerProps[innerProps.use || 'zh_CN'];
// 兼容乐高设计态 i18n 数据
if (isI18nData(props)) {
const i18nProp = handleLegaoI18n(props);
if (i18nProp) {
props = i18nProp;
} else {
return parseI18n(props, scope);
}
}
// 兼容乐高设计态的变量绑定
if (isVariable(props)) {
props = props.value;
if (isI18nData(props)) {
props = handleLegaoI18n(props);
}
}
if (isJSFunction(props)) {
props = transformStringToFunction(props.value);
}
if (isJSSlot(props)) {
const { params, value } = props;
if (!isSchema(value) || isEmpty(value)) return undefined;
return parseReactNode(value, params);
}
// 兼容通过componentInfo判断的情况
if (isSchema(props)) {
const isReactNodeFunction = !!(
propInfo?.type === 'ReactNode'
&& propInfo?.props?.type === 'function'
);
const isMixinReactNodeFunction = !!(
propInfo?.type === 'Mixin'
&& propInfo?.props?.types?.indexOf('ReactNode') > -1
&& propInfo?.props?.reactNodeProps?.type === 'function'
);
let params = null;
if (isReactNodeFunction) {
params = propInfo?.props?.params;
} else if (isMixinReactNodeFunction) {
params = propInfo?.props?.reactNodeProps?.params;
}
return parseReactNode(
props,
params,
);
}
if (Array.isArray(props)) {
return checkProps(props.map((item, idx) => this.__parseProps(item, scope, path ? `${path}.${idx}` : `${idx}`, info)));
}
if (typeof props === 'function') {
return checkProps(props.bind(scope));
}
if (props && typeof props === 'object') {
if (props.$$typeof) return checkProps(props);
const res: any = {};
forEach(props, (val: any, key: string) => {
if (key.startsWith('__')) {
res[key] = val;
return;
}
res[key] = this.__parseProps(val, scope, path ? `${path}.${key}` : key, info);
});
return checkProps(res);
}
if (typeof props === 'string') {
return checkProps(props.trim());
}
return checkProps(props);
};
$(filedId: string, instance?: any) {
this.__instanceMap = this.__instanceMap || {};
if (!filedId || typeof filedId !== 'string') {
return this.__instanceMap;
}
if (instance) {
this.__instanceMap[filedId] = instance;
}
return this.__instanceMap[filedId];
}
__debug = logger.log;
__renderContextProvider = (customProps?: object, children?: any) => {
return createElement(AppContext.Provider, {
value: {
...this.context,
blockContext: this,
...(customProps || {}),
},
children: children || this.__createDom(),
});
};
__renderContextConsumer = (children: any) => {
return createElement(AppContext.Consumer, {}, children);
};
__getHocComp(OriginalComp: any, schema: any, scope: any) {
let Comp = OriginalComp;
this.componentHoc.forEach((ComponentConstruct: IComponentConstruct) => {
Comp = ComponentConstruct(Comp || Div, {
schema,
componentInfo: {},
baseRenderer: this,
scope,
});
});
return Comp;
}
__renderComp(OriginalComp: any, ctxProps: object) {
let Comp = OriginalComp;
const { __schema } = this.props;
const { __ctx } = this.props;
const scope: any = {};
scope.__proto__ = __ctx || this;
Comp = this.__getHocComp(Comp, __schema, scope);
const data = this.__parseProps(__schema?.props, scope, '', {
schema: __schema,
Comp,
componentInfo: {},
});
const { className } = data;
const otherProps: any = {};
const { engine } = this.context || {};
if (!engine) {
return null;
}
if (this._designModeIsDesign) {
otherProps.__tag = Math.random();
}
const child = engine.createElement(
Comp,
{
...data,
...this.props,
ref: this.__getRef,
className: classnames(getFileCssName(__schema?.fileName), className, this.props.className),
__id: __schema?.id,
...otherProps,
},
this.__createDom(),
);
return this.__renderContextProvider(ctxProps, child);
}
__renderContent(children: any) {
const { __schema } = this.props;
const props = this.__parseData(__schema.props);
const { id, className, style = {} } = props;
return createElement('div', {
ref: this.__getRef,
className: classnames(`lce-${this.__namespace}`, getFileCssName(__schema.fileName), className, this.props.className),
id: this.props.id || id,
style: { ...style, ...(typeof this.props.style === 'object' ? this.props.style : {}) },
}, children);
}
__checkSchema = (schema: NodeSchema | undefined, originalExtraComponents: string | string[] = []) => {
let extraComponents = originalExtraComponents;
if (typeof extraComponents === 'string') {
extraComponents = [extraComponents];
}
const buitin = capitalizeFirstLetter(this.__namespace);
const componentNames = [buitin, ...extraComponents];
return !isSchema(schema) || !componentNames.includes(schema?.componentName ?? '');
};
get requestHandlersMap() {
return this.appHelper?.requestHandlersMap;
}
get utils() {
return this.appHelper?.utils;
}
get constants() {
return this.appHelper?.constants;
}
get history() {
return this.appHelper?.history;
}
get location() {
return this.appHelper?.location;
}
get match() {
return this.appHelper?.match;
}
render() {
return null;
}
};
}