@rlx-ui/mcp 0.0.16 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -326,7 +326,7 @@
326
326
  },
327
327
  {
328
328
  "name": "form",
329
- "code": "\"use client\"\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { Calendar } from \"@rlx-widgets/calendar\";\nimport { CalendarIcon } from \"lucide-react\";\nimport { cn } from \"@rlx-widgets/base\";\nimport { format } from \"date-fns\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@rlx-widgets/popover\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@rlx-widgets/form\";\n\nconst FormSchema = z.object({\n dob: z.date().refine((val) => val instanceof Date && !isNaN(val.getTime()), {\n message: \"A date of birth is required.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <FormField\n control={form.control}\n name=\"dob\"\n render={({ field }) => (\n <FormItem className=\"flex flex-col\">\n <FormLabel>Date of birth</FormLabel>\n <Popover>\n <PopoverTrigger asChild>\n <FormControl>\n <Button\n variant={\"outline\"}\n className={cn(\n \"w-[240px] pl-3 text-left font-normal\",\n !field.value && \"text-muted-foreground\"\n )}\n >\n {field.value ? (\n format(field.value, \"PPP\")\n ) : (\n <span>Pick a date</span>\n )}\n <CalendarIcon className=\"ml-auto h-4 w-4 opacity-50\" />\n </Button>\n </FormControl>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"start\">\n <Calendar\n mode=\"single\"\n selected={field.value}\n onSelect={field.onChange}\n disabled={(date) =>\n date > new Date() || date < new Date(\"1900-01-01\")\n }\n captionLayout=\"dropdown\"\n />\n </PopoverContent>\n </Popover>\n <FormDescription>\n Your date of birth is used to calculate your age.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n );\n};\n"
329
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { Calendar } from \"@rlx-widgets/calendar\";\nimport { CalendarIcon } from \"lucide-react\";\nimport { cn } from \"@rlx-widgets/base\";\nimport { format } from \"date-fns\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@rlx-widgets/popover\";\nimport { toast } from \"sonner\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\n\nconst FormSchema = z.object({\n dob: z.date().refine((val) => val instanceof Date && !isNaN(val.getTime()), {\n message: \"A date of birth is required.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <Controller\n control={form.control}\n name=\"dob\"\n render={({ field, fieldState }) => (\n <Field\n data-invalid={fieldState.invalid}\n orientation=\"vertical\"\n className=\"flex flex-col\"\n >\n <FieldContent>\n <FieldLabel>Date of birth</FieldLabel>\n <FieldDescription>\n Your date of birth is used to calculate your age.\n </FieldDescription>\n </FieldContent>\n <Popover>\n <PopoverTrigger asChild>\n <Button\n variant={\"outline\"}\n className={cn(\n \"w-[240px] pl-3 text-left font-normal\",\n !field.value && \"text-muted-foreground\"\n )}\n aria-invalid={fieldState.invalid}\n >\n {field.value ? (\n format(field.value, \"PPP\")\n ) : (\n <span>Pick a date</span>\n )}\n <CalendarIcon className=\"ml-auto h-4 w-4 opacity-50\" />\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"start\">\n <Calendar\n mode=\"single\"\n selected={field.value}\n onSelect={field.onChange}\n disabled={(date) =>\n date > new Date() || date < new Date(\"1900-01-01\")\n }\n captionLayout=\"dropdown\"\n />\n </PopoverContent>\n </Popover>\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
330
330
  },
331
331
  {
332
332
  "name": "month-year-selector",
@@ -403,7 +403,7 @@
403
403
  },
404
404
  {
405
405
  "name": "form",
406
- "code": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\nimport { Button } from \"rlx-widgets\";\nimport { Checkbox } from \"rlx-widgets\";\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"rlx-widgets\";\n\nconst items = [\n {\n id: \"recents\",\n label: \"Recents\",\n },\n {\n id: \"home\",\n label: \"Home\",\n },\n {\n id: \"applications\",\n label: \"Applications\",\n },\n {\n id: \"desktop\",\n label: \"Desktop\",\n },\n {\n id: \"downloads\",\n label: \"Downloads\",\n },\n {\n id: \"documents\",\n label: \"Documents\",\n },\n] as const;\n\nconst FormSchema = z.object({\n items: z.array(z.string()).refine((value) => value.some((item) => item), {\n message: \"You have to select at least one item.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n items: [\"recents\", \"home\"],\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <FormField\n control={form.control}\n name=\"items\"\n render={() => (\n <FormItem>\n <div className=\"mb-4\">\n <FormLabel className=\"text-base\">Sidebar</FormLabel>\n <FormDescription>\n Select the items you want to display in the sidebar.\n </FormDescription>\n </div>\n {items.map((item) => (\n <FormField\n key={item.id}\n control={form.control}\n name=\"items\"\n render={({ field }) => {\n return (\n <FormItem\n key={item.id}\n className=\"flex flex-row items-center gap-2\"\n >\n <FormControl>\n <Checkbox\n checked={field.value?.includes(item.id)}\n onCheckedChange={(checked) => {\n return checked\n ? field.onChange([...field.value, item.id])\n : field.onChange(\n field.value?.filter(\n (value) => value !== item.id\n )\n );\n }}\n />\n </FormControl>\n <FormLabel className=\"text-sm font-normal\">\n {item.label}\n </FormLabel>\n </FormItem>\n );\n }}\n />\n ))}\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n );\n};\n"
406
+ "code": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { Checkbox } from \"@rlx-widgets/checkbox\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\n\nconst items = [\n {\n id: \"recents\",\n label: \"Recents\",\n },\n {\n id: \"home\",\n label: \"Home\",\n },\n {\n id: \"applications\",\n label: \"Applications\",\n },\n {\n id: \"desktop\",\n label: \"Desktop\",\n },\n {\n id: \"downloads\",\n label: \"Downloads\",\n },\n {\n id: \"documents\",\n label: \"Documents\",\n },\n] as const;\n\nconst FormSchema = z.object({\n items: z.array(z.string()).refine((value) => value.some((item) => item), {\n message: \"You have to select at least one item.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n items: [\"recents\", \"home\"],\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-8\">\n <Controller\n control={form.control}\n name=\"items\"\n render={({ field, fieldState }) => (\n <Field data-invalid={fieldState.invalid} orientation=\"vertical\">\n <FieldContent className=\"mb-4\">\n <FieldLabel className=\"text-base\">Sidebar</FieldLabel>\n <FieldDescription>\n Select the items you want to display in the sidebar.\n </FieldDescription>\n </FieldContent>\n {items.map((item) => (\n <Field\n key={item.id}\n data-invalid={fieldState.invalid}\n orientation=\"horizontal\"\n className=\"flex flex-row items-center gap-2\"\n >\n <Checkbox\n checked={field.value?.includes(item.id)}\n onCheckedChange={(checked) => {\n return checked\n ? field.onChange([...field.value, item.id])\n : field.onChange(\n field.value?.filter((value) => value !== item.id)\n );\n }}\n aria-invalid={fieldState.invalid}\n />\n <FieldLabel className=\"text-sm font-normal\">\n {item.label}\n </FieldLabel>\n </Field>\n ))}\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
407
407
  }
408
408
  ]
409
409
  },
@@ -615,10 +615,32 @@
615
615
  "name": "Form",
616
616
  "exportName": "Form",
617
617
  "packageName": "@rlx-widgets/form",
618
- "version": "0.0.2",
618
+ "version": "0.1.0",
619
619
  "slug": "form",
620
620
  "category": "widget",
