From 0d82406e5f8ddb0d9b701574977c8c2e291de9d6 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 11:38:50 +0000 Subject: [PATCH 1/6] Add JSON UI support for feedback atoms --- json-components-registry.json | 6 +- src/components/JSONUIShowcasePage.tsx | 6 ++ src/lib/component-definitions.ts | 118 ++++++++++++++++++++++++++ src/schemas/page-schemas.ts | 114 +++++++++++++++++++++++++ src/types/json-ui.ts | 1 + 5 files changed, 242 insertions(+), 3 deletions(-) diff --git a/json-components-registry.json b/json-components-registry.json index bb68d7b..100f715 100644 --- a/json-components-registry.json +++ b/json-components-registry.json @@ -1088,7 +1088,7 @@ "category": "feedback", "canHaveChildren": false, "description": "Error state badge", - "status": "planned", + "status": "supported", "source": "atoms" }, { @@ -1164,7 +1164,7 @@ "category": "feedback", "canHaveChildren": true, "description": "Toast notification", - "status": "planned", + "status": "supported", "source": "atoms" }, { @@ -1201,7 +1201,7 @@ "category": "feedback", "canHaveChildren": false, "description": "Status indicator icon", - "status": "planned", + "status": "supported", "source": "atoms" }, { diff --git a/src/components/JSONUIShowcasePage.tsx b/src/components/JSONUIShowcasePage.tsx index 2b92f00..ad511da 100644 --- a/src/components/JSONUIShowcasePage.tsx +++ b/src/components/JSONUIShowcasePage.tsx @@ -3,6 +3,7 @@ import { AtomicComponentDemo } from '@/components/AtomicComponentDemo' import { DashboardDemoPage } from '@/components/DashboardDemoPage' import { PageRenderer } from '@/lib/json-ui/page-renderer' import { hydrateSchema } from '@/schemas/schema-loader' +import { feedbackAtomsDemoSchema } from '@/schemas/page-schemas' import todoListJson from '@/schemas/todo-list.json' import newMoleculesShowcaseJson from '@/schemas/new-molecules-showcase.json' @@ -24,6 +25,7 @@ export function JSONUIShowcasePage() { Atomic Components + Feedback Atoms New Molecules JSON Dashboard JSON Todo List @@ -34,6 +36,10 @@ export function JSONUIShowcasePage() { + + + + diff --git a/src/lib/component-definitions.ts b/src/lib/component-definitions.ts index 8093398..d9d8c71 100644 --- a/src/lib/component-definitions.ts +++ b/src/lib/component-definitions.ts @@ -7,6 +7,23 @@ export interface ComponentDefinition { icon: string defaultProps?: Record canHaveChildren?: boolean + props?: ComponentPropDefinition[] + events?: ComponentEventDefinition[] +} + +export interface ComponentPropDefinition { + name: string + type: string + description: string + required?: boolean + defaultValue?: string + options?: string[] + supportsBinding?: boolean +} + +export interface ComponentEventDefinition { + name: string + description: string } export const componentDefinitions: ComponentDefinition[] = [ @@ -256,6 +273,107 @@ export const componentDefinitions: ComponentDefinition[] = [ icon: 'Circle', defaultProps: { status: 'active', children: 'Active' } }, + { + type: 'ErrorBadge', + label: 'Error Badge', + category: 'feedback', + icon: 'WarningCircle', + defaultProps: { count: 3, variant: 'destructive', size: 'md' }, + props: [ + { + name: 'count', + type: 'number', + description: 'Number of errors to display. Hidden when set to 0.', + required: true, + supportsBinding: true, + }, + { + name: 'variant', + type: 'string', + description: 'Visual variant for the badge.', + defaultValue: 'destructive', + options: ['default', 'destructive'], + }, + { + name: 'size', + type: 'string', + description: 'Badge size.', + defaultValue: 'md', + options: ['sm', 'md'], + }, + ], + }, + { + type: 'Notification', + label: 'Notification', + category: 'feedback', + icon: 'Info', + defaultProps: { type: 'info', title: 'Notification', message: 'Details go here.' }, + props: [ + { + name: 'type', + type: 'string', + description: 'Notification style variant.', + required: true, + options: ['info', 'success', 'warning', 'error'], + }, + { + name: 'title', + type: 'string', + description: 'Primary notification title.', + required: true, + supportsBinding: true, + }, + { + name: 'message', + type: 'string', + description: 'Optional supporting message text.', + supportsBinding: true, + }, + { + name: 'className', + type: 'string', + description: 'Optional custom classes for spacing or layout tweaks.', + }, + ], + events: [ + { + name: 'onClose', + description: 'Fires when the close button is clicked. Bind to dismiss or trigger an action.', + }, + ], + }, + { + type: 'StatusIcon', + label: 'Status Icon', + category: 'feedback', + icon: 'CheckCircle', + defaultProps: { type: 'saved', size: 14, animate: false }, + props: [ + { + name: 'type', + type: 'string', + description: 'Status icon style.', + required: true, + supportsBinding: true, + options: ['saved', 'synced'], + }, + { + name: 'size', + type: 'number', + description: 'Icon size in pixels.', + defaultValue: '14', + supportsBinding: true, + }, + { + name: 'animate', + type: 'boolean', + description: 'Applies entry animation when true.', + defaultValue: 'false', + supportsBinding: true, + }, + ], + }, // Data Components { type: 'List', diff --git a/src/schemas/page-schemas.ts b/src/schemas/page-schemas.ts index 46ceb50..025fb26 100644 --- a/src/schemas/page-schemas.ts +++ b/src/schemas/page-schemas.ts @@ -73,3 +73,117 @@ export const stateBindingsDemoSchema: PageSchema = { }, ], } + +export const feedbackAtomsDemoSchema: PageSchema = { + id: 'feedback-atoms-demo', + name: 'Feedback Atoms Demo', + layout: { + type: 'single', + }, + dataSources: [ + { + id: 'errorCount', + type: 'static', + defaultValue: 3, + }, + { + id: 'showNotification', + type: 'static', + defaultValue: true, + }, + { + id: 'statusType', + type: 'static', + defaultValue: 'saved', + }, + ], + components: [ + { + id: 'feedback-atoms-root', + type: 'div', + props: { + className: 'space-y-6 rounded-lg border border-border bg-card p-6', + }, + children: [ + { + id: 'feedback-atoms-title', + type: 'Heading', + props: { + className: 'text-lg font-semibold', + children: 'Feedback Atoms', + }, + }, + { + id: 'feedback-atoms-row', + type: 'div', + props: { + className: 'flex flex-wrap items-center gap-6', + }, + children: [ + { + id: 'feedback-atoms-status-icon', + type: 'StatusIcon', + props: { + animate: true, + size: 18, + }, + bindings: { + type: { + source: 'statusType', + sourceType: 'data', + }, + }, + }, + { + id: 'feedback-atoms-badge-wrapper', + type: 'div', + props: { + className: 'relative h-10 w-10 rounded-full bg-muted', + }, + children: [ + { + id: 'feedback-atoms-error-badge', + type: 'ErrorBadge', + props: { + variant: 'destructive', + size: 'md', + }, + bindings: { + count: { + source: 'errorCount', + sourceType: 'data', + }, + }, + }, + ], + }, + ], + }, + { + id: 'feedback-atoms-notification', + type: 'Notification', + props: { + type: 'info', + title: 'Heads up!', + message: 'You have unsent changes ready to sync.', + }, + conditional: { + if: 'data.showNotification', + }, + events: { + onClose: { + actions: [ + { + id: 'dismiss-notification', + type: 'set-value', + target: 'showNotification', + value: false, + }, + ], + }, + }, + }, + ], + }, + ], +} diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index f3ce4c3..9b64f2e 100644 --- a/src/types/json-ui.ts +++ b/src/types/json-ui.ts @@ -6,6 +6,7 @@ export type ComponentType = | 'Text' | 'Heading' | 'Label' | 'List' | 'Grid' | 'Stack' | 'Flex' | 'Container' | 'Link' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton' | 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge' + | 'ErrorBadge' | 'Notification' | 'StatusIcon' | 'Table' | 'KeyValue' | 'StatCard' | 'DataCard' | 'SearchInput' | 'ActionBar' | 'AppBranding' | 'LabelWithBadge' | 'EmptyEditorState' | 'LoadingFallback' | 'LoadingState' | 'NavigationGroupHeader' From 64c3b5b12b43817bb5fb1ec2ee6887d0ef84223a Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 11:40:46 +0000 Subject: [PATCH 2/6] Convert page schemas to JSON --- src/components/JSONUIShowcasePage.tsx | 3 +- src/schemas/page-schemas.json | 212 +++++++++++++++++++++++++ src/schemas/page-schemas.ts | 213 -------------------------- 3 files changed, 214 insertions(+), 214 deletions(-) create mode 100644 src/schemas/page-schemas.json delete mode 100644 src/schemas/page-schemas.ts diff --git a/src/components/JSONUIShowcasePage.tsx b/src/components/JSONUIShowcasePage.tsx index 1cd4c08..e2aadbe 100644 --- a/src/components/JSONUIShowcasePage.tsx +++ b/src/components/JSONUIShowcasePage.tsx @@ -3,12 +3,13 @@ import { AtomicComponentDemo } from '@/components/AtomicComponentDemo' import { DashboardDemoPage } from '@/components/DashboardDemoPage' import { PageRenderer } from '@/lib/json-ui/page-renderer' import { hydrateSchema } from '@/schemas/schema-loader' -import { dataComponentsDemoSchema } from '@/schemas/page-schemas' +import pageSchemasJson from '@/schemas/page-schemas.json' import todoListJson from '@/schemas/todo-list.json' import newMoleculesShowcaseJson from '@/schemas/new-molecules-showcase.json' const todoListSchema = hydrateSchema(todoListJson) const newMoleculesShowcaseSchema = hydrateSchema(newMoleculesShowcaseJson) +const dataComponentsDemoSchema = hydrateSchema(pageSchemasJson.dataComponentsDemoSchema) export function JSONUIShowcasePage() { return ( diff --git a/src/schemas/page-schemas.json b/src/schemas/page-schemas.json new file mode 100644 index 0000000..0a38f9a --- /dev/null +++ b/src/schemas/page-schemas.json @@ -0,0 +1,212 @@ +{ + "stateBindingsDemoSchema": { + "id": "state-bindings-demo", + "name": "State & Bindings Demo", + "layout": { + "type": "single" + }, + "dataSources": [ + { + "id": "statusItems", + "type": "static", + "defaultValue": ["KV Ready", "Components Loaded", "Sync Enabled"] + } + ], + "components": [ + { + "id": "state-demo-root", + "type": "div", + "props": { + "className": "space-y-4 rounded-lg border border-border bg-card p-6" + }, + "children": [ + { + "id": "state-demo-title", + "type": "Heading", + "props": { + "className": "text-xl font-semibold", + "children": "Renderer State Binding Demo" + } + }, + { + "id": "state-demo-theme", + "type": "Text", + "props": { + "className": "text-sm text-muted-foreground" + }, + "bindings": { + "children": { + "sourceType": "state", + "source": "settings", + "path": "settings.theme" + } + } + }, + { + "id": "state-demo-list", + "type": "div", + "props": { + "className": "space-y-2" + }, + "loop": { + "source": "statusItems", + "itemVar": "statusItem" + }, + "children": [ + { + "id": "state-demo-list-item", + "type": "Text", + "props": { + "className": "text-sm" + }, + "bindings": { + "children": { + "sourceType": "bindings", + "source": "statusItem" + } + } + } + ] + } + ] + } + ] + }, + "dataComponentsDemoSchema": { + "id": "data-components-demo", + "name": "Data Components Demo", + "layout": { + "type": "single" + }, + "dataSources": [ + { + "id": "metricCards", + "type": "static", + "defaultValue": [ + { "label": "Active Users", "value": 1248, "trend": { "value": 12.4, "direction": "up" } }, + { "label": "Churn Rate", "value": "3.2%", "trend": { "value": 1.1, "direction": "down" } }, + { "label": "Net Revenue", "value": "$48.3k", "trend": { "value": 6.8, "direction": "up" } } + ] + }, + { + "id": "tableColumns", + "type": "static", + "defaultValue": [ + { "key": "initiative", "header": "Initiative" }, + { "key": "owner", "header": "Owner" }, + { "key": "status", "header": "Status" } + ] + }, + { + "id": "tableRows", + "type": "static", + "defaultValue": [ + { "initiative": "Landing Page", "owner": "Avery", "status": "In Progress" }, + { "initiative": "Retention Emails", "owner": "Jordan", "status": "Review" }, + { "initiative": "Billing Update", "owner": "Riley", "status": "Done" } + ] + }, + { + "id": "listItems", + "type": "static", + "defaultValue": ["Prepare briefing deck", "Confirm stakeholder approvals", "Publish roadmap update"] + }, + { + "id": "timelineItems", + "type": "static", + "defaultValue": [ + { + "title": "Kickoff", + "description": "Align on scope and milestones", + "timestamp": "Mon 9:00 AM", + "status": "completed" + }, + { + "title": "Execution", + "description": "Deliver initial workstream", + "timestamp": "Tue 11:00 AM", + "status": "current" + }, + { + "title": "Review", + "description": "Stakeholder walkthrough", + "timestamp": "Thu 3:00 PM", + "status": "pending" + } + ] + } + ], + "components": [ + { + "id": "data-components-root", + "type": "div", + "props": { + "className": "space-y-6 rounded-lg border border-border bg-card p-6" + }, + "children": [ + { + "id": "data-components-title", + "type": "Heading", + "props": { + "className": "text-xl font-semibold", + "children": "Data Components Showcase" + } + }, + { + "id": "data-components-metrics-grid", + "type": "div", + "props": { + "className": "grid gap-4 md:grid-cols-3" + }, + "loop": { + "source": "metricCards", + "itemVar": "metricCard" + }, + "children": [ + { + "id": "data-components-metric-card", + "type": "MetricCard", + "bindings": { + "label": { "sourceType": "bindings", "source": "metricCard", "path": "label" }, + "value": { "sourceType": "bindings", "source": "metricCard", "path": "value" }, + "trend": { "sourceType": "bindings", "source": "metricCard", "path": "trend" } + } + } + ] + }, + { + "id": "data-components-table", + "type": "DataTable", + "props": { + "className": "bg-background", + "emptyMessage": "No initiatives found" + }, + "bindings": { + "columns": { "source": "tableColumns", "sourceType": "data" }, + "data": { "source": "tableRows", "sourceType": "data" } + } + }, + { + "id": "data-components-list", + "type": "DataList", + "props": { + "className": "space-y-3", + "itemClassName": "rounded-md border border-border bg-card/50 px-4 py-2 text-sm", + "emptyMessage": "No action items" + }, + "bindings": { + "items": { "source": "listItems", "sourceType": "data" } + } + }, + { + "id": "data-components-timeline", + "type": "Timeline", + "bindings": { + "items": { "source": "timelineItems", "sourceType": "data" } + } + } + ] + } + ] + } +} diff --git a/src/schemas/page-schemas.ts b/src/schemas/page-schemas.ts deleted file mode 100644 index 68299c8..0000000 --- a/src/schemas/page-schemas.ts +++ /dev/null @@ -1,213 +0,0 @@ -import type { PageSchema } from '@/types/json-ui' - -export const stateBindingsDemoSchema: PageSchema = { - id: 'state-bindings-demo', - name: 'State & Bindings Demo', - layout: { - type: 'single', - }, - dataSources: [ - { - id: 'statusItems', - type: 'static', - defaultValue: ['KV Ready', 'Components Loaded', 'Sync Enabled'], - }, - ], - components: [ - { - id: 'state-demo-root', - type: 'div', - props: { - className: 'space-y-4 rounded-lg border border-border bg-card p-6', - }, - children: [ - { - id: 'state-demo-title', - type: 'Heading', - props: { - className: 'text-xl font-semibold', - children: 'Renderer State Binding Demo', - }, - }, - { - id: 'state-demo-theme', - type: 'Text', - props: { - className: 'text-sm text-muted-foreground', - }, - bindings: { - children: { - sourceType: 'state', - source: 'settings', - path: 'settings.theme', - }, - }, - }, - { - id: 'state-demo-list', - type: 'div', - props: { - className: 'space-y-2', - }, - loop: { - source: 'statusItems', - itemVar: 'statusItem', - }, - children: [ - { - id: 'state-demo-list-item', - type: 'Text', - props: { - className: 'text-sm', - }, - bindings: { - children: { - sourceType: 'bindings', - source: 'statusItem', - }, - }, - }, - ], - }, - ], - }, - ], -} - -export const dataComponentsDemoSchema: PageSchema = { - id: 'data-components-demo', - name: 'Data Components Demo', - layout: { - type: 'single', - }, - dataSources: [ - { - id: 'metricCards', - type: 'static', - defaultValue: [ - { label: 'Active Users', value: 1248, trend: { value: 12.4, direction: 'up' } }, - { label: 'Churn Rate', value: '3.2%', trend: { value: 1.1, direction: 'down' } }, - { label: 'Net Revenue', value: '$48.3k', trend: { value: 6.8, direction: 'up' } }, - ], - }, - { - id: 'tableColumns', - type: 'static', - defaultValue: [ - { key: 'initiative', header: 'Initiative' }, - { key: 'owner', header: 'Owner' }, - { key: 'status', header: 'Status' }, - ], - }, - { - id: 'tableRows', - type: 'static', - defaultValue: [ - { initiative: 'Landing Page', owner: 'Avery', status: 'In Progress' }, - { initiative: 'Retention Emails', owner: 'Jordan', status: 'Review' }, - { initiative: 'Billing Update', owner: 'Riley', status: 'Done' }, - ], - }, - { - id: 'listItems', - type: 'static', - defaultValue: ['Prepare briefing deck', 'Confirm stakeholder approvals', 'Publish roadmap update'], - }, - { - id: 'timelineItems', - type: 'static', - defaultValue: [ - { - title: 'Kickoff', - description: 'Align on scope and milestones', - timestamp: 'Mon 9:00 AM', - status: 'completed', - }, - { - title: 'Execution', - description: 'Deliver initial workstream', - timestamp: 'Tue 11:00 AM', - status: 'current', - }, - { - title: 'Review', - description: 'Stakeholder walkthrough', - timestamp: 'Thu 3:00 PM', - status: 'pending', - }, - ], - }, - ], - components: [ - { - id: 'data-components-root', - type: 'div', - props: { - className: 'space-y-6 rounded-lg border border-border bg-card p-6', - }, - children: [ - { - id: 'data-components-title', - type: 'Heading', - props: { - className: 'text-xl font-semibold', - children: 'Data Components Showcase', - }, - }, - { - id: 'data-components-metrics-grid', - type: 'div', - props: { - className: 'grid gap-4 md:grid-cols-3', - }, - loop: { - source: 'metricCards', - itemVar: 'metricCard', - }, - children: [ - { - id: 'data-components-metric-card', - type: 'MetricCard', - bindings: { - label: { sourceType: 'bindings', source: 'metricCard', path: 'label' }, - value: { sourceType: 'bindings', source: 'metricCard', path: 'value' }, - trend: { sourceType: 'bindings', source: 'metricCard', path: 'trend' }, - }, - }, - ], - }, - { - id: 'data-components-table', - type: 'DataTable', - props: { - className: 'bg-background', - emptyMessage: 'No initiatives found', - }, - bindings: { - columns: { source: 'tableColumns', sourceType: 'data' }, - data: { source: 'tableRows', sourceType: 'data' }, - }, - }, - { - id: 'data-components-list', - type: 'DataList', - props: { - className: 'space-y-3', - itemClassName: 'rounded-md border border-border bg-card/50 px-4 py-2 text-sm', - emptyMessage: 'No action items', - }, - bindings: { - items: { source: 'listItems', sourceType: 'data' }, - }, - }, - { - id: 'data-components-timeline', - type: 'Timeline', - bindings: { - items: { source: 'timelineItems', sourceType: 'data' }, - }, - }, - ], - }, - ], -} From 8945c746cbcf6b357dc9efe6eab0e7e042428aaf Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 11:42:27 +0000 Subject: [PATCH 3/6] Add breadcrumb to JSON component registry --- json-components-registry.json | 39 +++++++-------------------- src/components/atoms/Breadcrumb.tsx | 22 ++++++++------- src/components/atoms/index.ts | 3 +-- src/lib/component-definitions.ts | 13 +++++++++ src/lib/json-ui/component-registry.ts | 5 ++++ src/types/json-ui.ts | 2 +- 6 files changed, 42 insertions(+), 42 deletions(-) diff --git a/json-components-registry.json b/json-components-registry.json index bb68d7b..4d78225 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:24.191Z", "categories": { "layout": "Layout and container components", "input": "Form inputs and interactive controls", @@ -836,29 +836,10 @@ "name": "Breadcrumb", "category": "navigation", "canHaveChildren": false, - "description": "Navigation breadcrumb trail", - "status": "planned", + "description": "Navigation breadcrumb trail using the atoms/BreadcrumbNav JSON-safe items prop", + "status": "supported", "source": "atoms" }, - { - "type": "Breadcrumb", - "name": "Breadcrumb", - "category": "navigation", - "canHaveChildren": false, - "description": "Navigation breadcrumb trail", - "status": "json-compatible", - "source": "molecules", - "jsonCompatible": true - }, - { - "type": "Breadcrumb", - "name": "Breadcrumb", - "category": "navigation", - "canHaveChildren": false, - "description": "Navigation breadcrumb trail", - "status": "planned", - "source": "ui" - }, { "type": "ContextMenu", "name": "ContextMenu", @@ -2042,25 +2023,25 @@ } ], "statistics": { - "total": 219, - "supported": 150, - "planned": 14, - "jsonCompatible": 14, + "total": 217, + "supported": 151, + "planned": 12, + "jsonCompatible": 13, "maybeJsonCompatible": 41, "byCategory": { "layout": 25, "input": 34, "display": 31, - "navigation": 17, + "navigation": 15, "feedback": 23, "data": 20, "custom": 69 }, "bySource": { "atoms": 117, - "molecules": 41, + "molecules": 40, "organisms": 15, - "ui": 46 + "ui": 45 } } } diff --git a/src/components/atoms/Breadcrumb.tsx b/src/components/atoms/Breadcrumb.tsx index 3964503..4cce34b 100644 --- a/src/components/atoms/Breadcrumb.tsx +++ b/src/components/atoms/Breadcrumb.tsx @@ -17,19 +17,19 @@ export function BreadcrumbNav({ items, className }: BreadcrumbNavProps) { ) } + +export const Breadcrumb = BreadcrumbNav diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts index 75fde87..fb449ff 100644 --- a/src/components/atoms/index.ts +++ b/src/components/atoms/index.ts @@ -54,7 +54,7 @@ export { Timestamp } from './Timestamp' export { ScrollArea } from './ScrollArea' export { Tag } from './Tag' -export { BreadcrumbNav } from './Breadcrumb' +export { Breadcrumb, BreadcrumbNav } from './Breadcrumb' export { IconText } from './IconText' export { TextArea } from './TextArea' export { Input } from './Input' @@ -118,4 +118,3 @@ export { MetricDisplay } from './MetricDisplay' export { KeyValue } from './KeyValue' export { EmptyMessage } from './EmptyMessage' export { StepIndicator } from './StepIndicator' - diff --git a/src/lib/component-definitions.ts b/src/lib/component-definitions.ts index 8093398..714a816 100644 --- a/src/lib/component-definitions.ts +++ b/src/lib/component-definitions.ts @@ -224,6 +224,19 @@ export const componentDefinitions: ComponentDefinition[] = [ canHaveChildren: true, defaultProps: { href: '#', children: 'Link' } }, + { + type: 'Breadcrumb', + label: 'Breadcrumb', + category: 'navigation', + icon: 'CaretRight', + canHaveChildren: false, + defaultProps: { + items: [ + { label: 'Home', href: '/' }, + { label: 'Current' } + ] + } + }, // Feedback Components { type: 'Alert', diff --git a/src/lib/json-ui/component-registry.ts b/src/lib/json-ui/component-registry.ts index 3e53dfe..da23ed8 100644 --- a/src/lib/json-ui/component-registry.ts +++ b/src/lib/json-ui/component-registry.ts @@ -141,6 +141,11 @@ export const atomComponents: UIComponentRegistry = buildRegistryFromNames( AtomComponents as Record> ) +const breadcrumbComponent = AtomComponents.Breadcrumb ?? AtomComponents.BreadcrumbNav +if (breadcrumbComponent) { + atomComponents.Breadcrumb = breadcrumbComponent as ComponentType +} + export const moleculeComponents: UIComponentRegistry = buildRegistryFromNames( moleculeRegistryNames, MoleculeComponents as Record> diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index f3ce4c3..c3eb638 100644 --- a/src/types/json-ui.ts +++ b/src/types/json-ui.ts @@ -4,7 +4,7 @@ export type ComponentType = | 'Input' | 'TextArea' | 'Textarea' | 'Select' | 'Checkbox' | 'Radio' | 'Switch' | 'Slider' | 'NumberInput' | 'Badge' | 'Progress' | 'Separator' | 'Tabs' | 'TabsContent' | 'TabsList' | 'TabsTrigger' | 'Dialog' | 'Text' | 'Heading' | 'Label' | 'List' | 'Grid' | 'Stack' | 'Flex' | 'Container' - | 'Link' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton' + | 'Link' | 'Breadcrumb' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton' | 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge' | 'Table' | 'KeyValue' | 'StatCard' | 'DataCard' | 'SearchInput' | 'ActionBar' | 'AppBranding' | 'LabelWithBadge' | 'EmptyEditorState' | 'LoadingFallback' | 'LoadingState' | 'NavigationGroupHeader' From 53fdc3892d365b5bb514c1a3bc6c30edd5273c5e Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 11:53:08 +0000 Subject: [PATCH 4/6] Convert component definitions to JSON --- src/components/atoms/ComponentPaletteItem.tsx | 2 +- src/components/atoms/ComponentTreeNode.tsx | 2 +- src/components/molecules/CanvasRenderer.tsx | 2 +- src/components/molecules/ComponentPalette.tsx | 3 +- src/components/molecules/PropertyEditor.tsx | 2 +- .../organisms/SchemaEditorLayout.tsx | 2 +- .../organisms/SchemaEditorSidebar.tsx | 2 +- .../schema-editor/SchemaEditorWorkspace.tsx | 2 +- src/lib/component-definition-types.ts | 27 + src/lib/component-definition-utils.ts | 13 + src/lib/component-definitions.json | 476 ++++++++++++++++ src/lib/component-definitions.ts | 519 ------------------ 12 files changed, 525 insertions(+), 527 deletions(-) create mode 100644 src/lib/component-definition-types.ts create mode 100644 src/lib/component-definition-utils.ts create mode 100644 src/lib/component-definitions.json delete mode 100644 src/lib/component-definitions.ts diff --git a/src/components/atoms/ComponentPaletteItem.tsx b/src/components/atoms/ComponentPaletteItem.tsx index 99c2b48..5bc47e1 100644 --- a/src/components/atoms/ComponentPaletteItem.tsx +++ b/src/components/atoms/ComponentPaletteItem.tsx @@ -1,4 +1,4 @@ -import { ComponentDefinition } from '@/lib/component-definitions' +import { ComponentDefinition } from '@/lib/component-definition-types' import { Card } from '@/components/ui/card' import * as Icons from '@phosphor-icons/react' import { cn } from '@/lib/utils' diff --git a/src/components/atoms/ComponentTreeNode.tsx b/src/components/atoms/ComponentTreeNode.tsx index 8b42047..1655ea2 100644 --- a/src/components/atoms/ComponentTreeNode.tsx +++ b/src/components/atoms/ComponentTreeNode.tsx @@ -1,5 +1,5 @@ import { UIComponent } from '@/types/json-ui' -import { getComponentDef } from '@/lib/component-definitions' +import { getComponentDef } from '@/lib/component-definition-utils' import { cn } from '@/lib/utils' import * as Icons from '@phosphor-icons/react' diff --git a/src/components/molecules/CanvasRenderer.tsx b/src/components/molecules/CanvasRenderer.tsx index 10eecbf..e31ca54 100644 --- a/src/components/molecules/CanvasRenderer.tsx +++ b/src/components/molecules/CanvasRenderer.tsx @@ -1,6 +1,6 @@ import { UIComponent } from '@/types/json-ui' import { getUIComponent } from '@/lib/json-ui/component-registry' -import { getComponentDef } from '@/lib/component-definitions' +import { getComponentDef } from '@/lib/component-definition-utils' import { cn } from '@/lib/utils' import { createElement, ReactNode } from 'react' diff --git a/src/components/molecules/ComponentPalette.tsx b/src/components/molecules/ComponentPalette.tsx index fb60e05..1571f47 100644 --- a/src/components/molecules/ComponentPalette.tsx +++ b/src/components/molecules/ComponentPalette.tsx @@ -1,4 +1,5 @@ -import { ComponentDefinition, getCategoryComponents } from '@/lib/component-definitions' +import { ComponentDefinition } from '@/lib/component-definition-types' +import { getCategoryComponents } from '@/lib/component-definition-utils' import { ComponentPaletteItem } from '@/components/atoms/ComponentPaletteItem' import { PanelHeader, Stack } from '@/components/atoms' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' diff --git a/src/components/molecules/PropertyEditor.tsx b/src/components/molecules/PropertyEditor.tsx index 0cbd56b..cdbe172 100644 --- a/src/components/molecules/PropertyEditor.tsx +++ b/src/components/molecules/PropertyEditor.tsx @@ -1,7 +1,7 @@ import { UIComponent } from '@/types/json-ui' import { ScrollArea } from '@/components/ui/scroll-area' import { Separator } from '@/components/ui/separator' -import { getComponentDef } from '@/lib/component-definitions' +import { getComponentDef } from '@/lib/component-definition-utils' import { PropertyEditorEmptyState } from '@/components/molecules/property-editor/PropertyEditorEmptyState' import { propertyEditorConfig } from '@/components/molecules/property-editor/propertyEditorConfig' import { PropertyEditorHeader } from '@/components/molecules/property-editor/PropertyEditorHeader' diff --git a/src/components/organisms/SchemaEditorLayout.tsx b/src/components/organisms/SchemaEditorLayout.tsx index 78a9d9f..aea9be3 100644 --- a/src/components/organisms/SchemaEditorLayout.tsx +++ b/src/components/organisms/SchemaEditorLayout.tsx @@ -1,5 +1,5 @@ import { UIComponent, PageSchema } from '@/types/json-ui' -import { ComponentDefinition } from '@/lib/component-definitions' +import { ComponentDefinition } from '@/lib/component-definition-types' import { SchemaEditorToolbar } from './SchemaEditorToolbar' import { SchemaEditorSidebar } from './SchemaEditorSidebar' import { SchemaEditorCanvas } from './SchemaEditorCanvas' diff --git a/src/components/organisms/SchemaEditorSidebar.tsx b/src/components/organisms/SchemaEditorSidebar.tsx index 1ffb087..dfba600 100644 --- a/src/components/organisms/SchemaEditorSidebar.tsx +++ b/src/components/organisms/SchemaEditorSidebar.tsx @@ -1,5 +1,5 @@ import { ComponentPalette } from '@/components/molecules/ComponentPalette' -import { ComponentDefinition } from '@/lib/component-definitions' +import { ComponentDefinition } from '@/lib/component-definition-types' interface SchemaEditorSidebarProps { onDragStart: (component: ComponentDefinition, e: React.DragEvent) => void diff --git a/src/components/schema-editor/SchemaEditorWorkspace.tsx b/src/components/schema-editor/SchemaEditorWorkspace.tsx index 119ba50..c64d9a1 100644 --- a/src/components/schema-editor/SchemaEditorWorkspace.tsx +++ b/src/components/schema-editor/SchemaEditorWorkspace.tsx @@ -2,7 +2,7 @@ import { useSchemaEditor } from '@/hooks/ui/use-schema-editor' import { useDragDrop } from '@/hooks/ui/use-drag-drop' import { useJsonExport } from '@/hooks/ui/use-json-export' import { SchemaEditorLayout } from '@/components/organisms' -import { ComponentDefinition } from '@/lib/component-definitions' +import { ComponentDefinition } from '@/lib/component-definition-types' import { UIComponent, PageSchema } from '@/types/json-ui' import { toast } from 'sonner' import { schemaEditorConfig } from '@/components/schema-editor/schemaEditorConfig' diff --git a/src/lib/component-definition-types.ts b/src/lib/component-definition-types.ts new file mode 100644 index 0000000..ead5aea --- /dev/null +++ b/src/lib/component-definition-types.ts @@ -0,0 +1,27 @@ +import { ComponentType } from '@/types/json-ui' + +export interface ComponentDefinition { + type: ComponentType + label: string + category: 'layout' | 'input' | 'display' | 'navigation' | 'feedback' | 'data' | 'custom' + icon: string + defaultProps?: Record + canHaveChildren?: boolean + props?: ComponentPropDefinition[] + events?: ComponentEventDefinition[] +} + +export interface ComponentPropDefinition { + name: string + type: string + description: string + required?: boolean + defaultValue?: string + options?: string[] + supportsBinding?: boolean +} + +export interface ComponentEventDefinition { + name: string + description: string +} diff --git a/src/lib/component-definition-utils.ts b/src/lib/component-definition-utils.ts new file mode 100644 index 0000000..9796af7 --- /dev/null +++ b/src/lib/component-definition-utils.ts @@ -0,0 +1,13 @@ +import componentDefinitionsData from '@/lib/component-definitions.json' +import { ComponentDefinition } from '@/lib/component-definition-types' +import { ComponentType } from '@/types/json-ui' + +export const componentDefinitions = componentDefinitionsData as ComponentDefinition[] + +export function getCategoryComponents(category: string): ComponentDefinition[] { + return componentDefinitions.filter(c => c.category === category) +} + +export function getComponentDef(type: ComponentType): ComponentDefinition | undefined { + return componentDefinitions.find(c => c.type === type) +} diff --git a/src/lib/component-definitions.json b/src/lib/component-definitions.json new file mode 100644 index 0000000..a0ad390 --- /dev/null +++ b/src/lib/component-definitions.json @@ -0,0 +1,476 @@ +[ + { + "type": "div", + "label": "Container", + "category": "layout", + "icon": "Square", + "canHaveChildren": true, + "defaultProps": { "className": "p-4 space-y-2" } + }, + { + "type": "section", + "label": "Section", + "category": "layout", + "icon": "SquaresFour", + "canHaveChildren": true, + "defaultProps": { "className": "space-y-4" } + }, + { + "type": "Grid", + "label": "Grid", + "category": "layout", + "icon": "GridFour", + "canHaveChildren": true, + "defaultProps": { "columns": 2, "gap": 4 } + }, + { + "type": "Stack", + "label": "Stack", + "category": "layout", + "icon": "Stack", + "canHaveChildren": true, + "defaultProps": { "direction": "vertical", "gap": 2 } + }, + { + "type": "Flex", + "label": "Flex", + "category": "layout", + "icon": "ArrowsOutLineHorizontal", + "canHaveChildren": true, + "defaultProps": { "direction": "row", "gap": 2 } + }, + { + "type": "Container", + "label": "Container", + "category": "layout", + "icon": "Rectangle", + "canHaveChildren": true, + "defaultProps": { "maxWidth": "lg" } + }, + { + "type": "Card", + "label": "Card", + "category": "layout", + "icon": "Rectangle", + "canHaveChildren": true, + "defaultProps": { "className": "p-4" } + }, + { + "type": "Button", + "label": "Button", + "category": "input", + "icon": "Circle", + "canHaveChildren": true, + "defaultProps": { "children": "Click me", "variant": "default" } + }, + { + "type": "Input", + "label": "Input", + "category": "input", + "icon": "TextT", + "defaultProps": { "placeholder": "Enter text..." } + }, + { + "type": "TextArea", + "label": "TextArea", + "category": "input", + "icon": "TextAlignLeft", + "defaultProps": { "placeholder": "Enter text...", "rows": 4 } + }, + { + "type": "Select", + "label": "Select", + "category": "input", + "icon": "CaretDown", + "defaultProps": { "placeholder": "Choose option..." } + }, + { + "type": "Checkbox", + "label": "Checkbox", + "category": "input", + "icon": "CheckSquare", + "defaultProps": {} + }, + { + "type": "Radio", + "label": "Radio", + "category": "input", + "icon": "Circle", + "defaultProps": {} + }, + { + "type": "Switch", + "label": "Switch", + "category": "input", + "icon": "ToggleLeft", + "defaultProps": {} + }, + { + "type": "Slider", + "label": "Slider", + "category": "input", + "icon": "SlidersHorizontal", + "defaultProps": { "min": 0, "max": 100, "value": 50 } + }, + { + "type": "NumberInput", + "label": "Number Input", + "category": "input", + "icon": "NumberCircleOne", + "defaultProps": { "placeholder": "0", "min": 0 } + }, + { + "type": "Heading", + "label": "Heading", + "category": "display", + "icon": "TextHOne", + "canHaveChildren": true, + "defaultProps": { "level": 1, "children": "Heading" } + }, + { + "type": "Text", + "label": "Text", + "category": "display", + "icon": "Paragraph", + "canHaveChildren": true, + "defaultProps": { "children": "Text content" } + }, + { + "type": "Badge", + "label": "Badge", + "category": "display", + "icon": "Tag", + "canHaveChildren": true, + "defaultProps": { "children": "Badge", "variant": "default" } + }, + { + "type": "Tag", + "label": "Tag", + "category": "display", + "icon": "Tag", + "canHaveChildren": true, + "defaultProps": { "children": "Tag" } + }, + { + "type": "Code", + "label": "Code", + "category": "display", + "icon": "Code", + "canHaveChildren": true, + "defaultProps": { "children": "code" } + }, + { + "type": "Image", + "label": "Image", + "category": "display", + "icon": "Image", + "defaultProps": { "src": "", "alt": "Image" } + }, + { + "type": "Avatar", + "label": "Avatar", + "category": "display", + "icon": "UserCircle", + "defaultProps": { "src": "", "alt": "Avatar" } + }, + { + "type": "Progress", + "label": "Progress", + "category": "display", + "icon": "CircleNotch", + "defaultProps": { "value": 50 } + }, + { + "type": "Spinner", + "label": "Spinner", + "category": "display", + "icon": "CircleNotch", + "defaultProps": { "size": "md" } + }, + { + "type": "Skeleton", + "label": "Skeleton", + "category": "display", + "icon": "Rectangle", + "defaultProps": { "className": "h-4 w-full" } + }, + { + "type": "Separator", + "label": "Separator", + "category": "display", + "icon": "Minus", + "defaultProps": {} + }, + { + "type": "Link", + "label": "Link", + "category": "navigation", + "icon": "Link", + "canHaveChildren": true, + "defaultProps": { "href": "#", "children": "Link" } + }, + { + "type": "Breadcrumb", + "label": "Breadcrumb", + "category": "navigation", + "icon": "CaretRight", + "canHaveChildren": false, + "defaultProps": { + "items": [ + { "label": "Home", "href": "/" }, + { "label": "Current" } + ] + } + }, + { + "type": "Alert", + "label": "Alert", + "category": "feedback", + "icon": "Info", + "canHaveChildren": true, + "defaultProps": { "variant": "info", "children": "Alert message" } + }, + { + "type": "InfoBox", + "label": "Info Box", + "category": "feedback", + "icon": "Info", + "canHaveChildren": true, + "defaultProps": { "type": "info", "children": "Information" } + }, + { + "type": "EmptyState", + "label": "Empty State", + "category": "feedback", + "icon": "FolderOpen", + "canHaveChildren": true, + "defaultProps": { "message": "No items found" } + }, + { + "type": "StatusBadge", + "label": "Status Badge", + "category": "feedback", + "icon": "Circle", + "defaultProps": { "status": "active", "children": "Active" } + }, + { + "type": "ErrorBadge", + "label": "Error Badge", + "category": "feedback", + "icon": "WarningCircle", + "defaultProps": { "count": 3, "variant": "destructive", "size": "md" }, + "props": [ + { + "name": "count", + "type": "number", + "description": "Number of errors to display. Hidden when set to 0.", + "required": true, + "supportsBinding": true + }, + { + "name": "variant", + "type": "string", + "description": "Visual variant for the badge.", + "defaultValue": "destructive", + "options": ["default", "destructive"] + }, + { + "name": "size", + "type": "string", + "description": "Badge size.", + "defaultValue": "md", + "options": ["sm", "md"] + } + ] + }, + { + "type": "Notification", + "label": "Notification", + "category": "feedback", + "icon": "Info", + "defaultProps": { "type": "info", "title": "Notification", "message": "Details go here." }, + "props": [ + { + "name": "type", + "type": "string", + "description": "Notification style variant.", + "required": true, + "options": ["info", "success", "warning", "error"] + }, + { + "name": "title", + "type": "string", + "description": "Primary notification title.", + "required": true, + "supportsBinding": true + }, + { + "name": "message", + "type": "string", + "description": "Optional supporting message text.", + "supportsBinding": true + }, + { + "name": "className", + "type": "string", + "description": "Optional custom classes for spacing or layout tweaks." + } + ], + "events": [ + { + "name": "onClose", + "description": "Fires when the close button is clicked. Bind to dismiss or trigger an action." + } + ] + }, + { + "type": "StatusIcon", + "label": "Status Icon", + "category": "feedback", + "icon": "CheckCircle", + "defaultProps": { "type": "saved", "size": 14, "animate": false }, + "props": [ + { + "name": "type", + "type": "string", + "description": "Status icon style.", + "required": true, + "supportsBinding": true, + "options": ["saved", "synced"] + }, + { + "name": "size", + "type": "number", + "description": "Icon size in pixels.", + "defaultValue": "14", + "supportsBinding": true + }, + { + "name": "animate", + "type": "boolean", + "description": "Applies entry animation when true.", + "defaultValue": "false", + "supportsBinding": true + } + ] + }, + { + "type": "List", + "label": "List", + "category": "data", + "icon": "List", + "defaultProps": { "items": [], "emptyMessage": "No items" } + }, + { + "type": "DataList", + "label": "Data List", + "category": "data", + "icon": "List", + "defaultProps": { + "items": ["Daily summary", "New signups", "Pending approvals"], + "emptyMessage": "No updates", + "itemClassName": "rounded-md border border-border bg-card/50 px-4 py-2" + } + }, + { + "type": "Table", + "label": "Table", + "category": "data", + "icon": "Table", + "defaultProps": { "data": [], "columns": [] } + }, + { + "type": "DataTable", + "label": "Data Table", + "category": "data", + "icon": "Table", + "defaultProps": { + "columns": [ + { "key": "name", "header": "Name" }, + { "key": "status", "header": "Status" }, + { "key": "owner", "header": "Owner" } + ], + "data": [ + { "name": "Launch Plan", "status": "In Progress", "owner": "Avery" }, + { "name": "Design Review", "status": "Scheduled", "owner": "Jordan" }, + { "name": "QA Checklist", "status": "Done", "owner": "Riley" } + ], + "emptyMessage": "No records available" + } + }, + { + "type": "KeyValue", + "label": "Key Value", + "category": "data", + "icon": "Equals", + "defaultProps": { "label": "Key", "value": "Value" } + }, + { + "type": "StatCard", + "label": "Stat Card", + "category": "data", + "icon": "ChartBar", + "defaultProps": { "title": "Metric", "value": "0" } + }, + { + "type": "MetricCard", + "label": "Metric Card", + "category": "data", + "icon": "ChartBar", + "defaultProps": { + "label": "Active Users", + "value": "1,248", + "trend": { "value": 12.4, "direction": "up" } + } + }, + { + "type": "Timeline", + "label": "Timeline", + "category": "data", + "icon": "Clock", + "defaultProps": { + "items": [ + { + "title": "Planning", + "description": "Finalize milestones", + "timestamp": "Mon 9:00 AM", + "status": "completed" + }, + { + "title": "Execution", + "description": "Kick off delivery", + "timestamp": "Tue 11:00 AM", + "status": "current" + }, + { + "title": "Review", + "description": "Collect feedback", + "timestamp": "Wed 3:00 PM", + "status": "pending" + } + ] + } + }, + { + "type": "DataCard", + "label": "Data Card", + "category": "custom", + "icon": "ChartBar", + "defaultProps": { "title": "Metric", "value": "100", "icon": "TrendUp" } + }, + { + "type": "SearchInput", + "label": "Search Input", + "category": "custom", + "icon": "MagnifyingGlass", + "defaultProps": { "placeholder": "Search..." } + }, + { + "type": "ActionBar", + "label": "Action Bar", + "category": "custom", + "icon": "Toolbox", + "canHaveChildren": true, + "defaultProps": { "actions": [] } + } +] diff --git a/src/lib/component-definitions.ts b/src/lib/component-definitions.ts deleted file mode 100644 index c607634..0000000 --- a/src/lib/component-definitions.ts +++ /dev/null @@ -1,519 +0,0 @@ -import { ComponentType } from '@/types/json-ui' - -export interface ComponentDefinition { - type: ComponentType - label: string - category: 'layout' | 'input' | 'display' | 'navigation' | 'feedback' | 'data' | 'custom' - icon: string - defaultProps?: Record - canHaveChildren?: boolean - props?: ComponentPropDefinition[] - events?: ComponentEventDefinition[] -} - -export interface ComponentPropDefinition { - name: string - type: string - description: string - required?: boolean - defaultValue?: string - options?: string[] - supportsBinding?: boolean -} - -export interface ComponentEventDefinition { - name: string - description: string -} - -export const componentDefinitions: ComponentDefinition[] = [ - // Layout Components - { - type: 'div', - label: 'Container', - category: 'layout', - icon: 'Square', - canHaveChildren: true, - defaultProps: { className: 'p-4 space-y-2' } - }, - { - type: 'section', - label: 'Section', - category: 'layout', - icon: 'SquaresFour', - canHaveChildren: true, - defaultProps: { className: 'space-y-4' } - }, - { - type: 'Grid', - label: 'Grid', - category: 'layout', - icon: 'GridFour', - canHaveChildren: true, - defaultProps: { columns: 2, gap: 4 } - }, - { - type: 'Stack', - label: 'Stack', - category: 'layout', - icon: 'Stack', - canHaveChildren: true, - defaultProps: { direction: 'vertical', gap: 2 } - }, - { - type: 'Flex', - label: 'Flex', - category: 'layout', - icon: 'ArrowsOutLineHorizontal', - canHaveChildren: true, - defaultProps: { direction: 'row', gap: 2 } - }, - { - type: 'Container', - label: 'Container', - category: 'layout', - icon: 'Rectangle', - canHaveChildren: true, - defaultProps: { maxWidth: 'lg' } - }, - { - type: 'Card', - label: 'Card', - category: 'layout', - icon: 'Rectangle', - canHaveChildren: true, - defaultProps: { className: 'p-4' } - }, - // Input Components - { - type: 'Button', - label: 'Button', - category: 'input', - icon: 'Circle', - canHaveChildren: true, - defaultProps: { children: 'Click me', variant: 'default' } - }, - { - type: 'Input', - label: 'Input', - category: 'input', - icon: 'TextT', - defaultProps: { placeholder: 'Enter text...' } - }, - { - type: 'TextArea', - label: 'TextArea', - category: 'input', - icon: 'TextAlignLeft', - defaultProps: { placeholder: 'Enter text...', rows: 4 } - }, - { - type: 'Select', - label: 'Select', - category: 'input', - icon: 'CaretDown', - defaultProps: { placeholder: 'Choose option...' } - }, - { - type: 'Checkbox', - label: 'Checkbox', - category: 'input', - icon: 'CheckSquare', - defaultProps: {} - }, - { - type: 'Radio', - label: 'Radio', - category: 'input', - icon: 'Circle', - defaultProps: {} - }, - { - type: 'Switch', - label: 'Switch', - category: 'input', - icon: 'ToggleLeft', - defaultProps: {} - }, - { - type: 'Slider', - label: 'Slider', - category: 'input', - icon: 'SlidersHorizontal', - defaultProps: { min: 0, max: 100, value: 50 } - }, - { - type: 'NumberInput', - label: 'Number Input', - category: 'input', - icon: 'NumberCircleOne', - defaultProps: { placeholder: '0', min: 0 } - }, - // Display Components - { - type: 'Heading', - label: 'Heading', - category: 'display', - icon: 'TextHOne', - canHaveChildren: true, - defaultProps: { level: 1, children: 'Heading' } - }, - { - type: 'Text', - label: 'Text', - category: 'display', - icon: 'Paragraph', - canHaveChildren: true, - defaultProps: { children: 'Text content' } - }, - { - type: 'Badge', - label: 'Badge', - category: 'display', - icon: 'Tag', - canHaveChildren: true, - defaultProps: { children: 'Badge', variant: 'default' } - }, - { - type: 'Tag', - label: 'Tag', - category: 'display', - icon: 'Tag', - canHaveChildren: true, - defaultProps: { children: 'Tag' } - }, - { - type: 'Code', - label: 'Code', - category: 'display', - icon: 'Code', - canHaveChildren: true, - defaultProps: { children: 'code' } - }, - { - type: 'Image', - label: 'Image', - category: 'display', - icon: 'Image', - defaultProps: { src: '', alt: 'Image' } - }, - { - type: 'Avatar', - label: 'Avatar', - category: 'display', - icon: 'UserCircle', - defaultProps: { src: '', alt: 'Avatar' } - }, - { - type: 'Progress', - label: 'Progress', - category: 'display', - icon: 'CircleNotch', - defaultProps: { value: 50 } - }, - { - type: 'Spinner', - label: 'Spinner', - category: 'display', - icon: 'CircleNotch', - defaultProps: { size: 'md' } - }, - { - type: 'Skeleton', - label: 'Skeleton', - category: 'display', - icon: 'Rectangle', - defaultProps: { className: 'h-4 w-full' } - }, - { - type: 'Separator', - label: 'Separator', - category: 'display', - icon: 'Minus', - defaultProps: {} - }, - // Navigation Components - { - type: 'Link', - label: 'Link', - category: 'navigation', - icon: 'Link', - canHaveChildren: true, - defaultProps: { href: '#', children: 'Link' } - }, - { - type: 'Breadcrumb', - label: 'Breadcrumb', - category: 'navigation', - icon: 'CaretRight', - canHaveChildren: false, - defaultProps: { - items: [ - { label: 'Home', href: '/' }, - { label: 'Current' } - ] - } - }, - // Feedback Components - { - type: 'Alert', - label: 'Alert', - category: 'feedback', - icon: 'Info', - canHaveChildren: true, - defaultProps: { variant: 'info', children: 'Alert message' } - }, - { - type: 'InfoBox', - label: 'Info Box', - category: 'feedback', - icon: 'Info', - canHaveChildren: true, - defaultProps: { type: 'info', children: 'Information' } - }, - { - type: 'EmptyState', - label: 'Empty State', - category: 'feedback', - icon: 'FolderOpen', - canHaveChildren: true, - defaultProps: { message: 'No items found' } - }, - { - type: 'StatusBadge', - label: 'Status Badge', - category: 'feedback', - icon: 'Circle', - defaultProps: { status: 'active', children: 'Active' } - }, - { - type: 'ErrorBadge', - label: 'Error Badge', - category: 'feedback', - icon: 'WarningCircle', - defaultProps: { count: 3, variant: 'destructive', size: 'md' }, - props: [ - { - name: 'count', - type: 'number', - description: 'Number of errors to display. Hidden when set to 0.', - required: true, - supportsBinding: true, - }, - { - name: 'variant', - type: 'string', - description: 'Visual variant for the badge.', - defaultValue: 'destructive', - options: ['default', 'destructive'], - }, - { - name: 'size', - type: 'string', - description: 'Badge size.', - defaultValue: 'md', - options: ['sm', 'md'], - }, - ], - }, - { - type: 'Notification', - label: 'Notification', - category: 'feedback', - icon: 'Info', - defaultProps: { type: 'info', title: 'Notification', message: 'Details go here.' }, - props: [ - { - name: 'type', - type: 'string', - description: 'Notification style variant.', - required: true, - options: ['info', 'success', 'warning', 'error'], - }, - { - name: 'title', - type: 'string', - description: 'Primary notification title.', - required: true, - supportsBinding: true, - }, - { - name: 'message', - type: 'string', - description: 'Optional supporting message text.', - supportsBinding: true, - }, - { - name: 'className', - type: 'string', - description: 'Optional custom classes for spacing or layout tweaks.', - }, - ], - events: [ - { - name: 'onClose', - description: 'Fires when the close button is clicked. Bind to dismiss or trigger an action.', - }, - ], - }, - { - type: 'StatusIcon', - label: 'Status Icon', - category: 'feedback', - icon: 'CheckCircle', - defaultProps: { type: 'saved', size: 14, animate: false }, - props: [ - { - name: 'type', - type: 'string', - description: 'Status icon style.', - required: true, - supportsBinding: true, - options: ['saved', 'synced'], - }, - { - name: 'size', - type: 'number', - description: 'Icon size in pixels.', - defaultValue: '14', - supportsBinding: true, - }, - { - name: 'animate', - type: 'boolean', - description: 'Applies entry animation when true.', - defaultValue: 'false', - supportsBinding: true, - }, - ], - }, - // Data Components - { - type: 'List', - label: 'List', - category: 'data', - icon: 'List', - defaultProps: { items: [], emptyMessage: 'No items' } - }, - { - type: 'DataList', - label: 'Data List', - category: 'data', - icon: 'List', - defaultProps: { - items: ['Daily summary', 'New signups', 'Pending approvals'], - emptyMessage: 'No updates', - itemClassName: 'rounded-md border border-border bg-card/50 px-4 py-2' - } - }, - { - type: 'Table', - label: 'Table', - category: 'data', - icon: 'Table', - defaultProps: { data: [], columns: [] } - }, - { - type: 'DataTable', - label: 'Data Table', - category: 'data', - icon: 'Table', - defaultProps: { - columns: [ - { key: 'name', header: 'Name' }, - { key: 'status', header: 'Status' }, - { key: 'owner', header: 'Owner' }, - ], - data: [ - { name: 'Launch Plan', status: 'In Progress', owner: 'Avery' }, - { name: 'Design Review', status: 'Scheduled', owner: 'Jordan' }, - { name: 'QA Checklist', status: 'Done', owner: 'Riley' }, - ], - emptyMessage: 'No records available', - } - }, - { - type: 'KeyValue', - label: 'Key Value', - category: 'data', - icon: 'Equals', - defaultProps: { label: 'Key', value: 'Value' } - }, - { - type: 'StatCard', - label: 'Stat Card', - category: 'data', - icon: 'ChartBar', - defaultProps: { title: 'Metric', value: '0' } - }, - { - type: 'MetricCard', - label: 'Metric Card', - category: 'data', - icon: 'ChartBar', - defaultProps: { - label: 'Active Users', - value: '1,248', - trend: { value: 12.4, direction: 'up' }, - } - }, - { - type: 'Timeline', - label: 'Timeline', - category: 'data', - icon: 'Clock', - defaultProps: { - items: [ - { - title: 'Planning', - description: 'Finalize milestones', - timestamp: 'Mon 9:00 AM', - status: 'completed', - }, - { - title: 'Execution', - description: 'Kick off delivery', - timestamp: 'Tue 11:00 AM', - status: 'current', - }, - { - title: 'Review', - description: 'Collect feedback', - timestamp: 'Wed 3:00 PM', - status: 'pending', - }, - ], - } - }, - // Custom Components - { - type: 'DataCard', - label: 'Data Card', - category: 'custom', - icon: 'ChartBar', - defaultProps: { title: 'Metric', value: '100', icon: 'TrendUp' } - }, - { - type: 'SearchInput', - label: 'Search Input', - category: 'custom', - icon: 'MagnifyingGlass', - defaultProps: { placeholder: 'Search...' } - }, - { - type: 'ActionBar', - label: 'Action Bar', - category: 'custom', - icon: 'Toolbox', - canHaveChildren: true, - defaultProps: { actions: [] } - }, -] - -export function getCategoryComponents(category: string): ComponentDefinition[] { - return componentDefinitions.filter(c => c.category === category) -} - -export function getComponentDef(type: ComponentType): ComponentDefinition | undefined { - return componentDefinitions.find(c => c.type === type) -} From af58bcb7c2e24f964a024143f4720d45b0cf15c3 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 11:54:28 +0000 Subject: [PATCH 5/6] Add JSON UI support for progress and divider --- JSON_COMPONENTS.md | 6 +++--- json-components-list.json | 6 +++--- json-components-registry.json | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/JSON_COMPONENTS.md b/JSON_COMPONENTS.md index d3b497e..81048ce 100644 --- a/JSON_COMPONENTS.md +++ b/JSON_COMPONENTS.md @@ -110,9 +110,9 @@ Presentation and visual elements: - `Spinner` - Loading spinner - `Skeleton` - Loading placeholder - `Separator` - Visual divider -- `CircularProgress` - Circular indicator (planned) -- `ProgressBar` - Linear progress (planned) -- `Divider` - Section divider (planned) +- `CircularProgress` - Circular indicator +- `ProgressBar` - Linear progress +- `Divider` - Section divider ### Navigation Components (3) Navigation and routing: diff --git a/json-components-list.json b/json-components-list.json index 0c9a2ed..196a0e3 100644 --- a/json-components-list.json +++ b/json-components-list.json @@ -268,7 +268,7 @@ "category": "display", "canHaveChildren": false, "description": "Visual section divider", - "status": "planned" + "status": "supported" }, { "type": "Progress", @@ -284,7 +284,7 @@ "category": "display", "canHaveChildren": false, "description": "Linear progress bar", - "status": "planned" + "status": "supported" }, { "type": "CircularProgress", @@ -292,7 +292,7 @@ "category": "display", "canHaveChildren": false, "description": "Circular progress indicator", - "status": "planned" + "status": "supported" }, { "type": "Spinner", diff --git a/json-components-registry.json b/json-components-registry.json index bc0676f..b5510d5 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-18T11:30:24.191Z", + "lastUpdated": "2026-01-18T12:05:00.000Z", "categories": { "layout": "Layout and container components", "input": "Form inputs and interactive controls", @@ -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" }, { @@ -2024,8 +2024,8 @@ ], "statistics": { "total": 219, - "supported": 154, - "planned": 10, + "supported": 157, + "planned": 7, "jsonCompatible": 14, "maybeJsonCompatible": 41, "byCategory": { From 0d13710c0955659967bb705bd65f996a34d8253a Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Sun, 18 Jan 2026 11:59:51 +0000 Subject: [PATCH 6/6] Add DatePicker and FileUpload to JSON UI --- JSON_COMPONENTS.md | 4 +- json-components-list.json | 4 +- json-components-registry.json | 10 ++--- src/components/JSONUIPage.tsx | 10 +++++ src/config/ui-examples/form.json | 63 ++++++++++++++++++++++++++++++++ src/types/json-ui.ts | 1 + 6 files changed, 83 insertions(+), 9 deletions(-) diff --git a/JSON_COMPONENTS.md b/JSON_COMPONENTS.md index d3b497e..4858f58 100644 --- a/JSON_COMPONENTS.md +++ b/JSON_COMPONENTS.md @@ -92,8 +92,8 @@ Form inputs and interactive controls: - `Switch` - Toggle switch - `Slider` - Numeric range slider - `NumberInput` - Numeric input with increment/decrement -- `DatePicker` - Date selection (planned) -- `FileUpload` - File upload control (planned) +- `DatePicker` - Date selection +- `FileUpload` - File upload control ### Display Components (16) Presentation and visual elements: diff --git a/json-components-list.json b/json-components-list.json index 0c9a2ed..712450b 100644 --- a/json-components-list.json +++ b/json-components-list.json @@ -172,7 +172,7 @@ "category": "input", "canHaveChildren": false, "description": "Date selection input", - "status": "planned" + "status": "supported" }, { "type": "FileUpload", @@ -180,7 +180,7 @@ "category": "input", "canHaveChildren": false, "description": "File upload control", - "status": "planned" + "status": "supported" }, { "type": "Text", diff --git a/json-components-registry.json b/json-components-registry.json index bc0676f..c5bd857 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-18T11:30:24.191Z", + "lastUpdated": "2026-01-18T11:54:11.109Z", "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" }, { @@ -2024,8 +2024,8 @@ ], "statistics": { "total": 219, - "supported": 154, - "planned": 10, + "supported": 156, + "planned": 8, "jsonCompatible": 14, "maybeJsonCompatible": 41, "byCategory": { diff --git a/src/components/JSONUIPage.tsx b/src/components/JSONUIPage.tsx index 55f189c..08edaad 100644 --- a/src/components/JSONUIPage.tsx +++ b/src/components/JSONUIPage.tsx @@ -78,6 +78,16 @@ export function JSONUIPage({ jsonConfig }: JSONUIPageProps) { updateDataField('formData', action.params.field, event) } break + case 'update-date': + if (action.params?.field) { + updateDataField('formData', action.params.field, event) + } + break + case 'update-files': + if (action.params?.field) { + updateDataField('formData', action.params.field, event) + } + break case 'submit-form': toast.success('Form submitted!') console.log('Form data:', dataMap.formData) diff --git a/src/config/ui-examples/form.json b/src/config/ui-examples/form.json index ebc87c1..e0a3428 100644 --- a/src/config/ui-examples/form.json +++ b/src/config/ui-examples/form.json @@ -130,6 +130,37 @@ } ] }, + { + "id": "birthdate-field", + "type": "div", + "className": "space-y-2", + "children": [ + { + "id": "birthdate-label", + "type": "Label", + "props": { + "htmlFor": "birthDate" + }, + "children": "Birth date" + }, + { + "id": "birthdate-input", + "type": "DatePicker", + "props": { + "placeholder": "Select a date" + }, + "dataBinding": "formData.birthDate", + "events": { + "onChange": { + "action": "update-date", + "payload": { + "field": "birthDate" + } + } + } + } + ] + }, { "id": "bio-field", "type": "div", @@ -159,6 +190,36 @@ } ] }, + { + "id": "resume-field", + "type": "div", + "className": "space-y-2", + "children": [ + { + "id": "resume-label", + "type": "Label", + "props": { + "htmlFor": "resume" + }, + "children": "Resume" + }, + { + "id": "resume-upload", + "type": "FileUpload", + "props": { + "accept": ".pdf,.doc,.docx" + }, + "events": { + "onFilesSelected": { + "action": "update-files", + "payload": { + "field": "resumeFiles" + } + } + } + } + ] + }, { "id": "subscribe-field", "type": "div", @@ -234,6 +295,8 @@ "email": "", "password": "", "bio": "", + "birthDate": null, + "resumeFiles": [], "subscribe": false } } diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index 8c30f10..69b1d39 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' | 'Breadcrumb' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton'