From b453ead800cf647aa67126f71aee577c71d3b3b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 21:22:23 +0000 Subject: [PATCH 1/4] feat(types,core)!: an authored flex takes its props in the properties bag; the flat spelling is refused by name The `flex` batch of objectui#11276, under the maintainer's ruling A on objectui#11300. `@objectstack/spec`'s strict page component refuses a prop written on the node as mis-layered (ADR-0089 D3a); objectui's `flex` arm was the flat mirror, so the strict face refused the objectstack showcase's bag-shaped `flex` nodes and both faces accepted the flat node `os validate` refuses. - `FlexBlockSchema` arms the authored node: `BaseSchema` + the literal + `NODE_ENVELOPE` + `properties`. The spec has no `ComponentPropsMap['flex']` row, so the bag is the flat mirror's own members by reference (the `object-chart` construct); the child list is in the bag, its list entries judged once by the spec page walk. Flat members are refused by name through the shared `flatPropRefusals` (new optional no-row guidance), `body` toward `properties.children`. `FlexSchema` stays published as the post-hoist node. - objectui's own producers move to the bag: 70 schema-catalog documents (248 nodes), `layout.md`, `flex.mdx`, the `@object-ui/types` README and examples. `@object-ui/core`'s `flex()` builder emits the bag. - objectui#6751's fence is revoked for `flex` only; its pins flip to bag pins. `stack` and every other node-level arm are unchanged. The render path is untouched: a stored flat node still draws (pinned). Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude --- content/docs/components/layout/flex.mdx | 43 +- content/docs/guide/layout.md | 94 +- .../actions/action-button-variants.json | 56 +- .../src/schemas/actions/action-toolbar.json | 144 +-- .../schemas/actions/confirmation-dialog.json | 34 +- .../src/schemas/app/application-header.json | 132 ++- .../src/schemas/app/sidebar-navigation.json | 204 ++-- .../src/schemas/auth/login-simple.json | 40 +- .../src/schemas/auth/signup.json | 24 +- .../block-marketplace-listing.json | 150 +-- ...-variable-overrides-analytics-feature.json | 24 +- ...h-variable-overrides-security-feature.json | 24 +- .../block-schema/feature-card-block.json | 24 +- .../block-gallery-notification-item.json | 64 +- .../block-gallery-stats-card.json | 30 +- .../components-basic-div/flex-layout.json | 32 +- .../components-basic-icon/colored-icons.json | 38 +- .../components-basic-icon/icon-sizes.json | 50 +- .../components-basic-separator/vertical.json | 32 +- .../components-basic-span/status-badges.json | 38 +- .../text-with-colors.json | 38 +- .../photo-gallery.json | 88 +- .../search-interface.json | 30 +- .../horizontal-split.json | 44 +- .../chat-messages.json | 1050 +++++++++-------- .../document-browser.json | 1040 ++++++++-------- .../horizontal-scroll.json | 290 ++--- .../badge-variants.json | 46 +- .../inline-usage.json | 40 +- .../social-stats.json | 50 +- .../centered-spinner.json | 20 +- .../components-form-button/button-sizes.json | 40 +- .../common-action-buttons.json | 42 +- .../components-form-button/icon-button.json | 20 +- .../date-range-selector.json | 34 +- .../form-field.json | 30 +- .../verification-form.json | 38 +- .../components-form-label/form-label.json | 30 +- .../components-form-label/required-label.json | 30 +- .../form-field.json | 54 +- .../settings-example.json | 92 +- .../settings-toggles.json | 38 +- .../text-formatting.json | 52 +- .../components-form-toggle/toggle-sizes.json | 40 +- .../inventory-table-card.json | 434 +++---- .../profile-detail-card.json | 212 ++-- .../team-roster-card.json | 354 +++--- .../user-list-card.json | 296 ++--- .../components-layout-card/with-footer.json | 30 +- .../horizontal-layout.json | 32 +- .../settings-layout.json | 22 +- .../complete-layout.json | 40 +- .../components-layout-semantic/header.json | 30 +- .../navigation.json | 38 +- .../nested-schema-example.json | 26 +- .../schemas/dashboard/dashboard-overview.json | 112 +- .../dashboard/recent-activity-card.json | 362 +++--- .../src/schemas/ecommerce/order-summary.json | 262 ++-- .../src/schemas/ecommerce/product-card.json | 182 +-- .../src/schemas/ecommerce/product-grid.json | 128 +- .../src/schemas/ecommerce/shopping-cart.json | 434 +++---- .../src/schemas/forms/create-user-form.json | 34 +- .../src/schemas/forms/newsletter-signup.json | 34 +- .../src/schemas/forms/settings-form.json | 136 ++- .../src/schemas/marketing/call-to-action.json | 148 +-- .../src/schemas/marketing/features-grid.json | 144 +-- .../src/schemas/marketing/pricing-table.json | 424 +++---- .../src/schemas/marketing/testimonials.json | 336 +++--- .../report/report-breakdown-table.json | 344 +++--- .../report/report-header-with-kpis.json | 96 +- .../src/schemas/report/report-scheduling.json | 134 ++- .../theme/theme-aware-ui-elements.json | 110 +- .../test/badge-demo-label-6829.test.tsx | 24 +- .../test/flex-direction-col-8761.test.tsx | 18 +- .../test/layout-props-conversion.test.tsx | 48 +- .../flex-properties-bag-render-11276.test.tsx | 83 ++ .../builder/__tests__/schema-builder.test.ts | 41 +- packages/core/src/builder/schema-builder.ts | 30 +- packages/types/README.md | 34 +- packages/types/examples/dashboard.ts | 149 +-- .../types/examples/data-display-examples.json | 76 +- .../flat-arm-responsive-styles-10872.test.ts | 47 +- .../flex-properties-bag-11276.test.ts | 410 +++++++ .../flex-props-envelope-lift-6751.test.ts | 60 +- .../page-actions-refusal-7926.test.ts | 17 +- .../src/__tests__/zod-mirror-parity.test.ts | 7 + packages/types/src/layout.ts | 13 +- packages/types/src/zod/README.md | 6 +- packages/types/src/zod/index.zod.ts | 4 + packages/types/src/zod/layout.zod.ts | 162 ++- packages/types/src/zod/public-blocks.zod.ts | 30 +- 91 files changed, 6046 insertions(+), 4700 deletions(-) create mode 100644 packages/components/src/__tests__/flex-properties-bag-render-11276.test.tsx create mode 100644 packages/types/src/__tests__/flex-properties-bag-11276.test.ts diff --git a/content/docs/components/layout/flex.mdx b/content/docs/components/layout/flex.mdx index 73ee67246b..f7ef74f1c4 100644 --- a/content/docs/components/layout/flex.mdx +++ b/content/docs/components/layout/flex.mdx @@ -9,17 +9,46 @@ description: "Flexible box layout container" ## Schema +A `flex` node takes its props — the child list included — in its `properties` bag, the +spelling `@objectstack/spec`'s page component declares. `objectui validate` refuses a `flex` +prop written on the node itself, by name, and points at its bag member +(`properties.direction`, `properties.children`, …). `className` and the other base props stay +on the node. + +```json +{ + "type": "flex", + "className": "p-4", + "properties": { + "direction": "col", + "gap": 4, + "children": [ + { "type": "text", "content": "First" }, + { "type": "text", "content": "Second" } + ] + } +} +``` + +The bag's members are `FlexLayoutProps` from `@object-ui/types`: + ```ts import type { SchemaNode } from '@object-ui/types'; -interface FlexSchema { +interface FlexNode { type: 'flex'; - direction?: 'row' | 'col' | 'row-reverse' | 'col-reverse'; - gap?: number; - align?: 'start' | 'center' | 'end'; - justify?: 'start' | 'center' | 'end' | 'between'; - wrap?: boolean; - children: SchemaNode[]; className?: string; + properties?: { + direction?: 'row' | 'col' | 'row-reverse' | 'col-reverse'; + gap?: number; + align?: 'start' | 'end' | 'center' | 'baseline' | 'stretch'; + justify?: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'; + wrap?: boolean; + children?: SchemaNode[]; + }; } ``` + +Nothing changes at render time: `SchemaRenderer` hoists every `properties` key onto the node +before the `flex` renderer reads it, so a stored node that still writes these props flat keeps +rendering. diff --git a/content/docs/guide/layout.md b/content/docs/guide/layout.md index 65a79824db..924b6d0eed 100644 --- a/content/docs/guide/layout.md +++ b/content/docs/guide/layout.md @@ -154,22 +154,24 @@ A `page` node has no action row of its own. Buttons are NODES, and they go in `c "children": [ { "type": "flex", - "justify": "end", - "gap": 2, - "children": [ - { - "type": "button", - "label": "Add Product", - "variant": "default", - "icon": "plus" - }, - { - "type": "button", - "label": "Export", - "variant": "outline", - "icon": "download" - } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "button", + "label": "Add Product", + "variant": "default", + "icon": "plus" + }, + { + "type": "button", + "label": "Export", + "variant": "outline", + "icon": "download" + } + ] + } }, { "type": "object-grid", @@ -541,30 +543,32 @@ Same rule as above, and it governs the trail too: the breadcrumb and the buttons }, { "type": "flex", - "justify": "end", - "gap": 2, - "children": [ - { - "type": "action:button", - "properties": { - "name": "edit_record", - "label": "Edit", - "variant": "default", - "icon": "pencil", - "actionType": "editRecord" + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "action:button", + "properties": { + "name": "edit_record", + "label": "Edit", + "variant": "default", + "icon": "pencil", + "actionType": "editRecord" + } + }, + { + "type": "action:button", + "properties": { + "name": "delete_record", + "label": "Delete", + "variant": "destructive", + "icon": "trash", + "actionType": "deleteRecord" + } } - }, - { - "type": "action:button", - "properties": { - "name": "delete_record", - "label": "Delete", - "variant": "destructive", - "icon": "trash", - "actionType": "deleteRecord" - } - } - ] + ] + } }, { "type": "card", @@ -726,11 +730,13 @@ Place primary actions in the first `children` node, so they sit above the conten "children": [ { "type": "flex", - "justify": "end", - "gap": 2, - "children": [ - { "type": "button", "label": "New Order", "variant": "default" } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { "type": "button", "label": "New Order", "variant": "default" } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/actions/action-button-variants.json b/examples/schema-catalog/src/schemas/actions/action-button-variants.json index 14968babe4..fbc8098749 100644 --- a/examples/schema-catalog/src/schemas/actions/action-button-variants.json +++ b/examples/schema-catalog/src/schemas/actions/action-button-variants.json @@ -4,33 +4,35 @@ "children": [ { "type": "flex", - "gap": 2, - "wrap": true, - "children": [ - { - "type": "button", - "label": "Submit", - "variant": "default", - "icon": "send" - }, - { - "type": "button", - "label": "Save Draft", - "variant": "outline", - "icon": "save" - }, - { - "type": "button", - "label": "Delete", - "variant": "destructive", - "icon": "trash" - }, - { - "type": "button", - "label": "Cancel", - "variant": "ghost" - } - ] + "properties": { + "gap": 2, + "wrap": true, + "children": [ + { + "type": "button", + "label": "Submit", + "variant": "default", + "icon": "send" + }, + { + "type": "button", + "label": "Save Draft", + "variant": "outline", + "icon": "save" + }, + { + "type": "button", + "label": "Delete", + "variant": "destructive", + "icon": "trash" + }, + { + "type": "button", + "label": "Cancel", + "variant": "ghost" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/actions/action-toolbar.json b/examples/schema-catalog/src/schemas/actions/action-toolbar.json index 1f617feab4..a97b461396 100644 --- a/examples/schema-catalog/src/schemas/actions/action-toolbar.json +++ b/examples/schema-catalog/src/schemas/actions/action-toolbar.json @@ -3,21 +3,23 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "border-b pb-3 mb-4", - "children": [ - { - "type": "text", - "content": "Order #12345", - "className": "font-semibold" - }, - { - "type": "badge", - "label": "Pending", - "variant": "outline" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Order #12345", + "className": "font-semibold" + }, + { + "type": "badge", + "label": "Pending", + "variant": "outline" + } + ] + }, + "className": "border-b pb-3 mb-4" }, { "type": "stack", @@ -25,67 +27,73 @@ "children": [ { "type": "flex", - "gap": 2, - "align": "center", - "children": [ - { - "type": "text", - "content": "Customer:", - "className": "text-sm text-muted-foreground w-24" - }, - { - "type": "text", - "content": "Acme Corp", - "className": "text-sm font-medium" - } - ] + "properties": { + "gap": 2, + "align": "center", + "children": [ + { + "type": "text", + "content": "Customer:", + "className": "text-sm text-muted-foreground w-24" + }, + { + "type": "text", + "content": "Acme Corp", + "className": "text-sm font-medium" + } + ] + } }, { "type": "flex", - "gap": 2, - "align": "center", - "children": [ - { - "type": "text", - "content": "Amount:", - "className": "text-sm text-muted-foreground w-24" - }, - { - "type": "text", - "content": "$1,250.00", - "className": "text-sm font-medium" - } - ] + "properties": { + "gap": 2, + "align": "center", + "children": [ + { + "type": "text", + "content": "Amount:", + "className": "text-sm text-muted-foreground w-24" + }, + { + "type": "text", + "content": "$1,250.00", + "className": "text-sm font-medium" + } + ] + } } ] }, { "type": "flex", - "justify": "end", - "gap": 2, - "className": "pt-4 border-t mt-4", - "children": [ - { - "type": "button", - "label": "Reject", - "variant": "outline", - "size": "sm" - }, - { - "type": "button", - "label": "Request Review", - "variant": "secondary", - "size": "sm", - "icon": "eye" - }, - { - "type": "button", - "label": "Approve", - "variant": "default", - "size": "sm", - "icon": "check" - } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "button", + "label": "Reject", + "variant": "outline", + "size": "sm" + }, + { + "type": "button", + "label": "Request Review", + "variant": "secondary", + "size": "sm", + "icon": "eye" + }, + { + "type": "button", + "label": "Approve", + "variant": "default", + "size": "sm", + "icon": "check" + } + ] + }, + "className": "pt-4 border-t mt-4" } ] } diff --git a/examples/schema-catalog/src/schemas/actions/confirmation-dialog.json b/examples/schema-catalog/src/schemas/actions/confirmation-dialog.json index 3e840e2320..ca324ead6c 100644 --- a/examples/schema-catalog/src/schemas/actions/confirmation-dialog.json +++ b/examples/schema-catalog/src/schemas/actions/confirmation-dialog.json @@ -22,22 +22,24 @@ "children": [ { "type": "flex", - "justify": "end", - "gap": 2, - "className": "pt-4", - "children": [ - { - "type": "button", - "label": "Cancel", - "variant": "outline" - }, - { - "type": "button", - "label": "Yes, Delete", - "variant": "destructive", - "icon": "trash" - } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "button", + "label": "Cancel", + "variant": "outline" + }, + { + "type": "button", + "label": "Yes, Delete", + "variant": "destructive", + "icon": "trash" + } + ] + }, + "className": "pt-4" } ] } diff --git a/examples/schema-catalog/src/schemas/app/application-header.json b/examples/schema-catalog/src/schemas/app/application-header.json index 28375f17aa..195d818c6b 100644 --- a/examples/schema-catalog/src/schemas/app/application-header.json +++ b/examples/schema-catalog/src/schemas/app/application-header.json @@ -1,75 +1,83 @@ { "type": "flex", - "align": "center", - "justify": "between", - "className": "p-3 border rounded-lg bg-background", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 3, - "children": [ - { - "type": "icon", - "icon": "box", - "className": "h-6 w-6 text-primary" - }, - { - "type": "text", - "content": "Acme CRM", - "className": "font-semibold text-lg" + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "icon", + "icon": "box", + "className": "h-6 w-6 text-primary" + }, + { + "type": "text", + "content": "Acme CRM", + "className": "font-semibold text-lg" + } + ] } - ] - }, - { - "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "button", - "label": "Quick Actions", - "variant": "outline", - "size": "sm", - "icon": "zap" - }, - { - "type": "button", - "label": "Notifications", - "variant": "ghost", - "size": "sm", - "icon": "bell" - }, - { - "type": "flex", + }, + { + "type": "flex", + "properties": { "align": "center", "gap": 2, - "className": "ml-2 pl-2 border-l", "children": [ { - "type": "avatar", - "fallback": "JD", - "className": "h-8 w-8" + "type": "button", + "label": "Quick Actions", + "variant": "outline", + "size": "sm", + "icon": "zap" + }, + { + "type": "button", + "label": "Notifications", + "variant": "ghost", + "size": "sm", + "icon": "bell" }, { - "type": "stack", - "gap": 0, - "children": [ - { - "type": "text", - "content": "John Doe", - "className": "text-sm font-medium leading-none" - }, - { - "type": "text", - "content": "john@acme.com", - "className": "text-xs text-muted-foreground" - } - ] + "type": "flex", + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "avatar", + "fallback": "JD", + "className": "h-8 w-8" + }, + { + "type": "stack", + "gap": 0, + "children": [ + { + "type": "text", + "content": "John Doe", + "className": "text-sm font-medium leading-none" + }, + { + "type": "text", + "content": "john@acme.com", + "className": "text-xs text-muted-foreground" + } + ] + } + ] + }, + "className": "ml-2 pl-2 border-l" } ] } - ] - } - ] + } + ] + }, + "className": "p-3 border rounded-lg bg-background" } diff --git a/examples/schema-catalog/src/schemas/app/sidebar-navigation.json b/examples/schema-catalog/src/schemas/app/sidebar-navigation.json index 5600b393b2..31ad321763 100644 --- a/examples/schema-catalog/src/schemas/app/sidebar-navigation.json +++ b/examples/schema-catalog/src/schemas/app/sidebar-navigation.json @@ -8,63 +8,69 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 2, - "className": "px-3 py-2 rounded-md bg-accent text-accent-foreground", - "children": [ - { - "type": "icon", - "icon": "layout-dashboard", - "className": "h-4 w-4" - }, - { - "type": "text", - "content": "Dashboard", - "className": "text-sm font-medium" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "layout-dashboard", + "className": "h-4 w-4" + }, + { + "type": "text", + "content": "Dashboard", + "className": "text-sm font-medium" + } + ] + }, + "className": "px-3 py-2 rounded-md bg-accent text-accent-foreground" }, { "type": "flex", - "align": "center", - "gap": 2, - "className": "px-3 py-2 rounded-md hover:bg-accent/50", - "children": [ - { - "type": "icon", - "icon": "users", - "className": "h-4 w-4 text-muted-foreground" - }, - { - "type": "text", - "content": "Contacts", - "className": "text-sm" - }, - { - "type": "badge", - "label": "128", - "variant": "secondary", - "className": "ml-auto text-xs" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "users", + "className": "h-4 w-4 text-muted-foreground" + }, + { + "type": "text", + "content": "Contacts", + "className": "text-sm" + }, + { + "type": "badge", + "label": "128", + "variant": "secondary", + "className": "ml-auto text-xs" + } + ] + }, + "className": "px-3 py-2 rounded-md hover:bg-accent/50" }, { "type": "flex", - "align": "center", - "gap": 2, - "className": "px-3 py-2 rounded-md hover:bg-accent/50", - "children": [ - { - "type": "icon", - "icon": "target", - "className": "h-4 w-4 text-muted-foreground" - }, - { - "type": "text", - "content": "Opportunities", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "target", + "className": "h-4 w-4 text-muted-foreground" + }, + { + "type": "text", + "content": "Opportunities", + "className": "text-sm" + } + ] + }, + "className": "px-3 py-2 rounded-md hover:bg-accent/50" }, { "type": "separator", @@ -77,39 +83,43 @@ }, { "type": "flex", - "align": "center", - "gap": 2, - "className": "px-3 py-2 rounded-md hover:bg-accent/50", - "children": [ - { - "type": "icon", - "icon": "megaphone", - "className": "h-4 w-4 text-muted-foreground" - }, - { - "type": "text", - "content": "Campaigns", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "megaphone", + "className": "h-4 w-4 text-muted-foreground" + }, + { + "type": "text", + "content": "Campaigns", + "className": "text-sm" + } + ] + }, + "className": "px-3 py-2 rounded-md hover:bg-accent/50" }, { "type": "flex", - "align": "center", - "gap": 2, - "className": "px-3 py-2 rounded-md hover:bg-accent/50", - "children": [ - { - "type": "icon", - "icon": "mail", - "className": "h-4 w-4 text-muted-foreground" - }, - { - "type": "text", - "content": "Email Templates", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "mail", + "className": "h-4 w-4 text-muted-foreground" + }, + { + "type": "text", + "content": "Email Templates", + "className": "text-sm" + } + ] + }, + "className": "px-3 py-2 rounded-md hover:bg-accent/50" }, { "type": "separator", @@ -117,21 +127,23 @@ }, { "type": "flex", - "align": "center", - "gap": 2, - "className": "px-3 py-2 rounded-md hover:bg-accent/50", - "children": [ - { - "type": "icon", - "icon": "settings", - "className": "h-4 w-4 text-muted-foreground" - }, - { - "type": "text", - "content": "Settings", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "settings", + "className": "h-4 w-4 text-muted-foreground" + }, + { + "type": "text", + "content": "Settings", + "className": "text-sm" + } + ] + }, + "className": "px-3 py-2 rounded-md hover:bg-accent/50" } ] } diff --git a/examples/schema-catalog/src/schemas/auth/login-simple.json b/examples/schema-catalog/src/schemas/auth/login-simple.json index 40847457f4..899837c98d 100644 --- a/examples/schema-catalog/src/schemas/auth/login-simple.json +++ b/examples/schema-catalog/src/schemas/auth/login-simple.json @@ -49,24 +49,28 @@ }, { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { "type": "checkbox", "id": "remember", "name": "remember" }, - { "type": "label", "htmlFor": "remember", "label": "Remember me", "className": "text-sm font-normal" } - ] - }, - { - "type": "text", - "content": "Forgot password?", - "className": "text-sm text-primary hover:underline cursor-pointer" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 2, + "children": [ + { "type": "checkbox", "id": "remember", "name": "remember" }, + { "type": "label", "htmlFor": "remember", "label": "Remember me", "className": "text-sm font-normal" } + ] + } + }, + { + "type": "text", + "content": "Forgot password?", + "className": "text-sm text-primary hover:underline cursor-pointer" + } + ] + } }, { "type": "button", "label": "Sign In", "variant": "default", "className": "w-full" }, { "type": "separator", "className": "my-2" }, diff --git a/examples/schema-catalog/src/schemas/auth/signup.json b/examples/schema-catalog/src/schemas/auth/signup.json index cdd0bbff48..7814210308 100644 --- a/examples/schema-catalog/src/schemas/auth/signup.json +++ b/examples/schema-catalog/src/schemas/auth/signup.json @@ -72,17 +72,19 @@ }, { "type": "flex", - "align": "start", - "gap": 2, - "children": [ - { "type": "checkbox", "id": "terms", "name": "terms", "required": true }, - { - "type": "label", - "htmlFor": "terms", - "className": "text-sm font-normal leading-none", - "label": "I agree to the Terms of Service and Privacy Policy" - } - ] + "properties": { + "align": "start", + "gap": 2, + "children": [ + { "type": "checkbox", "id": "terms", "name": "terms", "required": true }, + { + "type": "label", + "htmlFor": "terms", + "className": "text-sm font-normal leading-none", + "label": "I agree to the Terms of Service and Privacy Policy" + } + ] + } }, { "type": "button", "label": "Create Account", "variant": "default", "className": "w-full" } ] diff --git a/examples/schema-catalog/src/schemas/block-schema/block-marketplace-listing.json b/examples/schema-catalog/src/schemas/block-schema/block-marketplace-listing.json index 7d6294cab2..01faacfd76 100644 --- a/examples/schema-catalog/src/schemas/block-schema/block-marketplace-listing.json +++ b/examples/schema-catalog/src/schemas/block-schema/block-marketplace-listing.json @@ -4,17 +4,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-32 bg-gradient-to-br from-blue-500/20 to-purple-500/20 rounded-t-lg", - "children": [ - { - "type": "icon", - "icon": "layout-template", - "className": "h-12 w-12 text-primary/60" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "layout-template", + "className": "h-12 w-12 text-primary/60" + } + ] + }, + "className": "h-32 bg-gradient-to-br from-blue-500/20 to-purple-500/20 rounded-t-lg" }, { "type": "stack", @@ -23,31 +25,33 @@ "children": [ { "type": "flex", - "align": "start", - "justify": "between", - "children": [ - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Pricing Table Pro", - "className": "font-semibold" - }, - { - "type": "text", - "content": "by ObjectUI Team", - "className": "text-xs text-muted-foreground" - } - ] - }, - { - "type": "badge", - "label": "v2.1.0", - "variant": "secondary" - } - ] + "properties": { + "align": "start", + "justify": "between", + "children": [ + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Pricing Table Pro", + "className": "font-semibold" + }, + { + "type": "text", + "content": "by ObjectUI Team", + "className": "text-xs text-muted-foreground" + } + ] + }, + { + "type": "badge", + "label": "v2.1.0", + "variant": "secondary" + } + ] + } }, { "type": "text", @@ -56,44 +60,48 @@ }, { "type": "flex", - "gap": 2, - "wrap": true, - "children": [ - { - "type": "badge", - "label": "Marketing", - "variant": "outline" - }, - { - "type": "badge", - "label": "SaaS", - "variant": "outline" - }, - { - "type": "badge", - "label": "Pricing", - "variant": "outline" - } - ] + "properties": { + "gap": 2, + "wrap": true, + "children": [ + { + "type": "badge", + "label": "Marketing", + "variant": "outline" + }, + { + "type": "badge", + "label": "SaaS", + "variant": "outline" + }, + { + "type": "badge", + "label": "Pricing", + "variant": "outline" + } + ] + } }, { "type": "flex", - "align": "center", - "justify": "between", - "className": "pt-2 border-t", - "children": [ - { - "type": "text", - "content": "★ 4.9 (234 ratings) · 5.4k installs", - "className": "text-xs text-muted-foreground" - }, - { - "type": "button", - "label": "Install", - "variant": "default", - "size": "sm" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "★ 4.9 (234 ratings) · 5.4k installs", + "className": "text-xs text-muted-foreground" + }, + { + "type": "button", + "label": "Install", + "variant": "default", + "size": "sm" + } + ] + }, + "className": "pt-2 border-t" } ] } diff --git a/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-analytics-feature.json b/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-analytics-feature.json index 41a1d6a6e2..319ce46f49 100644 --- a/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-analytics-feature.json +++ b/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-analytics-feature.json @@ -10,17 +10,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-12 h-12 rounded-lg bg-purple-500/10", - "children": [ - { - "type": "icon", - "icon": "chart-column", - "className": "h-6 w-6 text-purple-600" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "chart-column", + "className": "h-6 w-6 text-purple-600" + } + ] + }, + "className": "w-12 h-12 rounded-lg bg-purple-500/10" }, { "type": "text", diff --git a/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-security-feature.json b/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-security-feature.json index 1b1b10c4f9..fb50d8a74d 100644 --- a/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-security-feature.json +++ b/examples/schema-catalog/src/schemas/block-schema/block-with-variable-overrides-security-feature.json @@ -10,17 +10,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-12 h-12 rounded-lg bg-green-500/10", - "children": [ - { - "type": "icon", - "icon": "shield", - "className": "h-6 w-6 text-green-600" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "shield", + "className": "h-6 w-6 text-green-600" + } + ] + }, + "className": "w-12 h-12 rounded-lg bg-green-500/10" }, { "type": "text", diff --git a/examples/schema-catalog/src/schemas/block-schema/feature-card-block.json b/examples/schema-catalog/src/schemas/block-schema/feature-card-block.json index a50f6dfe1a..28648ff364 100644 --- a/examples/schema-catalog/src/schemas/block-schema/feature-card-block.json +++ b/examples/schema-catalog/src/schemas/block-schema/feature-card-block.json @@ -10,17 +10,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-12 h-12 rounded-lg bg-primary/10", - "children": [ - { - "type": "icon", - "icon": "zap", - "className": "h-6 w-6 text-primary" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "zap", + "className": "h-6 w-6 text-primary" + } + ] + }, + "className": "w-12 h-12 rounded-lg bg-primary/10" }, { "type": "text", diff --git a/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-notification-item.json b/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-notification-item.json index a626fac1cd..ece6109109 100644 --- a/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-notification-item.json +++ b/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-notification-item.json @@ -1,34 +1,36 @@ { "type": "flex", - "align": "start", - "gap": 3, - "className": "p-4 border rounded-lg max-w-md", - "children": [ - { - "type": "avatar", - "fallback": "JD", - "className": "h-10 w-10" - }, - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "John Doe commented on your post", - "className": "text-sm font-medium" - }, - { - "type": "text", - "content": "Great work on the new design! The color scheme looks amazing.", - "className": "text-sm text-muted-foreground" - }, - { - "type": "text", - "content": "2 minutes ago", - "className": "text-xs text-muted-foreground" - } - ] - } - ] + "properties": { + "align": "start", + "gap": 3, + "children": [ + { + "type": "avatar", + "fallback": "JD", + "className": "h-10 w-10" + }, + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "John Doe commented on your post", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "Great work on the new design! The color scheme looks amazing.", + "className": "text-sm text-muted-foreground" + }, + { + "type": "text", + "content": "2 minutes ago", + "className": "text-xs text-muted-foreground" + } + ] + } + ] + }, + "className": "p-4 border rounded-lg max-w-md" } diff --git a/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-stats-card.json b/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-stats-card.json index 6d686a4c30..f98beba170 100644 --- a/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-stats-card.json +++ b/examples/schema-catalog/src/schemas/blocks-gallery/block-gallery-stats-card.json @@ -9,20 +9,22 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "text", - "content": "Total Revenue", - "className": "text-sm font-medium text-muted-foreground" - }, - { - "type": "icon", - "icon": "dollar-sign", - "className": "h-4 w-4 text-muted-foreground" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Total Revenue", + "className": "text-sm font-medium text-muted-foreground" + }, + { + "type": "icon", + "icon": "dollar-sign", + "className": "h-4 w-4 text-muted-foreground" + } + ] + } }, { "type": "text", diff --git a/examples/schema-catalog/src/schemas/components-basic-div/flex-layout.json b/examples/schema-catalog/src/schemas/components-basic-div/flex-layout.json index 10019d4549..0df1d98b0e 100644 --- a/examples/schema-catalog/src/schemas/components-basic-div/flex-layout.json +++ b/examples/schema-catalog/src/schemas/components-basic-div/flex-layout.json @@ -1,18 +1,20 @@ { "type": "flex", - "gap": 4, - "children": [ - { - "type": "card", - "className": "w-20 h-20 bg-blue-500" - }, - { - "type": "card", - "className": "w-20 h-20 bg-green-500" - }, - { - "type": "card", - "className": "w-20 h-20 bg-purple-500" - } - ] + "properties": { + "gap": 4, + "children": [ + { + "type": "card", + "className": "w-20 h-20 bg-blue-500" + }, + { + "type": "card", + "className": "w-20 h-20 bg-green-500" + }, + { + "type": "card", + "className": "w-20 h-20 bg-purple-500" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-basic-icon/colored-icons.json b/examples/schema-catalog/src/schemas/components-basic-icon/colored-icons.json index afabaa1f0c..3ff6dce457 100644 --- a/examples/schema-catalog/src/schemas/components-basic-icon/colored-icons.json +++ b/examples/schema-catalog/src/schemas/components-basic-icon/colored-icons.json @@ -1,21 +1,23 @@ { "type": "flex", - "gap": 4, - "children": [ - { - "type": "icon", - "icon": "heart", - "color": "text-red-500" - }, - { - "type": "icon", - "icon": "star", - "color": "text-yellow-500" - }, - { - "type": "icon", - "icon": "check", - "color": "text-green-500" - } - ] + "properties": { + "gap": 4, + "children": [ + { + "type": "icon", + "icon": "heart", + "color": "text-red-500" + }, + { + "type": "icon", + "icon": "star", + "color": "text-yellow-500" + }, + { + "type": "icon", + "icon": "check", + "color": "text-green-500" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-basic-icon/icon-sizes.json b/examples/schema-catalog/src/schemas/components-basic-icon/icon-sizes.json index 64445f57fd..1088b3788c 100644 --- a/examples/schema-catalog/src/schemas/components-basic-icon/icon-sizes.json +++ b/examples/schema-catalog/src/schemas/components-basic-icon/icon-sizes.json @@ -1,27 +1,29 @@ { "type": "flex", - "gap": 4, - "align": "center", - "children": [ - { - "type": "icon", - "icon": "star", - "size": 16 - }, - { - "type": "icon", - "icon": "star", - "size": 24 - }, - { - "type": "icon", - "icon": "star", - "size": 32 - }, - { - "type": "icon", - "icon": "star", - "size": 48 - } - ] + "properties": { + "gap": 4, + "align": "center", + "children": [ + { + "type": "icon", + "icon": "star", + "size": 16 + }, + { + "type": "icon", + "icon": "star", + "size": 24 + }, + { + "type": "icon", + "icon": "star", + "size": 32 + }, + { + "type": "icon", + "icon": "star", + "size": 48 + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-basic-separator/vertical.json b/examples/schema-catalog/src/schemas/components-basic-separator/vertical.json index eef277ecd9..28c3183166 100644 --- a/examples/schema-catalog/src/schemas/components-basic-separator/vertical.json +++ b/examples/schema-catalog/src/schemas/components-basic-separator/vertical.json @@ -1,18 +1,20 @@ { "type": "flex", - "gap": 4, - "children": [ - { - "type": "text", - "content": "Left" - }, - { - "type": "separator", - "orientation": "vertical" - }, - { - "type": "text", - "content": "Right" - } - ] + "properties": { + "gap": 4, + "children": [ + { + "type": "text", + "content": "Left" + }, + { + "type": "separator", + "orientation": "vertical" + }, + { + "type": "text", + "content": "Right" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-basic-span/status-badges.json b/examples/schema-catalog/src/schemas/components-basic-span/status-badges.json index 4bf8715023..edd4034195 100644 --- a/examples/schema-catalog/src/schemas/components-basic-span/status-badges.json +++ b/examples/schema-catalog/src/schemas/components-basic-span/status-badges.json @@ -1,21 +1,23 @@ { "type": "flex", - "gap": 2, - "children": [ - { - "type": "badge", - "variant": "default", - "label": "● Active" - }, - { - "type": "badge", - "variant": "secondary", - "label": "● Pending" - }, - { - "type": "badge", - "variant": "destructive", - "label": "● Inactive" - } - ] + "properties": { + "gap": 2, + "children": [ + { + "type": "badge", + "variant": "default", + "label": "● Active" + }, + { + "type": "badge", + "variant": "secondary", + "label": "● Pending" + }, + { + "type": "badge", + "variant": "destructive", + "label": "● Inactive" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-basic-text/text-with-colors.json b/examples/schema-catalog/src/schemas/components-basic-text/text-with-colors.json index 4232156ccd..78a3a4635b 100644 --- a/examples/schema-catalog/src/schemas/components-basic-text/text-with-colors.json +++ b/examples/schema-catalog/src/schemas/components-basic-text/text-with-colors.json @@ -1,21 +1,23 @@ { "type": "flex", - "gap": 4, - "children": [ - { - "type": "text", - "content": "Primary Text", - "className": "text-primary" - }, - { - "type": "text", - "content": "Destructive Text", - "className": "text-destructive" - }, - { - "type": "text", - "content": "Muted Text", - "className": "text-muted-foreground" - } - ] + "properties": { + "gap": 4, + "children": [ + { + "type": "text", + "content": "Primary Text", + "className": "text-primary" + }, + { + "type": "text", + "content": "Destructive Text", + "className": "text-destructive" + }, + { + "type": "text", + "content": "Muted Text", + "className": "text-muted-foreground" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-complex-carousel/photo-gallery.json b/examples/schema-catalog/src/schemas/components-complex-carousel/photo-gallery.json index 75be6a9d3a..3b6945b897 100644 --- a/examples/schema-catalog/src/schemas/components-complex-carousel/photo-gallery.json +++ b/examples/schema-catalog/src/schemas/components-complex-carousel/photo-gallery.json @@ -8,61 +8,69 @@ [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-video bg-gradient-to-br from-blue-400 to-blue-600 rounded-lg text-white text-2xl font-bold", - "children": [ - { - "type": "text", - "content": "Photo 1" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Photo 1" + } + ] + }, + "className": "aspect-video bg-gradient-to-br from-blue-400 to-blue-600 rounded-lg text-white text-2xl font-bold" } ], [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-video bg-gradient-to-br from-purple-400 to-purple-600 rounded-lg text-white text-2xl font-bold", - "children": [ - { - "type": "text", - "content": "Photo 2" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Photo 2" + } + ] + }, + "className": "aspect-video bg-gradient-to-br from-purple-400 to-purple-600 rounded-lg text-white text-2xl font-bold" } ], [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-video bg-gradient-to-br from-green-400 to-green-600 rounded-lg text-white text-2xl font-bold", - "children": [ - { - "type": "text", - "content": "Photo 3" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Photo 3" + } + ] + }, + "className": "aspect-video bg-gradient-to-br from-green-400 to-green-600 rounded-lg text-white text-2xl font-bold" } ], [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-video bg-gradient-to-br from-orange-400 to-orange-600 rounded-lg text-white text-2xl font-bold", - "children": [ - { - "type": "text", - "content": "Photo 4" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Photo 4" + } + ] + }, + "className": "aspect-video bg-gradient-to-br from-orange-400 to-orange-600 rounded-lg text-white text-2xl font-bold" } ] ] diff --git a/examples/schema-catalog/src/schemas/components-complex-filter-builder/search-interface.json b/examples/schema-catalog/src/schemas/components-complex-filter-builder/search-interface.json index bcf74b51be..e3a041e46f 100644 --- a/examples/schema-catalog/src/schemas/components-complex-filter-builder/search-interface.json +++ b/examples/schema-catalog/src/schemas/components-complex-filter-builder/search-interface.json @@ -69,20 +69,22 @@ }, { "type": "flex", - "gap": 2, - "className": "mt-4", - "children": [ - { - "type": "button", - "label": "Apply Filters", - "variant": "default" - }, - { - "type": "button", - "label": "Clear", - "variant": "outline" - } - ] + "properties": { + "gap": 2, + "children": [ + { + "type": "button", + "label": "Apply Filters", + "variant": "default" + }, + { + "type": "button", + "label": "Clear", + "variant": "outline" + } + ] + }, + "className": "mt-4" } ] } diff --git a/examples/schema-catalog/src/schemas/components-complex-resizable/horizontal-split.json b/examples/schema-catalog/src/schemas/components-complex-resizable/horizontal-split.json index 183147d281..10240f3445 100644 --- a/examples/schema-catalog/src/schemas/components-complex-resizable/horizontal-split.json +++ b/examples/schema-catalog/src/schemas/components-complex-resizable/horizontal-split.json @@ -9,16 +9,18 @@ "content": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "p-4 h-full bg-slate-50", - "children": [ - { - "type": "text", - "content": "Panel 1" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Panel 1" + } + ] + }, + "className": "p-4 h-full bg-slate-50" } ] }, @@ -27,16 +29,18 @@ "content": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "p-4 h-full bg-slate-100", - "children": [ - { - "type": "text", - "content": "Panel 2" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Panel 2" + } + ] + }, + "className": "p-4 h-full bg-slate-100" } ] } diff --git a/examples/schema-catalog/src/schemas/components-complex-scroll-area/chat-messages.json b/examples/schema-catalog/src/schemas/components-complex-scroll-area/chat-messages.json index b4a06cd88b..7189afb311 100644 --- a/examples/schema-catalog/src/schemas/components-complex-scroll-area/chat-messages.json +++ b/examples/schema-catalog/src/schemas/components-complex-scroll-area/chat-messages.json @@ -10,558 +10,618 @@ "children": [ { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "A" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 1", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "A" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 1", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "B" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 2", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "B" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 2", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "C" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 3", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "C" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 3", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "D" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 4", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "D" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 4", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "E" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 5", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "E" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 5", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "F" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 6", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "F" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 6", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "G" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 7", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "G" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 7", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "H" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 8", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "H" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 8", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "I" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 9", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "I" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 9", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "J" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 10", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "J" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 10", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "K" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 11", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "K" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 11", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "L" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 12", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "L" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 12", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "M" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 13", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "M" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 13", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "N" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 14", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "N" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 14", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" }, { "type": "flex", - "align": "stretch", - "gap": 3, - "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0", - "children": [ - { - "type": "text", - "content": "O" - } - ] - }, - { - "type": "box", - "className": "flex-1", - "children": [ - { - "type": "text", - "content": "User 15", - "className": "font-semibold block" + "properties": { + "align": "stretch", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "O" + } + ] }, - { - "type": "text", - "content": "This is a sample message...", - "className": "text-sm text-muted-foreground block" - } - ] - } - ] + "className": "w-10 h-10 rounded-full bg-blue-500 text-white font-semibold flex-shrink-0" + }, + { + "type": "box", + "className": "flex-1", + "children": [ + { + "type": "text", + "content": "User 15", + "className": "font-semibold block" + }, + { + "type": "text", + "content": "This is a sample message...", + "className": "text-sm text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 rounded-lg bg-slate-50 hover:bg-slate-100" } ] } diff --git a/examples/schema-catalog/src/schemas/components-complex-scroll-area/document-browser.json b/examples/schema-catalog/src/schemas/components-complex-scroll-area/document-browser.json index 7ee61dca1a..3a0ff91cdb 100644 --- a/examples/schema-catalog/src/schemas/components-complex-scroll-area/document-browser.json +++ b/examples/schema-catalog/src/schemas/components-complex-scroll-area/document-browser.json @@ -9,563 +9,603 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_1.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "396KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_1.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "396KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_2.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "328KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_2.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "328KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_3.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "405KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_3.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "405KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_4.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "369KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_4.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "369KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_5.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "379KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_5.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "379KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_6.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "378KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_6.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "378KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_7.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "458KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_7.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "458KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_8.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "438KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_8.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "438KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_9.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "132KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_9.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "132KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_10.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "403KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_10.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "403KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_11.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "37KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_11.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "37KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_12.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "435KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_12.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "435KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_13.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "254KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_13.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "254KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_14.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "419KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_14.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "419KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_15.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "259KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_15.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "259KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_16.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "149KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_16.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "149KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_17.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "120KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_17.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "120KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_18.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "185KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_18.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "185KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_19.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "211KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_19.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "211KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" }, { "type": "flex", - "align": "center", - "gap": 3, - "className": "p-3 hover:bg-slate-50 cursor-pointer", - "children": [ - { - "type": "text", - "content": "📄", - "className": "text-2xl" - }, - { - "type": "box", - "children": [ - { - "type": "text", - "content": "document_20.pdf", - "className": "block font-medium" - }, - { - "type": "text", - "content": "137KB", - "className": "text-xs text-muted-foreground block" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "text", + "content": "📄", + "className": "text-2xl" + }, + { + "type": "box", + "children": [ + { + "type": "text", + "content": "document_20.pdf", + "className": "block font-medium" + }, + { + "type": "text", + "content": "137KB", + "className": "text-xs text-muted-foreground block" + } + ] + } + ] + }, + "className": "p-3 hover:bg-slate-50 cursor-pointer" } ] } diff --git a/examples/schema-catalog/src/schemas/components-complex-scroll-area/horizontal-scroll.json b/examples/schema-catalog/src/schemas/components-complex-scroll-area/horizontal-scroll.json index 23801d9606..939c5ce38b 100644 --- a/examples/schema-catalog/src/schemas/components-complex-scroll-area/horizontal-scroll.json +++ b/examples/schema-catalog/src/schemas/components-complex-scroll-area/horizontal-scroll.json @@ -7,140 +7,162 @@ "children": [ { "type": "flex", - "gap": 4, - "className": "p-4", - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 1" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 2" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 3" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 4" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 5" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 6" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 7" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 8" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 9" - } - ] - }, - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0", - "children": [ - { - "type": "text", - "content": "Item 10" - } - ] - } - ] + "properties": { + "gap": 4, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 1" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 2" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 3" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 4" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 5" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 6" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 7" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 8" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 9" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + }, + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "text", + "content": "Item 10" + } + ] + }, + "className": "w-40 h-32 bg-slate-200 rounded flex-shrink-0" + } + ] + }, + "className": "p-4" } ] } diff --git a/examples/schema-catalog/src/schemas/components-data-display-badge/badge-variants.json b/examples/schema-catalog/src/schemas/components-data-display-badge/badge-variants.json index efc2c671a4..6024f4474b 100644 --- a/examples/schema-catalog/src/schemas/components-data-display-badge/badge-variants.json +++ b/examples/schema-catalog/src/schemas/components-data-display-badge/badge-variants.json @@ -1,25 +1,27 @@ { "type": "flex", - "gap": 2, - "children": [ - { - "type": "badge", - "label": "Default" - }, - { - "type": "badge", - "label": "Secondary", - "variant": "secondary" - }, - { - "type": "badge", - "label": "Outline", - "variant": "outline" - }, - { - "type": "badge", - "label": "Destructive", - "variant": "destructive" - } - ] + "properties": { + "gap": 2, + "children": [ + { + "type": "badge", + "label": "Default" + }, + { + "type": "badge", + "label": "Secondary", + "variant": "secondary" + }, + { + "type": "badge", + "label": "Outline", + "variant": "outline" + }, + { + "type": "badge", + "label": "Destructive", + "variant": "destructive" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-data-display-kbd/inline-usage.json b/examples/schema-catalog/src/schemas/components-data-display-kbd/inline-usage.json index 4a6b189396..2753409bb7 100644 --- a/examples/schema-catalog/src/schemas/components-data-display-kbd/inline-usage.json +++ b/examples/schema-catalog/src/schemas/components-data-display-kbd/inline-usage.json @@ -1,22 +1,24 @@ { "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "text", - "content": "Press" - }, - { - "type": "kbd", - "keys": [ - "Ctrl", - "S" - ] - }, - { - "type": "text", - "content": "to save" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Press" + }, + { + "type": "kbd", + "keys": [ + "Ctrl", + "S" + ] + }, + { + "type": "text", + "content": "to save" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-data-display-statistic/social-stats.json b/examples/schema-catalog/src/schemas/components-data-display-statistic/social-stats.json index 8b0dde0489..c7e095a8ed 100644 --- a/examples/schema-catalog/src/schemas/components-data-display-statistic/social-stats.json +++ b/examples/schema-catalog/src/schemas/components-data-display-statistic/social-stats.json @@ -1,27 +1,29 @@ { "type": "flex", - "gap": 6, - "className": "p-4", - "children": [ - { - "type": "statistic", - "label": "Views", - "value": "124.5K" - }, - { - "type": "statistic", - "label": "Likes", - "value": "8.2K" - }, - { - "type": "statistic", - "label": "Comments", - "value": "342" - }, - { - "type": "statistic", - "label": "Shares", - "value": "89" - } - ] + "properties": { + "gap": 6, + "children": [ + { + "type": "statistic", + "label": "Views", + "value": "124.5K" + }, + { + "type": "statistic", + "label": "Likes", + "value": "8.2K" + }, + { + "type": "statistic", + "label": "Comments", + "value": "342" + }, + { + "type": "statistic", + "label": "Shares", + "value": "89" + } + ] + }, + "className": "p-4" } diff --git a/examples/schema-catalog/src/schemas/components-feedback-spinner/centered-spinner.json b/examples/schema-catalog/src/schemas/components-feedback-spinner/centered-spinner.json index be3e6dc3d2..ed9e31615d 100644 --- a/examples/schema-catalog/src/schemas/components-feedback-spinner/centered-spinner.json +++ b/examples/schema-catalog/src/schemas/components-feedback-spinner/centered-spinner.json @@ -1,12 +1,14 @@ { "type": "flex", - "justify": "center", - "align": "center", - "className": "h-32", - "children": [ - { - "type": "spinner", - "size": "lg" - } - ] + "properties": { + "justify": "center", + "align": "center", + "children": [ + { + "type": "spinner", + "size": "lg" + } + ] + }, + "className": "h-32" } diff --git a/examples/schema-catalog/src/schemas/components-form-button/button-sizes.json b/examples/schema-catalog/src/schemas/components-form-button/button-sizes.json index bfcf2fc7fd..dabefafb17 100644 --- a/examples/schema-catalog/src/schemas/components-form-button/button-sizes.json +++ b/examples/schema-catalog/src/schemas/components-form-button/button-sizes.json @@ -1,22 +1,24 @@ { "type": "flex", - "gap": 2, - "align": "center", - "children": [ - { - "type": "button", - "label": "Small", - "size": "sm" - }, - { - "type": "button", - "label": "Default", - "size": "default" - }, - { - "type": "button", - "label": "Large", - "size": "lg" - } - ] + "properties": { + "gap": 2, + "align": "center", + "children": [ + { + "type": "button", + "label": "Small", + "size": "sm" + }, + { + "type": "button", + "label": "Default", + "size": "default" + }, + { + "type": "button", + "label": "Large", + "size": "lg" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-button/common-action-buttons.json b/examples/schema-catalog/src/schemas/components-form-button/common-action-buttons.json index d1582fe8bd..e825b132b2 100644 --- a/examples/schema-catalog/src/schemas/components-form-button/common-action-buttons.json +++ b/examples/schema-catalog/src/schemas/components-form-button/common-action-buttons.json @@ -1,23 +1,25 @@ { "type": "flex", - "gap": 3, - "children": [ - { - "type": "button", - "label": "Save", - "variant": "default", - "icon": "save" - }, - { - "type": "button", - "label": "Cancel", - "variant": "outline" - }, - { - "type": "button", - "label": "Delete", - "variant": "destructive", - "icon": "trash" - } - ] + "properties": { + "gap": 3, + "children": [ + { + "type": "button", + "label": "Save", + "variant": "default", + "icon": "save" + }, + { + "type": "button", + "label": "Cancel", + "variant": "outline" + }, + { + "type": "button", + "label": "Delete", + "variant": "destructive", + "icon": "trash" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-button/icon-button.json b/examples/schema-catalog/src/schemas/components-form-button/icon-button.json index 1bb201cc4b..9604594524 100644 --- a/examples/schema-catalog/src/schemas/components-form-button/icon-button.json +++ b/examples/schema-catalog/src/schemas/components-form-button/icon-button.json @@ -1,12 +1,14 @@ { "type": "flex", - "gap": 2, - "align": "center", - "children": [ - { - "type": "button", - "label": "🎯", - "size": "icon" - } - ] + "properties": { + "gap": 2, + "align": "center", + "children": [ + { + "type": "button", + "label": "🎯", + "size": "icon" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-date-picker/date-range-selector.json b/examples/schema-catalog/src/schemas/components-form-date-picker/date-range-selector.json index 592b9ed299..3e2ea9f7ab 100644 --- a/examples/schema-catalog/src/schemas/components-form-date-picker/date-range-selector.json +++ b/examples/schema-catalog/src/schemas/components-form-date-picker/date-range-selector.json @@ -1,19 +1,21 @@ { "type": "flex", - "gap": 2, - "align": "center", - "children": [ - { - "type": "date-picker", - "placeholder": "Start date" - }, - { - "type": "text", - "content": "to" - }, - { - "type": "date-picker", - "placeholder": "End date" - } - ] + "properties": { + "gap": 2, + "align": "center", + "children": [ + { + "type": "date-picker", + "placeholder": "Start date" + }, + { + "type": "text", + "content": "to" + }, + { + "type": "date-picker", + "placeholder": "End date" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-date-picker/form-field.json b/examples/schema-catalog/src/schemas/components-form-date-picker/form-field.json index 76131196d2..ca8e1cb9ca 100644 --- a/examples/schema-catalog/src/schemas/components-form-date-picker/form-field.json +++ b/examples/schema-catalog/src/schemas/components-form-date-picker/form-field.json @@ -1,17 +1,19 @@ { "type": "flex", - "direction": "col", - "gap": 4, - "children": [ - { - "type": "label", - "text": "Birth Date", - "htmlFor": "birthdate" - }, - { - "type": "date-picker", - "id": "birthdate", - "placeholder": "Select your birth date" - } - ] + "properties": { + "direction": "col", + "gap": 4, + "children": [ + { + "type": "label", + "text": "Birth Date", + "htmlFor": "birthdate" + }, + { + "type": "date-picker", + "id": "birthdate", + "placeholder": "Select your birth date" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-input-otp/verification-form.json b/examples/schema-catalog/src/schemas/components-form-input-otp/verification-form.json index fd9772703b..886676c976 100644 --- a/examples/schema-catalog/src/schemas/components-form-input-otp/verification-form.json +++ b/examples/schema-catalog/src/schemas/components-form-input-otp/verification-form.json @@ -1,21 +1,23 @@ { "type": "flex", - "direction": "col", - "gap": 4, - "children": [ - { - "type": "label", - "text": "Enter verification code" - }, - { - "type": "input-otp", - "length": 6, - "separator": true - }, - { - "type": "text", - "content": "We sent a code to your email", - "className": "text-sm text-muted-foreground" - } - ] + "properties": { + "direction": "col", + "gap": 4, + "children": [ + { + "type": "label", + "text": "Enter verification code" + }, + { + "type": "input-otp", + "length": 6, + "separator": true + }, + { + "type": "text", + "content": "We sent a code to your email", + "className": "text-sm text-muted-foreground" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-label/form-label.json b/examples/schema-catalog/src/schemas/components-form-label/form-label.json index 50e5348d84..c6706431ab 100644 --- a/examples/schema-catalog/src/schemas/components-form-label/form-label.json +++ b/examples/schema-catalog/src/schemas/components-form-label/form-label.json @@ -1,17 +1,19 @@ { "type": "flex", - "direction": "col", - "gap": 2, - "children": [ - { - "type": "label", - "text": "Email", - "htmlFor": "email" - }, - { - "type": "input", - "id": "email", - "placeholder": "Enter your email" - } - ] + "properties": { + "direction": "col", + "gap": 2, + "children": [ + { + "type": "label", + "text": "Email", + "htmlFor": "email" + }, + { + "type": "input", + "id": "email", + "placeholder": "Enter your email" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-label/required-label.json b/examples/schema-catalog/src/schemas/components-form-label/required-label.json index 80b507f1be..37bf0cec07 100644 --- a/examples/schema-catalog/src/schemas/components-form-label/required-label.json +++ b/examples/schema-catalog/src/schemas/components-form-label/required-label.json @@ -1,17 +1,19 @@ { "type": "flex", - "direction": "col", - "gap": 2, - "children": [ - { - "type": "label", - "text": "Password", - "required": true, - "htmlFor": "password" - }, - { - "type": "password", - "id": "password" - } - ] + "properties": { + "direction": "col", + "gap": 2, + "children": [ + { + "type": "label", + "text": "Password", + "required": true, + "htmlFor": "password" + }, + { + "type": "password", + "id": "password" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-radio-group/form-field.json b/examples/schema-catalog/src/schemas/components-form-radio-group/form-field.json index 98e18dbb0d..cac56efc45 100644 --- a/examples/schema-catalog/src/schemas/components-form-radio-group/form-field.json +++ b/examples/schema-catalog/src/schemas/components-form-radio-group/form-field.json @@ -1,29 +1,31 @@ { "type": "flex", - "direction": "col", - "gap": 4, - "children": [ - { - "type": "label", - "text": "Notification Preference" - }, - { - "type": "radio-group", - "value": "email", - "options": [ - { - "value": "email", - "label": "Email notifications" - }, - { - "value": "sms", - "label": "SMS notifications" - }, - { - "value": "none", - "label": "No notifications" - } - ] - } - ] + "properties": { + "direction": "col", + "gap": 4, + "children": [ + { + "type": "label", + "text": "Notification Preference" + }, + { + "type": "radio-group", + "value": "email", + "options": [ + { + "value": "email", + "label": "Email notifications" + }, + { + "value": "sms", + "label": "SMS notifications" + }, + { + "value": "none", + "label": "No notifications" + } + ] + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-toggle/settings-example.json b/examples/schema-catalog/src/schemas/components-form-toggle/settings-example.json index 6847b048e5..9ef02eaa48 100644 --- a/examples/schema-catalog/src/schemas/components-form-toggle/settings-example.json +++ b/examples/schema-catalog/src/schemas/components-form-toggle/settings-example.json @@ -10,55 +10,61 @@ }, { "type": "flex", - "justify": "between", - "align": "center", - "className": "py-2", - "children": [ - { - "type": "text", - "content": "Email notifications", - "className": "text-sm" - }, - { - "type": "toggle", - "ariaLabel": "Email notifications" - } - ] + "properties": { + "justify": "between", + "align": "center", + "children": [ + { + "type": "text", + "content": "Email notifications", + "className": "text-sm" + }, + { + "type": "toggle", + "ariaLabel": "Email notifications" + } + ] + }, + "className": "py-2" }, { "type": "flex", - "justify": "between", - "align": "center", - "className": "py-2", - "children": [ - { - "type": "text", - "content": "Push notifications", - "className": "text-sm" - }, - { - "type": "toggle", - "ariaLabel": "Push notifications", - "pressed": true - } - ] + "properties": { + "justify": "between", + "align": "center", + "children": [ + { + "type": "text", + "content": "Push notifications", + "className": "text-sm" + }, + { + "type": "toggle", + "ariaLabel": "Push notifications", + "pressed": true + } + ] + }, + "className": "py-2" }, { "type": "flex", - "justify": "between", - "align": "center", - "className": "py-2", - "children": [ - { - "type": "text", - "content": "SMS notifications", - "className": "text-sm" - }, - { - "type": "toggle", - "ariaLabel": "SMS notifications" - } - ] + "properties": { + "justify": "between", + "align": "center", + "children": [ + { + "type": "text", + "content": "SMS notifications", + "className": "text-sm" + }, + { + "type": "toggle", + "ariaLabel": "SMS notifications" + } + ] + }, + "className": "py-2" } ] } diff --git a/examples/schema-catalog/src/schemas/components-form-toggle/settings-toggles.json b/examples/schema-catalog/src/schemas/components-form-toggle/settings-toggles.json index 0cac55ef5a..842781dae1 100644 --- a/examples/schema-catalog/src/schemas/components-form-toggle/settings-toggles.json +++ b/examples/schema-catalog/src/schemas/components-form-toggle/settings-toggles.json @@ -1,21 +1,23 @@ { "type": "flex", - "gap": 2, - "children": [ - { - "type": "toggle", - "label": "🔔 Notifications", - "variant": "outline" - }, - { - "type": "toggle", - "label": "🌙 Dark Mode", - "variant": "outline" - }, - { - "type": "toggle", - "label": "🔊 Sound", - "variant": "outline" - } - ] + "properties": { + "gap": 2, + "children": [ + { + "type": "toggle", + "label": "🔔 Notifications", + "variant": "outline" + }, + { + "type": "toggle", + "label": "🌙 Dark Mode", + "variant": "outline" + }, + { + "type": "toggle", + "label": "🔊 Sound", + "variant": "outline" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-form-toggle/text-formatting.json b/examples/schema-catalog/src/schemas/components-form-toggle/text-formatting.json index aaec466860..694ecef4d1 100644 --- a/examples/schema-catalog/src/schemas/components-form-toggle/text-formatting.json +++ b/examples/schema-catalog/src/schemas/components-form-toggle/text-formatting.json @@ -1,28 +1,30 @@ { "type": "flex", - "gap": 1, - "className": "p-2 bg-muted rounded-lg", - "children": [ - { - "type": "toggle", - "label": "B", - "size": "sm", - "variant": "outline", - "ariaLabel": "Bold" - }, - { - "type": "toggle", - "label": "I", - "size": "sm", - "variant": "outline", - "ariaLabel": "Italic" - }, - { - "type": "toggle", - "label": "U", - "size": "sm", - "variant": "outline", - "ariaLabel": "Underline" - } - ] + "properties": { + "gap": 1, + "children": [ + { + "type": "toggle", + "label": "B", + "size": "sm", + "variant": "outline", + "ariaLabel": "Bold" + }, + { + "type": "toggle", + "label": "I", + "size": "sm", + "variant": "outline", + "ariaLabel": "Italic" + }, + { + "type": "toggle", + "label": "U", + "size": "sm", + "variant": "outline", + "ariaLabel": "Underline" + } + ] + }, + "className": "p-2 bg-muted rounded-lg" } diff --git a/examples/schema-catalog/src/schemas/components-form-toggle/toggle-sizes.json b/examples/schema-catalog/src/schemas/components-form-toggle/toggle-sizes.json index 6f1745492d..94ab725af9 100644 --- a/examples/schema-catalog/src/schemas/components-form-toggle/toggle-sizes.json +++ b/examples/schema-catalog/src/schemas/components-form-toggle/toggle-sizes.json @@ -1,22 +1,24 @@ { "type": "flex", - "gap": 2, - "align": "center", - "children": [ - { - "type": "toggle", - "label": "Small", - "size": "sm" - }, - { - "type": "toggle", - "label": "Default", - "size": "default" - }, - { - "type": "toggle", - "label": "Large", - "size": "lg" - } - ] + "properties": { + "gap": 2, + "align": "center", + "children": [ + { + "type": "toggle", + "label": "Small", + "size": "sm" + }, + { + "type": "toggle", + "label": "Default", + "size": "default" + }, + { + "type": "toggle", + "label": "Large", + "size": "lg" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-layout-card/inventory-table-card.json b/examples/schema-catalog/src/schemas/components-layout-card/inventory-table-card.json index 06962d6b06..dc104d3853 100644 --- a/examples/schema-catalog/src/schemas/components-layout-card/inventory-table-card.json +++ b/examples/schema-catalog/src/schemas/components-layout-card/inventory-table-card.json @@ -3,36 +3,40 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "mb-4", - "children": [ - { - "type": "text", - "content": "Product Inventory", - "className": "text-lg font-semibold" - }, - { - "type": "flex", - "gap": 2, - "children": [ - { - "type": "button", - "label": "Filter", - "variant": "outline", - "size": "sm", - "icon": "funnel" - }, - { - "type": "button", - "label": "Export", - "variant": "outline", - "size": "sm", - "icon": "download" + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Product Inventory", + "className": "text-lg font-semibold" + }, + { + "type": "flex", + "properties": { + "gap": 2, + "children": [ + { + "type": "button", + "label": "Filter", + "variant": "outline", + "size": "sm", + "icon": "funnel" + }, + { + "type": "button", + "label": "Export", + "variant": "outline", + "size": "sm", + "icon": "download" + } + ] } - ] - } - ] + } + ] + }, + "className": "mb-4" }, { "type": "stack", @@ -40,201 +44,213 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b", - "children": [ - { - "type": "text", - "content": "Product", - "className": "w-40" - }, - { - "type": "text", - "content": "SKU", - "className": "w-24" - }, - { - "type": "text", - "content": "Price", - "className": "w-20" - }, - { - "type": "text", - "content": "Stock", - "className": "w-16" - }, - { - "type": "text", - "content": "Status", - "className": "w-24" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Product", + "className": "w-40" + }, + { + "type": "text", + "content": "SKU", + "className": "w-24" + }, + { + "type": "text", + "content": "Price", + "className": "w-20" + }, + { + "type": "text", + "content": "Stock", + "className": "w-16" + }, + { + "type": "text", + "content": "Status", + "className": "w-24" + } + ] + }, + "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Wireless Mouse", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "WM-001", - "className": "w-24 text-muted-foreground" - }, - { - "type": "text", - "content": "$29.99", - "className": "w-20" - }, - { - "type": "text", - "content": "150", - "className": "w-16" - }, - { - "type": "badge", - "label": "In Stock", - "variant": "default" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Wireless Mouse", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "WM-001", + "className": "w-24 text-muted-foreground" + }, + { + "type": "text", + "content": "$29.99", + "className": "w-20" + }, + { + "type": "text", + "content": "150", + "className": "w-16" + }, + { + "type": "badge", + "label": "In Stock", + "variant": "default" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Mechanical Keyboard", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "MK-002", - "className": "w-24 text-muted-foreground" - }, - { - "type": "text", - "content": "$89.99", - "className": "w-20" - }, - { - "type": "text", - "content": "42", - "className": "w-16" - }, - { - "type": "badge", - "label": "Low Stock", - "variant": "outline" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Mechanical Keyboard", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "MK-002", + "className": "w-24 text-muted-foreground" + }, + { + "type": "text", + "content": "$89.99", + "className": "w-20" + }, + { + "type": "text", + "content": "42", + "className": "w-16" + }, + { + "type": "badge", + "label": "Low Stock", + "variant": "outline" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "USB-C Hub", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "UH-003", - "className": "w-24 text-muted-foreground" - }, - { - "type": "text", - "content": "$45.00", - "className": "w-20" - }, - { - "type": "text", - "content": "200", - "className": "w-16" - }, - { - "type": "badge", - "label": "In Stock", - "variant": "default" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "USB-C Hub", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "UH-003", + "className": "w-24 text-muted-foreground" + }, + { + "type": "text", + "content": "$45.00", + "className": "w-20" + }, + { + "type": "text", + "content": "200", + "className": "w-16" + }, + { + "type": "badge", + "label": "In Stock", + "variant": "default" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Monitor Stand", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "MS-004", - "className": "w-24 text-muted-foreground" - }, - { - "type": "text", - "content": "$35.00", - "className": "w-20" - }, - { - "type": "text", - "content": "0", - "className": "w-16" - }, - { - "type": "badge", - "label": "Out of Stock", - "variant": "destructive" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Monitor Stand", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "MS-004", + "className": "w-24 text-muted-foreground" + }, + { + "type": "text", + "content": "$35.00", + "className": "w-20" + }, + { + "type": "text", + "content": "0", + "className": "w-16" + }, + { + "type": "badge", + "label": "Out of Stock", + "variant": "destructive" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 text-sm", - "children": [ - { - "type": "text", - "content": "Webcam HD", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "WC-005", - "className": "w-24 text-muted-foreground" - }, - { - "type": "text", - "content": "$65.00", - "className": "w-20" - }, - { - "type": "text", - "content": "88", - "className": "w-16" - }, - { - "type": "badge", - "label": "In Stock", - "variant": "default" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Webcam HD", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "WC-005", + "className": "w-24 text-muted-foreground" + }, + { + "type": "text", + "content": "$65.00", + "className": "w-20" + }, + { + "type": "text", + "content": "88", + "className": "w-16" + }, + { + "type": "badge", + "label": "In Stock", + "variant": "default" + } + ] + }, + "className": "px-4 py-3 text-sm" } ] } diff --git a/examples/schema-catalog/src/schemas/components-layout-card/profile-detail-card.json b/examples/schema-catalog/src/schemas/components-layout-card/profile-detail-card.json index 2fca357ef1..c52f1fe532 100644 --- a/examples/schema-catalog/src/schemas/components-layout-card/profile-detail-card.json +++ b/examples/schema-catalog/src/schemas/components-layout-card/profile-detail-card.json @@ -4,31 +4,33 @@ "header": [ { "type": "flex", - "align": "center", - "gap": 3, - "children": [ - { - "type": "avatar", - "fallback": "AJ", - "className": "h-12 w-12" - }, - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Alice Johnson", - "className": "text-lg font-semibold" - }, - { - "type": "text", - "content": "Admin · Joined Jan 2024", - "className": "text-sm text-muted-foreground" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "avatar", + "fallback": "AJ", + "className": "h-12 w-12" + }, + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Alice Johnson", + "className": "text-lg font-semibold" + }, + { + "type": "text", + "content": "Admin · Joined Jan 2024", + "className": "text-sm text-muted-foreground" + } + ] + } + ] + } } ], "children": [ @@ -38,95 +40,105 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "text", - "content": "Email", - "className": "text-sm text-muted-foreground w-24" - }, - { - "type": "text", - "content": "alice@example.com", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Email", + "className": "text-sm text-muted-foreground w-24" + }, + { + "type": "text", + "content": "alice@example.com", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "text", - "content": "Phone", - "className": "text-sm text-muted-foreground w-24" - }, - { - "type": "text", - "content": "+1 (555) 123-4567", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Phone", + "className": "text-sm text-muted-foreground w-24" + }, + { + "type": "text", + "content": "+1 (555) 123-4567", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "text", - "content": "Department", - "className": "text-sm text-muted-foreground w-24" - }, - { - "type": "text", - "content": "Engineering", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Department", + "className": "text-sm text-muted-foreground w-24" + }, + { + "type": "text", + "content": "Engineering", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "text", - "content": "Location", - "className": "text-sm text-muted-foreground w-24" - }, - { - "type": "text", - "content": "San Francisco, CA", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Location", + "className": "text-sm text-muted-foreground w-24" + }, + { + "type": "text", + "content": "San Francisco, CA", + "className": "text-sm" + } + ] + } } ] }, { "type": "flex", - "justify": "end", - "gap": 2, - "className": "pt-4 border-t mt-4", - "children": [ - { - "type": "button", - "label": "Edit", - "variant": "outline", - "size": "sm", - "icon": "pencil" - }, - { - "type": "button", - "label": "Delete", - "variant": "destructive", - "size": "sm", - "icon": "trash" - } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "button", + "label": "Edit", + "variant": "outline", + "size": "sm", + "icon": "pencil" + }, + { + "type": "button", + "label": "Delete", + "variant": "destructive", + "size": "sm", + "icon": "trash" + } + ] + }, + "className": "pt-4 border-t mt-4" } ] } diff --git a/examples/schema-catalog/src/schemas/components-layout-card/team-roster-card.json b/examples/schema-catalog/src/schemas/components-layout-card/team-roster-card.json index 21a7ee20c9..8ba827e5db 100644 --- a/examples/schema-catalog/src/schemas/components-layout-card/team-roster-card.json +++ b/examples/schema-catalog/src/schemas/components-layout-card/team-roster-card.json @@ -3,23 +3,25 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "mb-4", - "children": [ - { - "type": "text", - "content": "Team Members", - "className": "text-lg font-semibold" - }, - { - "type": "button", - "label": "Invite Member", - "variant": "default", - "size": "sm", - "icon": "user-plus" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Team Members", + "className": "text-lg font-semibold" + }, + { + "type": "button", + "label": "Invite Member", + "variant": "default", + "size": "sm", + "icon": "user-plus" + } + ] + }, + "className": "mb-4" }, { "type": "stack", @@ -27,172 +29,182 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b", - "children": [ - { - "type": "text", - "content": "Name", - "className": "w-40" - }, - { - "type": "text", - "content": "Role", - "className": "w-32" - }, - { - "type": "text", - "content": "Department", - "className": "w-28" - }, - { - "type": "text", - "content": "Status", - "className": "w-20" - }, - { - "type": "text", - "content": "Last Active", - "className": "w-24" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Name", + "className": "w-40" + }, + { + "type": "text", + "content": "Role", + "className": "w-32" + }, + { + "type": "text", + "content": "Department", + "className": "w-28" + }, + { + "type": "text", + "content": "Status", + "className": "w-20" + }, + { + "type": "text", + "content": "Last Active", + "className": "w-24" + } + ] + }, + "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Sarah Chen", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "Lead Engineer", - "className": "w-32" - }, - { - "type": "text", - "content": "Engineering", - "className": "w-28 text-muted-foreground" - }, - { - "type": "badge", - "label": "Online", - "variant": "default", - "className": "w-20" - }, - { - "type": "text", - "content": "Just now", - "className": "w-24 text-xs text-muted-foreground" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Sarah Chen", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "Lead Engineer", + "className": "w-32" + }, + { + "type": "text", + "content": "Engineering", + "className": "w-28 text-muted-foreground" + }, + { + "type": "badge", + "label": "Online", + "variant": "default", + "className": "w-20" + }, + { + "type": "text", + "content": "Just now", + "className": "w-24 text-xs text-muted-foreground" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Marcus Rodriguez", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "Designer", - "className": "w-32" - }, - { - "type": "text", - "content": "Design", - "className": "w-28 text-muted-foreground" - }, - { - "type": "badge", - "label": "Online", - "variant": "default", - "className": "w-20" - }, - { - "type": "text", - "content": "2m ago", - "className": "w-24 text-xs text-muted-foreground" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Marcus Rodriguez", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "Designer", + "className": "w-32" + }, + { + "type": "text", + "content": "Design", + "className": "w-28 text-muted-foreground" + }, + { + "type": "badge", + "label": "Online", + "variant": "default", + "className": "w-20" + }, + { + "type": "text", + "content": "2m ago", + "className": "w-24 text-xs text-muted-foreground" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Emily Watson", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "PM", - "className": "w-32" - }, - { - "type": "text", - "content": "Product", - "className": "w-28 text-muted-foreground" - }, - { - "type": "badge", - "label": "Away", - "variant": "secondary", - "className": "w-20" - }, - { - "type": "text", - "content": "1h ago", - "className": "w-24 text-xs text-muted-foreground" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Emily Watson", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "PM", + "className": "w-32" + }, + { + "type": "text", + "content": "Product", + "className": "w-28 text-muted-foreground" + }, + { + "type": "badge", + "label": "Away", + "variant": "secondary", + "className": "w-20" + }, + { + "type": "text", + "content": "1h ago", + "className": "w-24 text-xs text-muted-foreground" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 text-sm", - "children": [ - { - "type": "text", - "content": "James Kim", - "className": "w-40 font-medium" - }, - { - "type": "text", - "content": "DevOps", - "className": "w-32" - }, - { - "type": "text", - "content": "Engineering", - "className": "w-28 text-muted-foreground" - }, - { - "type": "badge", - "label": "Offline", - "variant": "outline", - "className": "w-20" - }, - { - "type": "text", - "content": "3h ago", - "className": "w-24 text-xs text-muted-foreground" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "James Kim", + "className": "w-40 font-medium" + }, + { + "type": "text", + "content": "DevOps", + "className": "w-32" + }, + { + "type": "text", + "content": "Engineering", + "className": "w-28 text-muted-foreground" + }, + { + "type": "badge", + "label": "Offline", + "variant": "outline", + "className": "w-20" + }, + { + "type": "text", + "content": "3h ago", + "className": "w-24 text-xs text-muted-foreground" + } + ] + }, + "className": "px-4 py-3 text-sm" } ] } diff --git a/examples/schema-catalog/src/schemas/components-layout-card/user-list-card.json b/examples/schema-catalog/src/schemas/components-layout-card/user-list-card.json index 3416be91e3..18b87afdde 100644 --- a/examples/schema-catalog/src/schemas/components-layout-card/user-list-card.json +++ b/examples/schema-catalog/src/schemas/components-layout-card/user-list-card.json @@ -3,23 +3,25 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "mb-4", - "children": [ - { - "type": "text", - "content": "Users", - "className": "text-lg font-semibold" - }, - { - "type": "button", - "label": "Add User", - "variant": "default", - "size": "sm", - "icon": "plus" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Users", + "className": "text-lg font-semibold" + }, + { + "type": "button", + "label": "Add User", + "variant": "default", + "size": "sm", + "icon": "plus" + } + ] + }, + "className": "mb-4" }, { "type": "stack", @@ -27,143 +29,153 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b", - "children": [ - { - "type": "text", - "content": "Name", - "className": "w-36" - }, - { - "type": "text", - "content": "Email", - "className": "w-48" - }, - { - "type": "text", - "content": "Role", - "className": "w-24" - }, - { - "type": "text", - "content": "Status", - "className": "w-20" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Name", + "className": "w-36" + }, + { + "type": "text", + "content": "Email", + "className": "w-48" + }, + { + "type": "text", + "content": "Role", + "className": "w-24" + }, + { + "type": "text", + "content": "Status", + "className": "w-20" + } + ] + }, + "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Alice Johnson", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "alice@example.com", - "className": "w-48 text-muted-foreground" - }, - { - "type": "text", - "content": "Admin", - "className": "w-24" - }, - { - "type": "badge", - "label": "Active", - "variant": "default" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Alice Johnson", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "alice@example.com", + "className": "w-48 text-muted-foreground" + }, + { + "type": "text", + "content": "Admin", + "className": "w-24" + }, + { + "type": "badge", + "label": "Active", + "variant": "default" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Bob Smith", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "bob@example.com", - "className": "w-48 text-muted-foreground" - }, - { - "type": "text", - "content": "Editor", - "className": "w-24" - }, - { - "type": "badge", - "label": "Active", - "variant": "default" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Bob Smith", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "bob@example.com", + "className": "w-48 text-muted-foreground" + }, + { + "type": "text", + "content": "Editor", + "className": "w-24" + }, + { + "type": "badge", + "label": "Active", + "variant": "default" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Carol White", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "carol@example.com", - "className": "w-48 text-muted-foreground" - }, - { - "type": "text", - "content": "Viewer", - "className": "w-24" - }, - { - "type": "badge", - "label": "Inactive", - "variant": "secondary" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Carol White", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "carol@example.com", + "className": "w-48 text-muted-foreground" + }, + { + "type": "text", + "content": "Viewer", + "className": "w-24" + }, + { + "type": "badge", + "label": "Inactive", + "variant": "secondary" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 text-sm", - "children": [ - { - "type": "text", - "content": "David Lee", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "david@example.com", - "className": "w-48 text-muted-foreground" - }, - { - "type": "text", - "content": "Editor", - "className": "w-24" - }, - { - "type": "badge", - "label": "Active", - "variant": "default" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "David Lee", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "david@example.com", + "className": "w-48 text-muted-foreground" + }, + { + "type": "text", + "content": "Editor", + "className": "w-24" + }, + { + "type": "badge", + "label": "Active", + "variant": "default" + } + ] + }, + "className": "px-4 py-3 text-sm" } ] } diff --git a/examples/schema-catalog/src/schemas/components-layout-card/with-footer.json b/examples/schema-catalog/src/schemas/components-layout-card/with-footer.json index fced06af85..af4d0950ec 100644 --- a/examples/schema-catalog/src/schemas/components-layout-card/with-footer.json +++ b/examples/schema-catalog/src/schemas/components-layout-card/with-footer.json @@ -10,20 +10,22 @@ "footer": [ { "type": "flex", - "gap": 2, - "justify": "end", - "children": [ - { - "type": "button", - "label": "Cancel", - "variant": "outline" - }, - { - "type": "button", - "label": "Save", - "variant": "default" - } - ] + "properties": { + "gap": 2, + "justify": "end", + "children": [ + { + "type": "button", + "label": "Cancel", + "variant": "outline" + }, + { + "type": "button", + "label": "Save", + "variant": "default" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/components-layout-flex/horizontal-layout.json b/examples/schema-catalog/src/schemas/components-layout-flex/horizontal-layout.json index 6b23408657..6cc5c06141 100644 --- a/examples/schema-catalog/src/schemas/components-layout-flex/horizontal-layout.json +++ b/examples/schema-catalog/src/schemas/components-layout-flex/horizontal-layout.json @@ -1,18 +1,20 @@ { "type": "flex", - "gap": 3, - "children": [ - { - "type": "button", - "label": "Button 1" - }, - { - "type": "button", - "label": "Button 2" - }, - { - "type": "button", - "label": "Button 3" - } - ] + "properties": { + "gap": 3, + "children": [ + { + "type": "button", + "label": "Button 1" + }, + { + "type": "button", + "label": "Button 2" + }, + { + "type": "button", + "label": "Button 3" + } + ] + } } diff --git a/examples/schema-catalog/src/schemas/components-layout-page/settings-layout.json b/examples/schema-catalog/src/schemas/components-layout-page/settings-layout.json index dea39305d1..47c415e73d 100644 --- a/examples/schema-catalog/src/schemas/components-layout-page/settings-layout.json +++ b/examples/schema-catalog/src/schemas/components-layout-page/settings-layout.json @@ -34,16 +34,18 @@ }, { "type": "flex", - "justify": "between", - "children": [ - { - "type": "text", - "content": "Email notifications" - }, - { - "type": "switch" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Email notifications" + }, + { + "type": "switch" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/components-layout-semantic/complete-layout.json b/examples/schema-catalog/src/schemas/components-layout-semantic/complete-layout.json index aa273ece6c..e69fc46562 100644 --- a/examples/schema-catalog/src/schemas/components-layout-semantic/complete-layout.json +++ b/examples/schema-catalog/src/schemas/components-layout-semantic/complete-layout.json @@ -22,25 +22,27 @@ "children": [ { "type": "flex", - "gap": 4, - "children": [ - { - "type": "text", - "content": "Home" - }, - { - "type": "text", - "content": "About" - }, - { - "type": "text", - "content": "Services" - }, - { - "type": "text", - "content": "Contact" - } - ] + "properties": { + "gap": 4, + "children": [ + { + "type": "text", + "content": "Home" + }, + { + "type": "text", + "content": "About" + }, + { + "type": "text", + "content": "Services" + }, + { + "type": "text", + "content": "Contact" + } + ] + } } ] }, diff --git a/examples/schema-catalog/src/schemas/components-layout-semantic/header.json b/examples/schema-catalog/src/schemas/components-layout-semantic/header.json index f8b36e894c..8f866a7b6f 100644 --- a/examples/schema-catalog/src/schemas/components-layout-semantic/header.json +++ b/examples/schema-catalog/src/schemas/components-layout-semantic/header.json @@ -4,20 +4,22 @@ "children": [ { "type": "flex", - "justify": "between", - "align": "center", - "children": [ - { - "type": "text", - "content": "Site Title", - "className": "text-xl font-bold" - }, - { - "type": "text", - "content": "Navigation", - "className": "text-sm text-muted-foreground" - } - ] + "properties": { + "justify": "between", + "align": "center", + "children": [ + { + "type": "text", + "content": "Site Title", + "className": "text-xl font-bold" + }, + { + "type": "text", + "content": "Navigation", + "className": "text-sm text-muted-foreground" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/components-layout-semantic/navigation.json b/examples/schema-catalog/src/schemas/components-layout-semantic/navigation.json index 6ae53dd78a..d2f252f66e 100644 --- a/examples/schema-catalog/src/schemas/components-layout-semantic/navigation.json +++ b/examples/schema-catalog/src/schemas/components-layout-semantic/navigation.json @@ -4,24 +4,26 @@ "children": [ { "type": "flex", - "gap": 4, - "children": [ - { - "type": "text", - "content": "Home", - "className": "font-medium cursor-pointer hover:text-primary" - }, - { - "type": "text", - "content": "About", - "className": "font-medium cursor-pointer hover:text-primary" - }, - { - "type": "text", - "content": "Contact", - "className": "font-medium cursor-pointer hover:text-primary" - } - ] + "properties": { + "gap": 4, + "children": [ + { + "type": "text", + "content": "Home", + "className": "font-medium cursor-pointer hover:text-primary" + }, + { + "type": "text", + "content": "About", + "className": "font-medium cursor-pointer hover:text-primary" + }, + { + "type": "text", + "content": "Contact", + "className": "font-medium cursor-pointer hover:text-primary" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/core-schema-renderer/nested-schema-example.json b/examples/schema-catalog/src/schemas/core-schema-renderer/nested-schema-example.json index 501ab34142..da8e361845 100644 --- a/examples/schema-catalog/src/schemas/core-schema-renderer/nested-schema-example.json +++ b/examples/schema-catalog/src/schemas/core-schema-renderer/nested-schema-example.json @@ -8,18 +8,20 @@ "children": [ { "type": "flex", - "gap": 2, - "children": [ - { - "type": "badge", - "label": "Nested" - }, - { - "type": "badge", - "label": "Components", - "variant": "outline" - } - ] + "properties": { + "gap": 2, + "children": [ + { + "type": "badge", + "label": "Nested" + }, + { + "type": "badge", + "label": "Components", + "variant": "outline" + } + ] + } } ] }, diff --git a/examples/schema-catalog/src/schemas/dashboard/dashboard-overview.json b/examples/schema-catalog/src/schemas/dashboard/dashboard-overview.json index 88ff1e52d9..28f8531eee 100644 --- a/examples/schema-catalog/src/schemas/dashboard/dashboard-overview.json +++ b/examples/schema-catalog/src/schemas/dashboard/dashboard-overview.json @@ -4,21 +4,23 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "text", - "content": "Dashboard", - "className": "text-3xl font-bold tracking-tight" - }, - { - "type": "button", - "label": "Download Report", - "variant": "outline", - "size": "sm" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Dashboard", + "className": "text-3xl font-bold tracking-tight" + }, + { + "type": "button", + "label": "Download Report", + "variant": "outline", + "size": "sm" + } + ] + } }, { "type": "grid", @@ -131,15 +133,17 @@ }, { "type": "flex", - "align": "center", - "justify": "center", - "className": "h-[300px] text-muted-foreground", - "children": [ - { - "type": "text", - "content": "Chart area - connect with plugin-charts" - } - ] + "properties": { + "align": "center", + "justify": "center", + "children": [ + { + "type": "text", + "content": "Chart area - connect with plugin-charts" + } + ] + }, + "className": "h-[300px] text-muted-foreground" } ] } @@ -164,35 +168,37 @@ "children": [ { "type": "flex", - "align": "center", - "children": [ - { - "type": "avatar", - "fallback": "OM", - "className": "h-9 w-9" - }, - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Olivia Martin", - "className": "text-sm font-medium" - }, - { - "type": "text", - "content": "olivia.martin@email.com", - "className": "text-xs text-muted-foreground" - } - ] - }, - { - "type": "text", - "content": "+$1,999.00", - "className": "ml-auto font-medium" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "avatar", + "fallback": "OM", + "className": "h-9 w-9" + }, + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Olivia Martin", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "olivia.martin@email.com", + "className": "text-xs text-muted-foreground" + } + ] + }, + { + "type": "text", + "content": "+$1,999.00", + "className": "ml-auto font-medium" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/dashboard/recent-activity-card.json b/examples/schema-catalog/src/schemas/dashboard/recent-activity-card.json index 898fecb245..977fdc0828 100644 --- a/examples/schema-catalog/src/schemas/dashboard/recent-activity-card.json +++ b/examples/schema-catalog/src/schemas/dashboard/recent-activity-card.json @@ -8,22 +8,24 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "mb-4", - "children": [ - { - "type": "text", - "content": "Recent Transactions", - "className": "text-lg font-semibold" - }, - { - "type": "button", - "label": "View All", - "variant": "ghost", - "size": "sm" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Recent Transactions", + "className": "text-lg font-semibold" + }, + { + "type": "button", + "label": "View All", + "variant": "ghost", + "size": "sm" + } + ] + }, + "className": "mb-4" }, { "type": "stack", @@ -31,207 +33,227 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "pb-4 border-b", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 4, - "children": [ - { - "type": "avatar", - "fallback": "OM", - "className": "h-9 w-9" - }, - { - "type": "stack", - "gap": 2, + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 4, "children": [ { - "type": "text", - "content": "Olivia Martin", - "className": "text-sm font-medium" + "type": "avatar", + "fallback": "OM", + "className": "h-9 w-9" }, { - "type": "text", - "content": "olivia.martin@email.com", - "className": "text-xs text-muted-foreground" + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Olivia Martin", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "olivia.martin@email.com", + "className": "text-xs text-muted-foreground" + } + ] } ] } - ] - }, - { - "type": "text", - "content": "+$1,999.00", - "className": "text-sm font-medium" - } - ] + }, + { + "type": "text", + "content": "+$1,999.00", + "className": "text-sm font-medium" + } + ] + }, + "className": "pb-4 border-b" }, { "type": "flex", - "align": "center", - "justify": "between", - "className": "pb-4 border-b", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 4, - "children": [ - { - "type": "avatar", - "fallback": "JL", - "className": "h-9 w-9" - }, - { - "type": "stack", - "gap": 2, + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 4, "children": [ { - "type": "text", - "content": "Jackson Lee", - "className": "text-sm font-medium" + "type": "avatar", + "fallback": "JL", + "className": "h-9 w-9" }, { - "type": "text", - "content": "jackson.lee@email.com", - "className": "text-xs text-muted-foreground" + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Jackson Lee", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "jackson.lee@email.com", + "className": "text-xs text-muted-foreground" + } + ] } ] } - ] - }, - { - "type": "text", - "content": "+$39.00", - "className": "text-sm font-medium" - } - ] + }, + { + "type": "text", + "content": "+$39.00", + "className": "text-sm font-medium" + } + ] + }, + "className": "pb-4 border-b" }, { "type": "flex", - "align": "center", - "justify": "between", - "className": "pb-4 border-b", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 4, - "children": [ - { - "type": "avatar", - "fallback": "IN", - "className": "h-9 w-9" - }, - { - "type": "stack", - "gap": 2, + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 4, "children": [ { - "type": "text", - "content": "Isabella Nguyen", - "className": "text-sm font-medium" + "type": "avatar", + "fallback": "IN", + "className": "h-9 w-9" }, { - "type": "text", - "content": "isabella.nguyen@email.com", - "className": "text-xs text-muted-foreground" + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Isabella Nguyen", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "isabella.nguyen@email.com", + "className": "text-xs text-muted-foreground" + } + ] } ] } - ] - }, - { - "type": "text", - "content": "+$299.00", - "className": "text-sm font-medium" - } - ] + }, + { + "type": "text", + "content": "+$299.00", + "className": "text-sm font-medium" + } + ] + }, + "className": "pb-4 border-b" }, { "type": "flex", - "align": "center", - "justify": "between", - "className": "pb-4 border-b", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 4, - "children": [ - { - "type": "avatar", - "fallback": "WK", - "className": "h-9 w-9" - }, - { - "type": "stack", - "gap": 2, + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 4, "children": [ { - "type": "text", - "content": "William Kim", - "className": "text-sm font-medium" + "type": "avatar", + "fallback": "WK", + "className": "h-9 w-9" }, { - "type": "text", - "content": "will@email.com", - "className": "text-xs text-muted-foreground" + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "William Kim", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "will@email.com", + "className": "text-xs text-muted-foreground" + } + ] } ] } - ] - }, - { - "type": "text", - "content": "+$99.00", - "className": "text-sm font-medium" - } - ] + }, + { + "type": "text", + "content": "+$99.00", + "className": "text-sm font-medium" + } + ] + }, + "className": "pb-4 border-b" }, { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 4, - "children": [ - { - "type": "avatar", - "fallback": "SD", - "className": "h-9 w-9" - }, - { - "type": "stack", - "gap": 2, + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 4, "children": [ { - "type": "text", - "content": "Sofia Davis", - "className": "text-sm font-medium" + "type": "avatar", + "fallback": "SD", + "className": "h-9 w-9" }, { - "type": "text", - "content": "sofia.davis@email.com", - "className": "text-xs text-muted-foreground" + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Sofia Davis", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "sofia.davis@email.com", + "className": "text-xs text-muted-foreground" + } + ] } ] } - ] - }, - { - "type": "text", - "content": "+$39.00", - "className": "text-sm font-medium" - } - ] + }, + { + "type": "text", + "content": "+$39.00", + "className": "text-sm font-medium" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/ecommerce/order-summary.json b/examples/schema-catalog/src/schemas/ecommerce/order-summary.json index 9e9fdcd0c8..e21ebce857 100644 --- a/examples/schema-catalog/src/schemas/ecommerce/order-summary.json +++ b/examples/schema-catalog/src/schemas/ecommerce/order-summary.json @@ -78,40 +78,44 @@ }, { "type": "flex", - "align": "center", - "gap": 3, - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-10 w-16 rounded bg-muted", - "children": [ - { - "type": "icon", - "icon": "credit-card", - "className": "h-6 w-6" - } - ] - }, - { - "type": "box", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Visa ending in 4242", - "className": "font-medium" + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "credit-card", + "className": "h-6 w-6" + } + ] }, - { - "type": "text", - "content": "Expires 12/24", - "className": "text-muted-foreground" - } - ] - } - ] + "className": "h-10 w-16 rounded bg-muted" + }, + { + "type": "box", + "className": "text-sm", + "children": [ + { + "type": "text", + "content": "Visa ending in 4242", + "className": "font-medium" + }, + { + "type": "text", + "content": "Expires 12/24", + "className": "text-muted-foreground" + } + ] + } + ] + } }, { "type": "button", @@ -143,35 +147,39 @@ "children": [ { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Smartphone Pro × 1" - }, - { - "type": "text", - "content": "$899.00", - "className": "font-medium" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Smartphone Pro × 1" + }, + { + "type": "text", + "content": "$899.00", + "className": "font-medium" + } + ] + }, + "className": "text-sm" }, { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Wireless Headphones × 2" - }, - { - "type": "text", - "content": "$598.00", - "className": "font-medium" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Wireless Headphones × 2" + }, + { + "type": "text", + "content": "$598.00", + "className": "font-medium" + } + ] + }, + "className": "text-sm" } ] }, @@ -184,66 +192,74 @@ "children": [ { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Subtotal" - }, - { - "type": "text", - "content": "$1,497.00" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Subtotal" + }, + { + "type": "text", + "content": "$1,497.00" + } + ] + }, + "className": "text-sm" }, { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Shipping" - }, - { - "type": "text", - "content": "$15.00" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Shipping" + }, + { + "type": "text", + "content": "$15.00" + } + ] + }, + "className": "text-sm" }, { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Tax" - }, - { - "type": "text", - "content": "$149.70" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Tax" + }, + { + "type": "text", + "content": "$149.70" + } + ] + }, + "className": "text-sm" }, { "type": "separator" }, { "type": "flex", - "justify": "between", - "className": "font-bold text-lg", - "children": [ - { - "type": "text", - "content": "Total" - }, - { - "type": "text", - "content": "$1,661.70" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Total" + }, + { + "type": "text", + "content": "$1,661.70" + } + ] + }, + "className": "font-bold text-lg" } ] } @@ -253,21 +269,23 @@ }, { "type": "flex", - "gap": 3, - "children": [ - { - "type": "button", - "label": "Back to Cart", - "variant": "outline", - "className": "flex-1" - }, - { - "type": "button", - "label": "Place Order", - "variant": "default", - "className": "flex-1" - } - ] + "properties": { + "gap": 3, + "children": [ + { + "type": "button", + "label": "Back to Cart", + "variant": "outline", + "className": "flex-1" + }, + { + "type": "button", + "label": "Place Order", + "variant": "default", + "className": "flex-1" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/ecommerce/product-card.json b/examples/schema-catalog/src/schemas/ecommerce/product-card.json index 7f67396828..4eaadfe727 100644 --- a/examples/schema-catalog/src/schemas/ecommerce/product-card.json +++ b/examples/schema-catalog/src/schemas/ecommerce/product-card.json @@ -4,17 +4,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-square bg-muted", - "children": [ - { - "type": "icon", - "icon": "package", - "className": "h-24 w-24 text-muted-foreground" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "package", + "className": "h-24 w-24 text-muted-foreground" + } + ] + }, + "className": "aspect-square bg-muted" }, { "type": "stack", @@ -38,88 +40,94 @@ }, { "type": "flex", - "align": "center", - "gap": 1, - "children": [ - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 text-muted" - }, - { - "type": "text", - "content": "4.0 (128 reviews)", - "className": "text-sm text-muted-foreground ml-2" - } - ] + "properties": { + "align": "center", + "gap": 1, + "children": [ + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 text-muted" + }, + { + "type": "text", + "content": "4.0 (128 reviews)", + "className": "text-sm text-muted-foreground ml-2" + } + ] + } }, { "type": "flex", - "align": "baseline", - "gap": 2, - "children": [ - { - "type": "text", - "content": "$299.99", - "className": "text-2xl font-bold" - }, - { - "type": "text", - "content": "$399.99", - "className": "text-sm text-muted-foreground line-through" - }, - { - "type": "badge", - "label": "25% OFF", - "variant": "destructive", - "className": "ml-auto" - } - ] + "properties": { + "align": "baseline", + "gap": 2, + "children": [ + { + "type": "text", + "content": "$299.99", + "className": "text-2xl font-bold" + }, + { + "type": "text", + "content": "$399.99", + "className": "text-sm text-muted-foreground line-through" + }, + { + "type": "badge", + "label": "25% OFF", + "variant": "destructive", + "className": "ml-auto" + } + ] + } }, { "type": "flex", - "gap": 2, - "children": [ - { - "type": "button", - "label": "Add to Cart", - "variant": "default", - "className": "flex-1" - }, - { - "type": "button", - "label": "", - "variant": "outline", - "size": "icon", - "children": [ - { - "type": "icon", - "icon": "heart", - "className": "h-4 w-4" - } - ] - } - ] + "properties": { + "gap": 2, + "children": [ + { + "type": "button", + "label": "Add to Cart", + "variant": "default", + "className": "flex-1" + }, + { + "type": "button", + "label": "", + "variant": "outline", + "size": "icon", + "children": [ + { + "type": "icon", + "icon": "heart", + "className": "h-4 w-4" + } + ] + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/ecommerce/product-grid.json b/examples/schema-catalog/src/schemas/ecommerce/product-grid.json index ee867c0337..dcdc07a035 100644 --- a/examples/schema-catalog/src/schemas/ecommerce/product-grid.json +++ b/examples/schema-catalog/src/schemas/ecommerce/product-grid.json @@ -5,21 +5,23 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "mb-6", - "children": [ - { - "type": "text", - "content": "Featured Products", - "className": "text-2xl font-bold" - }, - { - "type": "button", - "label": "View All", - "variant": "outline" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Featured Products", + "className": "text-2xl font-bold" + }, + { + "type": "button", + "label": "View All", + "variant": "outline" + } + ] + }, + "className": "mb-6" }, { "type": "grid", @@ -36,17 +38,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-square bg-muted", - "children": [ - { - "type": "icon", - "icon": "smartphone", - "className": "h-16 w-16 text-muted-foreground" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "smartphone", + "className": "h-16 w-16 text-muted-foreground" + } + ] + }, + "className": "aspect-square bg-muted" }, { "type": "stack", @@ -79,17 +83,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-square bg-muted", - "children": [ - { - "type": "icon", - "icon": "watch", - "className": "h-16 w-16 text-muted-foreground" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "watch", + "className": "h-16 w-16 text-muted-foreground" + } + ] + }, + "className": "aspect-square bg-muted" }, { "type": "stack", @@ -122,17 +128,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-square bg-muted", - "children": [ - { - "type": "icon", - "icon": "laptop", - "className": "h-16 w-16 text-muted-foreground" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "laptop", + "className": "h-16 w-16 text-muted-foreground" + } + ] + }, + "className": "aspect-square bg-muted" }, { "type": "stack", @@ -165,17 +173,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "aspect-square bg-muted", - "children": [ - { - "type": "icon", - "icon": "headphones", - "className": "h-16 w-16 text-muted-foreground" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "headphones", + "className": "h-16 w-16 text-muted-foreground" + } + ] + }, + "className": "aspect-square bg-muted" }, { "type": "stack", diff --git a/examples/schema-catalog/src/schemas/ecommerce/shopping-cart.json b/examples/schema-catalog/src/schemas/ecommerce/shopping-cart.json index 42bf4867b3..0d027c1d20 100644 --- a/examples/schema-catalog/src/schemas/ecommerce/shopping-cart.json +++ b/examples/schema-catalog/src/schemas/ecommerce/shopping-cart.json @@ -8,20 +8,22 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "text", - "content": "Shopping Cart", - "className": "text-2xl font-bold" - }, - { - "type": "badge", - "label": "3 items", - "variant": "secondary" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Shopping Cart", + "className": "text-2xl font-bold" + }, + { + "type": "badge", + "label": "3 items", + "variant": "secondary" + } + ] + } }, { "type": "separator" @@ -32,168 +34,180 @@ "children": [ { "type": "flex", - "gap": 4, - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-24 w-24 rounded-lg bg-muted flex-shrink-0", - "children": [ - { - "type": "icon", - "icon": "smartphone", - "className": "h-8 w-8 text-muted-foreground" - } - ] - }, - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Smartphone Pro", - "className": "font-semibold" - }, - { - "type": "text", - "content": "Color: Black, Storage: 256GB", - "className": "text-sm text-muted-foreground" - }, - { - "type": "flex", + "properties": { + "gap": 4, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", "align": "center", - "gap": 2, - "className": "mt-2", + "gap": 0, "children": [ { - "type": "button", - "label": "-", - "variant": "outline", - "size": "sm", - "className": "h-8 w-8 p-0" - }, - { - "type": "text", - "content": "1", - "className": "w-8 text-center" - }, - { - "type": "button", - "label": "+", - "variant": "outline", - "size": "sm", - "className": "h-8 w-8 p-0" + "type": "icon", + "icon": "smartphone", + "className": "h-8 w-8 text-muted-foreground" } ] - } - ] - }, - { - "type": "stack", - "gap": 2, - "children": [ - { - "type": "text", - "content": "$899.00", - "className": "font-bold" }, - { - "type": "button", - "label": "Remove", - "variant": "ghost", - "size": "sm", - "className": "text-destructive" - } - ] - } - ] + "className": "h-24 w-24 rounded-lg bg-muted flex-shrink-0" + }, + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Smartphone Pro", + "className": "font-semibold" + }, + { + "type": "text", + "content": "Color: Black, Storage: 256GB", + "className": "text-sm text-muted-foreground" + }, + { + "type": "flex", + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "button", + "label": "-", + "variant": "outline", + "size": "sm", + "className": "h-8 w-8 p-0" + }, + { + "type": "text", + "content": "1", + "className": "w-8 text-center" + }, + { + "type": "button", + "label": "+", + "variant": "outline", + "size": "sm", + "className": "h-8 w-8 p-0" + } + ] + }, + "className": "mt-2" + } + ] + }, + { + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "$899.00", + "className": "font-bold" + }, + { + "type": "button", + "label": "Remove", + "variant": "ghost", + "size": "sm", + "className": "text-destructive" + } + ] + } + ] + } }, { "type": "separator" }, { "type": "flex", - "gap": 4, - "children": [ - { - "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-24 w-24 rounded-lg bg-muted flex-shrink-0", - "children": [ - { - "type": "icon", - "icon": "headphones", - "className": "h-8 w-8 text-muted-foreground" - } - ] - }, - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Wireless Headphones", - "className": "font-semibold" - }, - { - "type": "text", - "content": "Color: Silver", - "className": "text-sm text-muted-foreground" - }, - { - "type": "flex", + "properties": { + "gap": 4, + "children": [ + { + "type": "flex", + "properties": { + "justify": "center", "align": "center", - "gap": 2, - "className": "mt-2", + "gap": 0, "children": [ { - "type": "button", - "label": "-", - "variant": "outline", - "size": "sm", - "className": "h-8 w-8 p-0" - }, - { - "type": "text", - "content": "2", - "className": "w-8 text-center" - }, - { - "type": "button", - "label": "+", - "variant": "outline", - "size": "sm", - "className": "h-8 w-8 p-0" + "type": "icon", + "icon": "headphones", + "className": "h-8 w-8 text-muted-foreground" } ] - } - ] - }, - { - "type": "stack", - "gap": 2, - "children": [ - { - "type": "text", - "content": "$598.00", - "className": "font-bold" }, - { - "type": "button", - "label": "Remove", - "variant": "ghost", - "size": "sm", - "className": "text-destructive" - } - ] - } - ] + "className": "h-24 w-24 rounded-lg bg-muted flex-shrink-0" + }, + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Wireless Headphones", + "className": "font-semibold" + }, + { + "type": "text", + "content": "Color: Silver", + "className": "text-sm text-muted-foreground" + }, + { + "type": "flex", + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "button", + "label": "-", + "variant": "outline", + "size": "sm", + "className": "h-8 w-8 p-0" + }, + { + "type": "text", + "content": "2", + "className": "w-8 text-center" + }, + { + "type": "button", + "label": "+", + "variant": "outline", + "size": "sm", + "className": "h-8 w-8 p-0" + } + ] + }, + "className": "mt-2" + } + ] + }, + { + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "$598.00", + "className": "font-bold" + }, + { + "type": "button", + "label": "Remove", + "variant": "ghost", + "size": "sm", + "className": "text-destructive" + } + ] + } + ] + } } ] }, @@ -206,66 +220,74 @@ "children": [ { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Subtotal" - }, - { - "type": "text", - "content": "$1,497.00" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Subtotal" + }, + { + "type": "text", + "content": "$1,497.00" + } + ] + }, + "className": "text-sm" }, { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Shipping" - }, - { - "type": "text", - "content": "$15.00" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Shipping" + }, + { + "type": "text", + "content": "$15.00" + } + ] + }, + "className": "text-sm" }, { "type": "flex", - "justify": "between", - "className": "text-sm", - "children": [ - { - "type": "text", - "content": "Tax" - }, - { - "type": "text", - "content": "$149.70" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Tax" + }, + { + "type": "text", + "content": "$149.70" + } + ] + }, + "className": "text-sm" }, { "type": "separator" }, { "type": "flex", - "justify": "between", - "className": "text-lg font-bold", - "children": [ - { - "type": "text", - "content": "Total" - }, - { - "type": "text", - "content": "$1,661.70" - } - ] + "properties": { + "justify": "between", + "children": [ + { + "type": "text", + "content": "Total" + }, + { + "type": "text", + "content": "$1,661.70" + } + ] + }, + "className": "text-lg font-bold" } ] }, diff --git a/examples/schema-catalog/src/schemas/forms/create-user-form.json b/examples/schema-catalog/src/schemas/forms/create-user-form.json index c2f219ad0f..64537c6251 100644 --- a/examples/schema-catalog/src/schemas/forms/create-user-form.json +++ b/examples/schema-catalog/src/schemas/forms/create-user-form.json @@ -92,22 +92,24 @@ }, { "type": "flex", - "justify": "end", - "gap": 2, - "className": "pt-2", - "children": [ - { - "type": "button", - "label": "Cancel", - "variant": "outline" - }, - { - "type": "button", - "label": "Create User", - "variant": "default", - "icon": "plus" - } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "button", + "label": "Cancel", + "variant": "outline" + }, + { + "type": "button", + "label": "Create User", + "variant": "default", + "icon": "plus" + } + ] + }, + "className": "pt-2" } ] } diff --git a/examples/schema-catalog/src/schemas/forms/newsletter-signup.json b/examples/schema-catalog/src/schemas/forms/newsletter-signup.json index 8381eb0120..b173ecdea5 100644 --- a/examples/schema-catalog/src/schemas/forms/newsletter-signup.json +++ b/examples/schema-catalog/src/schemas/forms/newsletter-signup.json @@ -49,22 +49,24 @@ }, { "type": "flex", - "align": "start", - "gap": 2, - "children": [ - { - "type": "checkbox", - "id": "terms", - "name": "terms", - "required": true - }, - { - "type": "label", - "htmlFor": "terms", - "label": "I agree to receive marketing emails and accept the Privacy Policy", - "className": "text-sm font-normal leading-none" - } - ] + "properties": { + "align": "start", + "gap": 2, + "children": [ + { + "type": "checkbox", + "id": "terms", + "name": "terms", + "required": true + }, + { + "type": "label", + "htmlFor": "terms", + "label": "I agree to receive marketing emails and accept the Privacy Policy", + "className": "text-sm font-normal leading-none" + } + ] + } }, { "type": "button", diff --git a/examples/schema-catalog/src/schemas/forms/settings-form.json b/examples/schema-catalog/src/schemas/forms/settings-form.json index 7791ad1199..7829ca7655 100644 --- a/examples/schema-catalog/src/schemas/forms/settings-form.json +++ b/examples/schema-catalog/src/schemas/forms/settings-form.json @@ -127,60 +127,64 @@ }, { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Email Notifications", - "className": "text-sm font-medium" - }, - { - "type": "text", - "content": "Receive email updates about your activity", - "className": "text-sm text-muted-foreground" - } - ] - }, - { - "type": "switch", - "id": "emailNotifications", - "name": "emailNotifications", - "defaultChecked": true - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Email Notifications", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "Receive email updates about your activity", + "className": "text-sm text-muted-foreground" + } + ] + }, + { + "type": "switch", + "id": "emailNotifications", + "name": "emailNotifications", + "defaultChecked": true + } + ] + } }, { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Marketing Emails", - "className": "text-sm font-medium" - }, - { - "type": "text", - "content": "Receive updates about new features and products", - "className": "text-sm text-muted-foreground" - } - ] - }, - { - "type": "switch", - "id": "marketingEmails", - "name": "marketingEmails" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Marketing Emails", + "className": "text-sm font-medium" + }, + { + "type": "text", + "content": "Receive updates about new features and products", + "className": "text-sm text-muted-foreground" + } + ] + }, + { + "type": "switch", + "id": "marketingEmails", + "name": "marketingEmails" + } + ] + } }, { "type": "stack", @@ -221,20 +225,22 @@ }, { "type": "flex", - "justify": "end", - "gap": 2, - "children": [ - { - "type": "button", - "label": "Cancel", - "variant": "outline" - }, - { - "type": "button", - "label": "Save Changes", - "variant": "default" - } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "button", + "label": "Cancel", + "variant": "outline" + }, + { + "type": "button", + "label": "Save Changes", + "variant": "default" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/marketing/call-to-action.json b/examples/schema-catalog/src/schemas/marketing/call-to-action.json index 78666b7b3b..318348e027 100644 --- a/examples/schema-catalog/src/schemas/marketing/call-to-action.json +++ b/examples/schema-catalog/src/schemas/marketing/call-to-action.json @@ -18,81 +18,91 @@ }, { "type": "flex", - "align": "center", - "justify": "center", - "gap": 4, - "children": [ - { - "type": "button", - "label": "Start Free Trial", - "variant": "secondary", - "size": "lg" - }, - { - "type": "button", - "label": "Schedule Demo", - "variant": "outline", - "size": "lg", - "className": "border-white text-white hover:bg-white/10" - } - ] + "properties": { + "align": "center", + "justify": "center", + "gap": 4, + "children": [ + { + "type": "button", + "label": "Start Free Trial", + "variant": "secondary", + "size": "lg" + }, + { + "type": "button", + "label": "Schedule Demo", + "variant": "outline", + "size": "lg", + "className": "border-white text-white hover:bg-white/10" + } + ] + } }, { "type": "flex", - "align": "center", - "justify": "center", - "gap": 8, - "className": "text-white/80 text-sm", - "children": [ - { - "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "icon", - "icon": "circle-check-big", - "className": "h-4 w-4" - }, - { - "type": "text", - "content": "No credit card required" + "properties": { + "align": "center", + "justify": "center", + "gap": 8, + "children": [ + { + "type": "flex", + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "circle-check-big", + "className": "h-4 w-4" + }, + { + "type": "text", + "content": "No credit card required" + } + ] } - ] - }, - { - "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "icon", - "icon": "circle-check-big", - "className": "h-4 w-4" - }, - { - "type": "text", - "content": "Cancel anytime" + }, + { + "type": "flex", + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "circle-check-big", + "className": "h-4 w-4" + }, + { + "type": "text", + "content": "Cancel anytime" + } + ] } - ] - }, - { - "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "icon", - "icon": "circle-check-big", - "className": "h-4 w-4" - }, - { - "type": "text", - "content": "24/7 support" + }, + { + "type": "flex", + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "circle-check-big", + "className": "h-4 w-4" + }, + { + "type": "text", + "content": "24/7 support" + } + ] } - ] - } - ] + } + ] + }, + "className": "text-white/80 text-sm" } ] } diff --git a/examples/schema-catalog/src/schemas/marketing/features-grid.json b/examples/schema-catalog/src/schemas/marketing/features-grid.json index 801879fc76..a8faef1e92 100644 --- a/examples/schema-catalog/src/schemas/marketing/features-grid.json +++ b/examples/schema-catalog/src/schemas/marketing/features-grid.json @@ -36,17 +36,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-12 w-12 rounded-lg bg-primary/10", - "children": [ - { - "type": "icon", - "icon": "zap", - "className": "h-6 w-6 text-primary" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "zap", + "className": "h-6 w-6 text-primary" + } + ] + }, + "className": "h-12 w-12 rounded-lg bg-primary/10" }, { "type": "text", @@ -71,17 +73,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-12 w-12 rounded-lg bg-primary/10", - "children": [ - { - "type": "icon", - "icon": "shield", - "className": "h-6 w-6 text-primary" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "shield", + "className": "h-6 w-6 text-primary" + } + ] + }, + "className": "h-12 w-12 rounded-lg bg-primary/10" }, { "type": "text", @@ -106,17 +110,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-12 w-12 rounded-lg bg-primary/10", - "children": [ - { - "type": "icon", - "icon": "smartphone", - "className": "h-6 w-6 text-primary" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "smartphone", + "className": "h-6 w-6 text-primary" + } + ] + }, + "className": "h-12 w-12 rounded-lg bg-primary/10" }, { "type": "text", @@ -141,17 +147,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-12 w-12 rounded-lg bg-primary/10", - "children": [ - { - "type": "icon", - "icon": "palette", - "className": "h-6 w-6 text-primary" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "palette", + "className": "h-6 w-6 text-primary" + } + ] + }, + "className": "h-12 w-12 rounded-lg bg-primary/10" }, { "type": "text", @@ -176,17 +184,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-12 w-12 rounded-lg bg-primary/10", - "children": [ - { - "type": "icon", - "icon": "settings", - "className": "h-6 w-6 text-primary" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "settings", + "className": "h-6 w-6 text-primary" + } + ] + }, + "className": "h-12 w-12 rounded-lg bg-primary/10" }, { "type": "text", @@ -211,17 +221,19 @@ "children": [ { "type": "flex", - "justify": "center", - "align": "center", - "gap": 0, - "className": "h-12 w-12 rounded-lg bg-primary/10", - "children": [ - { - "type": "icon", - "icon": "headphones", - "className": "h-6 w-6 text-primary" - } - ] + "properties": { + "justify": "center", + "align": "center", + "gap": 0, + "children": [ + { + "type": "icon", + "icon": "headphones", + "className": "h-6 w-6 text-primary" + } + ] + }, + "className": "h-12 w-12 rounded-lg bg-primary/10" }, { "type": "text", diff --git a/examples/schema-catalog/src/schemas/marketing/pricing-table.json b/examples/schema-catalog/src/schemas/marketing/pricing-table.json index ed9ca2f574..9a898d0276 100644 --- a/examples/schema-catalog/src/schemas/marketing/pricing-table.json +++ b/examples/schema-catalog/src/schemas/marketing/pricing-table.json @@ -53,19 +53,21 @@ }, { "type": "flex", - "align": "baseline", - "children": [ - { - "type": "text", - "content": "$9", - "className": "text-4xl font-bold" - }, - { - "type": "text", - "content": "/month", - "className": "text-muted-foreground ml-1" - } - ] + "properties": { + "align": "baseline", + "children": [ + { + "type": "text", + "content": "$9", + "className": "text-4xl font-bold" + }, + { + "type": "text", + "content": "/month", + "className": "text-muted-foreground ml-1" + } + ] + } }, { "type": "button", @@ -82,51 +84,57 @@ "children": [ { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "Up to 10 projects", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "Up to 10 projects", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "5GB storage", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "5GB storage", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "Email support", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "Email support", + "className": "text-sm" + } + ] + } } ] } @@ -148,21 +156,23 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "mb-1", - "children": [ - { - "type": "text", - "content": "Pro", - "className": "text-xl font-semibold" - }, - { - "type": "badge", - "label": "Popular", - "variant": "default" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Pro", + "className": "text-xl font-semibold" + }, + { + "type": "badge", + "label": "Popular", + "variant": "default" + } + ] + }, + "className": "mb-1" }, { "type": "text", @@ -173,19 +183,21 @@ }, { "type": "flex", - "align": "baseline", - "children": [ - { - "type": "text", - "content": "$29", - "className": "text-4xl font-bold" - }, - { - "type": "text", - "content": "/month", - "className": "text-muted-foreground ml-1" - } - ] + "properties": { + "align": "baseline", + "children": [ + { + "type": "text", + "content": "$29", + "className": "text-4xl font-bold" + }, + { + "type": "text", + "content": "/month", + "className": "text-muted-foreground ml-1" + } + ] + } }, { "type": "button", @@ -202,67 +214,75 @@ "children": [ { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "Unlimited projects", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "Unlimited projects", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "50GB storage", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "50GB storage", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "Priority support", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "Priority support", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "Advanced analytics", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "Advanced analytics", + "className": "text-sm" + } + ] + } } ] } @@ -296,19 +316,21 @@ }, { "type": "flex", - "align": "baseline", - "children": [ - { - "type": "text", - "content": "$99", - "className": "text-4xl font-bold" - }, - { - "type": "text", - "content": "/month", - "className": "text-muted-foreground ml-1" - } - ] + "properties": { + "align": "baseline", + "children": [ + { + "type": "text", + "content": "$99", + "className": "text-4xl font-bold" + }, + { + "type": "text", + "content": "/month", + "className": "text-muted-foreground ml-1" + } + ] + } }, { "type": "button", @@ -325,67 +347,75 @@ "children": [ { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "Unlimited everything", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "Unlimited everything", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "500GB storage", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "500GB storage", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "24/7 phone support", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "24/7 phone support", + "className": "text-sm" + } + ] + } }, { "type": "flex", - "align": "center", - "children": [ - { - "type": "icon", - "icon": "check", - "className": "h-4 w-4 text-primary mr-2" - }, - { - "type": "text", - "content": "Custom integrations", - "className": "text-sm" - } - ] + "properties": { + "align": "center", + "children": [ + { + "type": "icon", + "icon": "check", + "className": "h-4 w-4 text-primary mr-2" + }, + { + "type": "text", + "content": "Custom integrations", + "className": "text-sm" + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/marketing/testimonials.json b/examples/schema-catalog/src/schemas/marketing/testimonials.json index caad65a666..301652af73 100644 --- a/examples/schema-catalog/src/schemas/marketing/testimonials.json +++ b/examples/schema-catalog/src/schemas/marketing/testimonials.json @@ -36,35 +36,37 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 1, - "children": [ - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - } - ] + "properties": { + "align": "center", + "gap": 1, + "children": [ + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + } + ] + } }, { "type": "text", @@ -73,31 +75,33 @@ }, { "type": "flex", - "align": "center", - "gap": 3, - "children": [ - { - "type": "avatar", - "fallback": "JD", - "className": "h-10 w-10" - }, - { - "type": "stack", - "gap": 2, - "children": [ - { - "type": "text", - "content": "John Doe", - "className": "text-sm font-semibold" - }, - { - "type": "text", - "content": "CEO, Tech Corp", - "className": "text-xs text-muted-foreground" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "avatar", + "fallback": "JD", + "className": "h-10 w-10" + }, + { + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "John Doe", + "className": "text-sm font-semibold" + }, + { + "type": "text", + "content": "CEO, Tech Corp", + "className": "text-xs text-muted-foreground" + } + ] + } + ] + } } ] } @@ -112,35 +116,37 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 1, - "children": [ - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - } - ] + "properties": { + "align": "center", + "gap": 1, + "children": [ + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + } + ] + } }, { "type": "text", @@ -149,31 +155,33 @@ }, { "type": "flex", - "align": "center", - "gap": 3, - "children": [ - { - "type": "avatar", - "fallback": "JS", - "className": "h-10 w-10" - }, - { - "type": "stack", - "gap": 2, - "children": [ - { - "type": "text", - "content": "Jane Smith", - "className": "text-sm font-semibold" - }, - { - "type": "text", - "content": "CTO, StartupXYZ", - "className": "text-xs text-muted-foreground" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "avatar", + "fallback": "JS", + "className": "h-10 w-10" + }, + { + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Jane Smith", + "className": "text-sm font-semibold" + }, + { + "type": "text", + "content": "CTO, StartupXYZ", + "className": "text-xs text-muted-foreground" + } + ] + } + ] + } } ] } @@ -188,35 +196,37 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 1, - "children": [ - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - }, - { - "type": "icon", - "icon": "star", - "className": "h-4 w-4 fill-yellow-500 text-yellow-500" - } - ] + "properties": { + "align": "center", + "gap": 1, + "children": [ + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + }, + { + "type": "icon", + "icon": "star", + "className": "h-4 w-4 fill-yellow-500 text-yellow-500" + } + ] + } }, { "type": "text", @@ -225,31 +235,33 @@ }, { "type": "flex", - "align": "center", - "gap": 3, - "children": [ - { - "type": "avatar", - "fallback": "MJ", - "className": "h-10 w-10" - }, - { - "type": "stack", - "gap": 2, - "children": [ - { - "type": "text", - "content": "Mike Johnson", - "className": "text-sm font-semibold" - }, - { - "type": "text", - "content": "Product Lead, BigCo", - "className": "text-xs text-muted-foreground" - } - ] - } - ] + "properties": { + "align": "center", + "gap": 3, + "children": [ + { + "type": "avatar", + "fallback": "MJ", + "className": "h-10 w-10" + }, + { + "type": "stack", + "gap": 2, + "children": [ + { + "type": "text", + "content": "Mike Johnson", + "className": "text-sm font-semibold" + }, + { + "type": "text", + "content": "Product Lead, BigCo", + "className": "text-xs text-muted-foreground" + } + ] + } + ] + } } ] } diff --git a/examples/schema-catalog/src/schemas/report/report-breakdown-table.json b/examples/schema-catalog/src/schemas/report/report-breakdown-table.json index a5d24f7e1a..ef7fa0084e 100644 --- a/examples/schema-catalog/src/schemas/report/report-breakdown-table.json +++ b/examples/schema-catalog/src/schemas/report/report-breakdown-table.json @@ -3,21 +3,23 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "className": "mb-4", - "children": [ - { - "type": "text", - "content": "Revenue by Region", - "className": "font-semibold" - }, - { - "type": "badge", - "label": "5 regions", - "variant": "secondary" - } - ] + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "text", + "content": "Revenue by Region", + "className": "font-semibold" + }, + { + "type": "badge", + "label": "5 regions", + "variant": "secondary" + } + ] + }, + "className": "mb-4" }, { "type": "stack", @@ -25,171 +27,183 @@ "children": [ { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b", - "children": [ - { - "type": "text", - "content": "Region", - "className": "w-36" - }, - { - "type": "text", - "content": "Revenue", - "className": "w-24" - }, - { - "type": "text", - "content": "Orders", - "className": "w-20" - }, - { - "type": "text", - "content": "Growth", - "className": "w-20" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Region", + "className": "w-36" + }, + { + "type": "text", + "content": "Revenue", + "className": "w-24" + }, + { + "type": "text", + "content": "Orders", + "className": "w-20" + }, + { + "type": "text", + "content": "Growth", + "className": "w-20" + } + ] + }, + "className": "px-4 py-2 bg-muted text-xs font-medium text-muted-foreground border-b" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "North America", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "$125,400", - "className": "w-24" - }, - { - "type": "text", - "content": "824", - "className": "w-20" - }, - { - "type": "text", - "content": "+15.2%", - "className": "w-20 text-green-600" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "North America", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "$125,400", + "className": "w-24" + }, + { + "type": "text", + "content": "824", + "className": "w-20" + }, + { + "type": "text", + "content": "+15.2%", + "className": "w-20 text-green-600" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Europe", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "$89,200", - "className": "w-24" - }, - { - "type": "text", - "content": "562", - "className": "w-20" - }, - { - "type": "text", - "content": "+10.8%", - "className": "w-20 text-green-600" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Europe", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "$89,200", + "className": "w-24" + }, + { + "type": "text", + "content": "562", + "className": "w-20" + }, + { + "type": "text", + "content": "+10.8%", + "className": "w-20 text-green-600" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Asia Pacific", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "$42,300", - "className": "w-24" - }, - { - "type": "text", - "content": "298", - "className": "w-20" - }, - { - "type": "text", - "content": "+22.1%", - "className": "w-20 text-green-600" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Asia Pacific", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "$42,300", + "className": "w-24" + }, + { + "type": "text", + "content": "298", + "className": "w-20" + }, + { + "type": "text", + "content": "+22.1%", + "className": "w-20 text-green-600" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 border-b text-sm", - "children": [ - { - "type": "text", - "content": "Latin America", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "$18,600", - "className": "w-24" - }, - { - "type": "text", - "content": "108", - "className": "w-20" - }, - { - "type": "text", - "content": "+5.4%", - "className": "w-20 text-green-600" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Latin America", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "$18,600", + "className": "w-24" + }, + { + "type": "text", + "content": "108", + "className": "w-20" + }, + { + "type": "text", + "content": "+5.4%", + "className": "w-20 text-green-600" + } + ] + }, + "className": "px-4 py-3 border-b text-sm" }, { "type": "flex", - "align": "center", - "gap": 4, - "className": "px-4 py-3 text-sm", - "children": [ - { - "type": "text", - "content": "Middle East", - "className": "w-36 font-medium" - }, - { - "type": "text", - "content": "$9,000", - "className": "w-24" - }, - { - "type": "text", - "content": "50", - "className": "w-20" - }, - { - "type": "text", - "content": "+18.7%", - "className": "w-20 text-green-600" - } - ] + "properties": { + "align": "center", + "gap": 4, + "children": [ + { + "type": "text", + "content": "Middle East", + "className": "w-36 font-medium" + }, + { + "type": "text", + "content": "$9,000", + "className": "w-24" + }, + { + "type": "text", + "content": "50", + "className": "w-20" + }, + { + "type": "text", + "content": "+18.7%", + "className": "w-20 text-green-600" + } + ] + }, + "className": "px-4 py-3 text-sm" } ] } diff --git a/examples/schema-catalog/src/schemas/report/report-header-with-kpis.json b/examples/schema-catalog/src/schemas/report/report-header-with-kpis.json index 419a67f879..eb7c988a5e 100644 --- a/examples/schema-catalog/src/schemas/report/report-header-with-kpis.json +++ b/examples/schema-catalog/src/schemas/report/report-header-with-kpis.json @@ -4,53 +4,57 @@ "children": [ { "type": "flex", - "align": "center", - "justify": "between", - "children": [ - { - "type": "stack", - "gap": 1, - "children": [ - { - "type": "text", - "content": "Quarterly Sales Report", - "className": "text-xl font-bold" - }, - { - "type": "text", - "content": "Q1 2024 · January - March", - "className": "text-sm text-muted-foreground" + "properties": { + "align": "center", + "justify": "between", + "children": [ + { + "type": "stack", + "gap": 1, + "children": [ + { + "type": "text", + "content": "Quarterly Sales Report", + "className": "text-xl font-bold" + }, + { + "type": "text", + "content": "Q1 2024 · January - March", + "className": "text-sm text-muted-foreground" + } + ] + }, + { + "type": "flex", + "properties": { + "gap": 2, + "children": [ + { + "type": "button", + "label": "PDF", + "variant": "outline", + "size": "sm", + "icon": "file-text" + }, + { + "type": "button", + "label": "Excel", + "variant": "outline", + "size": "sm", + "icon": "table" + }, + { + "type": "button", + "label": "Print", + "variant": "outline", + "size": "sm", + "icon": "printer" + } + ] } - ] - }, - { - "type": "flex", - "gap": 2, - "children": [ - { - "type": "button", - "label": "PDF", - "variant": "outline", - "size": "sm", - "icon": "file-text" - }, - { - "type": "button", - "label": "Excel", - "variant": "outline", - "size": "sm", - "icon": "table" - }, - { - "type": "button", - "label": "Print", - "variant": "outline", - "size": "sm", - "icon": "printer" - } - ] - } - ] + } + ] + } }, { "type": "grid", diff --git a/examples/schema-catalog/src/schemas/report/report-scheduling.json b/examples/schema-catalog/src/schemas/report/report-scheduling.json index 7afd55a4f0..8124e6ae1f 100644 --- a/examples/schema-catalog/src/schemas/report/report-scheduling.json +++ b/examples/schema-catalog/src/schemas/report/report-scheduling.json @@ -4,20 +4,22 @@ "header": [ { "type": "flex", - "align": "center", - "gap": 2, - "children": [ - { - "type": "icon", - "icon": "clock", - "className": "h-5 w-5 text-primary" - }, - { - "type": "text", - "content": "Schedule Settings", - "className": "font-semibold" - } - ] + "properties": { + "align": "center", + "gap": 2, + "children": [ + { + "type": "icon", + "icon": "clock", + "className": "h-5 w-5 text-primary" + }, + { + "type": "text", + "content": "Schedule Settings", + "className": "font-semibold" + } + ] + } } ], "children": [ @@ -85,59 +87,63 @@ }, { "type": "flex", - "gap": 2, - "children": [ - { - "type": "checkbox", - "id": "pdf", - "name": "pdf", - "defaultChecked": true - }, - { - "type": "label", - "htmlFor": "pdf", - "label": "PDF" - }, - { - "type": "checkbox", - "id": "excel", - "name": "excel" - }, - { - "type": "label", - "htmlFor": "excel", - "label": "Excel" - }, - { - "type": "checkbox", - "id": "csv", - "name": "csv" - }, - { - "type": "label", - "htmlFor": "csv", - "label": "CSV" - } - ] + "properties": { + "gap": 2, + "children": [ + { + "type": "checkbox", + "id": "pdf", + "name": "pdf", + "defaultChecked": true + }, + { + "type": "label", + "htmlFor": "pdf", + "label": "PDF" + }, + { + "type": "checkbox", + "id": "excel", + "name": "excel" + }, + { + "type": "label", + "htmlFor": "excel", + "label": "Excel" + }, + { + "type": "checkbox", + "id": "csv", + "name": "csv" + }, + { + "type": "label", + "htmlFor": "csv", + "label": "CSV" + } + ] + } }, { "type": "flex", - "justify": "end", - "gap": 2, - "className": "pt-2", - "children": [ - { - "type": "button", - "label": "Cancel", - "variant": "outline" - }, - { - "type": "button", - "label": "Save Schedule", - "variant": "default", - "icon": "save" - } - ] + "properties": { + "justify": "end", + "gap": 2, + "children": [ + { + "type": "button", + "label": "Cancel", + "variant": "outline" + }, + { + "type": "button", + "label": "Save Schedule", + "variant": "default", + "icon": "save" + } + ] + }, + "className": "pt-2" } ] } diff --git a/examples/schema-catalog/src/schemas/theme/theme-aware-ui-elements.json b/examples/schema-catalog/src/schemas/theme/theme-aware-ui-elements.json index e275b43b8b..336097baa1 100644 --- a/examples/schema-catalog/src/schemas/theme/theme-aware-ui-elements.json +++ b/examples/schema-catalog/src/schemas/theme/theme-aware-ui-elements.json @@ -4,62 +4,66 @@ "children": [ { "type": "flex", - "gap": 2, - "wrap": true, - "children": [ - { - "type": "button", - "label": "Primary", - "variant": "default" - }, - { - "type": "button", - "label": "Secondary", - "variant": "secondary" - }, - { - "type": "button", - "label": "Outline", - "variant": "outline" - }, - { - "type": "button", - "label": "Ghost", - "variant": "ghost" - }, - { - "type": "button", - "label": "Destructive", - "variant": "destructive" - } - ] + "properties": { + "gap": 2, + "wrap": true, + "children": [ + { + "type": "button", + "label": "Primary", + "variant": "default" + }, + { + "type": "button", + "label": "Secondary", + "variant": "secondary" + }, + { + "type": "button", + "label": "Outline", + "variant": "outline" + }, + { + "type": "button", + "label": "Ghost", + "variant": "ghost" + }, + { + "type": "button", + "label": "Destructive", + "variant": "destructive" + } + ] + } }, { "type": "flex", - "gap": 2, - "wrap": true, - "children": [ - { - "type": "badge", - "label": "Default", - "variant": "default" - }, - { - "type": "badge", - "label": "Secondary", - "variant": "secondary" - }, - { - "type": "badge", - "label": "Outline", - "variant": "outline" - }, - { - "type": "badge", - "label": "Destructive", - "variant": "destructive" - } - ] + "properties": { + "gap": 2, + "wrap": true, + "children": [ + { + "type": "badge", + "label": "Default", + "variant": "default" + }, + { + "type": "badge", + "label": "Secondary", + "variant": "secondary" + }, + { + "type": "badge", + "label": "Outline", + "variant": "outline" + }, + { + "type": "badge", + "label": "Destructive", + "variant": "destructive" + } + ] + } }, { "type": "grid", diff --git a/examples/schema-catalog/test/badge-demo-label-6829.test.tsx b/examples/schema-catalog/test/badge-demo-label-6829.test.tsx index b4caf78f61..b488220534 100644 --- a/examples/schema-catalog/test/badge-demo-label-6829.test.tsx +++ b/examples/schema-catalog/test/badge-demo-label-6829.test.tsx @@ -136,26 +136,32 @@ const MEMBER_ENTRIES: Record = { 'Parent ComponentNestedComponentsSibling ComponentAll rendered from a single schema tree', }; -/** The seven node locations arm A re-authored — the former exact ledger. */ +/** + * The seven node locations arm A re-authored — the former exact ledger. A + * `flex` takes its child list in its `properties` bag since objectui#11276, so + * a badge under one sits at `….properties.children[i]`. + */ const REAUTHORED_NODES = [ 'components-basic-span/default-badge', 'components-basic-span/secondary-badge', - 'components-basic-span/status-badges.children[0]', - 'components-basic-span/status-badges.children[1]', - 'components-basic-span/status-badges.children[2]', - 'core-schema-renderer/nested-schema-example.children[0].children[0].children[0]', - 'core-schema-renderer/nested-schema-example.children[0].children[0].children[1]', + 'components-basic-span/status-badges.properties.children[0]', + 'components-basic-span/status-badges.properties.children[1]', + 'components-basic-span/status-badges.properties.children[2]', + 'core-schema-renderer/nested-schema-example.children[0].children[0].properties.children[0]', + 'core-schema-renderer/nested-schema-example.children[0].children[0].properties.children[1]', ]; /** * The container nodes inside the touched entries that legitimately author - * `children` and must go on doing so — the nodes a blind sweep would break. + * `children` and must go on doing so — the nodes a blind sweep would break. A + * `flex` authors its child list in its `properties` bag (objectui#11276), so + * for the two `flex` containers the location is that bag, `….properties`. */ const TOUCHED_CONTAINERS = [ - 'components-basic-span/status-badges', + 'components-basic-span/status-badges.properties', 'core-schema-renderer/nested-schema-example', 'core-schema-renderer/nested-schema-example.children[0]', - 'core-schema-renderer/nested-schema-example.children[0].children[0]', + 'core-schema-renderer/nested-schema-example.children[0].children[0].properties', 'core-schema-renderer/nested-schema-example.children[1]', ]; diff --git a/examples/schema-catalog/test/flex-direction-col-8761.test.tsx b/examples/schema-catalog/test/flex-direction-col-8761.test.tsx index d40a2ecfe0..108c87b1fa 100644 --- a/examples/schema-catalog/test/flex-direction-col-8761.test.tsx +++ b/examples/schema-catalog/test/flex-direction-col-8761.test.tsx @@ -40,6 +40,11 @@ * carried when the card was worked. This is deliberately not a catalog-wide * walker, and it pins nothing about how the renderer treats an UNDECLARED * direction value; that question is outside this card. + * + * Since objectui#11276 (the maintainer's ruling A on objectui#11300) an + * authored `flex` takes `direction` in its `properties` bag, so the key these + * pins read is `properties.direction`; `SchemaRenderer` hoists it onto the node + * before the renderer reads it, which the render block below still proves. */ import { describe, it, expect } from 'vitest'; import { render } from '@testing-library/react'; @@ -69,12 +74,13 @@ describe('objectui#8761 — the five stacked-label entries author the declared c expect(issues(getExample(id).schema)).toEqual([]); }); - it.each(IDS)('%s is a `flex` root that authors `direction: "col"`', (id) => { + it.each(IDS)('%s is a `flex` root that authors `direction: "col"` in its bag', (id) => { // Names the key that moved, so a later sweep that deleted `direction` // outright (which would still parse, and draw a row) cannot pass here. - const schema = getExample(id).schema as { type?: unknown; direction?: unknown }; + const schema = getExample(id).schema as { type?: unknown; direction?: unknown; properties?: { direction?: unknown } }; expect(schema.type).toBe('flex'); - expect(schema.direction).toBe('col'); + expect(schema.properties?.direction).toBe('col'); + expect(schema).not.toHaveProperty('direction'); }); it.each(IDS)('%s renders its `flex` root as a column', (id) => { @@ -92,11 +98,11 @@ describe('objectui#8761 — the five stacked-label entries author the declared c // Counter-probe for the parse block: the same document shape with the CSS // spelling must fail, and fail for this key — otherwise a green above would // not be evidence that the value was checked at all. - const result = safeValidateSchema({ type: 'flex', direction: 'column' }); + const result = safeValidateSchema({ type: 'flex', properties: { direction: 'column' } }); expect(result.success).toBe(false); const found = result.success ? [] : result.error.issues.map((i) => [i.code, i.path.join('.')]); - expect(found).toEqual([['invalid_value', 'direction']]); + expect(found).toEqual([['invalid_value', 'properties.direction']]); // …and the declared spelling passes the same probe. - expect(safeValidateSchema({ type: 'flex', direction: 'col' }).success).toBe(true); + expect(safeValidateSchema({ type: 'flex', properties: { direction: 'col' } }).success).toBe(true); }); }); diff --git a/examples/schema-catalog/test/layout-props-conversion.test.tsx b/examples/schema-catalog/test/layout-props-conversion.test.tsx index cdac6f47b1..0c1ffc2537 100644 --- a/examples/schema-catalog/test/layout-props-conversion.test.tsx +++ b/examples/schema-catalog/test/layout-props-conversion.test.tsx @@ -109,6 +109,19 @@ function collect(node: unknown, pred: (n: Node) => boolean, out: Node[] = []): N return out; } +/** + * Where a layout node's own props live. An authored `flex` takes them — its + * child list included — in its `properties` bag (objectui#11276, the + * maintainer's ruling A on objectui#11300); `stack`, `grid` and `container` + * keep them on the node. `SchemaRenderer` hoists the bag before the renderer + * runs, so both render alike; this only says where to READ them in a fixture. + */ +function propsOf(node: Node): Node { + return node.type === 'flex' && node.properties && typeof node.properties === 'object' + ? (node.properties as Node) + : node; +} + describe('schema-catalog — layout intent is authored as props, not className (#4003)', () => { it('no div carries layout-intent classes outside the documented exemptions', () => { const offenders = allExamples().flatMap((example) => @@ -258,7 +271,7 @@ describe('converted nodes render what their div rendered (#4003)', () => { // Rebuild the div this node replaced, around the SAME children. const asDiv: Node = { type: 'div', className: originalClassName }; - if (node!.children !== undefined) asDiv.children = node!.children; + if (propsOf(node!).children !== undefined) asDiv.children = propsOf(node!).children; const after = renderNode(node!); const before = renderNode(asDiv); @@ -477,7 +490,7 @@ describe('schema-catalog — a layout node configures itself with props (#4891)' example.schema, (n) => typeof n.type === 'string' && LAYOUT_TYPES.has(n.type as string), ).flatMap((n) => - ownPropTokens(String(n.type), n.className, n.direction).map( + ownPropTokens(String(n.type), n.className, propsOf(n).direction).map( (token) => `${example.id} :: ${String(n.type)} :: ${token} (in "${String(n.className)}")`, ), ), @@ -749,9 +762,10 @@ describe('the swept nodes render what they rendered before (#4891/#4890)', () => 'the pin is stale, or the sweep was undone', ).toBeTruthy(); - // Rebuild the pre-sweep node around the SAME children. + // Rebuild the pre-sweep node around the SAME children. It shipped flat, + // and a stored flat node still renders (the hoist reads both spellings). const asAuthoredBefore: Node = { type: testCase.type, ...testCase.before }; - if (node!.children !== undefined) asAuthoredBefore.children = node!.children; + if (propsOf(node!).children !== undefined) asAuthoredBefore.children = propsOf(node!).children; const after = renderClass(node!); const before = renderClass(asAuthoredBefore); @@ -822,16 +836,19 @@ describe('the nine converted `space-x` nodes were single-line static rows (#5690 /** * The nine sites, at the paths the card enumerated them by, so this table is * auditable against the card instead of against itself. Third element is the - * `gap` that replaced the node's `space-x-N` — the same N. + * `gap` that replaced the node's `space-x-N` — the same N. A step under a + * `flex` parent passes through that parent's bag (`properties.children.N`), + * where objectui#11276 moved the child list; the steps are otherwise the + * card's. */ const SPACE_X_SITES: ReadonlyArray = [ - ['auth/login-simple', 'children.0.children.2.children.0', 2], + ['auth/login-simple', 'children.0.children.2.properties.children.0', 2], ['auth/signup', 'children.0.children.3', 2], - ['dashboard/recent-activity-card', 'children.0.children.1.children.0.children.0', 4], - ['dashboard/recent-activity-card', 'children.0.children.1.children.1.children.0', 4], - ['dashboard/recent-activity-card', 'children.0.children.1.children.2.children.0', 4], - ['dashboard/recent-activity-card', 'children.0.children.1.children.3.children.0', 4], - ['dashboard/recent-activity-card', 'children.0.children.1.children.4.children.0', 4], + ['dashboard/recent-activity-card', 'children.0.children.1.children.0.properties.children.0', 4], + ['dashboard/recent-activity-card', 'children.0.children.1.children.1.properties.children.0', 4], + ['dashboard/recent-activity-card', 'children.0.children.1.children.2.properties.children.0', 4], + ['dashboard/recent-activity-card', 'children.0.children.1.children.3.properties.children.0', 4], + ['dashboard/recent-activity-card', 'children.0.children.1.children.4.properties.children.0', 4], ['forms/newsletter-signup', 'children.0.children.2', 2], ['forms/settings-form', 'children.1.children.0.children.3', 2], ]; @@ -844,9 +861,10 @@ describe('the nine converted `space-x` nodes were single-line static rows (#5690 schema, ) as Node | undefined; - /** The node exactly as it shipped before the conversion. */ + /** The node exactly as it shipped before the conversion: flat, `space-x-N` in place of `gap`. */ const asShipped = (node: Node): Node => { - const before: Node = { ...node, className: `space-x-${String(node.gap)}` }; + const before: Node = { ...node, ...propsOf(node), className: `space-x-${String(propsOf(node).gap)}` }; + delete before.properties; delete before.gap; return before; }; @@ -870,7 +888,7 @@ describe('the nine converted `space-x` nodes were single-line static rows (#5690 if (!node) return []; const problems: string[] = []; if (node.type !== 'flex') problems.push(`type is ${String(node.type)}, not flex`); - if (node.gap !== gap) problems.push(`gap is ${JSON.stringify(node.gap)}, not ${gap}`); + if (propsOf(node).gap !== gap) problems.push(`gap is ${JSON.stringify(propsOf(node).gap)}, not ${gap}`); if (node.className !== undefined) { problems.push(`className survived as ${JSON.stringify(node.className)}`); } @@ -901,7 +919,7 @@ describe('the nine converted `space-x` nodes were single-line static rows (#5690 } // ...and the selector axis: a fixed pair, neither half conditional. - const children = Array.isArray(node.children) ? (node.children as Node[]) : []; + const children = Array.isArray(propsOf(node).children) ? (propsOf(node).children as Node[]) : []; if (children.length !== 2) { reasons.push(`its child list is ${children.length} long, not the static pair measured`); } diff --git a/packages/components/src/__tests__/flex-properties-bag-render-11276.test.tsx b/packages/components/src/__tests__/flex-properties-bag-render-11276.test.tsx new file mode 100644 index 0000000000..8d774c96e6 --- /dev/null +++ b/packages/components/src/__tests__/flex-properties-bag-render-11276.test.tsx @@ -0,0 +1,83 @@ +/** + * ObjectUI + * Copyright (c) 2024-present ObjectStack Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * An authored `flex` node takes its props in its `properties` bag + * (objectui#11276, the maintainer's ruling A on objectui#11300), and the + * narrowing is on the AUTHORING faces only: `SchemaRenderer`'s `properties` + * hoist is untouched, so the bag node and a STORED flat node — a document + * saved before the change, or a node composed in code — draw the same element. + * + * Both halves are read off the real `SchemaRenderer` and the real registry: + * + * - the bag node draws what the flat node draws, class string and subtree + * alike, with every `flex` prop in play; + * - the stored flat node still draws (the hoist did not move), and the bag's + * own key never reaches the DOM as an attribute. + * + * The validator half (the flat spelling refused by name) is pinned with the + * arm, in `packages/types/src/__tests__/flex-properties-bag-11276.test.ts`. + * + * Module-scope import of the renderers, not `beforeAll` (AGENTS.md §测试纪律): + * registering them is an unbounded module load and must not be billed to a + * bounded hook timeout. + */ +import { describe, it, expect } from 'vitest'; +import { render } from '@testing-library/react'; +import '../renderers'; +import { SchemaRenderer } from '@object-ui/react'; + +function draw(schema: unknown) { + const { container, unmount } = render(); + const el = container.firstElementChild as HTMLElement; + const out = { className: el.className, innerHTML: el.innerHTML, attributes: el.getAttributeNames() }; + unmount(); + return out; +} + +const PROPS = { + direction: 'col', + justify: 'between', + align: 'center', + gap: 4, + wrap: true, + children: [ + { type: 'text', content: 'First' }, + { type: 'text', content: 'Second' }, + ], +} as const; + +const BAG = { type: 'flex', id: 'row', className: 'p-4', properties: PROPS }; +const STORED_FLAT = { type: 'flex', id: 'row', className: 'p-4', ...PROPS }; + +describe('a bag `flex` and a stored flat `flex` draw the same element (objectui#11276)', () => { + it('the bag node draws every prop: the classes they map to, and the children', () => { + const drawn = draw(BAG); + expect(drawn.className).toBe('flex flex-col justify-between items-center gap-2 sm:gap-3 md:gap-4 flex-wrap p-4'); + expect(drawn.innerHTML).toContain('First'); + expect(drawn.innerHTML).toContain('Second'); + }); + + it('the stored flat node still draws — `SchemaRenderer`\'s hoist reads both spellings', () => { + const bag = draw(BAG); + const flat = draw(STORED_FLAT); + expect(flat.className).toBe(bag.className); + expect(flat.innerHTML).toBe(bag.innerHTML); + }); + + it('the bag itself never reaches the DOM as an attribute', () => { + const drawn = draw(BAG); + expect(drawn.attributes).not.toContain('properties'); + for (const key of Object.keys(PROPS)) expect(drawn.attributes).not.toContain(key); + }); + + it('control: without the bag the same node draws the renderer defaults, so the readings above are the bag\'s', () => { + const bare = draw({ type: 'flex', id: 'row', className: 'p-4', children: [] }); + expect(bare.className).toBe('flex flex-row justify-start items-start gap-1.5 sm:gap-2 p-4'); + }); +}); diff --git a/packages/core/src/builder/__tests__/schema-builder.test.ts b/packages/core/src/builder/__tests__/schema-builder.test.ts index dcb8e1827c..dcc1d920c1 100644 --- a/packages/core/src/builder/__tests__/schema-builder.test.ts +++ b/packages/core/src/builder/__tests__/schema-builder.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect } from 'vitest'; import * as builder from '../../builder/schema-builder'; import { form, button, input, card, grid, flex } from '../../builder/schema-builder'; +import { StrictAnyComponentSchema, safeValidateSchema } from '@object-ui/types/zod'; describe('SchemaBuilder', () => { describe('form()', () => { @@ -144,7 +145,45 @@ describe('SchemaBuilder', () => { const schema = flex() .direction('row') .build(); - expect(schema.direction).toBe('row'); + expect(schema.properties.direction).toBe('row'); + }); + + // objectui#11276 (the maintainer's ruling A on objectui#11300): the builder + // emits the AUTHORED spelling — every `flex` prop, the child list included, + // in the `properties` bag — so what it builds passes the authoring faces, + // which refuse those props written flat on the node by name. + it('emits its props in the properties bag, which both authoring faces accept (objectui#11276)', () => { + const schema = flex() + .id('row') + .className('p-4') + .direction('col') + .justify('between') + .align('center') + .gap(4) + .child({ type: 'text', content: 'a' }) + .child({ type: 'text', content: 'b' }) + .build(); + + expect(schema).toEqual({ + type: 'flex', + id: 'row', + className: 'p-4', + properties: { + direction: 'col', + justify: 'between', + align: 'center', + gap: 4, + children: [ + { type: 'text', content: 'a' }, + { type: 'text', content: 'b' }, + ], + }, + }); + for (const key of ['direction', 'justify', 'align', 'gap', 'children']) { + expect(schema, key).not.toHaveProperty(key); + } + expect(safeValidateSchema(schema).success).toBe(true); + expect(StrictAnyComponentSchema.safeParse(schema).success).toBe(true); }); }); diff --git a/packages/core/src/builder/schema-builder.ts b/packages/core/src/builder/schema-builder.ts index 40acce12d2..4492425cb1 100644 --- a/packages/core/src/builder/schema-builder.ts +++ b/packages/core/src/builder/schema-builder.ts @@ -24,7 +24,7 @@ import type { InputSchema, CardSchema, GridSchema, - FlexSchema + FlexLayoutProps } from '@object-ui/types'; /** @@ -367,18 +367,28 @@ export class GridBuilder extends SchemaBuilder { /** * Flex builder + * + * Builds an AUTHORED `flex` node: its props in the `properties` bag + * (objectui#11276, the maintainer's ruling A on objectui#11300) — + * `{ type: 'flex', properties: { direction, justify, align, gap, children } }`, + * the spelling `@objectstack/spec`'s page component declares and + * `@object-ui/types`' authoring faces accept. A `flex` prop written flat on the + * node is refused there by name, so the builder never writes one. The base + * setters (`id`, `className`, `visible`, …) stay on the node. The bag's members + * are `FlexLayoutProps`; `SchemaRenderer` hoists them onto the node before the + * `flex` renderer reads them. */ -export class FlexBuilder extends SchemaBuilder { +export class FlexBuilder extends SchemaBuilder { constructor() { super('flex'); - this.schema.children = []; + this.schema.properties = { children: [] }; } /** * Set flex direction */ direction(direction: 'row' | 'col' | 'row-reverse' | 'col-reverse'): this { - this.schema.direction = direction; + this.schema.properties.direction = direction; return this; } @@ -386,7 +396,7 @@ export class FlexBuilder extends SchemaBuilder { * Set justify content */ justify(justify: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'): this { - this.schema.justify = justify; + this.schema.properties.justify = justify; return this; } @@ -394,7 +404,7 @@ export class FlexBuilder extends SchemaBuilder { * Set align items */ align(align: 'start' | 'end' | 'center' | 'baseline' | 'stretch'): this { - this.schema.align = align; + this.schema.properties.align = align; return this; } @@ -402,7 +412,7 @@ export class FlexBuilder extends SchemaBuilder { * Set gap */ gap(gap: number): this { - this.schema.gap = gap; + this.schema.properties.gap = gap; return this; } @@ -410,8 +420,8 @@ export class FlexBuilder extends SchemaBuilder { * Add a child */ child(child: BaseSchema): this { - const children = Array.isArray(this.schema.children) ? this.schema.children : []; - this.schema.children = [...children, child]; + const children = Array.isArray(this.schema.properties.children) ? this.schema.properties.children : []; + this.schema.properties.children = [...children, child]; return this; } @@ -419,7 +429,7 @@ export class FlexBuilder extends SchemaBuilder { * Set all children */ children(children: BaseSchema[]): this { - this.schema.children = children; + this.schema.properties.children = children; return this; } } diff --git a/packages/types/README.md b/packages/types/README.md index 1fe091d509..119a7ab617 100644 --- a/packages/types/README.md +++ b/packages/types/README.md @@ -262,7 +262,7 @@ Foundation types that all components build upon: Structure and organization: - `ContainerSchema` - Max-width container -- `FlexSchema` - Flexbox layout +- `FlexSchema` - Flexbox layout, as the renderer reads it; an authored `flex` node writes its `FlexLayoutProps` in its `properties` bag - `GridSchema` - CSS Grid layout - `CardSchema` - Card container - `TabsSchema` - Tabbed interface @@ -396,7 +396,7 @@ function render(schema: AnySchema) { Components can nest indefinitely: ```typescript -import type { ContainerSchema, FlexSchema, SidebarSchema } from '@object-ui/types'; +import type { BaseSchema, ContainerSchema, FlexLayoutProps, SidebarSchema } from '@object-ui/types'; // The two leaves are annotated so the nesting below is checked against the // shipped types rather than absorbed by `BaseSchema`'s index signature. @@ -410,20 +410,30 @@ const main: ContainerSchema = { children: [{ type: 'data-table', data: [] }] }; -const page: FlexSchema = { +// An authored `flex` node takes its props, the child list included, in its +// `properties` bag; `satisfies FlexLayoutProps` checks each bag. +const page: BaseSchema = { type: 'flex', - direction: 'col', - children: [ - { type: 'header-bar', crumbs: [{ label: 'My App' }] }, - { - type: 'flex', - direction: 'row', - children: [sidebar, main] - } - ] + properties: { + direction: 'col', + children: [ + { type: 'header-bar', crumbs: [{ label: 'My App' }] }, + { + type: 'flex', + properties: { + direction: 'row', + children: [sidebar, main] + } satisfies FlexLayoutProps + } + ] + } satisfies FlexLayoutProps }; ``` +`FlexSchema` is the same node as the `flex` renderer reads it, after `SchemaRenderer` +hoists the bag onto the node; `objectui validate` refuses those props written flat on an +authored node. + ## Comparison ### vs Amis Types diff --git a/packages/types/examples/dashboard.ts b/packages/types/examples/dashboard.ts index a85b1be42c..8de20790c2 100644 --- a/packages/types/examples/dashboard.ts +++ b/packages/types/examples/dashboard.ts @@ -13,82 +13,87 @@ * sidebar, header, and data table. */ -import type { FlexSchema, SidebarSchema, HeaderBarSchema, CardSchema, DataTableSchema } from '../src/index'; +import type { BaseSchema, FlexLayoutProps, SidebarSchema, HeaderBarSchema, CardSchema, DataTableSchema } from '../src/index'; -export const dashboardSchema: FlexSchema = { +// An authored `flex` node takes its props, the child list included, in its +// `properties` bag (objectui#11276); `satisfies FlexLayoutProps` checks each bag. +export const dashboardSchema: BaseSchema = { type: 'flex', - direction: 'col', className: 'h-screen', - - children: [ - // Header - { - type: 'header-bar', - crumbs: [{ label: 'Object UI Dashboard' }], - actions: [ - { - type: 'button', - label: 'Profile', - variant: 'ghost', - icon: 'User' - } - ] - } as HeaderBarSchema, - - // Main content with sidebar - { - type: 'flex', - direction: 'row', - className: 'flex-1', - children: [ - // Sidebar - { - type: 'sidebar', - collapsible: true, - nav: [ - { label: 'Dashboard', href: '/', icon: 'Home', active: true }, - { label: 'Users', href: '/users', icon: 'Users' }, - { label: 'Settings', href: '/settings', icon: 'Settings' } - ] - } as SidebarSchema, - - // Main content area - { - type: 'container', - className: 'flex-1 p-6', + properties: { + direction: 'col', + children: [ + // Header + { + type: 'header-bar', + crumbs: [{ label: 'Object UI Dashboard' }], + actions: [ + { + type: 'button', + label: 'Profile', + variant: 'ghost', + icon: 'User' + } + ] + } as HeaderBarSchema, + + // Main content with sidebar + { + type: 'flex', + className: 'flex-1', + properties: { + direction: 'row', children: [ + // Sidebar + { + type: 'sidebar', + collapsible: true, + nav: [ + { label: 'Dashboard', href: '/', icon: 'Home', active: true }, + { label: 'Users', href: '/users', icon: 'Users' }, + { label: 'Settings', href: '/settings', icon: 'Settings' } + ] + } as SidebarSchema, + + // Main content area { - type: 'card', - title: 'User Management', - description: 'Manage your users and permissions', - content: { - type: 'data-table', - columns: [ - { header: 'ID', accessorKey: 'id', width: '80px' }, - { header: 'Name', accessorKey: 'name' }, - { header: 'Email', accessorKey: 'email' }, - { header: 'Role', accessorKey: 'role' }, - { header: 'Status', accessorKey: 'status' } - ], - data: [ - { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'Active' }, - { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'Active' }, - { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'Inactive' } - ], - pagination: true, - pageSize: 10, - searchable: true, - selectable: true, - sortable: true, - exportable: true, - rowActions: true, - onRowEdit: (row) => console.log('Edit:', row), - onRowDelete: (row) => console.log('Delete:', row) - } as DataTableSchema - } as CardSchema + type: 'container', + className: 'flex-1 p-6', + children: [ + { + type: 'card', + title: 'User Management', + description: 'Manage your users and permissions', + content: { + type: 'data-table', + columns: [ + { header: 'ID', accessorKey: 'id', width: '80px' }, + { header: 'Name', accessorKey: 'name' }, + { header: 'Email', accessorKey: 'email' }, + { header: 'Role', accessorKey: 'role' }, + { header: 'Status', accessorKey: 'status' } + ], + data: [ + { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'Active' }, + { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'Active' }, + { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'Inactive' } + ], + pagination: true, + pageSize: 10, + searchable: true, + selectable: true, + sortable: true, + exportable: true, + rowActions: true, + onRowEdit: (row) => console.log('Edit:', row), + onRowDelete: (row) => console.log('Delete:', row) + } as DataTableSchema + } as CardSchema + ] + } ] - } - ] - } - ] + } satisfies FlexLayoutProps + } + ] + } satisfies FlexLayoutProps }; diff --git a/packages/types/examples/data-display-examples.json b/packages/types/examples/data-display-examples.json index 2c1a8be942..8a3f01b4b3 100644 --- a/packages/types/examples/data-display-examples.json +++ b/packages/types/examples/data-display-examples.json @@ -238,42 +238,44 @@ "compositeExample": { "type": "flex", "id": "user-profile-card", - "direction": "col", - "gap": 4, - "children": [ - { - "type": "avatar", - "src": "https://github.com/shadcn.png", - "alt": "User Avatar", - "fallback": "JD", - "size": "lg" - }, - { - "type": "statistic", - "label": "Followers", - "value": "1,234", - "trend": "up", - "icon": "users" - }, - { - "type": "badge", - "label": "Pro Member", - "variant": "default" - }, - { - "type": "list", - "items": [ - { - "label": "Posts", - "description": "42 articles" - }, - { - "label": "Achievements", - "description": "15 badges earned" - } - ], - "dividers": true - } - ] + "properties": { + "direction": "col", + "gap": 4, + "children": [ + { + "type": "avatar", + "src": "https://github.com/shadcn.png", + "alt": "User Avatar", + "fallback": "JD", + "size": "lg" + }, + { + "type": "statistic", + "label": "Followers", + "value": "1,234", + "trend": "up", + "icon": "users" + }, + { + "type": "badge", + "label": "Pro Member", + "variant": "default" + }, + { + "type": "list", + "items": [ + { + "label": "Posts", + "description": "42 articles" + }, + { + "label": "Achievements", + "description": "15 badges earned" + } + ], + "dividers": true + } + ] + } } } diff --git a/packages/types/src/__tests__/flat-arm-responsive-styles-10872.test.ts b/packages/types/src/__tests__/flat-arm-responsive-styles-10872.test.ts index 90921961cd..58a5b9a069 100644 --- a/packages/types/src/__tests__/flat-arm-responsive-styles-10872.test.ts +++ b/packages/types/src/__tests__/flat-arm-responsive-styles-10872.test.ts @@ -35,11 +35,14 @@ * ⚠️ objectui#11276 moved `object-chart`'s AUTHORED arm into the public-block * set (`ObjectChartBlockSchema`, its props in the `properties` bag), and its * `object-grid` batch moved `object-grid`'s the same way - * (`ObjectGridBlockSchema`), so the population below is the one arm the node - * union still selects a flat mirror for, `flex`. The flat `ObjectChartSchema` - * and `ObjectGridSchema` keep the shared member as the post-hoist reading, and - * the authored chart and grid arms are pinned with the other public blocks in - * `./public-block-responsive-styles-10872.test.ts`. + * (`ObjectGridBlockSchema`), so the authored chart and grid arms are pinned + * with the other public blocks in `./public-block-responsive-styles-10872.test.ts`. + * Its `flex` batch moved `flex`'s authored arm to the bag too + * (`FlexBlockSchema`), which stays in the layout union, outside the public-block + * set: so the population below is that one arm, `flex`, whose nodes now carry + * their props in the bag beside the node-level key. The flat `FlexSchema`, + * `ObjectChartSchema` and `ObjectGridSchema` mirrors keep the shared member as + * the post-hoist reading. * The key is NOT declared on `BaseSchema` and NOT on any arm without a measured * producer: `grid`, `stack` and `container` below are the control, and the * whole arm list is read at run time so an arm that gains the member silently @@ -63,6 +66,7 @@ import { import { AnyComponentSchema, BaseSchema, + FlexBlockSchema, FlexSchema, ObjectChartSchema, ObjectGridSchema, @@ -85,6 +89,7 @@ type InputOf = T extends z.ZodType ? z.input : never; /** Each zod arm's member accepts exactly the spec's published type, absent allowed. */ export type assertionZodMemberIsTheSpecType = [ + Expect['responsiveStyles']>, SpecResponsiveStyles | undefined>>, Expect['responsiveStyles']>, SpecResponsiveStyles | undefined>>, Expect['responsiveStyles']>, SpecResponsiveStyles | undefined>>, Expect['responsiveStyles']>, SpecResponsiveStyles | undefined>>, @@ -138,10 +143,11 @@ function armsByLiteral(): Map { * objectui#11276 moved its authored arm into the public-block set, and * `object-grid` the second until that card's `object-grid` batch moved its * authored arm the same way; each flat mirror keeps the member (the post-hoist - * rows below). + * rows below). `flex`'s authored arm is `FlexBlockSchema` since that card's + * `flex` batch: its props in the bag, still outside the public-block set. */ const FLAT_ARMS = [ - ['flex', FlexSchema], + ['flex', FlexBlockSchema], ] as const; /** Arms with no producer: the strict face still refuses the key on them. */ @@ -158,18 +164,24 @@ const DECLARES_DATA_SOURCE: ReadonlySet = new Set(); /** The five envelope keys the spec declares and this batch leaves undeclared. */ const UNDECLARED_ENVELOPE = ['events', 'dataSource', 'aria', 'visibility', 'responsive'] as const; -/** A valid flat node per arm, shaped like the producer's nodes, `responsiveStyles` included. */ +/** + * A valid node per arm, shaped like the producer's nodes, `responsiveStyles` + * included: the key on the node, the props in the `properties` bag + * (objectui#11276). + */ const VALID_NODES = { flex: { type: 'flex', id: 'cc_panel', - direction: 'col', responsiveStyles: { large: { display: 'block', minWidth: '0', padding: '15px 17px 17px', borderRadius: '16px' }, small: { padding: '12px', minHeight: '200px' }, }, - // A nested node carrying the key: judged at depth, through the node recursion. - children: [{ type: 'flex', id: 'cc_spacer', responsiveStyles: { large: { flex: '1 1 auto' } } }], + properties: { + direction: 'col', + // A nested node carrying the key: judged at depth, through the page walk. + children: [{ type: 'flex', id: 'cc_spacer', responsiveStyles: { large: { flex: '1 1 auto' } } }], + }, }, } as const; @@ -228,6 +240,13 @@ describe('`responsiveStyles` is declared on the batch-9 arms, from the public bl expect(arm.shape.responsiveStyles).toBe(shared); }); + it('the flat `FlexSchema` mirror keeps the SAME member as the post-hoist reading (objectui#11276)', () => { + // Its authored arm is `FlexBlockSchema` since the `flex` batch; the mirror + // is the node as the `flex` renderer reads it, and it still declares the key. + expect(FlexSchema.shape.responsiveStyles).toBe(PageSectionBlockSchema.shape.responsiveStyles); + expect(armsByLiteral().get('flex')).not.toBe(FlexSchema); + }); + it('the flat `ObjectChartSchema` mirror keeps the SAME member as the post-hoist reading (objectui#11276)', () => { // Its authored arm left the node union for the public-block set; the // mirror is the node as `ObjectChart` reads it, and it still declares the key. @@ -314,10 +333,10 @@ describe('the other five envelope keys stay undeclared on the batch-9 arms (obje ); it.each(cases)('%s: a node-level `%s` is still refused on the strict face, by name', (type, key) => { - // The valid node without the key under test and without a nested child, so - // the only refusal left is the one this row asks about. + // The valid node without the key under test and without its bag (where its + // nested child sits), so the only refusal left is the one this row asks about. const { responsiveStyles: _styles, ...rest } = VALID_NODES[type] as Record; - const { children: _children, ...bare } = rest; + const { properties: _bag, ...bare } = rest; const node = { ...bare, [key]: { x: 1 } }; expect(refusedKeys(StrictAnyComponentSchema.safeParse(node))).toEqual([key]); }); diff --git a/packages/types/src/__tests__/flex-properties-bag-11276.test.ts b/packages/types/src/__tests__/flex-properties-bag-11276.test.ts new file mode 100644 index 0000000000..8f373aabd0 --- /dev/null +++ b/packages/types/src/__tests__/flex-properties-bag-11276.test.ts @@ -0,0 +1,410 @@ +/** + * ObjectUI + * Copyright (c) 2024-present ObjectStack Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * `flex` takes its props in the `properties` bag, and the flat spelling retires + * from both authoring faces (objectui#11276, the `flex` batch, under the + * maintainer's ruling A on objectui#11300). + * + * ## The defect this pins + * + * `@objectstack/spec`'s strict `PageComponentSchema` refuses a prop written on + * a page component itself as mis-layered (ADR-0089 D3a), for every component + * type, and accepts the same props in `properties`. objectui's `flex` arm was + * the flat mirror of the TypeScript twin instead, so the two validators + * disagreed in both directions: + * + * - the objectstack showcase's layout boxes, `{ type: 'flex', + * responsiveStyles, properties: { children } }`, were refused by the strict + * face, which named `properties` as an unrecognized key; + * - the flat node `os validate` refuses parsed green on both faces. + * + * ## No spec row, so the bag is the mirror's own members + * + * `ComponentPropsMap` carries no `flex` row, so the spec's page component takes + * ANY bag for this type, and no row is invented here. The bag's `direction`, + * `justify`, `align`, `gap` and `wrap` are the flat `FlexSchema` mirror's own + * members, BY REFERENCE. Its `children` is the mirror's single-node-or-list + * slot with the LIST's entries left to the spec page walk's judgment, which + * `AnyComponentSchema` already runs at every `properties.children` (objectui#11223): + * the mirror's own member would judge every nested component a second time. + * + * ## What did NOT move + * + * The TypeScript `FlexSchema` (`../layout.ts`) and its zod mirror stay + * published: they are the node as the `flex` renderer reads it AFTER + * `SchemaRenderer` hoists `properties`, and as code composes it. The mirror is + * no longer an arm of `AnyComponentSchema`; `FlexBlockSchema` is. `stack` and + * every other node-level arm keep their flat props: the ruling is for `flex` + * only. That a stored flat `flex` node still DRAWS is pinned where the renderer + * lives, `packages/components/src/__tests__/flex-properties-bag-render-11276.test.tsx`. + * + * ## Two instruments, and which half each one reads + * + * The `Expect` / `Equal` block below is TYPE-level: `tsc -p tsconfig.test.json` + * (the third leg of this package's `type-check` script) reads it, and vitest — + * which strips types — does not. The `describe` blocks are RUNTIME. + */ + +import { describe, expect, it } from 'vitest'; +import type { z } from 'zod'; +import { ComponentPropsMap, PageComponentSchema } from '@objectstack/spec/ui'; + +import { + AnyComponentSchema, + BaseSchema, + FlexBlockSchema, + FlexSchema, + LayoutSchema, + StrictAnyComponentSchema, + safeValidateSchema, +} from '../zod/index.zod.js'; +import type { FlexLayoutProps } from '../layout'; + +/* ── Type-level pins (compiled by `tsc -p tsconfig.test.json`) ─────────────── */ + +type Equal = + (() => T extends A ? 1 : 2) extends (() => T extends B ? 1 : 2) ? true : false; +type Expect = T; +/** The bag object (its `.passthrough()` index signature is read off the shape, not the input). */ +type BagShape = ReturnType<(typeof FlexBlockSchema)['shape']['properties']['unwrap']>['shape']; + +/** The bag's members are exactly the TypeScript `FlexLayoutProps` members. */ +export type assertionBagKeysAreFlexLayoutProps = Expect>; +/** Each by-reference member accepts exactly what `FlexLayoutProps` declares. */ +export type assertionBagValueTypes = [ + Expect, FlexLayoutProps['direction']>>, + Expect, FlexLayoutProps['justify']>>, + Expect, FlexLayoutProps['align']>>, + Expect, FlexLayoutProps['gap']>>, + Expect, FlexLayoutProps['wrap']>>, +]; +/** A flex prop written flat on the authored node takes no value at all. */ +export type assertionFlatKeysTakeNothing = [ + Expect['direction'], undefined>>, + Expect['children'], undefined>>, +]; + +/* ── Runtime ──────────────────────────────────────────────────────────────── */ + +type Result = { success: boolean; error?: { issues: z.core.$ZodIssue[] } }; +type Judge = (document: unknown) => Result; + +const FACES: ReadonlyArray = [ + ['tolerant', (document) => safeValidateSchema(document)], + ['strict', (document) => StrictAnyComponentSchema.safeParse(document)], +]; + +const issuesOf = (result: Result) => + result.success + ? [] + : result.error!.issues.map((issue) => ({ code: issue.code, path: issue.path.join('.'), message: issue.message })); + +/** The bag's members, read off the arm on every run. */ +const bagShape = (): Record => + ((FlexBlockSchema.shape.properties as z.ZodOptional).unwrap() as z.ZodObject).shape as Record; + +/** The six members this batch moved into the bag, named so a reader sees them. */ +const BAG_KEYS = ['align', 'children', 'direction', 'gap', 'justify', 'wrap']; + +/** + * Nodes in the shapes the objectstack showcase writes (`examples/app-showcase`, + * `command-center.page.ts` and `styling-gallery.page.ts`, which build them with + * helpers): the node-level `id` and `responsiveStyles`, every prop — the child + * list included — in `properties`. The structure is the producer's; the + * strings are not. + */ +const SHOWCASE_SHAPES: ReadonlyArray = [ + [ + 'a leaf box with an empty child list (the rule / bar / spacer nodes)', + { id: 'cc_rule', type: 'flex', responsiveStyles: { large: { width: 'min(640px, 60%)', height: '1px' } }, properties: { children: [] } }, + ], + [ + 'a panel heading: a box of boxes and text blocks', + { + id: 'cc_status_h', + type: 'flex', + responsiveStyles: { large: { display: 'flex', alignItems: 'center', gap: '9px' } }, + properties: { + children: [ + { id: 'cc_status_bar', type: 'flex', responsiveStyles: { large: { width: '4px', height: '15px' } }, properties: { children: [] } }, + { id: 'cc_status_t', type: 'element:text', responsiveStyles: { large: { fontSize: '14px' } }, properties: { content: 'Task status' } }, + ], + }, + }, + ], + [ + 'a band laid out as a grid, holding a chart panel', + { + id: 'cc_rowA', + type: 'flex', + responsiveStyles: { + large: { display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0,1fr))', gap: '16px' }, + small: { gridTemplateColumns: '1fr' }, + }, + properties: { + children: [ + { + id: 'cc_health', + type: 'flex', + responsiveStyles: { large: { display: 'block', minWidth: '0' }, small: { padding: '12px' } }, + properties: { + children: [ + { + id: 'cc_health_c', + type: 'object-chart', + responsiveStyles: { large: { width: '100%' } }, + properties: { dataset: 'project_metrics', dimensions: ['health'], values: ['project_count'], chartType: 'donut' }, + }, + ], + }, + }, + ], + }, + }, + ], +]; + +/** + * objectstack's UI skill (`skills/objectstack-ui/rules/pages.md`, the "styled + * pricing card" under Styling) teaches this node; verbatim but for its comments. + */ +const TAUGHT_NODE = { + id: 'plan_solo', type: 'flex', + responsiveStyles: { + large: { + display: 'flex', flexDirection: 'column', gap: 'var(--space-4)', + padding: 'var(--space-6)', borderRadius: 'var(--radius-xl)', + backgroundColor: 'var(--surface)', border: '1px solid hsl(var(--primary))', + boxShadow: '0 0 0 3px hsl(var(--primary) / 0.25), var(--shadow-lg)', + }, + small: { padding: 'var(--space-4)', gap: 'var(--space-3)' }, + }, + properties: { + children: [ + { id: 'plan_solo_price', type: 'element:text', + responsiveStyles: { large: { fontSize: '40px', fontWeight: '700', color: 'var(--text-strong)' }, small: { fontSize: '32px' } }, + properties: { content: '$29' } }, + { id: 'cta_solo', type: 'element:button', + responsiveStyles: { large: { marginTop: 'auto', width: '100%' } }, + properties: { label: 'Upgrade', variant: 'primary', size: 'large' } }, + ], + }, +}; + +/** objectui's own authored spelling, every bag member in play. */ +const OWN_NODE = { + type: 'flex', + id: 'toolbar', + className: 'border-b pb-3', + properties: { + direction: 'row', + justify: 'between', + align: 'center', + gap: 4, + wrap: true, + children: [{ type: 'text', content: 'Orders' }, { type: 'button', label: 'New order' }], + }, +}; + +describe('flex validates in the `properties` bag (objectui#11276)', () => { + const nodes: ReadonlyArray = [ + ...SHOWCASE_SHAPES, + ['objectstack\'s UI skill\'s taught node', TAUGHT_NODE], + ['objectui\'s own spelling, every bag member in play', OWN_NODE], + ]; + + it.each(FACES.flatMap(([face, judge]) => nodes.map(([name, node]) => [face, name, judge, node] as const)))( + '%s face: %s', + (_face, _name, judge, node) => { + expect(issuesOf(judge(node))).toEqual([]); + }, + ); + + it('the fixtures are spec-valid by the spec\'s own page component (lit control)', () => { + for (const [, node] of nodes) expect(PageComponentSchema.safeParse(node).success).toBe(true); + }); + + it('the bag survives the parse unchanged on both faces', () => { + for (const [, judge] of FACES) { + const result = judge(OWN_NODE) as Result & { data?: { properties?: unknown } }; + expect(result.data?.properties).toEqual(OWN_NODE.properties); + } + }); + + it('reaches the arm at a node-level child slot and inside another flex\'s bag', () => { + const inSlot = { type: 'container', children: [{ type: 'flex', direction: 'col' }] }; + const inBag = { type: 'flex', properties: { children: [{ type: 'flex', direction: 'col' }] } }; + for (const [, judge] of FACES) { + expect(judge(inSlot).success).toBe(false); + expect(issuesOf(judge(inBag)).map((issue) => issue.path)).toEqual(['properties.children.0.direction']); + } + }); +}); + +describe('the spec has no `flex` row, and none is invented (objectui#11276)', () => { + it('`ComponentPropsMap` carries no `flex` row (lit control, read off the installed spec)', () => { + expect(Object.keys(ComponentPropsMap)).not.toContain('flex'); + // …while it does carry rows, so the absence is a reading. + expect(Object.keys(ComponentPropsMap)).toContain('object-grid'); + }); + + it('so the spec\'s page component takes any bag on this type', () => { + expect(PageComponentSchema.safeParse({ type: 'flex', properties: { anything: [1, 2] } }).success).toBe(true); + }); + + it('the bag\'s description names the mirror and says there is no row', () => { + const description = FlexBlockSchema.shape.properties.description ?? ''; + expect(description).toContain('`FlexSchema`'); + expect(description).toContain('has no `ComponentPropsMap[\'flex\']` row'); + }); +}); + +describe('the bag is the flat mirror\'s own members (objectui#11276)', () => { + it('its key set is the mirror\'s own members: the shape less the node base and the envelope, `children` kept', () => { + const nodeLevel = new Set([...Object.keys(BaseSchema.shape), 'responsiveStyles']); + nodeLevel.delete('children'); + const expected = Object.keys(FlexSchema.shape).filter((key) => !nodeLevel.has(key)).sort(); + expect(Object.keys(bagShape()).sort()).toEqual(expected); + expect(expected).toEqual(BAG_KEYS); + }); + + it('each member but `children` IS the mirror\'s schema object, not a copy', () => { + for (const key of BAG_KEYS.filter((k) => k !== 'children')) { + expect(bagShape()[key], key).toBe((FlexSchema.shape as Record)[key]); + } + }); + + it('the bag keeps the mirror\'s values: a value the mirror refuses is refused in the bag, at its own path', () => { + for (const [, judge] of FACES) { + expect(issuesOf(judge({ type: 'flex', properties: { direction: 'column' } })).map((i) => [i.code, i.path])) + .toEqual([['invalid_value', 'properties.direction']]); + expect(issuesOf(judge({ type: 'flex', properties: { gap: '4' } })).map((i) => [i.code, i.path])) + .toEqual([['invalid_type', 'properties.gap']]); + } + }); + + it('a key `flex` does not declare, in the bag, stays unjudged by the tolerant face and is refused by the strict face', () => { + const node = { type: 'flex', properties: { padding: 4, children: [] } }; + expect(safeValidateSchema(node).success).toBe(true); + const strict = StrictAnyComponentSchema.safeParse(node); + expect(strict.success).toBe(false); + expect(strict.error?.issues.map((issue) => [issue.code, issue.path.join('.'), (issue as { keys?: string[] }).keys])) + .toEqual([['unrecognized_keys', 'properties', ['padding']]]); + }); +}); + +describe('a nested component in the bag\'s child list is judged once, at its real path (objectui#11276)', () => { + it('a refused child reports its own issue and nothing else, as on a `page:` container', () => { + const child = { type: 'flex', properties: { direction: 'column' } }; + for (const [, judge] of FACES) { + const flex = issuesOf(judge({ type: 'flex', properties: { children: [child] } })); + const card = issuesOf(judge({ type: 'page:card', properties: { children: [child] } })); + expect(flex).toEqual([ + { code: 'invalid_value', path: 'properties.children.0.properties.direction', message: expect.any(String) }, + ]); + // The page container is the control: the same child, the same single issue. + expect(flex).toEqual(card); + } + }); + + it('two levels down, still once', () => { + const doc = { type: 'flex', properties: { children: [{ type: 'flex', properties: { children: [{ type: 'nope' }] } }] } }; + for (const [, judge] of FACES) { + expect(issuesOf(judge(doc)).map((issue) => issue.path)).toEqual(['properties.children.0.properties.children.0.type']); + } + }); + + it('the strict face closes a nested component too', () => { + const doc = { type: 'flex', properties: { children: [{ type: 'text', content: 'a', bogus: 1 }] } }; + expect(safeValidateSchema(doc).success).toBe(true); + expect(StrictAnyComponentSchema.safeParse(doc).error?.issues.map((issue) => issue.path.join('.'))) + .toEqual(['properties.children.0']); + }); + + it('a single node, which the page walk does not descend, is judged by the node union', () => { + for (const [, judge] of FACES) { + expect(judge({ type: 'flex', properties: { children: { type: 'text', content: 'a' } } }).success).toBe(true); + expect(judge({ type: 'flex', properties: { children: { type: 'nope' } } }).success).toBe(false); + } + }); +}); + +describe('the flat spelling is refused by name, with the bag member as the remedy (objectui#11276)', () => { + it('the spec\'s own page component refuses the flat node (lit control: the reason holds for this type)', () => { + const result = PageComponentSchema.safeParse({ type: 'flex', direction: 'col', gap: 2, children: [] }); + expect(result.success).toBe(false); + const keys = result.success ? [] : result.error.issues.flatMap((issue) => (issue as { keys?: string[] }).keys ?? []); + expect(keys.sort()).toEqual(['children', 'direction', 'gap']); + }); + + it.each(FACES.flatMap(([face, judge]) => BAG_KEYS.map((key) => [face, key, judge] as const)))( + '%s face: a flat `%s` is refused at its own path, naming `properties.KEY` and the bag', + (_face, key, judge) => { + const value = key === 'children' ? [] : key === 'gap' ? 2 : key === 'wrap' ? true : 'start'; + const issues = issuesOf(judge({ type: 'flex', [key]: value })); + expect(issues.map((issue) => [issue.code, issue.path])).toEqual([['invalid_type', key]]); + expect(issues[0]!.message).toContain(`Did you mean \`${key}\` → \`properties.${key}\`?`); + expect(issues[0]!.message).toContain('A `flex` node takes its props in its `properties` bag'); + // The detail names no row: the spec has none for this type. + expect(issues[0]!.message).toContain('has no `ComponentPropsMap[\'flex\']` row'); + }, + ); + + it('the whole flat node the docs taught before this batch is refused key by key', () => { + const flat = { type: 'flex', justify: 'end', gap: 2, children: [{ type: 'button', label: 'Add' }] }; + for (const [, judge] of FACES) { + expect(issuesOf(judge(flat)).map((issue) => issue.path).sort()).toEqual(['children', 'gap', 'justify']); + } + }); + + it('`body` is refused toward `properties.children`, not toward a node-level `children`', () => { + for (const [, judge] of FACES) { + const issues = issuesOf(judge({ type: 'flex', body: [] })); + expect(issues.map((issue) => [issue.code, issue.path])).toEqual([['invalid_type', 'body']]); + expect(issues[0]!.message).toContain('Did you mean `body` → `properties.children`?'); + } + }); + + it('`BaseSchema` keys and the node envelope stay on the node (control)', () => { + const node = { + type: 'flex', + id: 'row', + className: 'p-4', + visible: true, + responsiveStyles: { large: { padding: '8px' } }, + properties: { children: [] }, + }; + for (const [, judge] of FACES) expect(issuesOf(judge(node))).toEqual([]); + }); +}); + +describe('the arm moved; the post-hoist mirror stayed (objectui#11276)', () => { + const armFor = (type: string): unknown => { + const options = (LayoutSchema as unknown as { options: z.ZodObject[] }).options; + return options.find((arm) => (arm.shape.type as z.ZodLiteral).value === type); + }; + + it('`flex` is armed by the bag arm, not by the flat mirror', () => { + expect(armFor('flex')).toBe(FlexBlockSchema); + expect(armFor('flex')).not.toBe(FlexSchema); + // Non-vacuity: the node union is the one `safeValidateSchema` runs. + expect(AnyComponentSchema.safeParse(OWN_NODE).success).toBe(true); + }); + + it('the flat mirror is still published and still judges the post-hoist node', () => { + const hoisted = { type: 'flex', ...OWN_NODE.properties, id: OWN_NODE.id, className: OWN_NODE.className }; + expect(FlexSchema.safeParse(hoisted).success).toBe(true); + expect(FlexSchema.safeParse({ type: 'flex', direction: 'column' }).success).toBe(false); + }); + + it('`stack` keeps its flat props on both faces: the ruling is for `flex` only (control)', () => { + const stack = { type: 'stack', direction: 'col', gap: 4, children: [{ type: 'text', content: 'a' }] }; + for (const [, judge] of FACES) expect(issuesOf(judge(stack))).toEqual([]); + }); +}); diff --git a/packages/types/src/__tests__/flex-props-envelope-lift-6751.test.ts b/packages/types/src/__tests__/flex-props-envelope-lift-6751.test.ts index a4d6a06f88..f0812395e1 100644 --- a/packages/types/src/__tests__/flex-props-envelope-lift-6751.test.ts +++ b/packages/types/src/__tests__/flex-props-envelope-lift-6751.test.ts @@ -8,7 +8,7 @@ /** * objectui#6751 — the `flex` node in `data-display-examples.json` declares its - * layout keys at NODE level, not inside a `props` envelope. + * layout keys where they reach the renderer, not inside a `props` envelope. * * ## What was wrong * @@ -20,20 +20,29 @@ * React props instead, so a renderer declared `({ schema })` — the ordinary * component-renderer shape, which `flex.tsx` has — never sees the envelope. * The example therefore rendered with the DEFAULT `row` direction and the - * DEFAULT gap while presenting itself as a column with `gap: 4`. The fix is to - * lift the two keys onto the node, which is where `FlexSchema` declares them; - * it is NOT to rename `props` to `properties`. + * DEFAULT gap while presenting itself as a column with `gap: 4`. objectui#6751 + * lifted the two keys onto the node. + * + * ## Where they live now (objectui#11276) + * + * The maintainer's ruling A on objectui#11300 made the `properties` bag the + * contract on `flex`, as `@objectstack/spec`'s page component declares it, and + * revoked objectui#6751's fence ("`flex` declares its own keys", "it is NOT to + * rename `props` to `properties`") for `flex` only. So the keys sit in the + * `properties` bag — which `SchemaRenderer` DOES hoist, unlike `props` — and + * the authored arm (`FlexBlockSchema`) refuses them flat on the node by name. + * The `props` envelope stays wrong, for the reason above. * * ## Why the assertions here are structural rather than acceptance-shaped * - * `BaseSchema` is `.passthrough()`, so the broken document parsed GREEN through + * `BaseSchema` is `.passthrough()`, so a `props` envelope parses GREEN through * every schema in this package and would keep doing so. Acceptance cannot tell - * "lifted" from "still under `props`, admitted unexamined". What separates the - * two is the parsed VALUE: + * "in the bag" from "still under `props`, admitted unexamined". What separates + * the two is the parsed VALUE: * - * fixture state FlexSchema.parse(node).direction / .gap / 'props' in node - * props envelope 'row' (default) / 2 (default) / true - * lifted onto the node 'col' / 4 / false + * fixture state FlexBlockSchema.parse(node).properties?.direction / ?.gap / 'props' in node + * props envelope undefined / undefined / true + * in the properties bag 'col' / 4 / false * * ## The fence this file must not cross (objectui#6751 triage, twice) * @@ -42,12 +51,13 @@ * `skills/objectui/guides/schema-expressions.md`'s `card` and `text`, each * marked wrong where it stands. The walk below is scoped to this one fixture * for that reason: a repo-wide "no node carries `props`" assertion would make - * the teaching material fail. + * the teaching material fail. (That half of the fence stands: objectui#11276 + * revoked only its "`flex` declares its own keys" half, and only for `flex`.) */ import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { describe, expect, it } from 'vitest'; -import { FlexSchema, LayoutSchema } from '../zod/layout.zod'; +import { FlexBlockSchema, LayoutSchema } from '../zod/layout.zod'; const ROOT = resolve(__dirname, '../../../..'); const FIXTURE = 'packages/types/examples/data-display-examples.json'; @@ -90,16 +100,20 @@ function envelopeSites(doc: unknown, path = '$'): string[] { return hits; } -describe('objectui#6751 — flex layout keys sit on the node, not under `props`', () => { - it('compositeExample parses through FlexSchema with direction/gap as authored', () => { +describe('objectui#6751 — flex layout keys sit in the `properties` bag, not under `props` (objectui#11276)', () => { + it('compositeExample parses through FlexBlockSchema with direction/gap in its bag', () => { const node = objectAt(readFixture(), 'compositeExample'); expect(node.type).toBe('flex'); - const parsed = FlexSchema.parse(node); - // Under the envelope these two read the SCHEMA DEFAULTS ('row' / 2), which - // is precisely the bug: the document says one thing and renders another. - expect(parsed.direction).toBe('col'); - expect(parsed.gap).toBe(4); + const parsed = FlexBlockSchema.parse(node); + // Under the envelope these two read UNDEFINED, and the renderer applies its + // defaults ('row' / 2), which is precisely the bug: the document says one + // thing and renders another. + expect(parsed.properties?.direction).toBe('col'); + expect(parsed.properties?.gap).toBe(4); + // Not on the node: the authored arm refuses them there by name. + expect(Object.prototype.hasOwnProperty.call(node, 'direction')).toBe(false); + expect(Object.prototype.hasOwnProperty.call(node, 'gap')).toBe(false); // `.passthrough()` carries unknown keys through, so a surviving `props` on // the PARSED node is the direct reading that the envelope is still there. expect(Object.prototype.hasOwnProperty.call(parsed, 'props')).toBe(false); @@ -108,8 +122,7 @@ describe('objectui#6751 — flex layout keys sit on the node, not under `props`' it('compositeExample parses the same way through the published LayoutSchema union', () => { const parsed = LayoutSchema.parse(objectAt(readFixture(), 'compositeExample')) as JsonObject; expect(parsed.type).toBe('flex'); - expect(parsed.direction).toBe('col'); - expect(parsed.gap).toBe(4); + expect(parsed.properties).toMatchObject({ direction: 'col', gap: 4 }); expect(Object.prototype.hasOwnProperty.call(parsed, 'props')).toBe(false); }); @@ -132,10 +145,11 @@ describe('objectui#6751 — flex layout keys sit on the node, not under `props`' expect(envelopeSites({ type: 'element:div', props: { className: 'p-4' } })).toEqual([]); }); - it('negative control — the lift left the rest of the node untouched', () => { + it('negative control — the move left the rest of the node untouched', () => { const node = objectAt(readFixture(), 'compositeExample'); expect(node.id).toBe('user-profile-card'); - const children = node.children as JsonObject[]; + // The child list moved into the bag with the other `flex` props (objectui#11276). + const children = objectAt(node, 'properties').children as JsonObject[]; expect(children.map((c) => c.type)).toEqual(['avatar', 'statistic', 'badge', 'list']); expect(children[0]).toMatchObject({ type: 'avatar', alt: 'User Avatar', fallback: 'JD', size: 'lg', diff --git a/packages/types/src/__tests__/page-actions-refusal-7926.test.ts b/packages/types/src/__tests__/page-actions-refusal-7926.test.ts index ae2bd49f01..694ecdfe70 100644 --- a/packages/types/src/__tests__/page-actions-refusal-7926.test.ts +++ b/packages/types/src/__tests__/page-actions-refusal-7926.test.ts @@ -110,18 +110,23 @@ describe('objectui#7926 — the `page` node refuses `actions` (contract half)', }); it('the remedy the message names actually parses — buttons as nodes in `children`', () => { + // The guide's remedy, as `content/docs/guide/layout.md` teaches it: the + // `flex` row takes its props, its child list included, in its `properties` + // bag (objectui#11276). const r = PageNodeSchema.safeParse({ type: 'page', title: 'Products', children: [ { type: 'flex', - justify: 'end', - gap: 2, - children: [ - { type: 'button', label: 'Add Product', variant: 'default', icon: 'plus' }, - { type: 'button', label: 'Export', variant: 'outline', icon: 'download' }, - ], + properties: { + justify: 'end', + gap: 2, + children: [ + { type: 'button', label: 'Add Product', variant: 'default', icon: 'plus' }, + { type: 'button', label: 'Export', variant: 'outline', icon: 'download' }, + ], + }, }, ], }); diff --git a/packages/types/src/__tests__/zod-mirror-parity.test.ts b/packages/types/src/__tests__/zod-mirror-parity.test.ts index dc30a2ff50..24177141e8 100644 --- a/packages/types/src/__tests__/zod-mirror-parity.test.ts +++ b/packages/types/src/__tests__/zod-mirror-parity.test.ts @@ -4613,6 +4613,13 @@ const EXCLUSIONS: Readonly> = { "spec-owned BY REFERENCE — the local `.extend(…)` adds renderer props that no TS declaration in this package restates", 'layout.zod.ts#LayoutSchema': "a union OVER the mirrors, not an object of its own — its members are checked individually above", + // objectui#11276 (the `flex` batch, the maintainer's ruling A on + // objectui#11300) — the authored `flex` arm, built as the `object-chart` arm + // with NO spec row: its bag is the flat mirror's own members. Its TS twin is + // paired with that FLAT mirror (`FlexSchema`), which stays the renderer's + // post-hoist reading. + 'layout.zod.ts#FlexBlockSchema': + "mirror-owned BY REFERENCE — `BaseSchema` + the `flex` literal + `NODE_ENVELOPE` + `properties`, whose `direction` / `justify` / `align` / `gap` / `wrap` ARE the flat `layout.zod.ts#FlexSchema` mirror's own members (the same schema objects; `@objectstack/spec` has no `ComponentPropsMap['flex']` row) and whose `children` is the mirror's single-node-or-list slot with the list's entries left to the spec page walk's judgment, + `flatPropRefusals` over the bag (one `aliasKeyRefusal` per bag member, pointed at `properties.KEY`; the key set is read off the bag), + a `body` refusal pointed at `properties.children`; the TS declaration of the node, `FlexSchema` (`../layout.ts`), is the renderer's post-hoist reading and is paired with the flat mirror, not with this arm", 'navigation.zod.ts#NavLinkSchema': "recursive; a `z.lazy` exposes no `.shape` to read, so there is no key set for the per-key comparison. Since objectui#7760 it carries its TS declaration as BOTH type arguments, so the pair IS compared — as a whole type, by `tsc`, at the annotation itself", 'navigation.zod.ts#NavigationMenuItemSchema': diff --git a/packages/types/src/layout.ts b/packages/types/src/layout.ts index 1f97e51e96..89c0e35230 100644 --- a/packages/types/src/layout.ts +++ b/packages/types/src/layout.ts @@ -699,7 +699,18 @@ export interface FlexLayoutProps { } /** - * Flexbox layout component + * Flexbox layout component — the node as the `flex` renderer reads it. + * + * ⚠️ That is the node AFTER `SchemaRenderer` hoists the `properties` bag, and + * the node as code composes it. An AUTHORED `flex` node takes the + * {@link FlexLayoutProps} members — the child list included — in its + * `properties` bag (objectui#11276, the maintainer's ruling A on + * objectui#11300): `{ type: 'flex', properties: { direction: 'col', gap: 4, + * children: [ … ] } }`. `objectui validate` refuses them written flat on the + * node, by name (the zod `FlexBlockSchema`), as `@objectstack/spec`'s page + * component does. A typed authored node checks its bag with + * `satisfies FlexLayoutProps`. A stored flat node keeps rendering: the hoist + * reads both spellings. */ export interface FlexSchema extends BaseSchema, FlexLayoutProps { type: 'flex'; diff --git a/packages/types/src/zod/README.md b/packages/types/src/zod/README.md index 4220c2f286..580a92a993 100644 --- a/packages/types/src/zod/README.md +++ b/packages/types/src/zod/README.md @@ -189,11 +189,13 @@ function validateComponent(config: unknown) { ### Layout Components (18) - `DivSchema`, `BoxSchema`, `SpanSchema`, `TextSchema` - `ImageSchema`, `IconSchema`, `SeparatorSchema` -- `ContainerSchema`, `FlexSchema`, `StackSchema` +- `ContainerSchema`, `FlexSchema`, `FlexBlockSchema`, `StackSchema` - `GridSchema`, `CardSchema`, `TabsSchema` - `ScrollAreaSchema`, `ResizableSchema` - `AspectRatioSchema`, `PageSchema` +`FlexBlockSchema` is the authored `flex` node (objectui#11276's `flex` batch, the maintainer's ruling A on objectui#11300), built the way `ObjectChartBlockSchema` is: `@objectstack/spec` has no `ComponentPropsMap['flex']` row, so the bag's `direction`, `justify`, `align`, `gap` and `wrap` are the flat `FlexSchema` mirror's own members, by reference, and no row is invented. The child list is in the bag too, as `properties.children`: one node or a list, the list's entries judged by the spec's page walk like a `page:` container's. Each bag member written flat on the node is refused by name, naming `properties.KEY`, and `body` is refused toward `properties.children`. `FlexSchema` stays exported as the node as the `flex` renderer reads it after the hoist, and as code composes it; it is no longer an arm of `AnyComponentSchema`. `stack` and the other layout arms keep their props on the node. + ### Form Components (17) - `ButtonSchema`, `InputSchema`, `TextareaSchema` - `SelectSchema`, `CheckboxSchema`, `RadioGroupSchema` @@ -247,7 +249,7 @@ The ADR-0080 public blocks `object-metric`, `object-master-detail-form`, `object Each arm's `properties` is the block's `@objectstack/spec` `ComponentPropsMap` row, by reference. `ElementNumberBlockSchema` also declares the spec's `dataSource` binding, and mirrors the spec gate's one waiver: its bag may omit `object` when `dataSource.object` names the object. A member of an arm's row written flat on the node instead of in `properties` is refused by name on both faces, at its own path, with a message naming `properties.KEY`, as `@objectstack/spec`'s `PageComponentSchema` refuses it as mis-layered (objectui#10872 batch 10; one shared helper, `flatPropRefusals`, spread into every arm whose row declares a member). That includes the row members `BaseSchema` also declares (`visible`, `disabled`, `name`, `description`), which the base used to accept flat. Two kinds stay as they were: a member the spec's page component declares on the node itself (`label`, `aria`), and one the row retires, which keeps the row's own retirement message when written flat. An arm's own refusal of a row key (`record:alert`'s `body`, the `action:` controls' `onSuccess`, the content-channel refusals) keeps its message. Every arm here, and each of the seven ObjectQL public blocks above, declares the node-level `responsiveStyles` (ADR-0065 per-breakpoint style maps, which `SchemaRenderer` compiles to scoped CSS) as the spec's `ResponsiveStylesSchema`, by reference, from one shared fragment, `NODE_ENVELOPE` (objectui#10872 batch 8; named `PUBLIC_BLOCK_ENVELOPE` until batch 9). The other node-level keys the spec's `PageComponentSchema` declares (`events`, `aria`, `visibility`, `responsive`, and `dataSource` outside the arms that read it) stay undeclared, so the strict face refuses them by name. -Outside this set, `FlexSchema`, `ObjectGridSchema` and `ObjectChartSchema` spread the same fragment (objectui#10872 batch 9; `ObjectChartSchema` and `ObjectGridSchema` are post-hoist mirrors since objectui#11276, the authored arms being `ObjectChartBlockSchema` and `ObjectGridBlockSchema` above), and their TypeScript twins declare the key as the spec's `ResponsiveStyles`: they are the arms outside this set that objectui#10872's producer reading found `responsiveStyles` written on (a dated reading, not re-derived here). `BaseSchema` and every other arm leave it undeclared, so the strict face refuses it there by name. +Outside this set, `FlexSchema`, `ObjectGridSchema` and `ObjectChartSchema` spread the same fragment (objectui#10872 batch 9; all three are post-hoist mirrors since objectui#11276, the authored arms being `FlexBlockSchema`, `ObjectChartBlockSchema` and `ObjectGridBlockSchema` above, and `FlexBlockSchema` spreads it too), and their TypeScript twins declare the key as the spec's `ResponsiveStyles`: they are the arms outside this set that objectui#10872's producer reading found `responsiveStyles` written on (a dated reading, not re-derived here). `BaseSchema` and every other arm leave it undeclared, so the strict face refuses it there by name. Every arm except the four `page:` containers (`page:card`, `page:section`, `page:footer`, `page:sidebar`) refuses `children` and `body` by name, because its renderer reads neither content channel (objectui#9256; the last six below since objectui#10872 batch 5). The four containers do render a child list, and its home is `properties.children`, the member their spec row declares; they refuse the node-level `children` and `body` with a message naming it, as `@objectstack/spec`'s `PageComponentSchema` refuses a node-level `children` (objectui#10872 batch 6). Two carve-outs: `record:alert` refuses `children` with its own message, while its `body` is the message text and belongs in `properties`, so a flat `body` is refused with a pointer to `properties.body` rather than to `children` (objectui#10872); and `page:tabs` / `page:accordion` refuse the node's own `children` while each item's `children` in `items` stays live. - `PageHeaderBlockSchema`, `PageTabsBlockSchema`, `PageCardBlockSchema`, `PageAccordionBlockSchema` - `PageSectionBlockSchema`, `PageFooterBlockSchema`, `PageSidebarBlockSchema` diff --git a/packages/types/src/zod/index.zod.ts b/packages/types/src/zod/index.zod.ts index b2dac9d157..7c1710196a 100644 --- a/packages/types/src/zod/index.zod.ts +++ b/packages/types/src/zod/index.zod.ts @@ -99,7 +99,11 @@ export { IconSchema, SeparatorSchema, ContainerSchema, + // objectui#11276's `FlexBlockSchema` is the authored `flex` arm, its bag the + // flat mirror's own members (the spec has no row for it); `FlexSchema` stays + // the node as the `flex` renderer reads it. FlexSchema, + FlexBlockSchema, StackSchema, GridSchema, CardSchema, diff --git a/packages/types/src/zod/layout.zod.ts b/packages/types/src/zod/layout.zod.ts index 8e7757e7ef..1c3af5f2e6 100644 --- a/packages/types/src/zod/layout.zod.ts +++ b/packages/types/src/zod/layout.zod.ts @@ -27,9 +27,11 @@ import { import { BaseSchema, SchemaNodeSchema, specFieldsExcept } from './base.zod.js'; import { stripImportedDefaults } from './imported-defaults.js'; // objectui#10872 batch 9 — the node-level `responsiveStyles` fragment the public -// blocks declare, spread into `FlexSchema` below. `./public-blocks.zod.ts` -// imports nothing from this module, so this adds no cycle. -import { NODE_ENVELOPE } from './public-blocks.zod.js'; +// blocks declare, spread into `FlexSchema` below. objectui#11276 — the bag and +// flat-prop helpers the authored `flex` arm (`FlexBlockSchema`) is built with. +// `./public-blocks.zod.ts` imports nothing from this module, so this adds no +// cycle. +import { NODE_ENVELOPE, flatPropRefusals, propsBag } from './public-blocks.zod.js'; /** * ⭐ THE IMPORT BOUNDARY (objectui#8317, decision batch #90, 2026-09-08). @@ -326,6 +328,13 @@ export const ContainerSchema = BaseSchema.extend({ /** * Flex Schema - Flexbox layout component + * + * The node as the `flex` renderer reads it, after `SchemaRenderer` hoists the + * `properties` bag onto the node, and as code composes it. Paired with the + * TypeScript `FlexSchema` (`../layout.ts`). ⚠️ Not the authored arm: since + * objectui#11276 an authored `flex` node takes these props in its `properties` + * bag, and `AnyComponentSchema` judges it through {@link FlexBlockSchema} + * below, whose bag is this mirror's own members by reference. */ export const FlexSchema = BaseSchema.extend({ type: z.literal('flex'), @@ -358,6 +367,148 @@ export const FlexSchema = BaseSchema.extend({ ), }); +/* ── The authored `flex` node: its props in the `properties` bag ───────────── */ + +/** + * The node-level keys of `FlexSchema` above: everything the node base declares + * (`BaseSchema`, `type` and `body` among them) except `children`, plus the node + * envelope (`NODE_ENVELOPE`). Read off the declarations, not transcribed, so a + * key `BaseSchema` or the envelope gains stays at node level the day it lands. + * + * `children` is the one base key that is NOT node-level here: `flex` renders + * its child list, and its mirror declares the list as a member of its own, so + * the list is one of the props the bag holds. That is where the spec's page + * walk (`walkAddressedPageComponents`) and `SchemaRenderer`'s hoist read it + * (`properties.children`), as on the `page:` containers, whose rows declare it. + */ +const FLEX_NODE_LEVEL_KEYS = Object.fromEntries( + [...Object.keys(BaseSchema.shape).filter((key) => key !== 'children'), ...Object.keys(NODE_ENVELOPE)] + .map((key) => [key, true]), +) as { [K in Exclude | keyof typeof NODE_ENVELOPE]: true }; + +/** + * The child list in the `flex` bag (objectui#11276): the mirror's accept set — + * one node, or a list of them — judged ONCE. + * + * A list at `properties.children` is a position `@objectstack/spec`'s page + * walk descends, and `AnyComponentSchema` already judges every component the + * walk finds there, at its real path, on both faces (objectui#11223, + * `./nested-component-walk.ts`). So the list's entries are left to that + * judgment, as the `page:` containers' rows leave theirs (`z.array(z.unknown())`). + * Measured before this was written: with the mirror's own `children` member in + * the bag, a refused child was reported twice — an `invalid_union` at + * `properties.children` from the member, and the child's own issue from the + * walk — and every nesting level was judged twice over. A single node, which + * the walk does not descend, is judged by `SchemaNodeSchema`, the slot the + * mirror's member is built from. + */ +const FLEX_BAG_CHILDREN = z.union([z.array(z.unknown()), SchemaNodeSchema]) + .optional() + .describe( + 'Child components: a list, each entry judged as a component by the page walk (`properties.children`), ' + + 'or one node.', + ); + +/** + * The `flex` props bag (objectui#11276): the flat mirror's own members, BY + * REFERENCE — `direction`, `justify`, `align`, `gap` and `wrap`, each the SAME + * schema object `FlexSchema` holds — and the child list, `children`, in the + * spelling {@link FLEX_BAG_CHILDREN} gives it above. + * + * ⛔ `@objectstack/spec` has no `ComponentPropsMap['flex']` row, and none is + * invented here. The spec's `PageComponentSchema` types every `properties` bag + * as an open record and judges a bag only through a row, so for this type the + * spec accepts any bag at all. The bag's members are therefore objectui's own, + * the ones the TypeScript `FlexLayoutProps` (`../layout.ts`) declares. Nothing + * else is restated, so the bag and the post-hoist mirror cannot drift apart. + * + * The bag keeps the mirror's posture, `.passthrough()` (`BaseSchema`'s), so a + * key `flex` does not declare is judged in the bag exactly as it was judged on + * the flat node: unjudged by the tolerant face, refused by name by the strict + * authoring face, which closes every object it walks. + */ +const FlexPropsBag = FlexSchema.omit(FLEX_NODE_LEVEL_KEYS).extend({ children: FLEX_BAG_CHILDREN }); + +/** + * The ONE refusal detail every `flex` prop written flat on the node gets + * (objectui#11276). `aliasKeyRefusal` puts the key and its bag member in front + * of it: "Did you mean `gap` → `properties.gap`?". + */ +const FLEX_FLAT_PROP = + 'A `flex` node takes its props in its `properties` bag: write `{ "type": "flex", "properties": ' + + '{ "direction": "col", "gap": 4, "children": [ … ] } }` (objectui#11276). `@objectstack/spec`\'s own page ' + + 'component refuses a prop written on the node as mis-layered (ADR-0089 D3a), so this face and `os validate` ' + + 'agree. The spec has no `ComponentPropsMap[\'flex\']` row, so the bag\'s members are `FlexSchema`\'s own. ' + + 'Moving it changes nothing at render time: `SchemaRenderer` hoists every `properties` key onto the node ' + + 'before `flex` reads it.'; + +/** + * `flex` — the AUTHORED node: its props in the `properties` bag (objectui#11276, + * the `flex` batch, under the maintainer's ruling A on objectui#11300). + * + * ## Why the arm moved to the bag + * + * `@objectstack/spec`'s strict `PageComponentSchema` refuses a prop written on + * a page component itself as mis-layered (ADR-0089 D3a), for every component + * type: `properties` is the only home of a component's own props. This union + * used to arm the node with the flat `FlexSchema` above, so `objectui validate` + * refused the spec-shaped document — the objectstack showcase's layout boxes, + * `{ type: 'flex', responsiveStyles, properties: { children } }` — and accepted + * the flat one `os validate` refuses. The maintainer ruled A on objectui#11300: + * the bag is the contract on `flex` too, and objectui's own documents moved to + * it in the same change. objectui#6751's fence ("`flex` declares its own keys") + * is revoked for `flex` only; every other node-level arm is unchanged. + * + * It is the construct `ObjectChartBlockSchema` uses (`./objectql.zod.ts`) — + * `BaseSchema` + the `type` literal + `NODE_ENVELOPE` + `properties` through + * `propsBag` + one by-name refusal per bag member — because `flex`, like + * `object-chart`, has no spec row: the bag is `FlexPropsBag` above, the flat + * mirror's own members, and its description says so instead of naming a row. + * The refusals come from the shared `flatPropRefusals`, read off the bag, with + * a detail (`FLEX_FLAT_PROP`) that names no row. + * + * ## The flat spelling is refused by name + * + * Every member of the bag written FLAT on the node is refused on both faces, + * with a message naming its bag member — the child list included, which is + * `properties.children`. `body`, the child-list spelling objectui#6771 + * retired, is refused with a message naming `properties.children` too, + * replacing the mirror's refusal, whose remedy (a node-level `children`) this + * arm refuses. `BaseSchema`'s other keys stay on the node (`id`, `className`, + * `style`, `visible`, …), as on every arm, and so does the node envelope's + * `responsiveStyles`. A key `flex` does not declare is left as every arm leaves + * an undeclared key: unjudged by the tolerant face, refused by the strict one. + * + * ## What did not move + * + * The TypeScript `FlexSchema` and its zod mirror above stay published: they are + * the node as the `flex` renderer reads it after `SchemaRenderer` hoists the + * bag, and as code composes it. A stored flat `flex` node keeps rendering, + * because `SchemaRenderer` reads both spellings and the narrowing is on the + * authoring faces only; a node compiled from the `kind: 'html'` JSX tier + * (`@object-ui/sdui-parser`) never passes through this face. + */ +export const FlexBlockSchema = BaseSchema.extend({ + type: z.literal('flex'), + ...NODE_ENVELOPE, + ...flatPropRefusals('flex', FlexPropsBag, FLEX_FLAT_PROP), + properties: propsBag( + 'flex', + FlexPropsBag, + 'The `flex` props bag — the members `FlexSchema` declares beyond the node-level keys (`direction`, ' + + '`justify`, `align`, `gap`, `wrap` and the child list, `children`), by reference. `@objectstack/spec` has ' + + 'no `ComponentPropsMap[\'flex\']` row, so these are objectui\'s own members (objectui#11276).', + ), + body: aliasKeyRefusal( + 'body', + 'properties.children', + 'this `flex` node', + '`body` is the child-list spelling objectui#6771 retired, and a `flex` node takes its child list in its ' + + '`properties` bag: write `{ "type": "flex", "properties": { "children": [ … ] } }` (objectui#11276). ' + + 'objectui#8284.', + ), +}); + /** * Stack Schema - Vertical flex layout (shortcut) */ @@ -1003,7 +1154,10 @@ export const LayoutSchema = z.discriminatedUnion('type', [ IconSchema, SeparatorSchema, ContainerSchema, - FlexSchema, + // objectui#11276 — the AUTHORED `flex` node, its props in the `properties` + // bag. `FlexSchema` (the flat mirror) stays exported as the node the renderer + // reads after the hoist, and leaves this union. + FlexBlockSchema, StackSchema, GridSchema, CardSchema, diff --git a/packages/types/src/zod/public-blocks.zod.ts b/packages/types/src/zod/public-blocks.zod.ts index d0968d940a..272bc544ee 100644 --- a/packages/types/src/zod/public-blocks.zod.ts +++ b/packages/types/src/zod/public-blocks.zod.ts @@ -241,11 +241,11 @@ import { * restate it (objectui#10872). Internal to this package's zod modules — deliberately NOT * re-exported from `index.zod.ts`, like the helpers in `./tombstone.zod.ts`. * - * `description` replaces the provenance text for the one bag that is NOT a spec - * row: `object-chart` (objectui#11276) has no `ComponentPropsMap` row, so its - * bag is the flat mirror's own members, and its description says that rather - * than naming a row that does not exist. Every other caller omits it, and its - * description is unchanged. + * `description` replaces the provenance text for a bag that is NOT a spec row: + * `object-chart` and `flex` (objectui#11276) have no `ComponentPropsMap` row, + * so each bag is its flat mirror's own members, and its description says that + * rather than naming a row that does not exist. Every other caller omits it, + * and its description is unchanged. */ export function propsBag(type: string, row: T, description?: string) { return row @@ -413,17 +413,27 @@ type FlatPropRefusals = { * and a node composed in code (an action bar's menu, a dashboard's metric * tile, a form's master-detail node) never passes through this face. * - * The ONE copy: every public-block arm here, and `./objectql.zod.ts`'s + * The ONE copy: every public-block arm here, `./objectql.zod.ts`'s * `object-metric`, `object-master-detail-form`, `object-timeline` and - * `object-grid` (objectui#11276) arms, spread it. Internal to this package's zod modules, like `propsBag`: + * `object-grid` (objectui#11276) arms, and `./layout.zod.ts`'s `flex` arm + * (objectui#11276) spread it. Internal to this package's zod modules, like `propsBag`: * deliberately NOT re-exported from `index.zod.ts`. * - * @param type the registered `type`, spelled into every message - * @param row the spec row, already through the import boundary + * `guidance` replaces the refusal detail for a bag that is NOT a spec row, as + * `propsBag`'s `description` does: `flex` (objectui#11276) has no + * `ComponentPropsMap` row, so the default detail, which names the row, would + * point the author at a declaration that does not exist. Every other caller + * omits it, and its messages are unchanged. + * + * @param type the registered `type`, spelled into every message + * @param row the spec row, already through the import boundary (or, with + * `guidance`, the bag the arm declares in its place) + * @param guidance the refusal detail for a bag with no spec row */ export function flatPropRefusals>( type: string, row: { readonly shape: S }, + guidance?: string, ): FlatPropRefusals { return Object.fromEntries( Object.entries(row.shape) @@ -432,7 +442,7 @@ export function flatPropRefusals>( key, isRetiredRowMember(member) ? member - : aliasKeyRefusal(key, `properties.${key}`, `this \`${type}\` node`, flatPropGuidance(type, key)), + : aliasKeyRefusal(key, `properties.${key}`, `this \`${type}\` node`, guidance ?? flatPropGuidance(type, key)), ]), ) as FlatPropRefusals; } From bacbb4d79a9608f28fd4f7d317bd71af1bb75780 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 21:24:52 +0000 Subject: [PATCH 2/4] chore(changeset): the flex properties bag (types) and the flex() builder (core) Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude --- .../11276-core-flex-builder-properties-bag.md | 7 +++++ .changeset/11276-flex-properties-bag.md | 28 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 .changeset/11276-core-flex-builder-properties-bag.md create mode 100644 .changeset/11276-flex-properties-bag.md diff --git a/.changeset/11276-core-flex-builder-properties-bag.md b/.changeset/11276-core-flex-builder-properties-bag.md new file mode 100644 index 0000000000..6d8fce1cc5 --- /dev/null +++ b/.changeset/11276-core-flex-builder-properties-bag.md @@ -0,0 +1,7 @@ +--- +'@object-ui/core': minor +--- + +feat(core): the `flex()` builder emits its props in the `properties` bag (objectui#11276) + +**BREAKING (output shape):** `flex()` now builds `{ type: 'flex', properties: { direction, justify, align, gap, children } }` instead of writing those keys on the node. That is the spelling `@object-ui/types`' authoring faces accept for an authored `flex` node, which refuse the flat keys by name (objectui#11276, the maintainer's ruling A on objectui#11300); before this, `objectui validate` refused what the builder built. The base setters (`id`, `className`, `visible`, `disabled`, `testId`, `visibleOn`) still write on the node. Code that reads a built node's props reads them from `properties` (`flex().direction('col').build().properties.direction`); `build()` is typed `BaseSchema & { type: 'flex'; properties: FlexLayoutProps }`. Rendering is unchanged: `SchemaRenderer` hoists the bag onto the node before the `flex` renderer reads it. diff --git a/.changeset/11276-flex-properties-bag.md b/.changeset/11276-flex-properties-bag.md new file mode 100644 index 0000000000..a01c8211cd --- /dev/null +++ b/.changeset/11276-flex-properties-bag.md @@ -0,0 +1,28 @@ +--- +'@object-ui/types': minor +--- + +feat(types): an authored `flex` takes its props in the spec's `properties` bag; the flat spelling is refused by name (objectui#11276) + +**BREAKING (authoring):** a `flex` node that writes `direction`, `justify`, `align`, `gap`, `wrap` or its child list `children` on the node itself is now refused by `safeValidateSchema`, `validateSchema` and the strict authoring face, and so by `objectui validate`. Move each of them, the child list included, into the node's `properties` bag: + +```json +{ "type": "flex", "className": "p-4", "properties": { "direction": "col", "gap": 4, "children": [{ "type": "text", "content": "First" }] } } +``` + +`id`, `className`, `style`, `visible` and the other base props, and the node-level `responsiveStyles`, stay on the node. Nothing changes at render time: `SchemaRenderer` hoists every `properties` key onto the node before the `flex` renderer runs, so the bag node draws what the flat one drew, and a stored flat node, or a node built in code, keeps rendering with its flat keys. In TypeScript, `FlexLayoutProps` is the bag's type: check an authored bag with `satisfies FlexLayoutProps`. + +**Clause-②: yes** — the accept set narrows (a flat `flex` node that parsed is refused) and widens (a spec-shaped bag node that the strict face refused parses). Released as `minor` under the objectui narrowing rule, with this banner. + +**Why.** `@objectstack/spec`'s strict `PageComponentSchema` refuses a prop written on a page component itself as mis-layered (ADR-0089 D3a), for every component type, and the maintainer ruled A on objectui#11300: the `properties` bag is the contract on `flex` too. The arm here was the flat mirror of the TypeScript `FlexSchema`, so the two validators disagreed both ways: the strict face refused the objectstack showcase's layout boxes, written in the bag, and both faces accepted the flat node `os validate` refuses. objectui#6751's guidance that `flex` declares its keys on the node is withdrawn for `flex` only; `stack` and every other layout arm keep their props on the node. + +**What changed, in observable terms.** + +- `@object-ui/types/zod` exports `FlexBlockSchema`, built the way `ObjectChartBlockSchema` is: `BaseSchema`, the `flex` literal, the node-level `responsiveStyles`, and `properties`. `@objectstack/spec` has no `ComponentPropsMap['flex']` row, so the bag's `direction`, `justify`, `align`, `gap` and `wrap` are the flat `FlexSchema` mirror's own members: their values are judged exactly as before, now in the bag. A key `flex` does not declare stays unjudged in the bag by the tolerant face and is refused there by the strict face, as it was on the node. +- The child list is `properties.children`: one node or a list. Each component in the list is judged once, at its own path (`properties.children.0…`), by the same page walk that judges a `page:` container's child list. +- Each of the six props written flat on the node is refused on both faces, with a message that names its bag member (`Did you mean \`gap\` → \`properties.gap\`?`). `body` is refused toward `properties.children`. +- `FlexBlockSchema` is the `flex` arm of `AnyComponentSchema`. `FlexSchema` leaves the union. + +**The same release's changesets** that describe what the validators say about a `flex` written flat — `.changeset/10872-flat-arm-responsive-styles.md` (its example node writes `direction` flat), `.changeset/6751-flex-props-envelope-lift.md` (the keys lifted onto the node), `.changeset/8284-content-channel-per-component.md` (`body` refused toward `children`) and the remedy example in `.changeset/7926-page-node-refuses-actions.md` — describe the flat mirror, which still behaves as they say. On an authored node the props now live in the bag: `responsiveStyles` stays on the node as `.changeset/10872-flat-arm-responsive-styles.md` says, and its value checks hold there; the other props, the child list included, move into `properties`. + +**What did not move.** The TypeScript `FlexSchema` and its zod mirror `FlexSchema` stay published and unchanged in shape. They are the node as the `flex` renderer reads it after the hoist, and as code composes it. From 5af4d2e7b118fe7584e676d00b480b693779b58f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 21:55:46 +0000 Subject: [PATCH 3/4] chore(changeset): the core builder note states the order of the two changes Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 Co-authored-by: Claude --- .changeset/11276-core-flex-builder-properties-bag.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/11276-core-flex-builder-properties-bag.md b/.changeset/11276-core-flex-builder-properties-bag.md index 6d8fce1cc5..e664b964fb 100644 --- a/.changeset/11276-core-flex-builder-properties-bag.md +++ b/.changeset/11276-core-flex-builder-properties-bag.md @@ -4,4 +4,4 @@ feat(core): the `flex()` builder emits its props in the `properties` bag (objectui#11276) -**BREAKING (output shape):** `flex()` now builds `{ type: 'flex', properties: { direction, justify, align, gap, children } }` instead of writing those keys on the node. That is the spelling `@object-ui/types`' authoring faces accept for an authored `flex` node, which refuse the flat keys by name (objectui#11276, the maintainer's ruling A on objectui#11300); before this, `objectui validate` refused what the builder built. The base setters (`id`, `className`, `visible`, `disabled`, `testId`, `visibleOn`) still write on the node. Code that reads a built node's props reads them from `properties` (`flex().direction('col').build().properties.direction`); `build()` is typed `BaseSchema & { type: 'flex'; properties: FlexLayoutProps }`. Rendering is unchanged: `SchemaRenderer` hoists the bag onto the node before the `flex` renderer reads it. +**BREAKING (output shape):** `flex()` now builds `{ type: 'flex', properties: { direction, justify, align, gap, children } }` instead of writing those keys on the node. That is the spelling `@object-ui/types`' authoring faces accept for an authored `flex` node, which now refuse the flat keys by name (objectui#11276, the maintainer's ruling A on objectui#11300); without this change, `objectui validate` would refuse what the builder builds. The base setters (`id`, `className`, `visible`, `disabled`, `testId`, `visibleOn`) still write on the node. Code that reads a built node's props reads them from `properties` (`flex().direction('col').build().properties.direction`); `build()` is typed `BaseSchema & { type: 'flex'; properties: FlexLayoutProps }`. Rendering is unchanged: `SchemaRenderer` hoists the bag onto the node before the `flex` renderer reads it. From 56d5f7fd39f947799194760c778a419c6e217dfc Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 1 Oct 2026 22:18:53 +0000 Subject: [PATCH 4/4] =?UTF-8?q?docs(skills):=20page-builder=20=C2=A73b=20s?= =?UTF-8?q?ays=20where=20an=20authored=20flex=20takes=20its=20props=20and?= =?UTF-8?q?=20children=20(objectui#11276)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The guide's "Pick the right layout primitive" section said the layout primitives all "take `children` and read every key off the node", and tabulated `flex`'s props. With this PR an authored `flex` takes its props AND its child list in its `properties` bag, and the flat spelling is refused by name on both authoring faces, so the sentence taught a shape `objectui validate` refuses. Two sentences change, nothing else: the intro now says `stack`, `grid`, `container` and `box` keep reading every key off the node while `flex` takes its props and `children` in `properties`, and the `flex` / `stack` intent sentence names the refusal. The table's "Key props (renderer defaults)" column stays as it is: those are the renderer's defaults, and only where they are authored moved. Net `skills/**` lines: 0 (353 → 353). Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_01JG2jy8a9su7ia4Hx7zxv42 --- skills/objectui/guides/page-builder.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/skills/objectui/guides/page-builder.md b/skills/objectui/guides/page-builder.md index a9c817bc06..7e3990e457 100644 --- a/skills/objectui/guides/page-builder.md +++ b/skills/objectui/guides/page-builder.md @@ -74,8 +74,8 @@ branching in component code. These are the structural types to reach for. By volume in the schema catalogue, `flex`, `stack` and `box` are the three most-used layout nodes, so -reach for them before anything heavier. All take `children` and read every key -off the node. +reach for them before anything heavier. All but `flex` take `children` and read +every key off the node; `flex` takes its props and `children` in `properties`. | `type` | Renders | Key props (renderer defaults) | Reach for it when | |---|---|---|---| @@ -86,7 +86,7 @@ off the node. | `box` | a bare `div` | none — `className` passes through **verbatim**, and the renderer injects nothing | you want a wrapper that adds no layout of its own: a Tailwind-only block, a positioning anchor | Use whichever of `flex` / `stack` names your intent; do not set -`direction: "col"` on a `flex`. +`direction: "col"` on a `flex`; `objectui validate` refuses flat `flex` props. `box` exists because every other option injects layout — the props column above, plus `card`'s border, shadow and `CardContent` wrapper. When you want