import { Button } from "@pswui/Button"; import { Checkbox } from "@pswui/Checkbox"; import { Input } from "@pswui/Input"; import { Label } from "@pswui/Label"; import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover"; import { Switch } from "@pswui/Switch"; import { TabContent, TabList, TabProvider, TabTrigger } from "@pswui/Tabs"; import type { ReactNode } from "react"; import { GITHUB_COMP_PREVIEW, LoadedCode, type TEMPLATE } from "./LoadedCode"; import { Story } from "./Story"; export type Template = Record< string, Record< string, | { type: "boolean"; value: boolean; disabled?: boolean } | { type: "select"; options: string[]; value: string; disabled?: boolean } | { type: "string"; value: string; disabled?: boolean } | { type: "number"; value: number; disabled?: boolean } > >; export type ControlTemplate = Record< string, Record< string, | { type: "boolean"; value: boolean; disabled?: boolean; onChange: (value: boolean) => void; onToggle: (v: boolean) => void; } | { type: "select"; options: string[]; value: string; disabled?: boolean; onChange: (value: string) => void; onToggle: (v: boolean) => void; } | { type: "string"; value: string; disabled?: boolean; onChange: (value: string) => void; onToggle: (v: boolean) => void; } | { type: "number"; value: number; disabled?: boolean; onChange: (value: number) => void; onToggle: (v: boolean) => void; } > >; export function PlaygroundControl<T extends ControlTemplate>(props: { props: T; }): ReactNode { return ( <> <h3>Controls</h3> <div className={ "rounded-lg p-4 border border-neutral-300 dark:border-neutral-700 flex flex-col justify-center items-start gap-12" } > {Object.entries(props.props).map(([componentName, propEntries]) => ( <div key={componentName} className="w-full flex flex-col justify-center items-start gap-4" > <span className="font-thin opacity-50 w-full border-b border-b-current pb-2"> <{componentName.slice(0, componentName.length - 5)}> </span> {Object.entries(propEntries).map(([propName, propMeta]) => ( <div key={componentName + propName} className={`flex gap-2 justify-between w-full ${propMeta.type === "boolean" ? "flex-row items-center" : "flex-col md:flex-row md:items-center"}`} > <Label direction="horizontal" className="flex flex-row items-center gap-2" > <Switch checked={!propMeta.disabled} onChange={(e) => { propMeta.onToggle(!e.currentTarget.checked); }} /> {propMeta.disabled ? ( <s className="opacity-50">{propName}</s> ) : ( <span>{propName}</span> )} </Label> {propMeta.type === "boolean" ? ( <Checkbox checked={propMeta.value} onChange={(e) => propMeta.onChange(e.currentTarget.checked)} /> ) : propMeta.type === "string" ? ( <Input type="text" value={propMeta.value} onChange={(e) => propMeta.onChange(e.currentTarget.value)} className="w-full md:w-fit" /> ) : propMeta.type === "select" ? ( <Popover> <PopoverTrigger> <Button preset="default" className="w-full md:w-fit" > {propMeta.value} </Button> </PopoverTrigger> <PopoverContent className="min-w-36"> {propMeta.options.map((value) => ( <Button preset="ghost" key={value} onClick={() => propMeta.onChange(value)} > {value} </Button> ))} </PopoverContent> </Popover> ) : propMeta.type === "number" ? ( <Input type="number" value={propMeta.value} onChange={(e) => propMeta.onChange(e.currentTarget.valueAsNumber) } className="w-full md:w-fit" /> ) : null} </div> ))} </div> ))} </div> </> ); } export function PlaygroundLayout<T extends ControlTemplate>({ children, compName, props, control, }: { children: ReactNode; compName: string; props: TEMPLATE; control: T; }) { return ( <> <TabProvider defaultName="preview"> <TabList> <TabTrigger name="preview">Preview</TabTrigger> <TabTrigger name="code">Code</TabTrigger> </TabList> <TabContent name="preview"> <Story layout="centered">{children}</Story> </TabContent> <TabContent name="code"> <LoadedCode from={GITHUB_COMP_PREVIEW(compName)} template={props} /> </TabContent> </TabProvider> <PlaygroundControl props={control} /> </> ); }