Compare commits

..

1 Commits

Author SHA1 Message Date
8fd601357d Add JSON UI support for progress indicators 2026-01-18 11:48:06 +00:00
8 changed files with 125 additions and 143 deletions

View File

@@ -2,7 +2,7 @@
"$schema": "./schemas/json-components-registry-schema.json",
"version": "2.0.0",
"description": "Registry of all components in the application",
"lastUpdated": "2026-01-18T11:30:41.908Z",
"lastUpdated": "2026-01-17T22:10:22.582Z",
"categories": {
"layout": "Layout and container components",
"input": "Form inputs and interactive controls",
@@ -322,7 +322,7 @@
"category": "input",
"canHaveChildren": false,
"description": "Date selection input",
"status": "supported",
"status": "planned",
"source": "atoms"
},
{
@@ -331,7 +331,7 @@
"category": "input",
"canHaveChildren": false,
"description": "File upload control",
"status": "supported",
"status": "planned",
"source": "atoms"
},
{
@@ -611,7 +611,7 @@
"category": "display",
"canHaveChildren": false,
"description": "Circular progress indicator",
"status": "planned",
"status": "supported",
"source": "atoms"
},
{
@@ -629,7 +629,7 @@
"category": "display",
"canHaveChildren": false,
"description": "Visual section divider",
"status": "planned",
"status": "supported",
"source": "atoms"
},
{
@@ -719,7 +719,7 @@
"category": "display",
"canHaveChildren": false,
"description": "Linear progress bar",
"status": "planned",
"status": "supported",
"source": "atoms"
},
{
@@ -2043,8 +2043,8 @@
],
"statistics": {
"total": 219,
"supported": 152,
"planned": 12,
"supported": 150,
"planned": 14,
"jsonCompatible": 14,
"maybeJsonCompatible": 41,
"byCategory": {

View File

@@ -5,7 +5,6 @@ import { PageRenderer } from '@/lib/json-ui/page-renderer'
import { hydrateSchema } from '@/schemas/schema-loader'
import todoListJson from '@/schemas/todo-list.json'
import newMoleculesShowcaseJson from '@/schemas/new-molecules-showcase.json'
import { inputComponentsShowcaseSchema } from '@/schemas/page-schemas'
const todoListSchema = hydrateSchema(todoListJson)
const newMoleculesShowcaseSchema = hydrateSchema(newMoleculesShowcaseJson)
@@ -25,7 +24,6 @@ export function JSONUIShowcasePage() {
</div>
<TabsList className="w-full justify-start">
<TabsTrigger value="atomic">Atomic Components</TabsTrigger>
<TabsTrigger value="inputs">JSON Inputs</TabsTrigger>
<TabsTrigger value="molecules">New Molecules</TabsTrigger>
<TabsTrigger value="dashboard">JSON Dashboard</TabsTrigger>
<TabsTrigger value="todos">JSON Todo List</TabsTrigger>
@@ -36,10 +34,6 @@ export function JSONUIShowcasePage() {
<TabsContent value="atomic" className="h-full m-0 data-[state=active]:block">
<AtomicComponentDemo />
</TabsContent>
<TabsContent value="inputs" className="h-full m-0 data-[state=active]:block">
<PageRenderer schema={inputComponentsShowcaseSchema} />
</TabsContent>
<TabsContent value="molecules" className="h-full m-0 data-[state=active]:block">
<PageRenderer schema={newMoleculesShowcaseSchema} />

View File

@@ -7,7 +7,7 @@ import { cn } from '@/lib/utils'
interface DatePickerProps {
value?: Date
onChange?: (date: Date | undefined) => void
onChange: (date: Date | undefined) => void
placeholder?: string
disabled?: boolean
className?: string

View File

@@ -6,7 +6,7 @@ interface FileUploadProps {
accept?: string
multiple?: boolean
maxSize?: number
onFilesSelected?: (files: File[]) => void
onFilesSelected: (files: File[]) => void
disabled?: boolean
className?: string
}
@@ -34,7 +34,7 @@ export function FileUpload({
})
setSelectedFiles(validFiles)
onFilesSelected?.(validFiles)
onFilesSelected(validFiles)
}
const handleDrop = (e: React.DragEvent) => {
@@ -59,7 +59,7 @@ export function FileUpload({
const removeFile = (index: number) => {
const newFiles = selectedFiles.filter((_, i) => i !== index)
setSelectedFiles(newFiles)
onFilesSelected?.(newFiles)
onFilesSelected(newFiles)
}
return (

View File

@@ -6,10 +6,16 @@ export interface ComponentDefinition {
category: 'layout' | 'input' | 'display' | 'navigation' | 'feedback' | 'data' | 'custom'
icon: string
defaultProps?: Record<string, any>
propSchema?: Record<string, { type: string; description?: string; required?: boolean }>
propSchema?: Record<string, ComponentPropSchema>
canHaveChildren?: boolean
}
export interface ComponentPropSchema {
type: 'string' | 'number' | 'boolean' | 'enum'
description?: string
options?: string[]
}
export const componentDefinitions: ComponentDefinition[] = [
// Layout Components
{
@@ -98,33 +104,6 @@ export const componentDefinitions: ComponentDefinition[] = [
icon: 'CaretDown',
defaultProps: { placeholder: 'Choose option...' }
},
{
type: 'DatePicker',
label: 'Date Picker',
category: 'input',
icon: 'Calendar',
defaultProps: { placeholder: 'Pick a date' },
propSchema: {
value: { type: 'date', description: 'Selected date value' },
placeholder: { type: 'string', description: 'Placeholder when no date is selected' },
disabled: { type: 'boolean', description: 'Disable the date picker' },
onChange: { type: 'event', description: 'Fires when the date selection changes' }
}
},
{
type: 'FileUpload',
label: 'File Upload',
category: 'input',
icon: 'Upload',
defaultProps: { accept: '', multiple: false },
propSchema: {
accept: { type: 'string', description: 'Accepted file types (comma-separated)' },
multiple: { type: 'boolean', description: 'Allow multiple file selections' },
maxSize: { type: 'number', description: 'Maximum file size in bytes' },
disabled: { type: 'boolean', description: 'Disable file uploads' },
onFilesSelected: { type: 'event', description: 'Fires when files are selected' }
}
},
{
type: 'Checkbox',
label: 'Checkbox',
@@ -222,6 +201,40 @@ export const componentDefinitions: ComponentDefinition[] = [
icon: 'CircleNotch',
defaultProps: { value: 50 }
},
{
type: 'ProgressBar',
label: 'Progress Bar',
category: 'display',
icon: 'ChartBar',
defaultProps: { value: 60, max: 100, size: 'md', variant: 'default', showLabel: false },
propSchema: {
value: { type: 'number', description: 'Current progress value.' },
max: { type: 'number', description: 'Maximum progress value.' },
size: { type: 'enum', options: ['sm', 'md', 'lg'], description: 'Height size of the bar.' },
variant: {
type: 'enum',
options: ['default', 'accent', 'destructive'],
description: 'Color variant for the progress fill.',
},
showLabel: { type: 'boolean', description: 'Show percentage text below the bar.' },
className: { type: 'string', description: 'Additional class names for the container.' },
},
},
{
type: 'CircularProgress',
label: 'Circular Progress',
category: 'display',
icon: 'CircleNotch',
defaultProps: { value: 72, max: 100, size: 'md', showLabel: true },
propSchema: {
value: { type: 'number', description: 'Current progress value.' },
max: { type: 'number', description: 'Maximum progress value.' },
size: { type: 'enum', options: ['sm', 'md', 'lg', 'xl'], description: 'Rendered size.' },
showLabel: { type: 'boolean', description: 'Show percentage label in the center.' },
strokeWidth: { type: 'number', description: 'Override the default stroke width.' },
className: { type: 'string', description: 'Additional class names for the wrapper.' },
},
},
{
type: 'Spinner',
label: 'Spinner',
@@ -243,6 +256,22 @@ export const componentDefinitions: ComponentDefinition[] = [
icon: 'Minus',
defaultProps: {}
},
{
type: 'Divider',
label: 'Divider',
category: 'display',
icon: 'Minus',
defaultProps: { orientation: 'horizontal', decorative: true },
propSchema: {
orientation: {
type: 'enum',
options: ['horizontal', 'vertical'],
description: 'Divider orientation.',
},
decorative: { type: 'boolean', description: 'Whether the divider is decorative.' },
className: { type: 'string', description: 'Additional class names for the divider.' },
},
},
// Navigation Components
{
type: 'Link',

View File

@@ -141,8 +141,9 @@ export const atomComponents: UIComponentRegistry = {
atomRegistryNames,
AtomComponents as Record<string, ComponentType<any>>
),
DatePicker: AtomComponents.DatePicker,
FileUpload: AtomComponents.FileUpload,
CircularProgress: AtomComponents.CircularProgress,
Divider: AtomComponents.Divider,
ProgressBar: AtomComponents.ProgressBar,
}
export const moleculeComponents: UIComponentRegistry = buildRegistryFromNames(

View File

@@ -74,141 +74,100 @@ export const stateBindingsDemoSchema: PageSchema = {
],
}
export const inputComponentsShowcaseSchema: PageSchema = {
id: 'input-components-showcase',
name: 'Input Components Showcase',
export const progressIndicatorsDemoSchema: PageSchema = {
id: 'progress-indicators-demo',
name: 'Progress Indicators Demo',
layout: {
type: 'single',
},
dataSources: [
{
id: 'selectedDate',
type: 'static',
defaultValue: new Date(),
},
{
id: 'uploadedFiles',
type: 'static',
defaultValue: [],
},
],
dataSources: [],
components: [
{
id: 'input-showcase-root',
id: 'progress-demo-root',
type: 'div',
props: {
className: 'space-y-6 rounded-lg border border-border bg-card p-6',
},
children: [
{
id: 'input-showcase-title',
id: 'progress-demo-title',
type: 'Heading',
props: {
className: 'text-xl font-semibold',
children: 'Date Picker & File Upload',
children: 'Progress Indicators',
},
},
{
id: 'input-showcase-date-section',
id: 'progress-demo-subtitle',
type: 'Text',
props: {
className: 'text-sm text-muted-foreground',
children: 'Circular and linear progress components with JSON-friendly props.',
},
},
{
id: 'progress-demo-circular-row',
type: 'div',
props: {
className: 'space-y-3',
className: 'flex flex-wrap items-center gap-6',
},
children: [
{
id: 'input-showcase-date-label',
type: 'Text',
id: 'progress-demo-circular-primary',
type: 'CircularProgress',
props: {
className: 'text-sm font-medium text-muted-foreground',
children: 'Pick a date',
value: 72,
size: 'md',
showLabel: true,
},
},
{
id: 'input-showcase-date-picker',
type: 'DatePicker',
id: 'progress-demo-circular-large',
type: 'CircularProgress',
props: {
placeholder: 'Select a date',
},
bindings: {
value: {
source: 'selectedDate',
},
},
events: {
onChange: {
actions: [
{
id: 'update-selected-date',
type: 'set-value',
target: 'selectedDate',
expression: 'event',
},
],
},
},
},
{
id: 'input-showcase-date-value',
type: 'Text',
props: {
className: 'text-sm text-muted-foreground',
},
bindings: {
children: {
source: 'selectedDate',
transform: 'data ? `Selected: ${new Date(data).toLocaleDateString()}` : "Selected: none"',
},
value: 45,
size: 'lg',
showLabel: true,
},
},
],
},
{
id: 'input-showcase-file-section',
id: 'progress-demo-divider',
type: 'Divider',
props: {
orientation: 'horizontal',
decorative: true,
className: 'my-2',
},
},
{
id: 'progress-demo-linear-stack',
type: 'div',
props: {
className: 'space-y-3',
className: 'space-y-4',
},
children: [
{
id: 'input-showcase-file-label',
type: 'Text',
id: 'progress-demo-linear-accent',
type: 'ProgressBar',
props: {
className: 'text-sm font-medium text-muted-foreground',
children: 'Upload files',
value: 60,
max: 100,
size: 'md',
variant: 'accent',
showLabel: true,
},
},
{
id: 'input-showcase-file-upload',
type: 'FileUpload',
id: 'progress-demo-linear-compact',
type: 'ProgressBar',
props: {
accept: '.pdf,.png,.jpg,.jpeg',
multiple: true,
maxSize: 5000000,
},
events: {
onFilesSelected: {
actions: [
{
id: 'update-uploaded-files',
type: 'set-value',
target: 'uploadedFiles',
expression: 'event',
},
],
},
},
},
{
id: 'input-showcase-file-value',
type: 'Text',
props: {
className: 'text-sm text-muted-foreground',
},
bindings: {
children: {
source: 'uploadedFiles',
transform: 'Array.isArray(data) && data.length ? `Selected: ${data.map((file) => file.name).join(", ")}` : "Selected: none"',
},
value: 35,
max: 100,
size: 'sm',
variant: 'default',
showLabel: false,
},
},
],

View File

@@ -2,8 +2,7 @@ export type ComponentType =
| 'div' | 'section' | 'article' | 'header' | 'footer' | 'main'
| 'Button' | 'Card' | 'CardHeader' | 'CardTitle' | 'CardDescription' | 'CardContent' | 'CardFooter'
| 'Input' | 'TextArea' | 'Textarea' | 'Select' | 'Checkbox' | 'Radio' | 'Switch' | 'Slider' | 'NumberInput'
| 'DatePicker' | 'FileUpload'
| 'Badge' | 'Progress' | 'Separator' | 'Tabs' | 'TabsContent' | 'TabsList' | 'TabsTrigger' | 'Dialog'
| 'Badge' | 'CircularProgress' | 'Divider' | 'Progress' | 'ProgressBar' | 'Separator' | 'Tabs' | 'TabsContent' | 'TabsList' | 'TabsTrigger' | 'Dialog'
| 'Text' | 'Heading' | 'Label' | 'List' | 'Grid' | 'Stack' | 'Flex' | 'Container'
| 'Link' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton'
| 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge'