From 5d007721cf180bf93fd496bcf9b247e99bb61f38 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 11:50:47 +0000 Subject: [PATCH] Add JSON UI support for date picker and file upload --- json-components-registry.json | 10 +- src/components/JSONUIShowcasePage.tsx | 6 ++ src/components/atoms/DatePicker.tsx | 2 +- src/components/atoms/FileUpload.tsx | 6 +- src/lib/component-definitions.ts | 28 +++++ src/lib/json-ui/component-registry.ts | 12 ++- src/schemas/page-schemas.ts | 144 ++++++++++++++++++++++++++ src/types/json-ui.ts | 1 + 8 files changed, 196 insertions(+), 13 deletions(-) diff --git a/json-components-registry.json b/json-components-registry.json index bb68d7b..fe5bbaf 100644 --- a/json-components-registry.json +++ b/json-components-registry.json @@ -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-17T22:10:22.582Z", + "lastUpdated": "2026-01-18T11:30:41.908Z", "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": "planned", + "status": "supported", "source": "atoms" }, { @@ -331,7 +331,7 @@ "category": "input", "canHaveChildren": false, "description": "File upload control", - "status": "planned", + "status": "supported", "source": "atoms" }, { @@ -2043,8 +2043,8 @@ ], "statistics": { "total": 219, - "supported": 150, - "planned": 14, + "supported": 152, + "planned": 12, "jsonCompatible": 14, "maybeJsonCompatible": 41, "byCategory": { diff --git a/src/components/JSONUIShowcasePage.tsx b/src/components/JSONUIShowcasePage.tsx index 2b92f00..c532bc8 100644 --- a/src/components/JSONUIShowcasePage.tsx +++ b/src/components/JSONUIShowcasePage.tsx @@ -5,6 +5,7 @@ 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) @@ -24,6 +25,7 @@ export function JSONUIShowcasePage() { Atomic Components + JSON Inputs New Molecules JSON Dashboard JSON Todo List @@ -34,6 +36,10 @@ export function JSONUIShowcasePage() { + + + + diff --git a/src/components/atoms/DatePicker.tsx b/src/components/atoms/DatePicker.tsx index 4dc95a0..f72a790 100644 --- a/src/components/atoms/DatePicker.tsx +++ b/src/components/atoms/DatePicker.tsx @@ -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 diff --git a/src/components/atoms/FileUpload.tsx b/src/components/atoms/FileUpload.tsx index e942685..0df8990 100644 --- a/src/components/atoms/FileUpload.tsx +++ b/src/components/atoms/FileUpload.tsx @@ -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 ( diff --git a/src/lib/component-definitions.ts b/src/lib/component-definitions.ts index 8093398..9c1cad4 100644 --- a/src/lib/component-definitions.ts +++ b/src/lib/component-definitions.ts @@ -6,6 +6,7 @@ export interface ComponentDefinition { category: 'layout' | 'input' | 'display' | 'navigation' | 'feedback' | 'data' | 'custom' icon: string defaultProps?: Record + propSchema?: Record canHaveChildren?: boolean } @@ -97,6 +98,33 @@ 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', diff --git a/src/lib/json-ui/component-registry.ts b/src/lib/json-ui/component-registry.ts index 3e53dfe..33ba1da 100644 --- a/src/lib/json-ui/component-registry.ts +++ b/src/lib/json-ui/component-registry.ts @@ -136,10 +136,14 @@ export const shadcnComponents: UIComponentRegistry = { AvatarImage, } -export const atomComponents: UIComponentRegistry = buildRegistryFromNames( - atomRegistryNames, - AtomComponents as Record> -) +export const atomComponents: UIComponentRegistry = { + ...buildRegistryFromNames( + atomRegistryNames, + AtomComponents as Record> + ), + DatePicker: AtomComponents.DatePicker, + FileUpload: AtomComponents.FileUpload, +} export const moleculeComponents: UIComponentRegistry = buildRegistryFromNames( moleculeRegistryNames, diff --git a/src/schemas/page-schemas.ts b/src/schemas/page-schemas.ts index 46ceb50..541239d 100644 --- a/src/schemas/page-schemas.ts +++ b/src/schemas/page-schemas.ts @@ -73,3 +73,147 @@ export const stateBindingsDemoSchema: PageSchema = { }, ], } + +export const inputComponentsShowcaseSchema: PageSchema = { + id: 'input-components-showcase', + name: 'Input Components Showcase', + layout: { + type: 'single', + }, + dataSources: [ + { + id: 'selectedDate', + type: 'static', + defaultValue: new Date(), + }, + { + id: 'uploadedFiles', + type: 'static', + defaultValue: [], + }, + ], + components: [ + { + id: 'input-showcase-root', + type: 'div', + props: { + className: 'space-y-6 rounded-lg border border-border bg-card p-6', + }, + children: [ + { + id: 'input-showcase-title', + type: 'Heading', + props: { + className: 'text-xl font-semibold', + children: 'Date Picker & File Upload', + }, + }, + { + id: 'input-showcase-date-section', + type: 'div', + props: { + className: 'space-y-3', + }, + children: [ + { + id: 'input-showcase-date-label', + type: 'Text', + props: { + className: 'text-sm font-medium text-muted-foreground', + children: 'Pick a date', + }, + }, + { + id: 'input-showcase-date-picker', + type: 'DatePicker', + 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"', + }, + }, + }, + ], + }, + { + id: 'input-showcase-file-section', + type: 'div', + props: { + className: 'space-y-3', + }, + children: [ + { + id: 'input-showcase-file-label', + type: 'Text', + props: { + className: 'text-sm font-medium text-muted-foreground', + children: 'Upload files', + }, + }, + { + id: 'input-showcase-file-upload', + type: 'FileUpload', + 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"', + }, + }, + }, + ], + }, + ], + }, + ], +} diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index f3ce4c3..987057f 100644 --- a/src/types/json-ui.ts +++ b/src/types/json-ui.ts @@ -2,6 +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' | 'Text' | 'Heading' | 'Label' | 'List' | 'Grid' | 'Stack' | 'Flex' | 'Container' | 'Link' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton'