mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-25 14:14:57 +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:
33
src/components/atoms/json-ui/DataCard.tsx
Normal file
33
src/components/atoms/json-ui/DataCard.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { IconRenderer } from './IconRenderer'
|
||||
|
||||
interface DataCardProps {
|
||||
title: string
|
||||
description?: string
|
||||
icon?: string
|
||||
gradient?: string
|
||||
children: ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function DataCard({ title, description, icon, gradient, children, className }: DataCardProps) {
|
||||
return (
|
||||
<Card className={`${gradient ? `bg-gradient-to-br ${gradient} border-primary/20` : ''} ${className || ''}`}>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
{icon && (
|
||||
<span className="text-primary">
|
||||
<IconRenderer name={icon} />
|
||||
</span>
|
||||
)}
|
||||
{title}
|
||||
</CardTitle>
|
||||
{description && <CardDescription>{description}</CardDescription>}
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{children}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
21
src/components/atoms/json-ui/IconRenderer.tsx
Normal file
21
src/components/atoms/json-ui/IconRenderer.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { ComponentSchema } from '@/types/json-ui'
|
||||
import * as Icons from '@phosphor-icons/react'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface IconRendererProps {
|
||||
name: string
|
||||
size?: number
|
||||
weight?: 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone'
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function IconRenderer({ name, size = 24, weight = 'duotone', className }: IconRendererProps) {
|
||||
const IconComponent = (Icons as any)[name]
|
||||
|
||||
if (!IconComponent) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <IconComponent size={size} weight={weight} className={className} />
|
||||
}
|
||||
2
src/components/atoms/json-ui/index.ts
Normal file
2
src/components/atoms/json-ui/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { IconRenderer } from './IconRenderer'
|
||||
export { DataCard } from './DataCard'
|
||||
2
src/hooks/json-ui/index.ts
Normal file
2
src/hooks/json-ui/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { useJSONRenderer } from './use-json-renderer'
|
||||
export { useDataSources } from './use-data-sources'
|
||||
73
src/hooks/json-ui/use-data-sources.ts
Normal file
73
src/hooks/json-ui/use-data-sources.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { useState, useEffect, useMemo, useCallback } from 'react'
|
||||
import { DataSource } from '@/types/json-ui'
|
||||
|
||||
export function useDataSources(dataSources: DataSource[]) {
|
||||
const [data, setData] = useState<Record<string, any>>({})
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const staticSources = useMemo(
|
||||
() => dataSources.filter((ds) => ds.type === 'static'),
|
||||
[dataSources]
|
||||
)
|
||||
|
||||
const computedSources = useMemo(
|
||||
() => dataSources.filter((ds) => ds.type === 'computed'),
|
||||
[dataSources]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const initialData: Record<string, any> = {}
|
||||
|
||||
for (const ds of dataSources) {
|
||||
if (ds.type === 'kv' && ds.key) {
|
||||
try {
|
||||
const value = await spark.kv.get(ds.key)
|
||||
initialData[ds.id] = value !== undefined ? value : ds.defaultValue
|
||||
} catch {
|
||||
initialData[ds.id] = ds.defaultValue
|
||||
}
|
||||
} else if (ds.type === 'static') {
|
||||
initialData[ds.id] = ds.defaultValue
|
||||
}
|
||||
}
|
||||
|
||||
setData(initialData)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
loadData()
|
||||
}, [dataSources])
|
||||
|
||||
const updateDataSource = useCallback(async (id: string, value: any) => {
|
||||
setData((prev) => ({ ...prev, [id]: value }))
|
||||
|
||||
const kvSource = dataSources.find((ds) => ds.id === id && ds.type === 'kv')
|
||||
if (kvSource && kvSource.key) {
|
||||
await spark.kv.set(kvSource.key, value)
|
||||
}
|
||||
}, [dataSources])
|
||||
|
||||
const computedData = useMemo(() => {
|
||||
const result: Record<string, any> = {}
|
||||
|
||||
computedSources.forEach((ds) => {
|
||||
if (ds.compute && typeof ds.compute === 'function') {
|
||||
result[ds.id] = ds.compute(data)
|
||||
}
|
||||
})
|
||||
|
||||
return result
|
||||
}, [computedSources, data])
|
||||
|
||||
const allData = useMemo(
|
||||
() => ({ ...data, ...computedData }),
|
||||
[data, computedData]
|
||||
)
|
||||
|
||||
return {
|
||||
data: allData,
|
||||
loading,
|
||||
updateDataSource,
|
||||
}
|
||||
}
|
||||
45
src/hooks/json-ui/use-json-renderer.ts
Normal file
45
src/hooks/json-ui/use-json-renderer.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { useMemo } from 'react'
|
||||
import { ComponentSchema } from '@/types/json-ui'
|
||||
|
||||
export function useJSONRenderer() {
|
||||
const resolveBinding = useMemo(() => {
|
||||
return (binding: string, data: Record<string, any>): any => {
|
||||
if (!binding) return undefined
|
||||
|
||||
try {
|
||||
const func = new Function(...Object.keys(data), `return ${binding}`)
|
||||
return func(...Object.values(data))
|
||||
} catch {
|
||||
return binding
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const resolveValue = useMemo(() => {
|
||||
return (value: any, data: Record<string, any>): any => {
|
||||
if (typeof value === 'string' && value.startsWith('{{') && value.endsWith('}}')) {
|
||||
const binding = value.slice(2, -2).trim()
|
||||
return resolveBinding(binding, data)
|
||||
}
|
||||
return value
|
||||
}
|
||||
}, [resolveBinding])
|
||||
|
||||
const resolveProps = useMemo(() => {
|
||||
return (props: Record<string, any>, data: Record<string, any>): Record<string, any> => {
|
||||
const resolved: Record<string, any> = {}
|
||||
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
resolved[key] = resolveValue(value, data)
|
||||
}
|
||||
|
||||
return resolved
|
||||
}
|
||||
}, [resolveValue])
|
||||
|
||||
return {
|
||||
resolveBinding,
|
||||
resolveValue,
|
||||
resolveProps,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user