621
- "sourceCode": "\"use client\";\n\nimport * as React from \"react\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport {\n Controller,\n FormProvider,\n useFormContext,\n useFormState,\n type ControllerProps,\n type FieldPath,\n type FieldValues,\n} from \"react-hook-form\";\nimport { cn } from \"@rlx-widgets/base\";\nimport { Label } from \"@rlx-widgets/label\";\n\nconst Form = FormProvider;\n\ntype FormFieldContextValue<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n> = {\n name: TName;\n};\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>(\n {} as FormFieldContextValue\n);\n\nconst FormField = <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>\n>({\n ...props\n}: ControllerProps<TFieldValues, TName>) => {\n return (\n <FormFieldContext.Provider value={{ name: props.name }}>\n <Controller {...props} />\n </FormFieldContext.Provider>\n );\n};\n\nconst useFormField = () => {\n const fieldContext = React.useContext(FormFieldContext);\n const itemContext = React.useContext(FormItemContext);\n const { getFieldState } = useFormContext();\n const formState = useFormState({ name: fieldContext.name });\n const fieldState = getFieldState(fieldContext.name, formState);\n\n if (!fieldContext) {\n throw new Error(\"useFormField should be used within <FormField>\");\n }\n\n const { id } = itemContext;\n\n return {\n id,\n name: fieldContext.name,\n formItemId: `${id}-form-item`,\n formDescriptionId: `${id}-form-item-description`,\n formMessageId: `${id}-form-item-message`,\n ...fieldState,\n };\n};\n\ntype FormItemContextValue = {\n id: string;\n};\n\nconst FormItemContext = React.createContext<FormItemContextValue>(\n {} as FormItemContextValue\n);\n\nfunction FormItem({ className, ...props }: React.ComponentProps<\"div\">) {\n const id = React.useId();\n\n return (\n <FormItemContext.Provider value={{ id }}>\n <div\n data-slot=\"form-item\"\n className={cn(\"grid gap-2\", className)}\n {...props}\n />\n </FormItemContext.Provider>\n );\n}\n\nfunction FormLabel({\n className,\n ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n const { error, formItemId } = useFormField();\n\n return (\n <Label\n data-slot=\"form-label\"\n data-error={!!error}\n className={cn(\"data-[error=true]:text-destructive\", className)}\n htmlFor={formItemId}\n {...props}\n />\n );\n}\n\nfunction FormControl({ ...props }: React.ComponentProps<typeof Slot>) {\n const { error, formItemId, formDescriptionId, formMessageId } =\n useFormField();\n\n return (\n <Slot\n data-slot=\"form-control\"\n id={formItemId}\n aria-describedby={\n !error\n ? `${formDescriptionId}`\n : `${formDescriptionId} ${formMessageId}`\n }\n aria-invalid={!!error}\n {...props}\n />\n );\n}\n\nfunction FormDescription({ className, ...props }: React.ComponentProps<\"p\">) {\n const { formDescriptionId } = useFormField();\n\n return (\n <p\n data-slot=\"form-description\"\n id={formDescriptionId}\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nfunction FormMessage({ className, ...props }: React.ComponentProps<\"p\">) {\n const { error, formMessageId } = useFormField();\n const body = error ? String(error?.message ?? \"\") : props.children;\n\n if (!body) {\n return null;\n }\n\n return (\n <p\n data-slot=\"form-message\"\n id={formMessageId}\n className={cn(\"text-destructive text-sm\", className)}\n {...props}\n >\n {body}\n </p>\n );\n}\n\nexport {\n useFormField,\n Form,\n FormItem,\n FormLabel,\n FormControl,\n FormDescription,\n FormMessage,\n FormField,\n};\n"
621
+ "sourceCode": "// https://github.com/WebDevSimplified/shadcn-field-component/tree/main/src/components\n\nimport { Checkbox } from \"@rlx-widgets/checkbox\";\nimport { Input } from \"@rlx-widgets/input\";\nimport { ReactNode } from \"react\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@rlx-widgets/select\";\nimport { Textarea } from \"@rlx-widgets/textarea\";\nimport {\n Controller,\n ControllerProps,\n FieldPath,\n FieldValues,\n} from \"react-hook-form\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\n\ntype FormControlProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n TTransformedValues = TFieldValues\n> = {\n name: TName;\n label: ReactNode;\n description?: ReactNode;\n placeholder?: string;\n control: ControllerProps<TFieldValues, TName, TTransformedValues>[\"control\"];\n};\n\ntype FormBaseProps<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n TTransformedValues = TFieldValues\n> = FormControlProps<TFieldValues, TName, TTransformedValues> & {\n horizontal?: boolean;\n controlFirst?: boolean;\n children: (\n field: Parameters<\n ControllerProps<TFieldValues, TName, TTransformedValues>[\"render\"]\n >[number][\"field\"] & {\n \"aria-invalid\": boolean;\n id: string;\n }\n ) => ReactNode;\n};\n\ntype FormControlComponent<\n ExtraProps extends Record<string, unknown> = Record<never, never>\n> = <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n TTransformedValues = TFieldValues\n>(\n props: FormControlProps<TFieldValues, TName, TTransformedValues> & ExtraProps\n) => ReactNode;\n\nfunction FormBase<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n TTransformedValues = TFieldValues\n>({\n children,\n control,\n label,\n name,\n description,\n controlFirst,\n horizontal,\n}: FormBaseProps<TFieldValues, TName, TTransformedValues>) {\n return (\n <Controller\n control={control}\n name={name}\n render={({ field, fieldState }) => {\n const labelElement = (\n <>\n <FieldLabel htmlFor={field.name}>{label}</FieldLabel>\n {description && <FieldDescription>{description}</FieldDescription>}\n </>\n );\n const control = children({\n ...field,\n id: field.name,\n \"aria-invalid\": fieldState.invalid,\n });\n const errorElem = fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n );\n\n return (\n <Field\n data-invalid={fieldState.invalid}\n orientation={horizontal ? \"horizontal\" : undefined}\n >\n {controlFirst ? (\n <>\n {control}\n <FieldContent>\n {labelElement}\n {errorElem}\n </FieldContent>\n </>\n ) : (\n <>\n <FieldContent>{labelElement}</FieldContent>\n {control}\n {errorElem}\n </>\n )}\n </Field>\n );\n }}\n />\n );\n}\n\nexport const FormInput: FormControlComponent<{\n controlFirst?: boolean;\n horizontal?: boolean;\n}> = ({ placeholder, ...props }) => {\n return (\n <FormBase {...props}>\n {(field) => <Input {...field} placeholder={placeholder} />}\n </FormBase>\n );\n};\n\nexport const FormTextarea: FormControlComponent<{\n controlFirst?: boolean;\n horizontal?: boolean;\n}> = ({ placeholder, ...props }) => {\n return (\n <FormBase {...props}>\n {(field) => <Textarea {...field} placeholder={placeholder} />}\n </FormBase>\n );\n};\n\nexport const FormSelect: FormControlComponent<{\n items: Array<{\n value: string;\n label: string;\n }>;\n controlFirst?: boolean;\n horizontal?: boolean;\n}> = ({ items, placeholder, ...props }) => {\n return (\n <FormBase {...props}>\n {({ onChange, onBlur, ...field }) => (\n <Select {...field} onValueChange={onChange}>\n <SelectTrigger\n aria-invalid={field[\"aria-invalid\"]}\n id={field.id}\n onBlur={onBlur}\n >\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {items.map((item) => (\n <SelectItem key={item.value} value={item.value}>\n {item.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </FormBase>\n );\n};\n\nexport const FormCheckbox: FormControlComponent = (props) => {\n return (\n <FormBase {...props} horizontal controlFirst>\n {({ onChange, value, ...field }) => (\n <Checkbox {...field} checked={value} onCheckedChange={onChange} />\n )}\n </FormBase>\n );\n};\n",
622
+ "demos": [
623
+ {
624
+ "name": "complete-form",
625
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport {\n FormInput,\n FormTextarea,\n FormSelect,\n FormCheckbox,\n} from \"@rlx-widgets/form\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\n\nconst FormSchema = z.object({\n username: z.string().min(2, {\n message: \"Username must be at least 2 characters.\",\n }),\n email: z.string().email({\n message: \"Please select a valid email.\",\n }),\n bio: z.string().min(10, {\n message: \"Bio must be at least 10 characters.\",\n }),\n terms: z.boolean().refine((value) => value === true, {\n message: \"You must accept the terms and conditions.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n username: \"\",\n email: \"\",\n bio: \"\",\n terms: false,\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form\n onSubmit={form.handleSubmit(onSubmit)}\n className=\"w-full max-w-md space-y-6\"\n >\n <FormInput\n control={form.control}\n name=\"username\"\n label=\"Username\"\n description=\"This is your public display name.\"\n />\n <FormSelect\n control={form.control}\n name=\"email\"\n label=\"Email\"\n description=\"Select a verified email to display.\"\n items={[\n { value: \"m@example.com\", label: \"m@example.com\" },\n { value: \"m@google.com\", label: \"m@google.com\" },\n { value: \"m@support.com\", label: \"m@support.com\" },\n ]}\n />\n <FormTextarea\n control={form.control}\n name=\"bio\"\n label=\"Bio\"\n description=\"Tell us a little bit about yourself.\"\n />\n <FormCheckbox\n control={form.control}\n name=\"terms\"\n label=\"Accept terms and conditions\"\n description=\"You agree to our Terms of Service and Privacy Policy.\"\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
626
+ },
627
+ {
628
+ "name": "form-checkbox",
629
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { FormCheckbox } from \"@rlx-widgets/form\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\n\nconst FormSchema = z.object({\n terms: z.boolean().refine((value) => value === true, {\n message: \"You must accept the terms and conditions.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n terms: false,\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form\n onSubmit={form.handleSubmit(onSubmit)}\n className=\"w-full max-w-md space-y-6\"\n >\n <FormCheckbox\n control={form.control}\n name=\"terms\"\n label=\"Accept terms and conditions\"\n description=\"You agree to our Terms of Service and Privacy Policy.\"\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n\n"
630
+ },
631
+ {
632
+ "name": "form-input",
633
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { FormInput } from \"@rlx-widgets/form\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\n\nconst FormSchema = z.object({\n username: z.string().min(2, {\n message: \"Username must be at least 2 characters.\",\n }),\n displayName: z.string().min(2, {\n message: \"Display name must be at least 2 characters.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n username: \"\",\n displayName: \"\",\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form\n onSubmit={form.handleSubmit(onSubmit)}\n className=\"w-full max-w-md space-y-6\"\n >\n <FormInput\n control={form.control}\n name=\"username\"\n label=\"Username\"\n description=\"This is your public display name.\"\n />\n <FormInput\n control={form.control}\n name=\"displayName\"\n label=\"Display Name (Control First)\"\n description=\"Control appears before the label.\"\n controlFirst\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n\n"
634
+ },
635
+ {
636
+ "name": "form-select",
637
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { FormSelect } from \"@rlx-widgets/form\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\n\nconst FormSchema = z.object({\n email: z.string().email({\n message: \"Please select a valid email.\",\n }),\n backupEmail: z.string().email({\n message: \"Please select a valid backup email.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n email: \"\",\n backupEmail: \"\",\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form\n onSubmit={form.handleSubmit(onSubmit)}\n className=\"w-full max-w-md space-y-6\"\n >\n <FormSelect\n control={form.control}\n name=\"email\"\n label=\"Email\"\n description=\"Select a verified email to display.\"\n items={[\n { value: \"m@example.com\", label: \"m@example.com\" },\n { value: \"m@google.com\", label: \"m@google.com\" },\n { value: \"m@support.com\", label: \"m@support.com\" },\n ]}\n />\n <FormSelect\n control={form.control}\n name=\"backupEmail\"\n label=\"Backup Email (Control First)\"\n description=\"Control appears before the label.\"\n controlFirst\n items={[\n { value: \"m@example.com\", label: \"m@example.com\" },\n { value: \"m@google.com\", label: \"m@google.com\" },\n { value: \"m@support.com\", label: \"m@support.com\" },\n ]}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n\n"
638
+ },
639
+ {
640
+ "name": "form-textarea",
641
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { FormTextarea } from \"@rlx-widgets/form\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\n\nconst FormSchema = z.object({\n bio: z.string().min(10, {\n message: \"Bio must be at least 10 characters.\",\n }),\n notes: z.string().min(10, {\n message: \"Notes must be at least 10 characters.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n bio: \"\",\n notes: \"\",\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form\n onSubmit={form.handleSubmit(onSubmit)}\n className=\"w-full max-w-md space-y-6\"\n >\n <FormTextarea\n control={form.control}\n name=\"bio\"\n label=\"Bio\"\n description=\"Tell us a little bit about yourself.\"\n />\n <FormTextarea\n control={form.control}\n name=\"notes\"\n label=\"Notes (Control First)\"\n description=\"Control appears before the label.\"\n controlFirst\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n\n"
642
+ }
643
+ ]
622
644
  },
