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