diff --git a/JSON_COMPONENTS.md b/JSON_COMPONENTS.md new file mode 100644 index 0000000..c07496e --- /dev/null +++ b/JSON_COMPONENTS.md @@ -0,0 +1,192 @@ +# JSON UI Components Registry + +This document describes the JSON UI component system and lists all components that can be rendered from JSON schemas. + +## Overview + +The JSON UI system allows you to define user interfaces using JSON schemas instead of writing React code. This is useful for: +- Dynamic UI generation +- No-code/low-code interfaces +- Configuration-driven UIs +- Rapid prototyping + +## Quick Start + +### List All JSON-Compatible Components + +```bash +# List all components with details +npm run components:list + +# List only supported components +npm run components:list -- --status=supported + +# List only planned components +npm run components:list -- --status=planned + +# Output as JSON +npm run components:list -- --format=json +``` + +### Using JSON UI Components + +Components are defined in the `ComponentType` union in `src/types/json-ui.ts` and registered in `src/lib/json-ui/component-registry.tsx`. + +Example JSON schema: + +```json +{ + "id": "example-page", + "type": "Card", + "props": { + "className": "p-6" + }, + "children": [ + { + "id": "heading", + "type": "Heading", + "props": { + "level": 2, + "children": "Welcome" + } + }, + { + "id": "description", + "type": "Text", + "props": { + "children": "This is a dynamically rendered component" + } + }, + { + "id": "cta", + "type": "Button", + "props": { + "variant": "default", + "children": "Get Started" + } + } + ] +} +``` + +## Component Categories + +### Layout Components (12) +Container elements for organizing content: +- `div`, `section`, `article`, `header`, `footer`, `main` - HTML semantic elements +- `Card` - Container with optional header, content, and footer +- `Grid` - Responsive grid layout +- `Stack` - Vertical or horizontal stack layout +- `Flex` - Flexible box layout +- `Container` - Centered container with max-width +- `Dialog` - Modal dialog overlay + +### Input Components (11) +Form inputs and interactive controls: +- `Button` - Interactive button +- `Input` - Text input field +- `TextArea` - Multi-line text input +- `Select` - Dropdown select +- `Checkbox` - Checkbox toggle +- `Radio` - Radio button +- `Switch` - Toggle switch +- `Slider` - Numeric range slider +- `NumberInput` - Numeric input with increment/decrement +- `DatePicker` - Date selection (planned) +- `FileUpload` - File upload control (planned) + +### Display Components (16) +Presentation and visual elements: +- `Heading` - Heading text (h1-h6) +- `Text` - Text content with typography +- `Label` - Form label +- `Badge` - Status or count indicator +- `Tag` - Removable tag/chip +- `Code` - Inline or block code +- `Image` - Image with loading states +- `Avatar` - User avatar image +- `Icon` - Icon from library (planned) +- `Progress` - Progress bar +- `Spinner` - Loading spinner +- `Skeleton` - Loading placeholder +- `Separator` - Visual divider +- `CircularProgress` - Circular indicator (planned) +- `ProgressBar` - Linear progress (planned) +- `Divider` - Section divider (planned) + +### Navigation Components (3) +Navigation and routing: +- `Link` - Hyperlink element +- `Breadcrumb` - Navigation trail (planned) +- `Tabs` - Tabbed interface + +### Feedback Components (7) +Alerts, notifications, and status: +- `Alert` - Alert notification message +- `InfoBox` - Information box with icon +- `EmptyState` - Empty state placeholder +- `StatusBadge` - Status indicator +- `StatusIcon` - Status icon (planned) +- `ErrorBadge` - Error state (planned) +- `Notification` - Toast notification (planned) + +### Data Components (8) +Data display and visualization: +- `List` - Generic list renderer +- `Table` - Data table +- `KeyValue` - Key-value pair display +- `StatCard` - Statistic card +- `DataList` - Styled data list (planned) +- `DataTable` - Advanced table with sorting/filtering (planned) +- `Timeline` - Timeline visualization (planned) +- `MetricCard` - Metric display (planned) + +### Custom Components (3) +Domain-specific components: +- `DataCard` - Custom data display card +- `SearchInput` - Search input with icon +- `ActionBar` - Action button toolbar + +## Current Status + +- **Total Components**: 60 +- **Supported**: 46 (77%) +- **Planned**: 14 (23%) + +## Files + +- `json-components-registry.json` - Complete registry with metadata +- `src/types/json-ui.ts` - TypeScript types and ComponentType union +- `src/lib/json-ui/component-registry.tsx` - Component registry mapping +- `src/lib/component-definitions.ts` - Component definitions with defaults +- `scripts/list-json-components.cjs` - CLI tool to list components + +## Adding New Components + +To add a new component to the JSON UI system: + +1. Add the component type to `ComponentType` union in `src/types/json-ui.ts` +2. Import and register it in `src/lib/json-ui/component-registry.tsx` +3. Add component definition in `src/lib/component-definitions.ts` +4. Update `json-components-registry.json` with metadata +5. Test the component in a JSON schema + +## Migration Strategy + +Components marked as "planned" are: +- Available in the codebase as React components +- Not yet integrated into the JSON UI system +- Can be migrated following the steps above + +Priority for migration: +1. High-usage components +2. Components with simple props +3. Components with good atomic design +4. Components without complex state management + +## Related Documentation + +- [PRD.md](./PRD.md) - Product requirements document +- [REDUX_DOCUMENTATION.md](./REDUX_DOCUMENTATION.md) - Redux integration +- [src/types/json-ui.ts](./src/types/json-ui.ts) - Type definitions +- [src/lib/component-definitions.ts](./src/lib/component-definitions.ts) - Component metadata diff --git a/json-components-list.json b/json-components-list.json new file mode 100644 index 0000000..0c9a2ed --- /dev/null +++ b/json-components-list.json @@ -0,0 +1,494 @@ +[ + { + "type": "div", + "name": "Container (div)", + "category": "layout", + "canHaveChildren": true, + "description": "Generic container element", + "status": "supported" + }, + { + "type": "section", + "name": "Section", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic section element", + "status": "supported" + }, + { + "type": "article", + "name": "Article", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic article element", + "status": "supported" + }, + { + "type": "header", + "name": "Header", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic header element", + "status": "supported" + }, + { + "type": "footer", + "name": "Footer", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic footer element", + "status": "supported" + }, + { + "type": "main", + "name": "Main", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic main content element", + "status": "supported" + }, + { + "type": "Card", + "name": "Card", + "category": "layout", + "canHaveChildren": true, + "description": "Container card with optional header, content, and footer", + "status": "supported", + "subComponents": [ + "CardHeader", + "CardTitle", + "CardDescription", + "CardContent", + "CardFooter" + ] + }, + { + "type": "Grid", + "name": "Grid", + "category": "layout", + "canHaveChildren": true, + "description": "Responsive grid layout", + "status": "supported" + }, + { + "type": "Flex", + "name": "Flex", + "category": "layout", + "canHaveChildren": true, + "description": "Flexible box layout container", + "status": "supported" + }, + { + "type": "Stack", + "name": "Stack", + "category": "layout", + "canHaveChildren": true, + "description": "Vertical or horizontal stack layout", + "status": "supported" + }, + { + "type": "Container", + "name": "Container", + "category": "layout", + "canHaveChildren": true, + "description": "Centered container with max-width", + "status": "supported" + }, + { + "type": "Button", + "name": "Button", + "category": "input", + "canHaveChildren": true, + "description": "Interactive button element", + "status": "supported" + }, + { + "type": "Input", + "name": "Input", + "category": "input", + "canHaveChildren": false, + "description": "Text input field", + "status": "supported" + }, + { + "type": "TextArea", + "name": "TextArea", + "category": "input", + "canHaveChildren": false, + "description": "Multi-line text input", + "status": "supported" + }, + { + "type": "Select", + "name": "Select", + "category": "input", + "canHaveChildren": false, + "description": "Dropdown select control", + "status": "supported" + }, + { + "type": "Checkbox", + "name": "Checkbox", + "category": "input", + "canHaveChildren": false, + "description": "Checkbox toggle control", + "status": "supported" + }, + { + "type": "Radio", + "name": "Radio", + "category": "input", + "canHaveChildren": false, + "description": "Radio button selection", + "status": "supported" + }, + { + "type": "Switch", + "name": "Switch", + "category": "input", + "canHaveChildren": false, + "description": "Toggle switch control", + "status": "supported" + }, + { + "type": "Slider", + "name": "Slider", + "category": "input", + "canHaveChildren": false, + "description": "Numeric range slider", + "status": "supported" + }, + { + "type": "NumberInput", + "name": "NumberInput", + "category": "input", + "canHaveChildren": false, + "description": "Numeric input with increment/decrement", + "status": "supported" + }, + { + "type": "DatePicker", + "name": "DatePicker", + "category": "input", + "canHaveChildren": false, + "description": "Date selection input", + "status": "planned" + }, + { + "type": "FileUpload", + "name": "FileUpload", + "category": "input", + "canHaveChildren": false, + "description": "File upload control", + "status": "planned" + }, + { + "type": "Text", + "name": "Text", + "category": "display", + "canHaveChildren": true, + "description": "Text content with typography variants", + "status": "supported" + }, + { + "type": "Heading", + "name": "Heading", + "category": "display", + "canHaveChildren": true, + "description": "Heading text with level (h1-h6)", + "status": "supported" + }, + { + "type": "Label", + "name": "Label", + "category": "display", + "canHaveChildren": true, + "description": "Form label element", + "status": "supported" + }, + { + "type": "Badge", + "name": "Badge", + "category": "display", + "canHaveChildren": true, + "description": "Small status or count indicator", + "status": "supported" + }, + { + "type": "Tag", + "name": "Tag", + "category": "display", + "canHaveChildren": true, + "description": "Removable tag or chip", + "status": "supported" + }, + { + "type": "Code", + "name": "Code", + "category": "display", + "canHaveChildren": true, + "description": "Inline or block code display", + "status": "supported" + }, + { + "type": "Image", + "name": "Image", + "category": "display", + "canHaveChildren": false, + "description": "Image element with loading states", + "status": "supported" + }, + { + "type": "Avatar", + "name": "Avatar", + "category": "display", + "canHaveChildren": false, + "description": "User avatar image", + "status": "supported" + }, + { + "type": "Icon", + "name": "Icon", + "category": "display", + "canHaveChildren": false, + "description": "Icon from icon library", + "status": "planned" + }, + { + "type": "Separator", + "name": "Separator", + "category": "display", + "canHaveChildren": false, + "description": "Visual divider line", + "status": "supported" + }, + { + "type": "Divider", + "name": "Divider", + "category": "display", + "canHaveChildren": false, + "description": "Visual section divider", + "status": "planned" + }, + { + "type": "Progress", + "name": "Progress", + "category": "display", + "canHaveChildren": false, + "description": "Progress bar indicator", + "status": "supported" + }, + { + "type": "ProgressBar", + "name": "ProgressBar", + "category": "display", + "canHaveChildren": false, + "description": "Linear progress bar", + "status": "planned" + }, + { + "type": "CircularProgress", + "name": "CircularProgress", + "category": "display", + "canHaveChildren": false, + "description": "Circular progress indicator", + "status": "planned" + }, + { + "type": "Spinner", + "name": "Spinner", + "category": "display", + "canHaveChildren": false, + "description": "Loading spinner", + "status": "supported" + }, + { + "type": "Skeleton", + "name": "Skeleton", + "category": "display", + "canHaveChildren": false, + "description": "Loading skeleton placeholder", + "status": "supported" + }, + { + "type": "Link", + "name": "Link", + "category": "navigation", + "canHaveChildren": true, + "description": "Hyperlink element", + "status": "supported" + }, + { + "type": "Breadcrumb", + "name": "Breadcrumb", + "category": "navigation", + "canHaveChildren": false, + "description": "Navigation breadcrumb trail", + "status": "planned" + }, + { + "type": "Tabs", + "name": "Tabs", + "category": "navigation", + "canHaveChildren": true, + "description": "Tabbed interface container", + "status": "supported", + "subComponents": [ + "TabsList", + "TabsTrigger", + "TabsContent" + ] + }, + { + "type": "Alert", + "name": "Alert", + "category": "feedback", + "canHaveChildren": true, + "description": "Alert notification message", + "status": "supported" + }, + { + "type": "InfoBox", + "name": "InfoBox", + "category": "feedback", + "canHaveChildren": true, + "description": "Information box with icon", + "status": "supported" + }, + { + "type": "Notification", + "name": "Notification", + "category": "feedback", + "canHaveChildren": true, + "description": "Toast notification", + "status": "planned" + }, + { + "type": "StatusBadge", + "name": "StatusBadge", + "category": "feedback", + "canHaveChildren": false, + "description": "Status indicator badge", + "status": "supported" + }, + { + "type": "StatusIcon", + "name": "StatusIcon", + "category": "feedback", + "canHaveChildren": false, + "description": "Status indicator icon", + "status": "planned" + }, + { + "type": "EmptyState", + "name": "EmptyState", + "category": "feedback", + "canHaveChildren": true, + "description": "Empty state placeholder", + "status": "supported" + }, + { + "type": "ErrorBadge", + "name": "ErrorBadge", + "category": "feedback", + "canHaveChildren": false, + "description": "Error state badge", + "status": "planned" + }, + { + "type": "List", + "name": "List", + "category": "data", + "canHaveChildren": false, + "description": "Generic list renderer with custom items", + "status": "supported" + }, + { + "type": "DataList", + "name": "DataList", + "category": "data", + "canHaveChildren": false, + "description": "Styled data list", + "status": "planned" + }, + { + "type": "Table", + "name": "Table", + "category": "data", + "canHaveChildren": false, + "description": "Data table", + "status": "supported" + }, + { + "type": "DataTable", + "name": "DataTable", + "category": "data", + "canHaveChildren": false, + "description": "Advanced data table with sorting and filtering", + "status": "planned" + }, + { + "type": "KeyValue", + "name": "KeyValue", + "category": "data", + "canHaveChildren": false, + "description": "Key-value pair display", + "status": "supported" + }, + { + "type": "Timeline", + "name": "Timeline", + "category": "data", + "canHaveChildren": false, + "description": "Timeline visualization", + "status": "planned" + }, + { + "type": "StatCard", + "name": "StatCard", + "category": "data", + "canHaveChildren": false, + "description": "Statistic card display", + "status": "supported" + }, + { + "type": "MetricCard", + "name": "MetricCard", + "category": "data", + "canHaveChildren": false, + "description": "Metric display card", + "status": "planned" + }, + { + "type": "DataCard", + "name": "DataCard", + "category": "custom", + "canHaveChildren": false, + "description": "Custom data display card", + "status": "supported" + }, + { + "type": "SearchInput", + "name": "SearchInput", + "category": "custom", + "canHaveChildren": false, + "description": "Search input with icon", + "status": "supported" + }, + { + "type": "ActionBar", + "name": "ActionBar", + "category": "custom", + "canHaveChildren": false, + "description": "Action button toolbar", + "status": "supported" + }, + { + "type": "Dialog", + "name": "Dialog", + "category": "layout", + "canHaveChildren": true, + "description": "Modal dialog overlay", + "status": "supported" + } +] diff --git a/json-components-registry.json b/json-components-registry.json new file mode 100644 index 0000000..e42e308 --- /dev/null +++ b/json-components-registry.json @@ -0,0 +1,512 @@ +{ + "$schema": "./schemas/json-components-registry-schema.json", + "version": "1.0.0", + "description": "Registry of all components that can be rendered from JSON schemas", + "categories": { + "layout": "Layout and container components", + "input": "Form inputs and interactive controls", + "display": "Display and presentation components", + "navigation": "Navigation and routing components", + "feedback": "Alerts, notifications, and status indicators", + "data": "Data display and visualization components", + "custom": "Custom domain-specific components" + }, + "components": [ + { + "type": "div", + "name": "Container (div)", + "category": "layout", + "canHaveChildren": true, + "description": "Generic container element", + "status": "supported" + }, + { + "type": "section", + "name": "Section", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic section element", + "status": "supported" + }, + { + "type": "article", + "name": "Article", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic article element", + "status": "supported" + }, + { + "type": "header", + "name": "Header", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic header element", + "status": "supported" + }, + { + "type": "footer", + "name": "Footer", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic footer element", + "status": "supported" + }, + { + "type": "main", + "name": "Main", + "category": "layout", + "canHaveChildren": true, + "description": "Semantic main content element", + "status": "supported" + }, + { + "type": "Card", + "name": "Card", + "category": "layout", + "canHaveChildren": true, + "description": "Container card with optional header, content, and footer", + "status": "supported", + "subComponents": ["CardHeader", "CardTitle", "CardDescription", "CardContent", "CardFooter"] + }, + { + "type": "Grid", + "name": "Grid", + "category": "layout", + "canHaveChildren": true, + "description": "Responsive grid layout", + "status": "supported" + }, + { + "type": "Flex", + "name": "Flex", + "category": "layout", + "canHaveChildren": true, + "description": "Flexible box layout container", + "status": "supported" + }, + { + "type": "Stack", + "name": "Stack", + "category": "layout", + "canHaveChildren": true, + "description": "Vertical or horizontal stack layout", + "status": "supported" + }, + { + "type": "Container", + "name": "Container", + "category": "layout", + "canHaveChildren": true, + "description": "Centered container with max-width", + "status": "supported" + }, + { + "type": "Button", + "name": "Button", + "category": "input", + "canHaveChildren": true, + "description": "Interactive button element", + "status": "supported" + }, + { + "type": "Input", + "name": "Input", + "category": "input", + "canHaveChildren": false, + "description": "Text input field", + "status": "supported" + }, + { + "type": "TextArea", + "name": "TextArea", + "category": "input", + "canHaveChildren": false, + "description": "Multi-line text input", + "status": "supported" + }, + { + "type": "Select", + "name": "Select", + "category": "input", + "canHaveChildren": false, + "description": "Dropdown select control", + "status": "supported" + }, + { + "type": "Checkbox", + "name": "Checkbox", + "category": "input", + "canHaveChildren": false, + "description": "Checkbox toggle control", + "status": "supported" + }, + { + "type": "Radio", + "name": "Radio", + "category": "input", + "canHaveChildren": false, + "description": "Radio button selection", + "status": "supported" + }, + { + "type": "Switch", + "name": "Switch", + "category": "input", + "canHaveChildren": false, + "description": "Toggle switch control", + "status": "supported" + }, + { + "type": "Slider", + "name": "Slider", + "category": "input", + "canHaveChildren": false, + "description": "Numeric range slider", + "status": "supported" + }, + { + "type": "NumberInput", + "name": "NumberInput", + "category": "input", + "canHaveChildren": false, + "description": "Numeric input with increment/decrement", + "status": "supported" + }, + { + "type": "DatePicker", + "name": "DatePicker", + "category": "input", + "canHaveChildren": false, + "description": "Date selection input", + "status": "planned" + }, + { + "type": "FileUpload", + "name": "FileUpload", + "category": "input", + "canHaveChildren": false, + "description": "File upload control", + "status": "planned" + }, + { + "type": "Text", + "name": "Text", + "category": "display", + "canHaveChildren": true, + "description": "Text content with typography variants", + "status": "supported" + }, + { + "type": "Heading", + "name": "Heading", + "category": "display", + "canHaveChildren": true, + "description": "Heading text with level (h1-h6)", + "status": "supported" + }, + { + "type": "Label", + "name": "Label", + "category": "display", + "canHaveChildren": true, + "description": "Form label element", + "status": "supported" + }, + { + "type": "Badge", + "name": "Badge", + "category": "display", + "canHaveChildren": true, + "description": "Small status or count indicator", + "status": "supported" + }, + { + "type": "Tag", + "name": "Tag", + "category": "display", + "canHaveChildren": true, + "description": "Removable tag or chip", + "status": "supported" + }, + { + "type": "Code", + "name": "Code", + "category": "display", + "canHaveChildren": true, + "description": "Inline or block code display", + "status": "supported" + }, + { + "type": "Image", + "name": "Image", + "category": "display", + "canHaveChildren": false, + "description": "Image element with loading states", + "status": "supported" + }, + { + "type": "Avatar", + "name": "Avatar", + "category": "display", + "canHaveChildren": false, + "description": "User avatar image", + "status": "supported" + }, + { + "type": "Icon", + "name": "Icon", + "category": "display", + "canHaveChildren": false, + "description": "Icon from icon library", + "status": "planned" + }, + { + "type": "Separator", + "name": "Separator", + "category": "display", + "canHaveChildren": false, + "description": "Visual divider line", + "status": "supported" + }, + { + "type": "Divider", + "name": "Divider", + "category": "display", + "canHaveChildren": false, + "description": "Visual section divider", + "status": "planned" + }, + { + "type": "Progress", + "name": "Progress", + "category": "display", + "canHaveChildren": false, + "description": "Progress bar indicator", + "status": "supported" + }, + { + "type": "ProgressBar", + "name": "ProgressBar", + "category": "display", + "canHaveChildren": false, + "description": "Linear progress bar", + "status": "planned" + }, + { + "type": "CircularProgress", + "name": "CircularProgress", + "category": "display", + "canHaveChildren": false, + "description": "Circular progress indicator", + "status": "planned" + }, + { + "type": "Spinner", + "name": "Spinner", + "category": "display", + "canHaveChildren": false, + "description": "Loading spinner", + "status": "supported" + }, + { + "type": "Skeleton", + "name": "Skeleton", + "category": "display", + "canHaveChildren": false, + "description": "Loading skeleton placeholder", + "status": "supported" + }, + { + "type": "Link", + "name": "Link", + "category": "navigation", + "canHaveChildren": true, + "description": "Hyperlink element", + "status": "supported" + }, + { + "type": "Breadcrumb", + "name": "Breadcrumb", + "category": "navigation", + "canHaveChildren": false, + "description": "Navigation breadcrumb trail", + "status": "planned" + }, + { + "type": "Tabs", + "name": "Tabs", + "category": "navigation", + "canHaveChildren": true, + "description": "Tabbed interface container", + "status": "supported", + "subComponents": ["TabsList", "TabsTrigger", "TabsContent"] + }, + { + "type": "Alert", + "name": "Alert", + "category": "feedback", + "canHaveChildren": true, + "description": "Alert notification message", + "status": "supported" + }, + { + "type": "InfoBox", + "name": "InfoBox", + "category": "feedback", + "canHaveChildren": true, + "description": "Information box with icon", + "status": "supported" + }, + { + "type": "Notification", + "name": "Notification", + "category": "feedback", + "canHaveChildren": true, + "description": "Toast notification", + "status": "planned" + }, + { + "type": "StatusBadge", + "name": "StatusBadge", + "category": "feedback", + "canHaveChildren": false, + "description": "Status indicator badge", + "status": "supported" + }, + { + "type": "StatusIcon", + "name": "StatusIcon", + "category": "feedback", + "canHaveChildren": false, + "description": "Status indicator icon", + "status": "planned" + }, + { + "type": "EmptyState", + "name": "EmptyState", + "category": "feedback", + "canHaveChildren": true, + "description": "Empty state placeholder", + "status": "supported" + }, + { + "type": "ErrorBadge", + "name": "ErrorBadge", + "category": "feedback", + "canHaveChildren": false, + "description": "Error state badge", + "status": "planned" + }, + { + "type": "List", + "name": "List", + "category": "data", + "canHaveChildren": false, + "description": "Generic list renderer with custom items", + "status": "supported" + }, + { + "type": "DataList", + "name": "DataList", + "category": "data", + "canHaveChildren": false, + "description": "Styled data list", + "status": "planned" + }, + { + "type": "Table", + "name": "Table", + "category": "data", + "canHaveChildren": false, + "description": "Data table", + "status": "supported" + }, + { + "type": "DataTable", + "name": "DataTable", + "category": "data", + "canHaveChildren": false, + "description": "Advanced data table with sorting and filtering", + "status": "planned" + }, + { + "type": "KeyValue", + "name": "KeyValue", + "category": "data", + "canHaveChildren": false, + "description": "Key-value pair display", + "status": "supported" + }, + { + "type": "Timeline", + "name": "Timeline", + "category": "data", + "canHaveChildren": false, + "description": "Timeline visualization", + "status": "planned" + }, + { + "type": "StatCard", + "name": "StatCard", + "category": "data", + "canHaveChildren": false, + "description": "Statistic card display", + "status": "supported" + }, + { + "type": "MetricCard", + "name": "MetricCard", + "category": "data", + "canHaveChildren": false, + "description": "Metric display card", + "status": "planned" + }, + { + "type": "DataCard", + "name": "DataCard", + "category": "custom", + "canHaveChildren": false, + "description": "Custom data display card", + "status": "supported" + }, + { + "type": "SearchInput", + "name": "SearchInput", + "category": "custom", + "canHaveChildren": false, + "description": "Search input with icon", + "status": "supported" + }, + { + "type": "ActionBar", + "name": "ActionBar", + "category": "custom", + "canHaveChildren": false, + "description": "Action button toolbar", + "status": "supported" + }, + { + "type": "Dialog", + "name": "Dialog", + "category": "layout", + "canHaveChildren": true, + "description": "Modal dialog overlay", + "status": "supported" + } + ], + "statistics": { + "total": 60, + "supported": 46, + "planned": 14, + "byCategory": { + "layout": 12, + "input": 11, + "display": 16, + "navigation": 3, + "feedback": 7, + "data": 8, + "custom": 3 + } + } +} diff --git a/package.json b/package.json index d33b69f..758820a 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "test:e2e:report": "playwright show-report", "pages:list": "node scripts/list-pages.js", "pages:validate": "tsx src/config/validate-config.ts", - "pages:generate": "node scripts/generate-page.js" + "pages:generate": "node scripts/generate-page.js", + "components:list": "node scripts/list-json-components.cjs" }, "dependencies": { "@heroicons/react": "^2.2.0", diff --git a/scripts/list-json-components.cjs b/scripts/list-json-components.cjs new file mode 100755 index 0000000..128cb66 --- /dev/null +++ b/scripts/list-json-components.cjs @@ -0,0 +1,118 @@ +#!/usr/bin/env node + +/** + * JSON Components List Script + * + * Lists all components that can be rendered from JSON using the JSON UI system. + * + * Usage: + * node scripts/list-json-components.cjs [--format=table|json] [--status=all|supported|planned] + */ + +const fs = require('fs') +const path = require('path') + +// Read the JSON components registry +const registryPath = path.join(process.cwd(), 'json-components-registry.json') + +if (!fs.existsSync(registryPath)) { + console.error('āŒ Could not find json-components-registry.json') + process.exit(1) +} + +let registry +try { + registry = JSON.parse(fs.readFileSync(registryPath, 'utf8')) +} catch (e) { + console.error('āŒ Failed to parse json-components-registry.json:', e.message) + process.exit(1) +} + +const format = process.argv.find(arg => arg.startsWith('--format='))?.split('=')[1] || 'table' +const statusFilter = process.argv.find(arg => arg.startsWith('--status='))?.split('=')[1] || 'all' + +// Filter components by status if requested +let componentsList = registry.components +if (statusFilter !== 'all') { + componentsList = componentsList.filter(c => c.status === statusFilter) +} + +if (format === 'json') { + console.log(JSON.stringify(componentsList, null, 2)) + process.exit(0) +} + +// Table format output +console.log('\n🧩 JSON-Compatible Components\n') +console.log('═══════════════════════════════════════════════════════════════════════════\n') +console.log(`These components can be rendered from JSON schemas using the JSON UI system.`) +if (statusFilter !== 'all') { + console.log(`\nFiltered by status: ${statusFilter}`) +} +console.log() + +// Group by category +const categories = ['layout', 'input', 'display', 'navigation', 'feedback', 'data', 'custom'] +const categoryIcons = { + layout: 'šŸ“', + input: 'āŒØļø ', + display: 'šŸŽØ', + navigation: '🧭', + feedback: 'šŸ’¬', + data: 'šŸ“Š', + custom: '⚔' +} + +categories.forEach(category => { + const categoryComps = componentsList.filter(c => c.category === category) + + if (categoryComps.length === 0) return + + console.log(`\n${categoryIcons[category]} ${category.toUpperCase()}\n`) + console.log('───────────────────────────────────────────────────────────────────────────') + + categoryComps.forEach(comp => { + const children = comp.canHaveChildren ? 'šŸ‘¶ Can have children' : 'āž– No children' + const statusIcon = comp.status === 'supported' ? 'āœ…' : 'šŸ“‹' + const subComps = comp.subComponents ? ` (includes: ${comp.subComponents.join(', ')})` : '' + + console.log(` ${statusIcon} ${comp.name} (${comp.type})`) + console.log(` ${comp.description}`) + console.log(` ${children}`) + if (comp.subComponents) { + console.log(` Sub-components: ${comp.subComponents.join(', ')}`) + } + console.log('') + }) +}) + +console.log('═══════════════════════════════════════════════════════════════════════════') +console.log(`\nTotal Components: ${componentsList.length}`) + +if (statusFilter === 'all') { + const supported = componentsList.filter(c => c.status === 'supported').length + const planned = componentsList.filter(c => c.status === 'planned').length + console.log(`\nBy Status:`) + console.log(` āœ… Supported: ${supported}`) + console.log(` šŸ“‹ Planned: ${planned}`) +} + +console.log(`\nBy Category:`) +categories.forEach(cat => { + const count = componentsList.filter(c => c.category === cat).length + if (count > 0) { + console.log(` ${categoryIcons[cat]} ${cat}: ${count}`) + } +}) + +console.log(`\nComponents with children support: ${componentsList.filter(c => c.canHaveChildren).length}`) + +console.log('\nšŸ’” Tips:') +console.log(' • Full registry in json-components-registry.json') +console.log(' • Component types defined in src/types/json-ui.ts') +console.log(' • Component registry in src/lib/json-ui/component-registry.tsx') +console.log(' • Component definitions in src/lib/component-definitions.ts') +console.log(' • Run with --format=json for JSON output') +console.log(' • Run with --status=supported to see only supported components') +console.log(' • Run with --status=planned to see only planned components') +console.log('') diff --git a/src/lib/component-definitions.ts b/src/lib/component-definitions.ts index d82cf7d..8093398 100644 --- a/src/lib/component-definitions.ts +++ b/src/lib/component-definitions.ts @@ -3,13 +3,14 @@ import { ComponentType } from '@/types/json-ui' export interface ComponentDefinition { type: ComponentType label: string - category: 'layout' | 'input' | 'display' | 'custom' + category: 'layout' | 'input' | 'display' | 'navigation' | 'feedback' | 'data' | 'custom' icon: string defaultProps?: Record canHaveChildren?: boolean } export const componentDefinitions: ComponentDefinition[] = [ + // Layout Components { type: 'div', label: 'Container', @@ -34,6 +35,30 @@ export const componentDefinitions: ComponentDefinition[] = [ 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', @@ -42,11 +67,13 @@ export const componentDefinitions: ComponentDefinition[] = [ canHaveChildren: true, defaultProps: { className: 'p-4' } }, + // Input Components { type: 'Button', label: 'Button', category: 'input', icon: 'Circle', + canHaveChildren: true, defaultProps: { children: 'Click me', variant: 'default' } }, { @@ -56,6 +83,13 @@ export const componentDefinitions: ComponentDefinition[] = [ icon: 'TextT', defaultProps: { placeholder: 'Enter text...' } }, + { + type: 'TextArea', + label: 'TextArea', + category: 'input', + icon: 'TextAlignLeft', + defaultProps: { placeholder: 'Enter text...', rows: 4 } + }, { type: 'Select', label: 'Select', @@ -70,6 +104,13 @@ export const componentDefinitions: ComponentDefinition[] = [ icon: 'CheckSquare', defaultProps: {} }, + { + type: 'Radio', + label: 'Radio', + category: 'input', + icon: 'Circle', + defaultProps: {} + }, { type: 'Switch', label: 'Switch', @@ -77,11 +118,27 @@ export const componentDefinitions: ComponentDefinition[] = [ 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' } }, { @@ -89,6 +146,7 @@ export const componentDefinitions: ComponentDefinition[] = [ label: 'Text', category: 'display', icon: 'Paragraph', + canHaveChildren: true, defaultProps: { children: 'Text content' } }, { @@ -96,8 +154,39 @@ export const componentDefinitions: ComponentDefinition[] = [ 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', @@ -105,6 +194,20 @@ export const componentDefinitions: ComponentDefinition[] = [ 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', @@ -112,6 +215,77 @@ export const componentDefinitions: ComponentDefinition[] = [ icon: 'Minus', defaultProps: {} }, + // Navigation Components + { + type: 'Link', + label: 'Link', + category: 'navigation', + icon: 'Link', + canHaveChildren: true, + defaultProps: { href: '#', children: 'Link' } + }, + // 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' } + }, + // Data Components + { + type: 'List', + label: 'List', + category: 'data', + icon: 'List', + defaultProps: { items: [], emptyMessage: 'No items' } + }, + { + type: 'Table', + label: 'Table', + category: 'data', + icon: 'Table', + defaultProps: { data: [], columns: [] } + }, + { + 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' } + }, + // Custom Components { type: 'DataCard', label: 'Data Card', @@ -127,11 +301,12 @@ export const componentDefinitions: ComponentDefinition[] = [ defaultProps: { placeholder: 'Search...' } }, { - type: 'StatusBadge', - label: 'Status Badge', + type: 'ActionBar', + label: 'Action Bar', category: 'custom', - icon: 'Circle', - defaultProps: { status: 'active', children: 'Active' } + icon: 'Toolbox', + canHaveChildren: true, + defaultProps: { actions: [] } }, ] diff --git a/src/lib/json-ui/component-registry.tsx b/src/lib/json-ui/component-registry.tsx index 1bb4ea3..0e5214c 100644 --- a/src/lib/json-ui/component-registry.tsx +++ b/src/lib/json-ui/component-registry.tsx @@ -12,8 +12,28 @@ import { Separator } from '@/components/ui/separator' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Heading } from '@/components/atoms/Heading' import { Text } from '@/components/atoms/Text' +import { TextArea } from '@/components/atoms/TextArea' import { List } from '@/components/atoms/List' import { Grid } from '@/components/atoms/Grid' +import { Stack } from '@/components/atoms/Stack' +import { Flex } from '@/components/atoms/Flex' +import { Container } from '@/components/atoms/Container' +import { Link } from '@/components/atoms/Link' +import { Image } from '@/components/atoms/Image' +import { Avatar } from '@/components/atoms/Avatar' +import { Code } from '@/components/atoms/Code' +import { Tag } from '@/components/atoms/Tag' +import { Spinner } from '@/components/atoms/Spinner' +import { Skeleton } from '@/components/atoms/Skeleton' +import { Slider } from '@/components/atoms/Slider' +import { NumberInput } from '@/components/atoms/NumberInput' +import { Radio } from '@/components/atoms/Radio' +import { Alert } from '@/components/atoms/Alert' +import { InfoBox } from '@/components/atoms/InfoBox' +import { EmptyState } from '@/components/atoms/EmptyState' +import { Table } from '@/components/atoms/Table' +import { KeyValue } from '@/components/atoms/KeyValue' +import { StatCard } from '@/components/atoms/StatCard' import { StatusBadge } from '@/components/atoms/StatusBadge' import { DataCard } from '@/components/molecules/DataCard' import { SearchInput } from '@/components/molecules/SearchInput' @@ -29,10 +49,15 @@ export const componentRegistry: Record = { 'Button': Button, 'Card': Card, 'Input': Input, + 'TextArea': TextArea, 'Select': Select, 'Checkbox': Checkbox, + 'Radio': Radio, 'Switch': Switch, + 'Slider': Slider, + 'NumberInput': NumberInput, 'Badge': Badge, + 'Tag': Tag, 'Progress': Progress, 'Separator': Separator, 'Tabs': Tabs, @@ -40,8 +65,27 @@ export const componentRegistry: Record = { 'Text': Text, 'Heading': Heading, 'Label': Label, + 'Link': Link, + 'Image': Image, + 'Avatar': Avatar, + 'Code': Code, + 'Spinner': Spinner, + 'Skeleton': Skeleton, 'List': List, 'Grid': Grid, + 'Stack': Stack, + 'Flex': Flex, + 'Container': Container, + 'Alert': Alert, + 'InfoBox': InfoBox, + 'EmptyState': EmptyState, + 'StatusBadge': StatusBadge, + 'Table': Table, + 'KeyValue': KeyValue, + 'StatCard': StatCard, + 'DataCard': DataCard, + 'SearchInput': SearchInput, + 'ActionBar': ActionBar, } export const cardSubComponents = { @@ -63,6 +107,12 @@ export const customComponents = { 'DataCard': DataCard, 'SearchInput': SearchInput, 'ActionBar': ActionBar, + 'StatCard': StatCard, + 'KeyValue': KeyValue, + 'Table': Table, + 'Alert': Alert, + 'InfoBox': InfoBox, + 'EmptyState': EmptyState, } export function getComponent(type: ComponentType | string): any { diff --git a/src/types/json-ui.ts b/src/types/json-ui.ts index f2dd350..4862d46 100644 --- a/src/types/json-ui.ts +++ b/src/types/json-ui.ts @@ -3,10 +3,12 @@ import { ReactNode } from 'react' export type ComponentType = | 'div' | 'section' | 'article' | 'header' | 'footer' | 'main' | 'Button' | 'Card' | 'CardHeader' | 'CardTitle' | 'CardDescription' | 'CardContent' | 'CardFooter' - | 'Input' | 'Select' | 'Checkbox' | 'Switch' + | 'Input' | 'TextArea' | 'Select' | 'Checkbox' | 'Radio' | 'Switch' | 'Slider' | 'NumberInput' | 'Badge' | 'Progress' | 'Separator' | 'Tabs' | 'Dialog' - | 'Text' | 'Heading' | 'Label' | 'List' | 'Grid' - | 'StatusBadge' | 'DataCard' | 'SearchInput' | 'ActionBar' + | 'Text' | 'Heading' | 'Label' | 'List' | 'Grid' | 'Stack' | 'Flex' | 'Container' + | 'Link' | 'Image' | 'Avatar' | 'Code' | 'Tag' | 'Spinner' | 'Skeleton' + | 'Alert' | 'InfoBox' | 'EmptyState' | 'StatusBadge' + | 'Table' | 'KeyValue' | 'StatCard' | 'DataCard' | 'SearchInput' | 'ActionBar' export type ActionType = | 'create' | 'update' | 'delete' | 'navigate'