623
645
  {
624
646
  "name": "Hover Card",
@@ -658,7 +680,7 @@
658
680
  },
659
681
  {
660
682
  "name": "form",
661
- "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { Input } from \"@rlx-widgets/input\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@rlx-widgets/form\";\n\nconst FormSchema = z.object({\n username: z.string().min(2, {\n message: \"Username must be at least 2 characters.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n username: \"\",\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <FormField\n control={form.control}\n name=\"username\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Username</FormLabel>\n <FormControl>\n <Input placeholder=\"shadcn\" {...field} />\n </FormControl>\n <FormDescription>\n This is your public display name.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n );\n};\n"
683
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { Input } from \"@rlx-widgets/input\";\nimport { toast } from \"sonner\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\n\nconst FormSchema = z.object({\n username: z.string().min(2, {\n message: \"Username must be at least 2 characters.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n username: \"\",\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <Controller\n control={form.control}\n name=\"username\"\n render={({ field, fieldState }) => (\n <Field data-invalid={fieldState.invalid} orientation=\"vertical\">\n <FieldContent>\n <FieldLabel>Username</FieldLabel>\n <FieldDescription>\n This is your public display name.\n </FieldDescription>\n </FieldContent>\n <Input\n placeholder=\"shadcn\"\n {...field}\n aria-invalid={fieldState.invalid}\n />\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
662
684
  },
663
685
  {
664
686
  "name": "with-button",
@@ -742,7 +764,7 @@
742
764
  },
743
765
  {
744
766
  "name": "form",
745
- "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from \"@rlx-widgets/input-otp\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@rlx-widgets/form\";\n\nconst FormSchema = z.object({\n pin: z.string().min(6, {\n message: \"Your one-time password must be 6 characters.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n pin: \"\",\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <FormField\n control={form.control}\n name=\"pin\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>One-Time Password</FormLabel>\n <FormControl>\n <InputOTP maxLength={6} {...field}>\n <InputOTPGroup>\n <InputOTPSlot index={0} />\n <InputOTPSlot index={1} />\n <InputOTPSlot index={2} />\n <InputOTPSlot index={3} />\n <InputOTPSlot index={4} />\n <InputOTPSlot index={5} />\n </InputOTPGroup>\n </InputOTP>\n </FormControl>\n <FormDescription>\n Please enter the one-time password sent to your phone.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n );\n};\n"
767
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { InputOTP, InputOTPGroup, InputOTPSlot } from \"@rlx-widgets/input-otp\";\nimport { toast } from \"sonner\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\n\nconst FormSchema = z.object({\n pin: z.string().min(6, {\n message: \"Your one-time password must be 6 characters.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n pin: \"\",\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <Controller\n control={form.control}\n name=\"pin\"\n render={({ field, fieldState }) => (\n <Field data-invalid={fieldState.invalid} orientation=\"vertical\">\n <FieldContent>\n <FieldLabel>One-Time Password</FieldLabel>\n <FieldDescription>\n Please enter the one-time password sent to your phone.\n </FieldDescription>\n </FieldContent>\n <InputOTP\n maxLength={6}\n {...field}\n aria-invalid={fieldState.invalid}\n >\n <InputOTPGroup>\n <InputOTPSlot index={0} />\n <InputOTPSlot index={1} />\n <InputOTPSlot index={2} />\n <InputOTPSlot index={3} />\n <InputOTPSlot index={4} />\n <InputOTPSlot index={5} />\n </InputOTPGroup>\n </InputOTP>\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
746
768
  },
747
769
  {
748
770
  "name": "pattern",
@@ -944,7 +966,7 @@
944
966
  },
