import {IS_SERVER} from "@gongt/ts-stl-library/check-environment"; import {createLogger} from "@gongt/ts-stl-library/debug/create-logger"; import {LOG_LEVEL} from "@gongt/ts-stl-library/debug/levels"; import {GlobalVariable} from "@gongt/ts-stl-library/pattern/global-page-data"; import * as React from "react"; import {render} from "react-dom"; import {inspect} from "util"; import {GlobalContextProvider, ReactEmptyWrapper} from "./global-context"; export interface IContextCreate { (window: Window): GlobalVariable; } export const REACT_ROOT_ELEMENT_ID_VARNAME = 'REACT_ROOT_ELEMENT_ID'; export interface WrapComponent { componentName: string; Component: React.ComponentClass; props: Function; } export type MainAppCreator = (global: GlobalVariable) => React.ReactElement; export abstract class ReactRenderBase { protected _getComponent: MainAppCreator; protected wrapStack: WrapComponent[] = []; setMainApp(component: MainAppCreator) { this._getComponent = component; } protected getComponent(global: GlobalVariable): React.ReactElement { if (!this._getComponent) { throw new TypeError(this.constructor.name + ': no main app.') } let component = this._getComponent(global); this.wrapStack.forEach((props) => { if (props) { component = {component}; } else { const Component = props.Component; component = {component} } }); return component; } wrapComponent

(componentName: string, Component: React.ComponentClass

, props?: (global: GlobalVariable) => P) { this.wrapStack.push({componentName, Component, props}); } abstract render(arg?: GlobalVariable): any; } export class ReactRender extends ReactRenderBase { constructor() { super(); } render(global: GlobalVariable = new GlobalVariable()) { if (!global.has(REACT_ROOT_ELEMENT_ID_VARNAME)) { global.set(REACT_ROOT_ELEMENT_ID_VARNAME, 'react-root-random-' + (Math.random() * 10000).toFixed(0)); } const react_root_id = global.get(REACT_ROOT_ELEMENT_ID_VARNAME); let root = document.getElementById(react_root_id); if (!root) { root = document.createElement('DIV'); root.id = react_root_id; document.body.appendChild(root); } const component = this.getComponent(global); if (debugComponentEnabled) { debugComponent(component); } render( , root, ); } } const debug = createLogger(LOG_LEVEL.SILLY, 'react-render'); const warn = createLogger(LOG_LEVEL.WARN, 'react-render'); export function debugComponent(comp: React.ReactElement) { let itr = comp; debug('ReactRender:'); while (itr && itr['type']) { let Component = itr['type']; if (Component === ReactEmptyWrapper) { Component = itr['props'].Component; if (!Component) { warn('ReactEmptyWrapper without children: ', itr); itr = itr['props'].children; continue; } } const name = Component['displayName'] || Component['name'] || Component; const obj = Object.assign({}, itr.props); const dbg = []; wellknownProps.forEach((item) => { if (obj[item]) { delete obj[item]; dbg.push(pgreen); } else { dbg.push(pred); } dbg.push(item); dbg.push(preset); }); let ins: any; if (IS_SERVER) { ins = inspect(obj, false, 1, true).replace(/\s+/g, ' '); if (process.stdout['columns']) { ins = ins.substr(0, process.stdout['columns']); } } else { ins = obj; } debug(messageStr, name, ...dbg, ins); if (!itr['props']) { break; } itr = itr['props'].children; } } const pred = IS_SERVER? '\x1B[38;5;9m' : 'color:red'; const pgreen = IS_SERVER? '\x1B[38;5;10m' : 'color:lime'; const preset = IS_SERVER? '\x1B[0m' : 'color:black'; const wellknownProps = ['global', 'store', 'children', 'Component']; const pcolor = IS_SERVER? '%s' : '%c'; const messagePart = wellknownProps.map(e => pcolor + '%s' + pcolor).join(', '); const messageStr = IS_SERVER ? '> %s (' + messagePart + ')\n%s' : '> %s (' + messagePart + ', %O)'; export const debugComponentEnabled = debug.enabled;