| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488 |
- import { defineCatalog } from "@json-render/core";
- import { schema } from "@json-render/react/schema";
- import { z } from "zod";
- /**
- * Web playground component catalog
- *
- * This defines the components available for AI generation in the playground.
- * Components and actions are implemented in lib/registry.tsx via defineRegistry.
- *
- * Keep schemas simple — one format per prop, no unions.
- * Fewer components = less confusion for the AI.
- */
- export const playgroundCatalog = defineCatalog(schema, {
- components: {
- // ── Layout ──────────────────────────────────────────────────────────
- Card: {
- props: z.object({
- title: z.string().nullable(),
- description: z.string().nullable(),
- maxWidth: z.enum(["sm", "md", "lg", "full"]).nullable(),
- centered: z.boolean().nullable(),
- }),
- slots: ["default"],
- description:
- "Container card for content sections. Use for forms/content boxes, NOT for page headers.",
- },
- Stack: {
- props: z.object({
- direction: z.enum(["horizontal", "vertical"]).nullable(),
- gap: z.enum(["none", "sm", "md", "lg"]).nullable(),
- align: z.enum(["start", "center", "end", "stretch"]).nullable(),
- justify: z
- .enum(["start", "center", "end", "between", "around"])
- .nullable(),
- }),
- slots: ["default"],
- description: "Flex container for layouts",
- },
- Grid: {
- props: z.object({
- columns: z.number().nullable(),
- gap: z.enum(["sm", "md", "lg"]).nullable(),
- }),
- slots: ["default"],
- description: "Grid layout (1-6 columns)",
- },
- Separator: {
- props: z.object({
- orientation: z.enum(["horizontal", "vertical"]).nullable(),
- }),
- description: "Visual separator line",
- },
- Tabs: {
- props: z.object({
- tabs: z.array(
- z.object({
- label: z.string(),
- value: z.string(),
- }),
- ),
- defaultValue: z.string().nullable(),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description:
- "Tab navigation. Use statePath to bind the active tab value.",
- },
- Accordion: {
- props: z.object({
- items: z.array(
- z.object({
- title: z.string(),
- content: z.string(),
- }),
- ),
- type: z.enum(["single", "multiple"]).nullable(),
- }),
- description:
- "Collapsible sections. Items as [{title, content}]. Type 'single' (default) or 'multiple'.",
- },
- Collapsible: {
- props: z.object({
- title: z.string(),
- defaultOpen: z.boolean().nullable(),
- }),
- slots: ["default"],
- description: "Collapsible section with trigger. Children render inside.",
- },
- Dialog: {
- props: z.object({
- title: z.string(),
- description: z.string().nullable(),
- openPath: z.string(),
- }),
- slots: ["default"],
- description:
- "Modal dialog. Set openPath to a boolean state path. Use setState to toggle.",
- },
- Drawer: {
- props: z.object({
- title: z.string(),
- description: z.string().nullable(),
- openPath: z.string(),
- }),
- slots: ["default"],
- description:
- "Bottom sheet drawer. Set openPath to a boolean state path. Use setState to toggle.",
- },
- Carousel: {
- props: z.object({
- items: z.array(
- z.object({
- title: z.string().nullable(),
- description: z.string().nullable(),
- }),
- ),
- }),
- description: "Horizontally scrollable carousel of cards.",
- },
- // ── Data Display ────────────────────────────────────────────────────
- Table: {
- props: z.object({
- columns: z.array(z.string()),
- rows: z.array(z.array(z.string())),
- caption: z.string().nullable(),
- }),
- description:
- 'Data table. columns: header labels. rows: 2D array of cell strings, e.g. [["Alice","admin"],["Bob","user"]].',
- },
- Heading: {
- props: z.object({
- text: z.string(),
- level: z.enum(["h1", "h2", "h3", "h4"]).nullable(),
- }),
- description: "Heading text (h1-h4)",
- },
- Text: {
- props: z.object({
- text: z.string(),
- variant: z
- .enum(["body", "caption", "muted", "lead", "code"])
- .nullable(),
- }),
- description: "Paragraph text",
- },
- Image: {
- props: z.object({
- alt: z.string(),
- width: z.number().nullable(),
- height: z.number().nullable(),
- }),
- description: "Placeholder image (displays alt text in a styled box)",
- },
- Avatar: {
- props: z.object({
- src: z.string().nullable(),
- name: z.string(),
- size: z.enum(["sm", "md", "lg"]).nullable(),
- }),
- description: "User avatar with fallback initials",
- },
- Badge: {
- props: z.object({
- text: z.string(),
- variant: z.enum(["default", "success", "warning", "danger"]).nullable(),
- }),
- description: "Status badge",
- },
- Alert: {
- props: z.object({
- title: z.string(),
- message: z.string().nullable(),
- type: z.enum(["info", "success", "warning", "error"]).nullable(),
- }),
- description: "Alert banner",
- },
- Progress: {
- props: z.object({
- value: z.number(),
- max: z.number().nullable(),
- label: z.string().nullable(),
- }),
- description: "Progress bar (value 0-100)",
- },
- Skeleton: {
- props: z.object({
- width: z.string().nullable(),
- height: z.string().nullable(),
- rounded: z.boolean().nullable(),
- }),
- description: "Loading placeholder skeleton",
- },
- Spinner: {
- props: z.object({
- size: z.enum(["sm", "md", "lg"]).nullable(),
- label: z.string().nullable(),
- }),
- description: "Loading spinner indicator",
- },
- Tooltip: {
- props: z.object({
- content: z.string(),
- text: z.string(),
- }),
- description: "Hover tooltip. Shows content on hover over text.",
- },
- Popover: {
- props: z.object({
- trigger: z.string(),
- content: z.string(),
- }),
- description: "Popover that appears on click of trigger.",
- },
- Rating: {
- props: z.object({
- value: z.number(),
- max: z.number().nullable(),
- label: z.string().nullable(),
- }),
- description: "Star rating display",
- },
- // ── Charts ──────────────────────────────────────────────────────────
- BarGraph: {
- props: z.object({
- title: z.string().nullable(),
- data: z.array(
- z.object({
- label: z.string(),
- value: z.number(),
- }),
- ),
- }),
- description: "Vertical bar chart",
- },
- LineGraph: {
- props: z.object({
- title: z.string().nullable(),
- data: z.array(
- z.object({
- label: z.string(),
- value: z.number(),
- }),
- ),
- }),
- description: "Line chart with points",
- },
- // ── Form Inputs ─────────────────────────────────────────────────────
- Input: {
- props: z.object({
- label: z.string(),
- name: z.string(),
- type: z.enum(["text", "email", "password", "number"]).nullable(),
- placeholder: z.string().nullable(),
- statePath: z.string().nullable(),
- }),
- events: ["submit", "focus", "blur"],
- description: "Text input field. Use statePath for two-way binding.",
- },
- Textarea: {
- props: z.object({
- label: z.string(),
- name: z.string(),
- placeholder: z.string().nullable(),
- rows: z.number().nullable(),
- statePath: z.string().nullable(),
- }),
- description: "Multi-line text input. Use statePath for binding.",
- },
- Select: {
- props: z.object({
- label: z.string(),
- name: z.string(),
- options: z.array(z.string()),
- placeholder: z.string().nullable(),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description: "Dropdown select input. Use statePath for binding.",
- },
- Checkbox: {
- props: z.object({
- label: z.string(),
- name: z.string(),
- checked: z.boolean().nullable(),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description: "Checkbox input. Use statePath for binding.",
- },
- Radio: {
- props: z.object({
- label: z.string(),
- name: z.string(),
- options: z.array(z.string()),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description: "Radio button group. Use statePath for binding.",
- },
- Switch: {
- props: z.object({
- label: z.string(),
- name: z.string(),
- checked: z.boolean().nullable(),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description: "Toggle switch. Use statePath for binding.",
- },
- Slider: {
- props: z.object({
- label: z.string().nullable(),
- min: z.number().nullable(),
- max: z.number().nullable(),
- step: z.number().nullable(),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description: "Range slider input. Use statePath for binding.",
- },
- // ── Actions ─────────────────────────────────────────────────────────
- Button: {
- props: z.object({
- label: z.string(),
- variant: z.enum(["primary", "secondary", "danger"]).nullable(),
- disabled: z.boolean().nullable(),
- }),
- events: ["press"],
- description: "Clickable button. Bind on.press for handler.",
- },
- Link: {
- props: z.object({
- label: z.string(),
- href: z.string(),
- }),
- events: ["press"],
- description: "Anchor link. Bind on.press for click handler.",
- },
- DropdownMenu: {
- props: z.object({
- label: z.string(),
- items: z.array(
- z.object({
- label: z.string(),
- value: z.string(),
- }),
- ),
- }),
- events: ["select"],
- description: "Dropdown menu with trigger button and selectable items.",
- },
- Toggle: {
- props: z.object({
- label: z.string(),
- pressed: z.boolean().nullable(),
- statePath: z.string().nullable(),
- variant: z.enum(["default", "outline"]).nullable(),
- }),
- events: ["change"],
- description: "Toggle button. Use statePath for pressed state binding.",
- },
- ToggleGroup: {
- props: z.object({
- items: z.array(
- z.object({
- label: z.string(),
- value: z.string(),
- }),
- ),
- type: z.enum(["single", "multiple"]).nullable(),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description:
- "Group of toggle buttons. Type 'single' (default) or 'multiple'.",
- },
- ButtonGroup: {
- props: z.object({
- buttons: z.array(
- z.object({
- label: z.string(),
- value: z.string(),
- }),
- ),
- statePath: z.string().nullable(),
- }),
- events: ["change"],
- description: "Segmented button group. Use statePath for selected value.",
- },
- Pagination: {
- props: z.object({
- totalPages: z.number(),
- statePath: z.string(),
- }),
- events: ["change"],
- description:
- "Page navigation. Bind statePath to a number for current page.",
- },
- },
- actions: {
- setState: {
- params: z.object({
- path: z.string(),
- value: z.unknown(),
- }),
- description: "Update a value in the state model at the given path.",
- },
- pushState: {
- params: z.object({
- path: z.string(),
- value: z.unknown(),
- clearPath: z.string().optional(),
- }),
- description:
- 'Append an item to an array in state. Value can contain {path:"/statePath"} refs and "$id" for auto IDs. clearPath resets another path after pushing.',
- },
- removeState: {
- params: z.object({
- path: z.string(),
- index: z.number(),
- }),
- description: "Remove an item from an array in state at the given index.",
- },
- buttonClick: {
- params: z.object({
- message: z.string().nullable(),
- }),
- description: "Shows a toast with the message.",
- },
- formSubmit: {
- params: z.object({
- formName: z.string().nullable(),
- }),
- description: "Shows a toast confirming form submission.",
- },
- linkClick: {
- params: z.object({
- href: z.string(),
- }),
- description: "Shows a toast with the link destination.",
- },
- },
- });
|