import React from 'react'; import isPlainObject from 'lodash/isPlainObject'; import {RendererEnv} from './env'; import {RendererProps} from './factory'; import {LocaleContext, TranslateFn} from './locale'; import {RootRenderer} from './RootRenderer'; import {SchemaRenderer} from './SchemaRenderer'; import Scoped from './Scoped'; import {IRendererStore} from './store'; import {ThemeContext} from './theme'; import {Schema, SchemaNode} from './types'; import {autobind, isEmpty} from './utils/helper'; import {RootStoreContext} from './WithRootStore'; import {StatusScoped, StatusScopedProps} from './StatusScoped'; export interface RootRenderProps { location?: Location; theme?: string; data?: Record; locale?: string; [propName: string]: any; } export interface RootProps extends StatusScopedProps { schema: SchemaNode; rootStore: IRendererStore; env: RendererEnv; theme: string; pathPrefix?: string; locale?: string; translate?: TranslateFn; [propName: string]: any; } export interface RootWrapperProps { env: RendererEnv; children: React.ReactNode | Array; schema: SchemaNode; rootStore: IRendererStore; theme: string; data?: Record; context?: Record; [propName: string]: any; } const rootWrappers: Array<(props: RootWrapperProps) => React.ReactNode> = []; export function addRootWrapper( fn: (props: RootWrapperProps) => React.ReactNode ) { rootWrappers.push(fn); } export class Root extends React.Component { @autobind resolveDefinitions(name: string) { const definitions = (this.props.schema as Schema).definitions; if (!name || isEmpty(definitions)) { return {}; } return definitions && definitions[name]; } render() { const { schema, rootStore, env, pathPrefix, location, data, context, locale, translate, ...rest } = this.props; const theme = env.theme; let themeName = this.props.theme || 'cxd'; if (themeName === 'default') { themeName = 'cxd'; } return ( { rootWrappers.reduce( (props: RootWrapperProps, wrapper) => { return { ...props, children: wrapper(props) }; }, { pathPrefix: pathPrefix || '', schema: isPlainObject(schema) ? { type: 'page', ...(schema as any) } : schema, ...rest, render: renderChild, rootStore: rootStore, resolveDefinitions: this.resolveDefinitions, location: location, data, env: env, classnames: theme.classnames, classPrefix: theme.classPrefix, locale: locale, translate: translate, children: ( ) } as RootWrapperProps ).children } ); } } export interface renderChildProps extends Partial>, StatusScopedProps { env: RendererEnv; } export type ReactElement = React.ReactNode[] | JSX.Element | null | false; const StatusScopedSchemaRenderer = StatusScoped(SchemaRenderer); export function renderChildren( prefix: string, node: SchemaNode, props: renderChildProps ): ReactElement { if (Array.isArray(node)) { var elemKey = props.key || props.propKey || props.id || ''; return node.map((node, index) => renderChild(`${prefix}/${index}`, node, { ...props, key: `${elemKey ? `${elemKey}-` : ''}${index}` }) ); } return renderChild(prefix, node, props); } export function renderChild( prefix: string, node: SchemaNode, props: renderChildProps ): ReactElement { if (Array.isArray(node)) { return renderChildren(prefix, node, props); } const typeofnode = typeof node; if (typeofnode === 'undefined' || node === null) { return null; } else if (React.isValidElement(node)) { return node; } let schema: Schema = typeofnode === 'string' || typeofnode === 'number' ? {type: 'tpl', tpl: String(node)} : (node as Schema); const transform = props.propsTransform; if (transform) { props = {...props}; delete props.propsTransform; props = transform(props); } if ( ['dialog', 'drawer'].includes(schema?.type) && !schema?.component && !schema?.children ) { // 因为状态判断实在 SchemaRenderer 里面判断的 // 找渲染器也是在那,所以没办法在之前根据渲染器信息来包裹个组件下发 statusStore // 所以这里先根据 type 来处理一下 // 等后续把状态处理再抽一层,可以把此处放到 SchemaRenderer 里面去 return ( ); } return ( ); } export default StatusScoped(Scoped(Root));