import type { Component, CSSProperties, Prop, VNodeChild } from 'vue' import type { Axis, Block, DataRefer, DynamicTheme, DynamicValue, ToStatic, ViewChildren } from './types' import type { EngineContext } from './context' import { defineComponent, h } from 'vue' import { provideBlockId, useBlockId, useContext } from './context' import { align, background, bordering, clip, dynamic, gap, insets, isRefer, radii, shadowing, size, stack, unit, } from './utils' import DddView from './views/View.vue' // /** // * 相关框架组件渲染函数 // */ // export type WidgetRenderFunction = (widget: Widget, view: View, axis?: Axis) => VNodeChild // /** // * 注册的外部组件渲染函数 // */ // const widgets: Record = {} const views: Record = {} export function registerView(v: Record): void { for (const [key, comp] of Object.entries(v)) { views[key] = comp } } // export function registerWidget(some: Record): void { // for (const [key, func] of Object.entries(some)) { // widgets[key] = func // } // } // // function renderWidget(widget: Widget, view: View, axis?: Axis): VNodeChild { // const func = widgets[widget.name] // if (typeof func !== 'function') { // throw new TypeError(`unknown widget: ${widget.name}`) // } // return func(widget, view, axis) // } // // const isWidget = (s: any): s is Widget => s != null && 'name' in s export function renderRefer(refer: DataRefer, axis?: Axis): VNodeChild { const { key, type, ...attrs } = refer const component = views[type] if (component) { return h(component, { source: key, axis, ...attrs, }) } return h('b', { style: { color: 'red', fontSize: '36px', }, }, 'unimplemented') } export function isFlexible(theme: DynamicTheme, value: ToStatic) { return value(theme.axis) != null || value(theme.mainAlign) != null || value(theme.crossAlign) != null || value(theme.wrap) != null || value(theme.gap) != null } export function render( ctx: EngineContext, blockId: string, view?: ViewChildren, axis?: Axis, ): VNodeChild { if (view == null) { return null } if (typeof view === 'string' || typeof view === 'number') { return view } if (Array.isArray(view)) { return view.map(child => render(ctx, blockId, child, axis)) } if (isRefer(view)) { return renderRefer(view) } // if (isWidget(view.theme)) { // return renderWidget(view.theme, view, axis) // } const theme = view.theme const value = (v: DynamicValue): T => dynamic(ctx, blockId, v) const css: CSSProperties = { // boxed & spatial ...insets('padding', value(theme?.padding)), ...insets('margin', value(theme?.margin)), ...size(axis, theme, value), // todo // decoration ...background(value(theme?.color), value(theme?.image), value(theme?.gradient)), ...bordering(value(theme?.border)), ...radii(value(theme?.radius)), ...shadowing(value(theme?.shadow)), // textual color: value(theme?.textColor), fontSize: unit(value(theme?.fontSize)), textAlign: value(theme?.textAlign), lineHeight: value(theme?.lineHeight), // clip ...clip(value(theme?.clip)), // stack ...stack(theme, value), } // note: 在前面初始化 css 时使用了参数 axis, // 所以在这里复用它用于子视图构建 axis = value(theme?.axis) const flexible = value(theme?.flexible) if (flexible || (theme && flexible == null && isFlexible(theme, value))) { Object.assign(css, { display: 'flex', flexDirection: axis === 'y' ? 'column' : undefined, justifyContent: align(value(theme?.mainAlign)), alignItems: align(value(theme?.crossAlign)), flexWrap: value(theme?.wrap) ? 'wrap' : undefined, ...gap(value(theme?.gap)), }) } return h(DddView, { vid: view.vid, class: 'ddd-view', style: css, }, () => render(ctx, blockId, view.children, axis)) } export const RenderView = defineComponent({ name: 'RenderView', props: { view: { // type: [Object | Array], required: true, } as Prop, }, setup(props) { const ctx = useContext() const blockId = useBlockId() return () => render(ctx, blockId, props.view) } }) export const RenderBlock = defineComponent({ name: 'RenderBlock', props: { block: { type: Object, required: true, } as Prop, }, setup(props) { // const ctx = useContext() provideBlockId(props.block!.vid) return () => { if (props.block == null) { return null } // return render(ctx, props.block.vid, props.block) return h(RenderView, { view: props.block, }) } }, })