mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-26 06:34:54 +00:00
Generated by Spark: Load more of UI from JSON declarations and break up large components into atomic and create hooks as needed
This commit is contained in:
147
src/lib/schema-renderer.tsx
Normal file
147
src/lib/schema-renderer.tsx
Normal file
@@ -0,0 +1,147 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Component as ComponentSchema, Layout } from '@/schemas/ui-schema'
|
||||
import { useDataBinding, useEventHandlers, useComponentRegistry } from '@/hooks/ui'
|
||||
|
||||
interface SchemaRendererProps {
|
||||
schema: ComponentSchema
|
||||
data: Record<string, any>
|
||||
functions?: Record<string, (...args: any[]) => any>
|
||||
}
|
||||
|
||||
interface LayoutRendererProps {
|
||||
layout: Layout
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
function LayoutRenderer({ layout, children }: LayoutRendererProps) {
|
||||
const getLayoutClasses = () => {
|
||||
const classes: string[] = []
|
||||
|
||||
if (layout.type === 'flex') {
|
||||
classes.push('flex')
|
||||
if (layout.direction) {
|
||||
classes.push(layout.direction === 'column' ? 'flex-col' : 'flex-row')
|
||||
}
|
||||
} else if (layout.type === 'grid') {
|
||||
classes.push('grid')
|
||||
if (layout.columns) {
|
||||
const { base = 1, sm, md, lg, xl } = layout.columns
|
||||
classes.push(`grid-cols-${base}`)
|
||||
if (sm) classes.push(`sm:grid-cols-${sm}`)
|
||||
if (md) classes.push(`md:grid-cols-${md}`)
|
||||
if (lg) classes.push(`lg:grid-cols-${lg}`)
|
||||
if (xl) classes.push(`xl:grid-cols-${xl}`)
|
||||
}
|
||||
} else if (layout.type === 'stack') {
|
||||
classes.push('flex flex-col')
|
||||
}
|
||||
|
||||
if (layout.gap) {
|
||||
classes.push(`gap-${layout.gap}`)
|
||||
}
|
||||
|
||||
if (layout.className) {
|
||||
classes.push(layout.className)
|
||||
}
|
||||
|
||||
return cn(...classes)
|
||||
}
|
||||
|
||||
return <div className={getLayoutClasses()}>{children}</div>
|
||||
}
|
||||
|
||||
export function SchemaRenderer({ schema, data, functions = {} }: SchemaRendererProps) {
|
||||
const { resolveCondition, resolveProps, resolveBinding } = useDataBinding({ data })
|
||||
const { resolveEvents } = useEventHandlers({ functions })
|
||||
const { getComponent, getIcon } = useComponentRegistry()
|
||||
|
||||
if (schema.condition && !resolveCondition(schema.condition)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (schema.repeat) {
|
||||
const items = resolveBinding(schema.repeat.items, []) as any[]
|
||||
return (
|
||||
<>
|
||||
{items.map((item, index) => {
|
||||
const itemData = {
|
||||
...data,
|
||||
[schema.repeat!.itemVar]: item,
|
||||
...(schema.repeat!.indexVar ? { [schema.repeat!.indexVar]: index } : {}),
|
||||
}
|
||||
return (
|
||||
<SchemaRenderer
|
||||
key={index}
|
||||
schema={{ ...schema, repeat: undefined }}
|
||||
data={itemData}
|
||||
functions={functions}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const Component = getComponent(schema.type)
|
||||
|
||||
if (!Component) {
|
||||
console.warn(`Component type "${schema.type}" not found in registry`)
|
||||
return (
|
||||
<div className="border-2 border-dashed border-destructive p-4 rounded-md">
|
||||
<p className="text-destructive font-mono text-sm">
|
||||
Unknown component: {schema.type}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const props = resolveProps(schema.props || {})
|
||||
const events = resolveEvents(schema.events)
|
||||
const combinedProps = { ...props, ...events }
|
||||
|
||||
if (schema.binding) {
|
||||
const iconName = resolveBinding(schema.binding)
|
||||
if (iconName && schema.type === 'Icon') {
|
||||
return getIcon(iconName, props)
|
||||
}
|
||||
}
|
||||
|
||||
const children = schema.children?.map((child, index) => (
|
||||
<SchemaRenderer
|
||||
key={child.id || index}
|
||||
schema={child}
|
||||
data={data}
|
||||
functions={functions}
|
||||
/>
|
||||
))
|
||||
|
||||
return <Component {...combinedProps}>{children}</Component>
|
||||
}
|
||||
|
||||
interface PageRendererProps {
|
||||
schema: {
|
||||
id: string
|
||||
title?: string
|
||||
description?: string
|
||||
layout: Layout
|
||||
components: ComponentSchema[]
|
||||
}
|
||||
data: Record<string, any>
|
||||
functions?: Record<string, (...args: any[]) => any>
|
||||
}
|
||||
|
||||
export function PageRenderer({ schema, data, functions = {} }: PageRendererProps) {
|
||||
return (
|
||||
<LayoutRenderer layout={schema.layout}>
|
||||
{schema.components.map((component) => (
|
||||
<SchemaRenderer
|
||||
key={component.id}
|
||||
schema={component}
|
||||
data={data}
|
||||
functions={functions}
|
||||
/>
|
||||
))}
|
||||
</LayoutRenderer>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user