945
967
  {
946
968
  "name": "form",
947
- "code": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\nimport { Button } from \"@rlx-widgets/button\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@rlx-widgets/form\";\nimport { RadioGroup, RadioGroupItem } from \"@rlx-widgets/radio-group\";\n\nconst FormSchema = z.object({\n type: z.enum([\"all\", \"mentions\", \"none\"]).refine((val) => !!val, {\n message: \"You need to select a notification type.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <FormField\n control={form.control}\n name=\"type\"\n render={({ field }) => (\n <FormItem className=\"space-y-3\">\n <FormLabel>Notify me about...</FormLabel>\n <FormControl>\n <RadioGroup\n onValueChange={field.onChange}\n defaultValue={field.value}\n className=\"flex flex-col\"\n >\n <FormItem className=\"flex items-center gap-3\">\n <FormControl>\n <RadioGroupItem value=\"all\" />\n </FormControl>\n <FormLabel className=\"font-normal\">\n All new messages\n </FormLabel>\n </FormItem>\n <FormItem className=\"flex items-center gap-3\">\n <FormControl>\n <RadioGroupItem value=\"mentions\" />\n </FormControl>\n <FormLabel className=\"font-normal\">\n Direct messages and mentions\n </FormLabel>\n </FormItem>\n <FormItem className=\"flex items-center gap-3\">\n <FormControl>\n <RadioGroupItem value=\"none\" />\n </FormControl>\n <FormLabel className=\"font-normal\">Nothing</FormLabel>\n </FormItem>\n </RadioGroup>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n );\n};\n"
969
+ "code": "\"use client\";\n\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\nimport { Button } from \"@rlx-widgets/button\";\nimport {\n Field,\n FieldContent,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\nimport { RadioGroup, RadioGroupItem } from \"@rlx-widgets/radio-group\";\n\nconst FormSchema = z.object({\n type: z.enum([\"all\", \"mentions\", \"none\"]).refine((val) => !!val, {\n message: \"You need to select a notification type.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <Controller\n control={form.control}\n name=\"type\"\n render={({ field, fieldState }) => (\n <Field data-invalid={fieldState.invalid} orientation=\"vertical\" className=\"space-y-3\">\n <FieldContent>\n <FieldLabel>Notify me about...</FieldLabel>\n </FieldContent>\n <RadioGroup\n onValueChange={field.onChange}\n value={field.value}\n className=\"flex flex-col\"\n >\n <Field\n data-invalid={fieldState.invalid}\n orientation=\"horizontal\"\n className=\"flex items-center gap-3\"\n >\n <RadioGroupItem value=\"all\" aria-invalid={fieldState.invalid} />\n <FieldLabel className=\"font-normal\">\n All new messages\n </FieldLabel>\n </Field>\n <Field\n data-invalid={fieldState.invalid}\n orientation=\"horizontal\"\n className=\"flex items-center gap-3\"\n >\n <RadioGroupItem value=\"mentions\" aria-invalid={fieldState.invalid} />\n <FieldLabel className=\"font-normal\">\n Direct messages and mentions\n </FieldLabel>\n </Field>\n <Field\n data-invalid={fieldState.invalid}\n orientation=\"horizontal\"\n className=\"flex items-center gap-3\"\n >\n <RadioGroupItem value=\"none\" aria-invalid={fieldState.invalid} />\n <FieldLabel className=\"font-normal\">Nothing</FieldLabel>\n </Field>\n </RadioGroup>\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
948
970
  }
949
971
  ]
950
972
  },
@@ -1005,7 +1027,7 @@
1005
1027
  },
1006
1028
  {
1007
1029
  "name": "form",
1008
- "code": "\"use client\";\n\nimport Link from \"next/link\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@rlx-widgets/form\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@rlx-widgets/select\";\n\nconst FormSchema = z.object({\n email: z.email({\n error: \"Please select an email to display.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <FormField\n control={form.control}\n name=\"email\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email</FormLabel>\n <Select onValueChange={field.onChange} defaultValue={field.value}>\n <FormControl>\n <SelectTrigger>\n <SelectValue placeholder=\"Select a verified email to display\" />\n </SelectTrigger>\n </FormControl>\n <SelectContent>\n <SelectItem value=\"m@example.com\">m@example.com</SelectItem>\n <SelectItem value=\"m@google.com\">m@google.com</SelectItem>\n <SelectItem value=\"m@support.com\">m@support.com</SelectItem>\n </SelectContent>\n </Select>\n <FormDescription>\n You can manage email addresses in your{\" \"}\n <Link href=\"/examples/forms\">email settings</Link>.\n </FormDescription>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n );\n};\n"
1030
+ "code": "\"use client\";\n\nimport Link from \"next/link\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@rlx-widgets/select\";\n\nconst FormSchema = z.object({\n email: z.email({\n error: \"Please select an email to display.\",\n }),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-2/3 space-y-6\">\n <Controller\n control={form.control}\n name=\"email\"\n render={({ field, fieldState }) => (\n <Field data-invalid={fieldState.invalid} orientation=\"vertical\">\n <FieldContent>\n <FieldLabel>Email</FieldLabel>\n <FieldDescription>\n You can manage email addresses in your{\" \"}\n <Link href=\"/examples/forms\">email settings</Link>.\n </FieldDescription>\n </FieldContent>\n <Select onValueChange={field.onChange} value={field.value}>\n <SelectTrigger\n aria-invalid={fieldState.invalid}\n onBlur={field.onBlur}\n >\n <SelectValue placeholder=\"Select a verified email to display\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"m@example.com\">m@example.com</SelectItem>\n <SelectItem value=\"m@google.com\">m@google.com</SelectItem>\n <SelectItem value=\"m@support.com\">m@support.com</SelectItem>\n </SelectContent>\n </Select>\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
1009
1031
  },
1010
1032
  {
1011
1033
  "name": "scrollable",
@@ -1144,7 +1166,7 @@
1144
1166
  },
1145
1167
  {
1146
1168
  "name": "form",
1147
- "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { Switch } from \"@rlx-widgets/switch\";\nimport { toast } from \"sonner\";\nimport { useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n} from \"@rlx-widgets/form\";\n\nconst FormSchema = z.object({\n marketing_emails: z.boolean().default(false).optional(),\n security_emails: z.boolean(),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n security_emails: true,\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-full space-y-6\">\n <div>\n <h3 className=\"mb-4 text-lg font-medium\">Email Notifications</h3>\n <div className=\"space-y-4\">\n <FormField\n control={form.control}\n name=\"marketing_emails\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm\">\n <div className=\"space-y-0.5\">\n <FormLabel>Marketing emails</FormLabel>\n <FormDescription>\n Receive emails about new products, features, and more.\n </FormDescription>\n </div>\n <FormControl>\n <Switch\n checked={field.value}\n onCheckedChange={field.onChange}\n />\n </FormControl>\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"security_emails\"\n render={({ field }) => (\n <FormItem className=\"flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm\">\n <div className=\"space-y-0.5\">\n <FormLabel>Security emails</FormLabel>\n <FormDescription>\n Receive emails about your account security.\n </FormDescription>\n </div>\n <FormControl>\n <Switch\n checked={field.value}\n onCheckedChange={field.onChange}\n disabled\n aria-readonly\n />\n </FormControl>\n </FormItem>\n )}\n />\n </div>\n </div>\n <Button type=\"submit\">Submit</Button>\n </form>\n </Form>\n );\n};\n"
1169
+ "code": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { Switch } from \"@rlx-widgets/switch\";\nimport { toast } from \"sonner\";\nimport { Controller, useForm } from \"react-hook-form\";\nimport { z } from \"zod\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldLabel,\n} from \"@rlx-widgets/field\";\n\nconst FormSchema = z.object({\n marketing_emails: z.boolean().default(false).optional(),\n security_emails: z.boolean(),\n});\n\nexport const Preview = () => {\n const form = useForm<z.infer<typeof FormSchema>>({\n resolver: zodResolver(FormSchema),\n defaultValues: {\n security_emails: true,\n },\n });\n\n function onSubmit(data: z.infer<typeof FormSchema>) {\n toast(\"You submitted the following values\", {\n description: (\n <pre className=\"mt-2 w-[320px] rounded-md bg-neutral-950 p-4\">\n <code className=\"text-white\">{JSON.stringify(data, null, 2)}</code>\n </pre>\n ),\n });\n }\n\n return (\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"w-full space-y-6\">\n <div>\n <h3 className=\"mb-4 text-lg font-medium\">Email Notifications</h3>\n <div className=\"space-y-4\">\n <Controller\n control={form.control}\n name=\"marketing_emails\"\n render={({ field, fieldState }) => (\n <Field\n data-invalid={fieldState.invalid}\n orientation=\"horizontal\"\n className=\"flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm\"\n >\n <FieldContent className=\"space-y-0.5\">\n <FieldLabel>Marketing emails</FieldLabel>\n <FieldDescription>\n Receive emails about new products, features, and more.\n </FieldDescription>\n </FieldContent>\n <Switch\n checked={field.value}\n onCheckedChange={field.onChange}\n aria-invalid={fieldState.invalid}\n />\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n <Controller\n control={form.control}\n name=\"security_emails\"\n render={({ field, fieldState }) => (\n <Field\n data-invalid={fieldState.invalid}\n orientation=\"horizontal\"\n className=\"flex flex-row items-center justify-between rounded-lg border p-3 shadow-sm\"\n >\n <FieldContent className=\"space-y-0.5\">\n <FieldLabel>Security emails</FieldLabel>\n <FieldDescription>\n Receive emails about your account security.\n </FieldDescription>\n </FieldContent>\n <Switch\n checked={field.value}\n onCheckedChange={field.onChange}\n disabled\n aria-readonly\n aria-invalid={fieldState.invalid}\n />\n {fieldState.invalid && (\n <FieldError errors={[fieldState.error]} />\n )}\n </Field>\n )}\n />\n </div>\n </div>\n <Button type=\"submit\">Submit</Button>\n </form>\n );\n};\n"
1148
1170
  }
