"use client"; import { useState, type ReactNode } from "react"; import type { z } from "zod"; // shadcn components import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Checkbox } from "@/components/ui/checkbox"; import { Switch } from "@/components/ui/switch"; import { Progress } from "@/components/ui/progress"; import { Separator } from "@/components/ui/separator"; import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { playgroundCatalog } from "../catalog"; // ============================================================================= // Types - Inferred from Catalog // ============================================================================= type CatalogComponents = typeof playgroundCatalog.data.components; export type InferProps = CatalogComponents[K] extends { props: z.ZodType } ? P : never; export interface ComponentContext { props: InferProps; children?: ReactNode; onAction?: (action: { name: string; params?: Record; }) => void; loading?: boolean; } export type ComponentFn = ( ctx: ComponentContext, ) => ReactNode; // ============================================================================= // Components - Type-safe with Catalog using shadcn/ui // ============================================================================= export const components: { [K in keyof CatalogComponents]: ComponentFn } = { // Layout Components Card: ({ props, children }) => { const maxWidthClass = props.maxWidth === "sm" ? "max-w-xs sm:min-w-[280px]" : props.maxWidth === "md" ? "max-w-sm sm:min-w-[320px]" : props.maxWidth === "lg" ? "max-w-md sm:min-w-[360px]" : "w-full"; const centeredClass = props.centered ? "mx-auto" : ""; return (
{props.title && (
{props.title}
)} {props.description && (
{props.description}
)}
{children}
); }, Stack: ({ props, children }) => { const isHorizontal = props.direction === "horizontal"; const gapClass = props.gap === "lg" ? "gap-4" : props.gap === "md" ? "gap-3" : props.gap === "sm" ? "gap-2" : props.gap === "none" ? "gap-0" : "gap-3"; const alignClass = props.align === "center" ? "items-center" : props.align === "end" ? "items-end" : props.align === "stretch" ? "items-stretch" : "items-start"; const justifyClass = props.justify === "center" ? "justify-center" : props.justify === "end" ? "justify-end" : props.justify === "between" ? "justify-between" : props.justify === "around" ? "justify-around" : ""; return (
{children}
); }, Grid: ({ props, children }) => { const cols = props.columns === 6 ? "grid-cols-6" : props.columns === 5 ? "grid-cols-5" : props.columns === 4 ? "grid-cols-4" : props.columns === 3 ? "grid-cols-3" : props.columns === 2 ? "grid-cols-2" : "grid-cols-1"; const gridGap = props.gap === "lg" ? "gap-4" : props.gap === "sm" ? "gap-2" : "gap-3"; return
{children}
; }, Divider: () => , // Form Inputs Input: ({ props }) => (
), Textarea: ({ props }) => (