diff --git a/json-components-registry.json b/json-components-registry.json index bb68d7b..e3f8c9c 100644 --- a/json-components-registry.json +++ b/json-components-registry.json @@ -1219,7 +1219,7 @@ "category": "data", "canHaveChildren": false, "description": "Styled data list", - "status": "planned", + "status": "supported", "source": "atoms" }, { @@ -1238,7 +1238,7 @@ "category": "data", "canHaveChildren": false, "description": "Advanced data table with sorting and filtering", - "status": "planned", + "status": "supported", "source": "atoms" }, { @@ -1304,7 +1304,7 @@ "category": "data", "canHaveChildren": false, "description": "Metric display card", - "status": "planned", + "status": "supported", "source": "atoms" }, { @@ -1369,7 +1369,7 @@ "category": "data", "canHaveChildren": false, "description": "Timeline visualization", - "status": "planned", + "status": "supported", "source": "atoms" }, { @@ -2043,8 +2043,8 @@ ], "statistics": { "total": 219, - "supported": 150, - "planned": 14, + "supported": 154, + "planned": 10, "jsonCompatible": 14, "maybeJsonCompatible": 41, "byCategory": { diff --git a/src/components/JSONUIShowcasePage.tsx b/src/components/JSONUIShowcasePage.tsx index 2b92f00..1cd4c08 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 { dataComponentsDemoSchema } from '@/schemas/page-schemas' import todoListJson from '@/schemas/todo-list.json' import newMoleculesShowcaseJson from '@/schemas/new-molecules-showcase.json' @@ -25,6 +26,7 @@ export function JSONUIShowcasePage() { Atomic Components New Molecules + Data Components JSON Dashboard JSON Todo List @@ -38,6 +40,10 @@ export function JSONUIShowcasePage() { + + + + diff --git a/src/components/atoms/DataList.tsx b/src/components/atoms/DataList.tsx index c12db88..109cb41 100644 --- a/src/components/atoms/DataList.tsx +++ b/src/components/atoms/DataList.tsx @@ -3,10 +3,11 @@ import { cn } from '@/lib/utils' export interface DataListProps { items: any[] - renderItem: (item: any, index: number) => ReactNode + renderItem?: (item: any, index: number) => ReactNode emptyMessage?: string className?: string itemClassName?: string + itemKey?: string } export function DataList({ @@ -15,6 +16,7 @@ export function DataList({ emptyMessage = 'No items', className, itemClassName, + itemKey, }: DataListProps) { if (items.length === 0) { return ( @@ -24,11 +26,28 @@ export function DataList({ ) } + const renderFallbackItem = (item: any) => { + if (itemKey && item && typeof item === 'object') { + const value = item[itemKey] + if (value !== undefined && value !== null) { + return typeof value === 'string' || typeof value === 'number' + ? value + : JSON.stringify(value) + } + } + + if (typeof item === 'string' || typeof item === 'number') { + return item + } + + return JSON.stringify(item) + } + return (
{items.map((item, index) => (
- {renderItem(item, index)} + {renderItem ? renderItem(item, index) : renderFallbackItem(item)}
))}
diff --git a/src/lib/component-definitions.ts b/src/lib/component-definitions.ts index 8093398..f5d9e36 100644 --- a/src/lib/component-definitions.ts +++ b/src/lib/component-definitions.ts @@ -264,6 +264,17 @@ export const componentDefinitions: ComponentDefinition[] = [ 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', @@ -271,6 +282,25 @@ export const componentDefinitions: ComponentDefinition[] = [ 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', @@ -285,6 +315,45 @@ export const componentDefinitions: ComponentDefinition[] = [ 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', diff --git a/src/lib/json-ui/component-registry.ts b/src/lib/json-ui/component-registry.ts index 3e53dfe..507a5d1 100644 --- a/src/lib/json-ui/component-registry.ts +++ b/src/lib/json-ui/component-registry.ts @@ -136,10 +136,16 @@ export const shadcnComponents: UIComponentRegistry = { AvatarImage, } -export const atomComponents: UIComponentRegistry = buildRegistryFromNames( - atomRegistryNames, - AtomComponents as Record> -) +export const atomComponents: UIComponentRegistry = { + ...buildRegistryFromNames( + atomRegistryNames, + AtomComponents as Record> + ), + DataList: (AtomComponents as Record>).DataList, + DataTable: (AtomComponents as Record>).DataTable, + MetricCard: (AtomComponents as Record>).MetricCard, + Timeline: (AtomComponents as Record>).Timeline, +} export const moleculeComponents: UIComponentRegistry = buildRegistryFromNames( moleculeRegistryNames, diff --git a/src/schemas/page-schemas.ts b/src/schemas/page-schemas.ts index 46ceb50..68299c8 100644 --- a/src/schemas/page-schemas.ts +++ b/src/schemas/page-schemas.ts @@ -73,3 +73,141 @@ export const stateBindingsDemoSchema: PageSchema = { }, ], } + +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' }, + }, + }, + ], + }, + ], +} diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index f3ce4c3..5bed643 100644 --- a/src/types/json-ui.ts +++ b/src/types/json-ui.ts @@ -7,6 +7,7 @@ export type ComponentType = | 'Link' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton' | 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge' | 'Table' | 'KeyValue' | 'StatCard' | 'DataCard' | 'SearchInput' | 'ActionBar' + | 'DataList' | 'DataTable' | 'MetricCard' | 'Timeline' | 'AppBranding' | 'LabelWithBadge' | 'EmptyEditorState' | 'LoadingFallback' | 'LoadingState' | 'NavigationGroupHeader' export type ActionType =