1149
1171
  ]
1150
1172
  },
@@ -1355,7 +1377,7 @@
1355
1377
  "name": "Code Preview Tabs",
1356
1378
  "exportName": "CodePreviewTabs",
1357
1379
  "packageName": "@rlx-components/code-preview-tabs",
1358
- "version": "0.0.7",
1380
+ "version": "1.0.0",
1359
1381
  "slug": "code-preview-tabs",
1360
1382
  "category": "component",
1361
1383
  "sourceCode": "\"use client\";\n\nimport { cn } from \"@rlx-widgets/base\";\nimport { CodePreviewTabsProps, PreviewVariants, TabType } from \"./types\";\nimport { ShikiCodeBlock } from \"@rlx-components/shiki-code-block\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@rlx-widgets/tabs\";\nimport { TabTypeEnum } from \"./enums\";\nimport { useMemo, useState } from \"react\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@rlx-widgets/select\";\n\nconst defaultClassNames = {};\n\nexport const CodePreviewTabs = ({\n variants: variantsProp,\n preview,\n code,\n classNames = defaultClassNames,\n lang = \"tsx\",\n}: CodePreviewTabsProps) => {\n const variants = useMemo(() => {\n return variantsProp || { Default: { preview, code } };\n }, [variantsProp, preview, code]);\n\n const [value, setValue] = useState<TabType>(\"preview\");\n const [variant, setVariant] = useState<string>(Object.keys(variants)[0]);\n\n const handleValueChange = (newValue: string) => {\n setValue(newValue as TabType);\n };\n\n return (\n <Tabs\n value={value}\n onValueChange={handleValueChange}\n className=\"relative mt-6 w-full\"\n >\n <div className=\"flex items-center justify-between\">\n <TabsList>\n <TabsTrigger value={TabTypeEnum.PREVIEW}>Preview</TabsTrigger>\n <TabsTrigger value={TabTypeEnum.CODE}>Code</TabsTrigger>\n </TabsList>\n {Object.keys(variants).length > 1 && (\n <Select\n onValueChange={setVariant}\n defaultValue={Object.keys(variants)[0]}\n value={variant}\n >\n <SelectTrigger>\n <SelectValue placeholder=\"Default\" />\n </SelectTrigger>\n <SelectContent>\n {Object.keys(variants).map((variant) => (\n <SelectItem key={variant} value={variant}>\n {variant}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </div>\n\n <TabsContent value={TabTypeEnum.PREVIEW}>\n <div\n className={cn(\"rounded-md border p-14 h-[400px]\", classNames.preview)}\n >\n <Preview variant={variant} variants={variants} />\n </div>\n </TabsContent>\n\n <TabsContent value={TabTypeEnum.CODE}>\n <ShikiCodeBlock\n code={variants[variant].code}\n lang={lang}\n className={classNames.code}\n />\n </TabsContent>\n </Tabs>\n );\n};\n\nconst Preview = ({\n variant,\n variants,\n}: {\n variant: string;\n variants: PreviewVariants;\n}) => {\n if (typeof variants[variant].preview !== \"function\") {\n return <>{variants[variant].preview}</>;\n }\n\n const Component = variants[variant].preview;\n\n return <Component />;\n};\n",
@@ -1381,7 +1403,7 @@
1381
1403
  "name": "Code Tabs",
1382
1404
  "exportName": "CodeTabs",
1383
1405
  "packageName": "@rlx-components/code-tabs",
1384
- "version": "0.0.2",
1406
+ "version": "1.0.0",
1385
1407
  "slug": "code-tabs",
1386
1408
  "category": "component",
1387
1409
  "sourceCode": "\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\nimport { ShikiCodeBlock } from \"@rlx-components/shiki-code-block\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@rlx-widgets/tabs\";\nimport { cn } from \"@rlx-widgets/base\";\nimport { getThemeBackgroundHex } from \"./utils\";\nimport { CodeTabsProps } from \"./types\";\n\nexport const CodeTabs = ({\n tabs,\n theme = \"ayu-dark\",\n startAdornment,\n}: CodeTabsProps) => {\n const [backgroundColor, setBackgroundColor] = useState<string | undefined>(\n undefined\n );\n const [value, setValue] = useState<string>(tabs[0]?.id ?? \"\");\n\n const handleValueChange = useCallback((newValue: string) => {\n setValue(newValue);\n }, []);\n\n useEffect(() => {\n void getThemeBackgroundHex(theme)\n .then((bgHex) => {\n if (bgHex) {\n setBackgroundColor(bgHex);\n }\n })\n .catch(() => {\n // Silently handle errors - fallback to default bg-muted/30\n });\n }, [theme]);\n\n if (!tabs.length || !value) {\n return null;\n }\n\n return (\n <div className=\"relative w-full\">\n <Tabs\n value={value}\n onValueChange={handleValueChange}\n className=\"w-full gap-0\"\n >\n <div\n className={cn(\n \"border-b pb-0 rounded-lg rounded-b-none\",\n backgroundColor ? \"\" : \"bg-muted/30\"\n )}\n style={backgroundColor ? { backgroundColor } : undefined}\n >\n <TabsList className=\"bg-transparent h-auto p-0\">\n {startAdornment}\n {tabs.map((tab) => (\n <TabsTrigger\n key={tab.id}\n value={tab.id}\n className=\"px-4 py-2.5 border-b-0\"\n >\n {tab.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </div>\n\n <div className=\"p-0\">\n {tabs.map((tab) => (\n <TabsContent key={tab.id} value={tab.id}>\n <ShikiCodeBlock\n code={tab.code}\n lang={tab.lang}\n className={cn(\n \"h-fit rounded-lg rounded-t-none border-0\",\n tab.codeBlockClassName\n )}\n />\n </TabsContent>\n ))}\n </div>\n </Tabs>\n </div>\n );\n};\n",
@@ -1421,10 +1443,10 @@
1421
1443
  "name": "Inline Code",
1422
1444
  "exportName": "InlineCode",
1423
1445
  "packageName": "@rlx-components/inline-code",
1424
- "version": "0.0.2",
1446
+ "version": "1.0.0",
1425
1447
  "slug": "inline-code",
1426
1448
  "category": "component",
1427
- "sourceCode": "import { cn } from \"@rlx-widgets/base\";\nimport { ReactNode } from \"react\";\n\nexport const InlineCode = ({ className, code, children }: InlineCodeProps) => {\n return (\n <span\n className={cn(\n \"bg-muted rounded px-1 py-0.5 overflow-x-auto text-sm\",\n className\n )}\n >\n <code>{code ?? children}</code>\n </span>\n );\n};\ntype InlineCodeProps =\n | { code: string; children?: never; className?: string }\n | { children: ReactNode; code?: never; className?: string };\n\nexport default InlineCode;\n",
1449
+ "sourceCode": "import { cn } from \"@rlx-widgets/base\";\nimport { ReactNode } from \"react\";\n\nexport const InlineCode = ({ className, code, children }: InlineCodeProps) => {\n return (\n <span\n className={cn(\n \"bg-muted rounded px-1 py-0.5 overflow-x-auto text-sm\",\n className\n )}\n data-slot=\"inline-code-root\"\n >\n <code data-slot=\"inline-code-content\">{code ?? children}</code>\n </span>\n );\n};\ntype InlineCodeProps =\n | { code: string; children?: never; className?: string }\n | { children: ReactNode; code?: never; className?: string };\n\nexport default InlineCode;\n",
1428
1450
  "demos": [
1429
1451
  {
1430
1452
  "name": "default",
@@ -1432,6 +1454,32 @@
1432
1454
  }
1433
1455
  ]
