mirror of
https://github.com/johndoe6345789/low-code-react-app-b.git
synced 2026-04-25 06:04:54 +00:00
Convert TypeScript schemas to JSON with compute functions
Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { PageRenderer } from '@/lib/json-ui/page-renderer'
|
||||
import { dashboardSchema } from '@/schemas/dashboard-schema'
|
||||
import { hydrateSchema } from '@/schemas/schema-loader'
|
||||
import analyticsDashboardJson from '@/schemas/analytics-dashboard.json'
|
||||
|
||||
const dashboardSchema = hydrateSchema(analyticsDashboardJson)
|
||||
|
||||
export function DashboardDemoPage() {
|
||||
return <PageRenderer schema={dashboardSchema} />
|
||||
|
||||
@@ -2,7 +2,12 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
import { AtomicComponentDemo } from '@/components/AtomicComponentDemo'
|
||||
import { DashboardDemoPage } from '@/components/DashboardDemoPage'
|
||||
import { PageRenderer } from '@/lib/json-ui/page-renderer'
|
||||
import { todoListSchema, newMoleculesShowcaseSchema } from '@/schemas/page-schemas'
|
||||
import { hydrateSchema } from '@/schemas/schema-loader'
|
||||
import todoListJson from '@/schemas/todo-list.json'
|
||||
import newMoleculesShowcaseJson from '@/schemas/new-molecules-showcase.json'
|
||||
|
||||
const todoListSchema = hydrateSchema(todoListJson)
|
||||
const newMoleculesShowcaseSchema = hydrateSchema(newMoleculesShowcaseJson)
|
||||
|
||||
export function JSONUIShowcasePage() {
|
||||
return (
|
||||
|
||||
256
src/schemas/analytics-dashboard.json
Normal file
256
src/schemas/analytics-dashboard.json
Normal file
@@ -0,0 +1,256 @@
|
||||
{
|
||||
"id": "analytics-dashboard",
|
||||
"name": "Analytics Dashboard",
|
||||
"layout": {
|
||||
"type": "single"
|
||||
},
|
||||
"dataSources": [
|
||||
{
|
||||
"id": "users",
|
||||
"type": "kv",
|
||||
"key": "dashboard-users",
|
||||
"defaultValue": [
|
||||
{ "id": 1, "name": "Alice Johnson", "email": "alice@example.com", "status": "active", "joined": "2024-01-15" },
|
||||
{ "id": 2, "name": "Bob Smith", "email": "bob@example.com", "status": "active", "joined": "2024-02-20" },
|
||||
{ "id": 3, "name": "Charlie Brown", "email": "charlie@example.com", "status": "inactive", "joined": "2023-12-10" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "filterQuery",
|
||||
"type": "static",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "filteredUsers",
|
||||
"type": "computed",
|
||||
"compute": "computeFilteredUsers",
|
||||
"dependencies": ["users", "filterQuery"]
|
||||
},
|
||||
{
|
||||
"id": "stats",
|
||||
"type": "computed",
|
||||
"compute": "computeStats",
|
||||
"dependencies": ["users"]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"id": "root",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "h-full overflow-auto p-6 bg-gradient-to-br from-background via-background to-accent/5"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "header",
|
||||
"type": "div",
|
||||
"props": { "className": "mb-8" },
|
||||
"children": [
|
||||
{
|
||||
"id": "title",
|
||||
"type": "Heading",
|
||||
"props": {
|
||||
"className": "text-4xl font-bold mb-2 bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent",
|
||||
"children": "Analytics Dashboard"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "subtitle",
|
||||
"type": "Text",
|
||||
"props": {
|
||||
"className": "text-muted-foreground text-lg",
|
||||
"children": "Monitor your user activity and key metrics"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "metrics-row",
|
||||
"type": "div",
|
||||
"props": { "className": "grid grid-cols-1 md:grid-cols-3 gap-6 mb-8" },
|
||||
"children": [
|
||||
{
|
||||
"id": "metric-total",
|
||||
"type": "Card",
|
||||
"props": { "className": "bg-gradient-to-br from-primary/10 to-primary/5 border-primary/20" },
|
||||
"children": [
|
||||
{
|
||||
"id": "metric-total-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "pt-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "metric-total-label",
|
||||
"type": "div",
|
||||
"props": { "className": "text-sm font-medium text-muted-foreground mb-2", "children": "Total Users" }
|
||||
},
|
||||
{
|
||||
"id": "metric-total-value",
|
||||
"type": "div",
|
||||
"props": { "className": "text-4xl font-bold text-primary" },
|
||||
"bindings": {
|
||||
"children": { "source": "stats", "path": "total" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "metric-total-description",
|
||||
"type": "div",
|
||||
"props": { "className": "text-xs text-muted-foreground mt-2", "children": "Registered accounts" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "metric-active",
|
||||
"type": "Card",
|
||||
"props": { "className": "bg-gradient-to-br from-green-500/10 to-green-500/5 border-green-500/20" },
|
||||
"children": [
|
||||
{
|
||||
"id": "metric-active-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "pt-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "metric-active-label",
|
||||
"type": "div",
|
||||
"props": { "className": "text-sm font-medium text-muted-foreground mb-2", "children": "Active Users" }
|
||||
},
|
||||
{
|
||||
"id": "metric-active-value",
|
||||
"type": "div",
|
||||
"props": { "className": "text-4xl font-bold text-green-600" },
|
||||
"bindings": {
|
||||
"children": { "source": "stats", "path": "active" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "metric-active-description",
|
||||
"type": "div",
|
||||
"props": { "className": "text-xs text-muted-foreground mt-2", "children": "Currently engaged" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "metric-inactive",
|
||||
"type": "Card",
|
||||
"props": { "className": "bg-gradient-to-br from-orange-500/10 to-orange-500/5 border-orange-500/20" },
|
||||
"children": [
|
||||
{
|
||||
"id": "metric-inactive-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "pt-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "metric-inactive-label",
|
||||
"type": "div",
|
||||
"props": { "className": "text-sm font-medium text-muted-foreground mb-2", "children": "Inactive Users" }
|
||||
},
|
||||
{
|
||||
"id": "metric-inactive-value",
|
||||
"type": "div",
|
||||
"props": { "className": "text-4xl font-bold text-orange-600" },
|
||||
"bindings": {
|
||||
"children": { "source": "stats", "path": "inactive" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "metric-inactive-description",
|
||||
"type": "div",
|
||||
"props": { "className": "text-xs text-muted-foreground mt-2", "children": "Need re-engagement" }
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "users-section",
|
||||
"type": "Card",
|
||||
"props": { "className": "bg-card/50 backdrop-blur" },
|
||||
"children": [
|
||||
{
|
||||
"id": "users-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "users-title-row",
|
||||
"type": "div",
|
||||
"props": { "className": "flex items-center justify-between" },
|
||||
"children": [
|
||||
{
|
||||
"id": "users-title",
|
||||
"type": "CardTitle",
|
||||
"props": { "children": "User Directory" }
|
||||
},
|
||||
{
|
||||
"id": "users-badge",
|
||||
"type": "Badge",
|
||||
"props": { "variant": "secondary" },
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "filteredUsers",
|
||||
"transform": "transformFilteredUsers"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "users-description",
|
||||
"type": "CardDescription",
|
||||
"props": { "children": "Manage and filter your user base" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "users-content",
|
||||
"type": "CardContent",
|
||||
"children": [
|
||||
{
|
||||
"id": "filter-row",
|
||||
"type": "div",
|
||||
"props": { "className": "mb-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "filter-input",
|
||||
"type": "Input",
|
||||
"props": { "placeholder": "Search users by name or email..." },
|
||||
"events": [
|
||||
{
|
||||
"event": "onChange",
|
||||
"actions": [
|
||||
{
|
||||
"id": "update-filter",
|
||||
"type": "set-value",
|
||||
"target": "filterQuery",
|
||||
"compute": "updateFilterQuery"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "users-list",
|
||||
"type": "div",
|
||||
"props": { "className": "space-y-4" },
|
||||
"bindings": {
|
||||
"children": {
|
||||
"source": "filteredUsers",
|
||||
"transform": "transformUserList"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
88
src/schemas/compute-functions.ts
Normal file
88
src/schemas/compute-functions.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
export const computeFilteredUsers = (data: any) => {
|
||||
const query = (data.filterQuery || '').toLowerCase()
|
||||
if (!query) return data.users || []
|
||||
return (data.users || []).filter((user: any) =>
|
||||
user.name.toLowerCase().includes(query) ||
|
||||
user.email.toLowerCase().includes(query)
|
||||
)
|
||||
}
|
||||
|
||||
export const computeStats = (data: any) => ({
|
||||
total: data.users?.length || 0,
|
||||
active: data.users?.filter((u: any) => u.status === 'active').length || 0,
|
||||
inactive: data.users?.filter((u: any) => u.status === 'inactive').length || 0,
|
||||
})
|
||||
|
||||
export const computeTodoStats = (data: any) => ({
|
||||
total: data.todos?.length || 0,
|
||||
completed: data.todos?.filter((t: any) => t.completed).length || 0,
|
||||
remaining: data.todos?.filter((t: any) => !t.completed).length || 0,
|
||||
})
|
||||
|
||||
export const computeAddTodo = (data: any) => ({
|
||||
id: Date.now(),
|
||||
text: data.newTodo,
|
||||
completed: false,
|
||||
})
|
||||
|
||||
export const updateFilterQuery = (_: any, event: any) => event.target.value
|
||||
|
||||
export const updateNewTodo = (data: any, event: any) => event.target.value
|
||||
|
||||
export const checkCanAddTodo = (data: any) => data.newTodo?.trim().length > 0
|
||||
|
||||
export const transformFilteredUsers = (users: any[]) => `${users.length} users`
|
||||
|
||||
export const transformUserList = (users: any[]) => users.map((user: any) => ({
|
||||
type: 'Card',
|
||||
id: `user-${user.id}`,
|
||||
props: {
|
||||
className: 'bg-background/50 hover:bg-background/80 transition-colors border-l-4 border-l-primary',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: 'CardContent',
|
||||
id: `user-content-${user.id}`,
|
||||
props: { className: 'pt-6' },
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
id: `user-row-${user.id}`,
|
||||
props: { className: 'flex items-start justify-between' },
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
id: `user-info-${user.id}`,
|
||||
props: { className: 'flex-1' },
|
||||
children: [
|
||||
{
|
||||
type: 'div',
|
||||
id: `user-name-${user.id}`,
|
||||
props: { className: 'font-semibold text-lg mb-1', children: user.name },
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
id: `user-email-${user.id}`,
|
||||
props: { className: 'text-sm text-muted-foreground', children: user.email },
|
||||
},
|
||||
{
|
||||
type: 'div',
|
||||
id: `user-joined-${user.id}`,
|
||||
props: { className: 'text-xs text-muted-foreground mt-2', children: `Joined ${user.joined}` },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'Badge',
|
||||
id: `user-status-${user.id}`,
|
||||
props: {
|
||||
variant: user.status === 'active' ? 'default' : 'secondary',
|
||||
children: user.status,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}))
|
||||
23
src/schemas/dashboard-simple.json
Normal file
23
src/schemas/dashboard-simple.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"id": "dashboard",
|
||||
"name": "Dashboard",
|
||||
"layout": {
|
||||
"type": "grid",
|
||||
"columns": 2,
|
||||
"gap": 4
|
||||
},
|
||||
"dataSources": [
|
||||
{
|
||||
"id": "stats",
|
||||
"type": "static",
|
||||
"defaultValue": {
|
||||
"users": 1247,
|
||||
"revenue": 45230,
|
||||
"orders": 892,
|
||||
"conversion": 3.2
|
||||
}
|
||||
}
|
||||
],
|
||||
"components": [],
|
||||
"globalActions": []
|
||||
}
|
||||
303
src/schemas/new-molecules-showcase.json
Normal file
303
src/schemas/new-molecules-showcase.json
Normal file
@@ -0,0 +1,303 @@
|
||||
{
|
||||
"id": "new-molecules-showcase",
|
||||
"name": "New Molecules Showcase",
|
||||
"layout": {
|
||||
"type": "single"
|
||||
},
|
||||
"dataSources": [
|
||||
{
|
||||
"id": "itemCount",
|
||||
"type": "static",
|
||||
"defaultValue": 42
|
||||
},
|
||||
{
|
||||
"id": "isLoading",
|
||||
"type": "static",
|
||||
"defaultValue": false
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"id": "root",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "h-full overflow-auto p-8 bg-background"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "page-header",
|
||||
"type": "div",
|
||||
"props": { "className": "mb-8" },
|
||||
"children": [
|
||||
{
|
||||
"id": "page-title",
|
||||
"type": "Heading",
|
||||
"props": {
|
||||
"level": 1,
|
||||
"className": "text-4xl font-bold mb-2",
|
||||
"children": "New JSON-Compatible Molecules"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "page-description",
|
||||
"type": "Text",
|
||||
"props": {
|
||||
"className": "text-muted-foreground text-lg",
|
||||
"children": "Showcasing the newly added molecular components"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "showcase-grid",
|
||||
"type": "Grid",
|
||||
"props": { "cols": 2, "gap": "lg", "className": "max-w-5xl" },
|
||||
"children": [
|
||||
{
|
||||
"id": "branding-card",
|
||||
"type": "Card",
|
||||
"children": [
|
||||
{
|
||||
"id": "branding-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "branding-title",
|
||||
"type": "CardTitle",
|
||||
"props": { "children": "AppBranding" }
|
||||
},
|
||||
{
|
||||
"id": "branding-description",
|
||||
"type": "CardDescription",
|
||||
"props": { "children": "Application branding with logo, title, and subtitle" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "branding-content",
|
||||
"type": "CardContent",
|
||||
"children": [
|
||||
{
|
||||
"id": "branding-demo",
|
||||
"type": "AppBranding",
|
||||
"props": {
|
||||
"title": "My Amazing App",
|
||||
"subtitle": "Built with JSON-Powered Components"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "label-badge-card",
|
||||
"type": "Card",
|
||||
"children": [
|
||||
{
|
||||
"id": "label-badge-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "label-badge-title",
|
||||
"type": "CardTitle",
|
||||
"props": { "children": "LabelWithBadge" }
|
||||
},
|
||||
{
|
||||
"id": "label-badge-description",
|
||||
"type": "CardDescription",
|
||||
"props": { "children": "Label with optional badge indicator" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "label-badge-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "space-y-3" },
|
||||
"children": [
|
||||
{
|
||||
"id": "label-badge-demo-1",
|
||||
"type": "LabelWithBadge",
|
||||
"props": {
|
||||
"label": "Total Items"
|
||||
},
|
||||
"bindings": {
|
||||
"badge": { "source": "itemCount" }
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "label-badge-demo-2",
|
||||
"type": "LabelWithBadge",
|
||||
"props": {
|
||||
"label": "Warning",
|
||||
"badge": "3",
|
||||
"badgeVariant": "destructive"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "label-badge-demo-3",
|
||||
"type": "LabelWithBadge",
|
||||
"props": {
|
||||
"label": "Success",
|
||||
"badge": "New",
|
||||
"badgeVariant": "default"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "empty-state-card",
|
||||
"type": "Card",
|
||||
"children": [
|
||||
{
|
||||
"id": "empty-state-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "empty-state-title",
|
||||
"type": "CardTitle",
|
||||
"props": { "children": "EmptyEditorState" }
|
||||
},
|
||||
{
|
||||
"id": "empty-state-description",
|
||||
"type": "CardDescription",
|
||||
"props": { "children": "Empty state display for editor contexts" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "empty-state-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "h-48" },
|
||||
"children": [
|
||||
{
|
||||
"id": "empty-state-demo",
|
||||
"type": "EmptyEditorState",
|
||||
"props": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "loading-states-card",
|
||||
"type": "Card",
|
||||
"children": [
|
||||
{
|
||||
"id": "loading-states-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "loading-states-title",
|
||||
"type": "CardTitle",
|
||||
"props": { "children": "Loading States" }
|
||||
},
|
||||
{
|
||||
"id": "loading-states-description",
|
||||
"type": "CardDescription",
|
||||
"props": { "children": "LoadingFallback and LoadingState components" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "loading-states-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "space-y-4" },
|
||||
"children": [
|
||||
{
|
||||
"id": "loading-fallback-wrapper",
|
||||
"type": "div",
|
||||
"props": { "className": "h-24 border border-border rounded-md" },
|
||||
"children": [
|
||||
{
|
||||
"id": "loading-fallback-demo",
|
||||
"type": "LoadingFallback",
|
||||
"props": {
|
||||
"message": "Loading your data..."
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "loading-state-demo",
|
||||
"type": "LoadingState",
|
||||
"props": {
|
||||
"message": "Processing request...",
|
||||
"size": "sm"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nav-header-card",
|
||||
"type": "Card",
|
||||
"props": { "className": "col-span-2" },
|
||||
"children": [
|
||||
{
|
||||
"id": "nav-header-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "nav-header-title",
|
||||
"type": "CardTitle",
|
||||
"props": { "children": "NavigationGroupHeader" }
|
||||
},
|
||||
{
|
||||
"id": "nav-header-description",
|
||||
"type": "CardDescription",
|
||||
"props": { "children": "Collapsible navigation group header (Note: requires Collapsible wrapper in production)" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "nav-header-content",
|
||||
"type": "CardContent",
|
||||
"children": [
|
||||
{
|
||||
"id": "nav-header-demo",
|
||||
"type": "NavigationGroupHeader",
|
||||
"props": {
|
||||
"label": "Components",
|
||||
"count": 24,
|
||||
"isExpanded": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "info-section",
|
||||
"type": "Alert",
|
||||
"props": {
|
||||
"className": "max-w-5xl mt-8"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "info-title",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "font-semibold mb-2",
|
||||
"children": "✅ Successfully Added to JSON Registry"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "info-text",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "text-sm",
|
||||
"children": "All components shown above are now available in the JSON UI component registry and can be used in JSON schemas."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalActions": []
|
||||
}
|
||||
89
src/schemas/schema-loader.ts
Normal file
89
src/schemas/schema-loader.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { PageSchema } from '@/types/json-ui'
|
||||
import * as computeFunctions from './compute-functions'
|
||||
|
||||
type ComputeFunctionMap = typeof computeFunctions
|
||||
|
||||
export function hydrateSchema(jsonSchema: any): PageSchema {
|
||||
const schema = { ...jsonSchema }
|
||||
|
||||
if (schema.dataSources) {
|
||||
schema.dataSources = schema.dataSources.map((ds: any) => {
|
||||
if (ds.type === 'computed' && typeof ds.compute === 'string') {
|
||||
const functionName = ds.compute as keyof ComputeFunctionMap
|
||||
const computeFunction = computeFunctions[functionName]
|
||||
if (!computeFunction) {
|
||||
console.warn(`Compute function "${functionName}" not found`)
|
||||
}
|
||||
return {
|
||||
...ds,
|
||||
compute: computeFunction || (() => null)
|
||||
}
|
||||
}
|
||||
return ds
|
||||
})
|
||||
}
|
||||
|
||||
if (schema.components) {
|
||||
schema.components = hydrateComponents(schema.components)
|
||||
}
|
||||
|
||||
return schema as PageSchema
|
||||
}
|
||||
|
||||
function hydrateComponents(components: any[]): any[] {
|
||||
return components.map(component => {
|
||||
const hydratedComponent = { ...component }
|
||||
|
||||
if (component.events) {
|
||||
hydratedComponent.events = component.events.map((event: any) => {
|
||||
const hydratedEvent = { ...event }
|
||||
|
||||
if (event.condition && typeof event.condition === 'string') {
|
||||
const functionName = event.condition as keyof ComputeFunctionMap
|
||||
const conditionFunction = computeFunctions[functionName]
|
||||
hydratedEvent.condition = conditionFunction || (() => false)
|
||||
}
|
||||
|
||||
if (event.actions) {
|
||||
hydratedEvent.actions = event.actions.map((action: any) => {
|
||||
if (action.compute && typeof action.compute === 'string') {
|
||||
const functionName = action.compute as keyof ComputeFunctionMap
|
||||
const computeFunction = computeFunctions[functionName]
|
||||
return {
|
||||
...action,
|
||||
compute: computeFunction || (() => null)
|
||||
}
|
||||
}
|
||||
return action
|
||||
})
|
||||
}
|
||||
|
||||
return hydratedEvent
|
||||
})
|
||||
}
|
||||
|
||||
if (component.bindings) {
|
||||
const hydratedBindings: Record<string, any> = {}
|
||||
for (const [key, binding] of Object.entries(component.bindings)) {
|
||||
const b = binding as any
|
||||
if (b.transform && typeof b.transform === 'string') {
|
||||
const functionName = b.transform as keyof ComputeFunctionMap
|
||||
const transformFunction = computeFunctions[functionName]
|
||||
hydratedBindings[key] = {
|
||||
...b,
|
||||
transform: transformFunction || ((x: any) => x)
|
||||
}
|
||||
} else {
|
||||
hydratedBindings[key] = b
|
||||
}
|
||||
}
|
||||
hydratedComponent.bindings = hydratedBindings
|
||||
}
|
||||
|
||||
if (component.children) {
|
||||
hydratedComponent.children = hydrateComponents(component.children)
|
||||
}
|
||||
|
||||
return hydratedComponent
|
||||
})
|
||||
}
|
||||
255
src/schemas/todo-list.json
Normal file
255
src/schemas/todo-list.json
Normal file
@@ -0,0 +1,255 @@
|
||||
{
|
||||
"id": "todo-list",
|
||||
"name": "Todo List",
|
||||
"layout": {
|
||||
"type": "single"
|
||||
},
|
||||
"dataSources": [
|
||||
{
|
||||
"id": "todos",
|
||||
"type": "kv",
|
||||
"key": "app-todos",
|
||||
"defaultValue": [
|
||||
{ "id": 1, "text": "Learn JSON-driven UI", "completed": true },
|
||||
{ "id": 2, "text": "Build atomic components", "completed": false },
|
||||
{ "id": 3, "text": "Create custom hooks", "completed": false }
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "newTodo",
|
||||
"type": "static",
|
||||
"defaultValue": ""
|
||||
},
|
||||
{
|
||||
"id": "stats",
|
||||
"type": "computed",
|
||||
"compute": "computeTodoStats",
|
||||
"dependencies": ["todos"]
|
||||
}
|
||||
],
|
||||
"components": [
|
||||
{
|
||||
"id": "root",
|
||||
"type": "div",
|
||||
"props": {
|
||||
"className": "h-full overflow-auto p-6 bg-gradient-to-br from-background via-background to-primary/5"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"id": "header",
|
||||
"type": "div",
|
||||
"props": { "className": "mb-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "title",
|
||||
"type": "Heading",
|
||||
"props": {
|
||||
"className": "text-4xl font-bold mb-2 bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent",
|
||||
"children": "Task Manager"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "subtitle",
|
||||
"type": "Text",
|
||||
"props": {
|
||||
"className": "text-muted-foreground",
|
||||
"children": "Built entirely from JSON schema"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "stats-row",
|
||||
"type": "div",
|
||||
"props": { "className": "grid grid-cols-1 md:grid-cols-3 gap-4 mb-6 max-w-3xl" },
|
||||
"children": [
|
||||
{
|
||||
"id": "stat-total",
|
||||
"type": "Card",
|
||||
"props": { "className": "bg-card/50 backdrop-blur" },
|
||||
"children": [
|
||||
{
|
||||
"id": "stat-total-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "pt-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "stat-total-label",
|
||||
"type": "div",
|
||||
"props": { "className": "text-sm text-muted-foreground mb-1", "children": "Total Tasks" }
|
||||
},
|
||||
{
|
||||
"id": "stat-total-value",
|
||||
"type": "div",
|
||||
"props": { "className": "text-3xl font-bold" },
|
||||
"bindings": {
|
||||
"children": { "source": "stats", "path": "total" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "stat-completed",
|
||||
"type": "Card",
|
||||
"props": { "className": "bg-accent/10 backdrop-blur border-accent/20" },
|
||||
"children": [
|
||||
{
|
||||
"id": "stat-completed-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "pt-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "stat-completed-label",
|
||||
"type": "div",
|
||||
"props": { "className": "text-sm text-muted-foreground mb-1", "children": "Completed" }
|
||||
},
|
||||
{
|
||||
"id": "stat-completed-value",
|
||||
"type": "div",
|
||||
"props": { "className": "text-3xl font-bold text-accent" },
|
||||
"bindings": {
|
||||
"children": { "source": "stats", "path": "completed" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "stat-remaining",
|
||||
"type": "Card",
|
||||
"props": { "className": "bg-primary/5 backdrop-blur border-primary/20" },
|
||||
"children": [
|
||||
{
|
||||
"id": "stat-remaining-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "pt-6" },
|
||||
"children": [
|
||||
{
|
||||
"id": "stat-remaining-label",
|
||||
"type": "div",
|
||||
"props": { "className": "text-sm text-muted-foreground mb-1", "children": "Remaining" }
|
||||
},
|
||||
{
|
||||
"id": "stat-remaining-value",
|
||||
"type": "div",
|
||||
"props": { "className": "text-3xl font-bold text-primary" },
|
||||
"bindings": {
|
||||
"children": { "source": "stats", "path": "remaining" }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "main-card",
|
||||
"type": "Card",
|
||||
"props": { "className": "max-w-3xl" },
|
||||
"children": [
|
||||
{
|
||||
"id": "card-header",
|
||||
"type": "CardHeader",
|
||||
"children": [
|
||||
{
|
||||
"id": "card-title",
|
||||
"type": "CardTitle",
|
||||
"props": { "children": "Your Tasks" }
|
||||
},
|
||||
{
|
||||
"id": "card-description",
|
||||
"type": "CardDescription",
|
||||
"props": { "children": "Manage your daily tasks efficiently" }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "card-content",
|
||||
"type": "CardContent",
|
||||
"props": { "className": "space-y-4" },
|
||||
"children": [
|
||||
{
|
||||
"id": "input-group",
|
||||
"type": "div",
|
||||
"props": { "className": "flex gap-2" },
|
||||
"children": [
|
||||
{
|
||||
"id": "todo-input",
|
||||
"type": "Input",
|
||||
"props": {
|
||||
"placeholder": "What needs to be done?"
|
||||
},
|
||||
"bindings": {
|
||||
"value": { "source": "newTodo" }
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"event": "change",
|
||||
"actions": [
|
||||
{
|
||||
"id": "update-input",
|
||||
"type": "set-value",
|
||||
"target": "newTodo",
|
||||
"compute": "updateNewTodo"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "add-button",
|
||||
"type": "Button",
|
||||
"props": { "children": "Add Task" },
|
||||
"events": [
|
||||
{
|
||||
"event": "click",
|
||||
"actions": [
|
||||
{
|
||||
"id": "add-todo",
|
||||
"type": "create",
|
||||
"target": "todos",
|
||||
"compute": "computeAddTodo"
|
||||
},
|
||||
{
|
||||
"id": "clear-input",
|
||||
"type": "set-value",
|
||||
"target": "newTodo",
|
||||
"value": ""
|
||||
},
|
||||
{
|
||||
"id": "show-success",
|
||||
"type": "show-toast",
|
||||
"message": "Task added successfully!",
|
||||
"variant": "success"
|
||||
}
|
||||
],
|
||||
"condition": "checkCanAddTodo"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "separator",
|
||||
"type": "Separator",
|
||||
"props": { "className": "my-4" }
|
||||
},
|
||||
{
|
||||
"id": "todo-list",
|
||||
"type": "div",
|
||||
"props": { "className": "space-y-2" },
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"globalActions": []
|
||||
}
|
||||
Reference in New Issue
Block a user