1434
1456
  },
1457
+ {
1458
+ "name": "Quiz Box",
1459
+ "exportName": "QuizBox",
1460
+ "packageName": "@rlx-components/quiz-box",
1461
+ "version": "0.0.1",
1462
+ "slug": "quiz-box",
1463
+ "category": "component",
1464
+ "sourceCode": "\"use client\";\n\nimport { Button } from \"@rlx-widgets/button\";\nimport { CheckCircle2, XCircle } from \"lucide-react\";\nimport { cn } from \"@rlx-widgets/base\";\nimport { ConfettiOverlay, QuizOptionButton } from \"./components\";\nimport { ConfettiPiece, QuizBoxProps } from \"./types\";\nimport { generateConfettis, getMessage } from \"./utils\";\nimport { Pill } from \"./components\";\nimport { useEffect, useState } from \"react\";\n\nexport function QuizBox({ quizzes }: QuizBoxProps) {\n const [currentIndex, setCurrentIndex] = useState<number>(0);\n const [selectedIndex, setSelectedIndex] = useState<number | null>(null);\n const [finished, setFinished] = useState<boolean>(false);\n const [numCorrect, setNumCorrect] = useState<number>(0);\n const [showConfetti, setShowConfetti] = useState<boolean>(false);\n const [confettiPieces, setConfettiPieces] = useState<Array<ConfettiPiece>>(\n []\n );\n const [correctAnswerFound, setCorrectAnswerFound] = useState<boolean>(false);\n\n const current = quizzes[currentIndex];\n const hasSelection = typeof selectedIndex === \"number\";\n const selectedIsCorrect =\n hasSelection && selectedIndex !== null\n ? !!current.options[selectedIndex].correct\n : false;\n\n const displayConfetti = (\n options?: Parameters<typeof generateConfettis>[0]\n ) => {\n setConfettiPieces(generateConfettis(options || {}));\n setShowConfetti(true);\n };\n\n const handleSelect = (optionIndex: number) => {\n // If correct answer was already found, prevent further selections\n if (correctAnswerFound) return;\n\n setSelectedIndex(optionIndex);\n if (current.options[optionIndex]?.correct) {\n setCorrectAnswerFound(true);\n setNumCorrect((n) => n + 1);\n displayConfetti();\n }\n };\n\n const handleAnother = () => {\n if (currentIndex + 1 < quizzes.length) {\n setCurrentIndex((idx) => idx + 1);\n setSelectedIndex(null);\n setCorrectAnswerFound(false);\n setShowConfetti(false);\n } else {\n setFinished(true);\n // Longer, denser confetti at the end\n displayConfetti({\n numPieces: 48,\n baseDurationMs: 2400,\n extraRandomMs: 2400,\n });\n }\n };\n\n const handleFinish = () => {\n setFinished(true);\n // Longer, denser confetti at the end\n displayConfetti({\n numPieces: 48,\n baseDurationMs: 2400,\n extraRandomMs: 2400,\n });\n };\n\n const handleRestart = () => {\n setCurrentIndex(0);\n setSelectedIndex(null);\n setFinished(false);\n setShowConfetti(false);\n setCorrectAnswerFound(false);\n setNumCorrect(0);\n };\n\n useEffect(() => {\n if (!showConfetti) return;\n const timeoutMs = finished ? 5200 : 2200;\n const t = setTimeout(() => setShowConfetti(false), timeoutMs);\n return () => clearTimeout(t);\n }, [showConfetti, finished]);\n\n if (finished) {\n const percentage = Math.round((numCorrect / quizzes.length) * 100);\n\n return (\n <div className=\"relative min-h-[400px] flex flex-col items-center justify-center space-y-6 py-8\">\n <ConfettiOverlay pieces={confettiPieces} />\n <div className=\"flex flex-col items-center gap-4 text-center\">\n <div className=\"flex items-center justify-center size-16 rounded-full bg-green-100\">\n <CheckCircle2 className=\"size-10 text-green-600\" />\n </div>\n <h3 className=\"text-2xl font-bold\">{getMessage(percentage)}</h3>\n <div className=\"space-y-2\">\n <div className=\"flex items-baseline justify-center gap-2\">\n <span className=\"text-5xl font-bold text-primary\">\n {numCorrect}\n </span>\n <span className=\"text-xl text-muted-foreground\">\n / {quizzes.length}\n </span>\n </div>\n <div className=\"flex items-center gap-2 justify-center\">\n <Pill className=\"text-sm font-semibold\">\n {percentage}% Correct\n </Pill>\n </div>\n </div>\n <p className=\"text-sm text-muted-foreground max-w-md\">\n Thanks for taking the quiz! You got{\" \"}\n <span className=\"font-semibold text-foreground\">{numCorrect}</span>{\" \"}\n {numCorrect === 1 ? \"question\" : \"questions\"} right out of{\" \"}\n {quizzes.length}.\n </p>\n </div>\n <div className=\"flex gap-3 pt-4\">\n <Button size=\"sm\" onClick={handleRestart}>\n Start over\n </Button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"min-h-[300px] space-y-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"inline-flex items-center gap-2 text-xs text-muted-foreground\">\n <Pill>\n Question {currentIndex + 1} of {quizzes.length}\n </Pill>\n <span className=\"hidden sm:inline\">•</span>\n <Pill>Score: {numCorrect}</Pill>\n </span>\n </div>\n <h4 className=\"font-medium\">{current.question}</h4>\n <div className=\"grid grid-cols-1 gap-3\">\n {current.options.map((opt, idx) => {\n const isSelected = selectedIndex === idx;\n const isCorrect = !!opt.correct;\n const showCorrect = hasSelection && isSelected && isCorrect;\n const showWrong = hasSelection && isSelected && !isCorrect;\n\n return (\n <QuizOptionButton\n key={idx}\n state={showCorrect ? \"correct\" : showWrong ? \"wrong\" : \"default\"}\n serial={String.fromCharCode(65 + idx)}\n disabled={correctAnswerFound}\n onClick={() => handleSelect(idx)}\n selected={isSelected}\n >\n {opt.text}\n </QuizOptionButton>\n );\n })}\n </div>\n\n <div className=\"space-y-3\">\n {hasSelection && (\n <div\n className={cn(\n \"flex items-center gap-2 rounded-md px-3 py-2\",\n selectedIsCorrect\n ? \"bg-green-50 text-green-800\"\n : \"bg-red-50 text-red-800\"\n )}\n >\n {selectedIsCorrect ? (\n <CheckCircle2 className=\"size-4\" />\n ) : (\n <XCircle className=\"size-4\" />\n )}\n <p className=\"text-sm\">\n {selectedIsCorrect\n ? \"Correct!\"\n : \"Not quite. Try again or skip to next question!\"}\n </p>\n </div>\n )}\n <div className=\"flex gap-3\">\n <Button size=\"sm\" onClick={handleAnother}>\n {hasSelection && !correctAnswerFound ? \"Skip\" : \"Next Question\"}\n </Button>\n <Button size=\"sm\" variant=\"outline\" onClick={handleFinish}>\n Finish\n </Button>\n </div>\n </div>\n </div>\n );\n}\n",
1465
+ "demos": [
1466
+ {
1467
+ "name": "default",
1468
+ "code": "\"use client\";\n\nimport { QuizBox, QuizQuestion } from \"@rlx-components/quiz-box\";\n\nexport const Preview = () => {\n const quizzes: Array<QuizQuestion> = [\n {\n question: \"Which of the following is a JavaScript framework?\",\n options: [\n { text: \"React\", correct: true },\n { text: \"Python\" },\n { text: \"Java\" },\n { text: \"C++\" },\n ],\n },\n {\n question: \"What is the capital of France?\",\n options: [\n { text: \"London\" },\n { text: \"Berlin\" },\n { text: \"Paris\", correct: true },\n { text: \"Madrid\" },\n ],\n },\n {\n question: \"Which HTML tag is used for the largest heading?\",\n options: [\n { text: \"<h6>\" },\n { text: \"<h1>\", correct: true },\n { text: \"<heading>\" },\n { text: \"<head>\" },\n ],\n },\n ];\n\n return (\n <div className=\"w-full max-w-2xl\">\n <QuizBox quizzes={quizzes} />\n </div>\n );\n};\n\n"
1469
+ }
1470
+ ],
1471
+ "sourceFiles": {
1472
+ "components/ConfettiOverlay/ConfettiOverlay.tsx": "\"use client\";\n\nimport { cn } from \"@rlx-widgets/base\";\nimport type { ConfettiOverlayProps } from \"./types\";\n\nconst ConfettiOverlay = ({ pieces, className }: ConfettiOverlayProps) => {\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"pointer-events-none absolute inset-0 overflow-hidden\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t>\n\t\t\t{pieces.map((cfg, i) => (\n\t\t\t\t<span\n\t\t\t\t\tkey={i}\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\tleft: `${cfg.left}%`,\n\t\t\t\t\t\ttop: `${cfg.top}%`,\n\t\t\t\t\t\tanimationDelay: `${cfg.delayMs}ms`,\n\t\t\t\t\t\tanimationDuration: `${cfg.durationMs}ms`,\n\t\t\t\t\t\ttransform: `rotate(${cfg.rotate}deg)`,\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"absolute top-0 inline-block\"\n\t\t\t\t>\n\t\t\t\t\t<span\n\t\t\t\t\t\tstyle={{\n\t\t\t\t\t\t\tdisplay: \"inline-block\",\n\t\t\t\t\t\t\twidth: cfg.size,\n\t\t\t\t\t\t\theight: cfg.size * 2,\n\t\t\t\t\t\t\tbackgroundColor: cfg.color,\n\t\t\t\t\t\t\tborderRadius: 2,\n\t\t\t\t\t\t\tanimationName: \"confettiFall\",\n\t\t\t\t\t\t\tanimationTimingFunction:\n\t\t\t\t\t\t\t\t\"cubic-bezier(0.2, 0.6, 0.2, 1)\",\n\t\t\t\t\t\t\tanimationIterationCount: 1,\n\t\t\t\t\t\t\tanimationFillMode: \"forwards\",\n\t\t\t\t\t\t\tanimationDelay: `${cfg.delayMs}ms`,\n\t\t\t\t\t\t\tanimationDuration: `${cfg.durationMs}ms`,\n\t\t\t\t\t\t}}\n\t\t\t\t\t/>\n\t\t\t\t</span>\n\t\t\t))}\n\t\t\t<style\n\t\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t\t__html: `\n\t\t\t\t@keyframes confettiFall {\n\t\t\t\t\t0% {\n\t\t\t\t\t\topacity: 0;\n\t\t\t\t\t\ttransform: translateY(-10px) rotate(0deg);\n\t\t\t\t\t}\n\t\t\t\t\t10% {\n\t\t\t\t\t\topacity: 1;\n\t\t\t\t\t}\n\t\t\t\t\t100% {\n\t\t\t\t\t\topacity: 0;\n\t\t\t\t\t\ttransform: translateY(110%) rotate(720deg);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t</div>\n\t);\n};\n\nexport { ConfettiOverlay };\n",
1473
+ "components/ConfettiOverlay/types/ConfettiOverlayProps.t.ts": "import { ConfettiPiece } from \"../../../types\";\n\nexport type ConfettiOverlayProps = {\n pieces: Array<ConfettiPiece>;\n className?: string;\n};\n\n",
1474
+ "components/Pill.tsx": "import { cn } from \"@rlx-widgets/base\";\nimport { ComponentProps } from \"react\";\n\nexport const Pill = ({\n\tchildren,\n\tclassName,\n\t...props\n}: ComponentProps<\"span\">) => {\n\treturn (\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"inline-flex items-center rounded-full bg-secondary px-2 py-1\",\n\t\t\t\tclassName\n\t\t\t)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{children}\n\t\t</span>\n\t);\n};\n",
1475
+ "components/QuizOptionButton/QuizOptionButton.tsx": "\"use client\";\n\nimport { cn } from \"@rlx-widgets/base\";\nimport { ConfettiOverlay } from \"../ConfettiOverlay\";\nimport { cva, VariantProps } from \"class-variance-authority\";\nimport { generateConfettis } from \"../../utils\";\nimport { ComponentProps, useMemo } from \"react\";\n\nconst quizOptionButtonVariants = cva(\n \"relative rounded-lg border p-3 text-left transition-all hover:shadow-md active:scale-[0.99] cursor-pointer\",\n {\n variants: {\n state: {\n default: \"bg-muted/40 hover:bg-muted/60\",\n correct: \"bg-green-100 border-green-300 text-green-900\",\n wrong: \"bg-red-50 border-red-300 text-red-900\",\n },\n },\n defaultVariants: {\n state: \"default\",\n },\n }\n);\n\nexport type QuizOptionButtonProps = ComponentProps<\"button\"> &\n VariantProps<typeof quizOptionButtonVariants> & {\n selected?: boolean;\n serial: number | string;\n };\n\nexport const QuizOptionButton = ({\n className,\n state,\n selected = false,\n serial,\n children,\n ...props\n}: QuizOptionButtonProps) => {\n const confettiPieces = useMemo(() => {\n if (selected && state === \"correct\") {\n return generateConfettis();\n }\n return [];\n }, [selected, state]);\n\n return (\n <button\n data-slot=\"button\"\n className={cn(quizOptionButtonVariants({ state, className }))}\n type=\"button\"\n {...props}\n >\n {selected && state === \"correct\" && (\n <ConfettiOverlay pieces={confettiPieces} />\n )}\n <span className=\"flex items-center gap-3\">\n <span className=\"inline-flex size-6 items-center justify-center rounded-full text-xs font-semibold\">\n {serial}\n </span>\n <span className=\"text-sm font-medium\">{children}</span>\n </span>\n </button>\n );\n};\n",
1476
+ "types/ConfettiPiece.t.ts": "export type ConfettiPiece = {\n\tleft: number;\n\ttop: number;\n\tdelayMs: number;\n\tdurationMs: number;\n\trotate: number;\n\tsize: number;\n\tcolor: string;\n};\n",
1477
+ "types/QuizBoxProps.t.ts": "import { QuizQuestion } from \"./QuizQuestion.t\";\n\nexport type QuizBoxProps = {\n quizzes: Array<QuizQuestion>;\n};\n\n",
1478
+ "types/QuizQuestion.t.ts": "export type QuizQuestion = {\n\tquestion: string;\n\toptions: Array<{\n\t\ttext: string;\n\t\tcorrect?: boolean;\n\t}>;\n};\n",
1479
+ "utils/generateConfettis.ts": "export const generateConfettis = (options?: {\n\tnumPieces?: number;\n\tbaseDurationMs?: number;\n\textraRandomMs?: number;\n}) => {\n\tconst {\n\t\tnumPieces = 24,\n\t\tbaseDurationMs = 2000,\n\t\textraRandomMs = 900,\n\t} = options || {};\n\n\tconst colors = [\"#22c55e\", \"#3b82f6\", \"#eab308\", \"#ef4444\", \"#a855f7\"];\n\tconst pieces = Array.from({ length: numPieces }).map((_, i) => ({\n\t\tleft: Math.random() * 100,\n\t\ttop: Math.random() * 100,\n\t\tdelayMs: 0,\n\t\tdurationMs: baseDurationMs + Math.floor(Math.random() * extraRandomMs),\n\t\trotate: Math.floor(Math.random() * 360),\n\t\tsize: 8 + Math.floor(Math.random() * 8),\n\t\tcolor: colors[i % colors.length],\n\t}));\n\n\treturn pieces;\n};\n",
1480
+ "utils/getMessage.ts": "export const getMessage = (percentage: number): string => {\n if (percentage === 100) return \"Perfect Score! 🎉\";\n if (percentage >= 80) return \"Excellent Work! 🚀\";\n if (percentage >= 60) return \"Great Job! 👍\";\n return \"Nice Try! 💪\";\n};\n\n"
1481
+ }
1482
+ },
1435
1483
  {
1436
1484
  "name": "Selectable Button",
1437
1485
  "exportName": "SelectableButton",
@@ -1472,10 +1520,10 @@
1472
1520
  "name": "Shiki Code Block",
1473
1521
  "exportName": "ShikiCodeBlock",
1474
1522
  "packageName": "@rlx-components/shiki-code-block",
1475
- "version": "0.0.4",
1523
+ "version": "1.0.0",
1476
1524
  "slug": "shiki-code-block",
1477
1525
  "category": "component",
1478
- "sourceCode": "\"use client\";\n\nimport { JSX, useLayoutEffect, useState } from \"react\";\nimport { highlight } from \"./utils\";\nimport { Button } from \"@rlx-widgets/button\";\nimport { cn } from \"@rlx-widgets/base\";\nimport { BundledLanguage } from \"shiki/bundle/web\";\nimport { Copy } from \"lucide-react\";\n\nexport const ShikiCodeBlock = ({\n code,\n lang,\n className,\n}: {\n code: string;\n lang: BundledLanguage;\n className?: string;\n}) => {\n const [onHover, setOnHover] = useState(false);\n const [highlightedCode, setHighlightedCode] = useState<JSX.Element>();\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n };\n\n useLayoutEffect(() => {\n void highlight({\n code,\n lang,\n className: cn(\"h-[400px]\", className),\n }).then(setHighlightedCode);\n }, [className, code, lang]);\n\n return (\n <div\n onMouseEnter={() => setOnHover(true)}\n onMouseLeave={() => setOnHover(false)}\n className={className}\n data-slot=\"shiki-code-block-root\"\n >\n <div className=\"relative\" data-slot=\"shiki-code-block-container\">\n <div data-slot=\"shiki-code-block-content\">\n {highlightedCode ?? <p>Loading...</p>}\n </div>\n <span\n className={cn(\n \"absolute top-[8px] right-[16px]\",\n \"text-xs\",\n onHover && \"hidden\"\n )}\n data-slot=\"shiki-code-block-lang\"\n >\n {lang}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCopy}\n className={cn(\n \"absolute top-[8px] right-[16px]\",\n \"cursor-pointer\",\n !onHover && \"hidden\"\n )}\n data-slot=\"shiki-code-block-copy-button\"\n >\n <Copy /> {copied ? \"Copied!\" : \"Copy\"}\n </Button>\n </div>\n </div>\n );\n};\n",
1526
+ "sourceCode": "\"use client\";\n\nimport { JSX, useLayoutEffect, useState } from \"react\";\nimport { highlight } from \"./utils\";\nimport { Button } from \"@rlx-widgets/button\";\nimport { cn } from \"@rlx-widgets/base\";\nimport { BundledLanguage, BundledTheme } from \"shiki/bundle/web\";\nimport { Copy } from \"lucide-react\";\n\nexport const ShikiCodeBlock = ({\n code,\n lang,\n className,\n theme = \"ayu-dark\",\n showLineNumbers = false,\n}: {\n code: string;\n lang: BundledLanguage;\n className?: string;\n theme?: BundledTheme;\n showLineNumbers?: boolean;\n}) => {\n const [onHover, setOnHover] = useState(false);\n const [highlightedCode, setHighlightedCode] = useState<JSX.Element>();\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n };\n\n useLayoutEffect(() => {\n void highlight({\n code,\n lang,\n theme,\n showLineNumbers,\n className: cn(\"h-[400px]\", className),\n }).then(setHighlightedCode);\n }, [className, code, lang, theme, showLineNumbers]);\n\n return (\n <div\n onMouseEnter={() => setOnHover(true)}\n onMouseLeave={() => setOnHover(false)}\n className={className}\n data-slot=\"shiki-code-block-root\"\n >\n <div className=\"relative\" data-slot=\"shiki-code-block-container\">\n <div data-slot=\"shiki-code-block-content\">\n {highlightedCode ?? <p>Loading...</p>}\n </div>\n <span\n className={cn(\n \"absolute top-[8px] right-[16px]\",\n \"text-xs\",\n onHover && \"hidden\"\n )}\n data-slot=\"shiki-code-block-lang\"\n >\n {lang}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCopy}\n className={cn(\n \"absolute top-[8px] right-[16px]\",\n \"cursor-pointer\",\n !onHover && \"hidden\"\n )}\n data-slot=\"shiki-code-block-copy-button\"\n >\n <Copy /> {copied ? \"Copied!\" : \"Copy\"}\n </Button>\n </div>\n </div>\n );\n};\n",
1479
1527
  "demos": [
1480
1528
  {
1481
1529
  "name": "default",
@@ -1483,7 +1531,7 @@
1483
1531
  }
1484
1532
  ],
1485
1533
  "sourceFiles": {
1486
- "utils/highlight.tsx": "import { codeToHast } from \"shiki/bundle/web\";\nimport { Fragment } from \"react\";\nimport { jsx, jsxs } from \"react/jsx-runtime\";\nimport { toJsxRuntime } from \"hast-util-to-jsx-runtime\";\nimport type { JSX } from \"react\";\nimport type { BundledLanguage } from \"shiki/bundle/web\";\nimport { cn } from \"@rlx-widgets/base\";\n\nexport async function highlight({\n code,\n lang,\n className,\n}: {\n code: string;\n lang: BundledLanguage;\n className?: string;\n}) {\n const out = await codeToHast(code, {\n lang,\n theme: \"ayu-dark\",\n });\n\n return toJsxRuntime(out, {\n Fragment,\n jsx,\n jsxs,\n components: {\n pre: (props) => (\n <pre\n {...props}\n className={cn(\"rounded p-4 overflow-x-auto\", className)}\n data-slot=\"shiki-code-block-code\"\n />\n ),\n },\n }) as JSX.Element;\n}\n"
1534
+ "utils/highlight.tsx": "import { codeToHast } from \"shiki/bundle/web\";\nimport { Fragment } from \"react\";\nimport { jsx, jsxs } from \"react/jsx-runtime\";\nimport { toJsxRuntime } from \"hast-util-to-jsx-runtime\";\nimport type { JSX } from \"react\";\nimport type { BundledLanguage, BundledTheme } from \"shiki/bundle/web\";\nimport { cn } from \"@rlx-widgets/base\";\n\nexport async function highlight({\n code,\n lang,\n className,\n theme = \"ayu-dark\",\n showLineNumbers = false,\n}: {\n code: string;\n lang: BundledLanguage;\n className?: string;\n theme?: BundledTheme;\n showLineNumbers?: boolean;\n}) {\n const out = await codeToHast(code, {\n lang,\n theme,\n });\n\n return toJsxRuntime(out, {\n Fragment,\n jsx,\n jsxs,\n components: {\n pre: (props) => (\n <pre\n {...props}\n className={cn(\"rounded p-4 overflow-x-auto\", className)}\n data-slot=\"shiki-code-block-code\"\n />\n ),\n code: (props) => (\n <code\n {...props}\n className={cn(\n showLineNumbers &&\n \"[counter-reset:step] [counter-increment:step_0] [&_.line::before]:[content:counter(step)] [&_.line]:before:[counter-increment:step] [&_.line]:before:w-4 [&_.line]:before:mr-6 [&_.line]:before:inline-block [&_.line]:before:text-right [&_.line]:before:[color:rgba(115,138,148,.4)]\"\n )}\n />\n ),\n },\n }) as JSX.Element;\n}\n"
1487
1535
  }
1488
1536
  },
1489
1537
  {
@@ -1648,5 +1696,5 @@
1648
1696
  "sourceCode": "export const isValidDate = (date: Date) => {\n return !Number.isNaN(date.getTime());\n};\n\nexport default isValidDate;\n"
1649
1697
  }
1650
1698
  ],
1651
- "extractedAt": "2025-12-08T03:17:41.438Z"
1699
+ "extractedAt": "2026-05-10T06:04:14.124Z"
1652
1700
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rlx-ui/mcp",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "MCP (Model Context Protocol) Server for RLX UI components",
5
5
  "publishConfig": {
6
6
  "access": "public"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rlx-ui/mcp",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "MCP (Model Context Protocol) Server for RLX UI components",
5
5
  "publishConfig": {
6
6
  "access": "public"