Compare commits

..

13 Commits

Author SHA1 Message Date
shadcn
d9f57eb0e8 Revert "feat: add @next-ui registry (#10693)"
This reverts commit d7066f4a2d.
2026-05-20 17:12:30 +04:00
Tushar Jolly
d7066f4a2d feat: add @next-ui registry (#10693)
* feat: add @next-ui registry

## New Registry: @next-ui

- **Homepage:** https://nexus-ui.com
- **Registry URL:** https://nexus-ui.com/r/{name}.json
- **Registry index:** https://nexus-ui.com/registry.json
- **Components:** 57 motion-native components for Next.js

### Checklist
- [x] registry.json live at https://nexus-ui.com/registry.json
- [x] Component files live at https://nexus-ui.com/r/{name}.json
- [x] Open source
- [x] Works with shadcn CLI

* Add Nexus Labs registry entry to registries.json
2026-05-20 16:49:50 +04:00
Matt
5274de83d6 feat(registry): add @bklit registry (#10707)
Add Bklit UI to the open source registry index (directory.json and
registries.json) for shadcn add and shadcn search.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-20 16:44:20 +04:00
Fabian Hiller
7e4dac7f31 feat: add Formisch (form library) examples to docs (#10342)
* feat: add Formisch (form library) examples to docs

* docs: update Formisch docs with additional form methods and examples

* chore: update valibot dependency to version 1.4.0

* style(docs): format formisch imports

---------

Co-authored-by: shadcn <m@shadcn.com>
2026-05-20 15:18:41 +04:00
Charlie Hopkins-Brinicombe
28122dba18 feat: add @trophy-ui registry (#10700) 2026-05-20 09:44:13 +04:00
Parth Sharma
93cde61946 feat(registry): add Wensity (#10676) 2026-05-18 22:13:41 +04:00
lcorrigan704
c2dc06a99c add ui.corr.sh registry (#10675) 2026-05-18 18:57:42 +04:00
shadcn
c9930b7fda fix(toggle-group): update default spacing (#10681) 2026-05-17 12:52:42 +04:00
shadcn
d1149454a8 fix(create): preserve settings when changing style (#10680) 2026-05-17 12:31:37 +04:00
Michael McGovern
36139f6200 feat(registry): add @paddle registry (#10643)
* Add Paddle UI to directory

* Update description for @ncdai #10591
2026-05-14 15:45:15 +04:00
shadcn
15ac1be92b feat: shadcn/create for existing projects (#10622)
* feat: update project form

* feat: switch to toggle

* fix

* fix: color
2026-05-12 15:15:07 +04:00
Sergey Kuznetsov
8ca30ed32c feat(registry): add @turbopills-ui registry (#10579) 2026-05-10 20:34:04 +04:00
Alex Kostyniuk
e2605bc7c2 Update glasscn registry URL (#10597) 2026-05-10 20:33:38 +04:00
73 changed files with 3590 additions and 458 deletions

View File

@@ -1,5 +0,0 @@
---
"shadcn": patch
---
handle cn-\* class transformation in non-className contexts

View File

@@ -12,20 +12,25 @@ import { HugeiconsIcon } from "@hugeicons/react"
import { cn } from "@/lib/utils"
import { useConfig } from "@/hooks/use-config"
import { copyToClipboardWithMeta } from "@/components/copy-button"
import { BASES, type BaseName } from "@/registry/config"
import {
BASES,
buildThemeForPreset,
DEFAULT_CONFIG,
type BaseName,
type DesignSystemConfig,
} from "@/registry/config"
import { Button } from "@/styles/base-nova/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/styles/base-nova/ui/dialog"
import {
Field,
FieldContent,
FieldDescription,
FieldGroup,
FieldLabel,
FieldLegend,
@@ -41,6 +46,10 @@ import {
TabsList,
TabsTrigger,
} from "@/styles/base-nova/ui/tabs"
import {
ToggleGroup,
ToggleGroupItem,
} from "@/styles/base-nova/ui/toggle-group"
import { usePresetCode } from "@/app/(app)/create/hooks/use-design-system"
import {
useDesignSystemSearchParams,
@@ -61,6 +70,54 @@ const SHADCN_VERSION = process.env.NEXT_PUBLIC_RC ? "@rc" : "@latest"
const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"] as const
type PackageManager = (typeof PACKAGE_MANAGERS)[number]
const APPLY_MODES = [
{
value: "full",
title: "Full preset",
description:
"Everything from the preset, including components, theme, and fonts.",
flag: null,
label: "full preset",
},
{
value: "theme",
title: "Theme only",
description:
"Theme tokens only, like colors, radii, and shadows. Components stay as they are.",
flag: "--only theme",
label: "--only theme",
},
{
value: "font",
title: "Fonts only",
description:
"Only preset fonts for body and headings. Components stay as they are.",
flag: "--only font",
label: "--only font",
},
] as const
type ApplyMode = (typeof APPLY_MODES)[number]["value"]
type ProjectFormTab = "new-project" | "existing-project" | "theme"
type CopyTarget = "command" | "apply" | "theme"
type ThemeCssVars = NonNullable<
ReturnType<typeof buildThemeForPreset>["cssVars"]
>
function formatCssVarsRule(selector: string, cssVars?: Record<string, string>) {
const declarations = Object.entries(cssVars ?? {})
.map(([key, value]) => ` --${key}: ${value};`)
.join("\n")
return `${selector} {\n${declarations}\n}`
}
function formatThemeCss(cssVars: ThemeCssVars) {
return [
formatCssVarsRule(":root", cssVars.light),
formatCssVarsRule(".dark", cssVars.dark),
].join("\n\n")
}
export function ProjectForm({
className,
}: React.ComponentProps<typeof Button>) {
@@ -68,7 +125,12 @@ export function ProjectForm({
const [params, setParams] = useDesignSystemSearchParams()
const presetCode = usePresetCode()
const [config, setConfig] = useConfig()
const [hasCopied, setHasCopied] = React.useState(false)
const [copiedTarget, setCopiedTarget] = React.useState<CopyTarget | null>(
null
)
const [applyMode, setApplyMode] = React.useState<ApplyMode>("full")
const [activeTab, setActiveTab] =
React.useState<ProjectFormTab>("new-project")
const packageManager = (config.packageManager || "pnpm") as PackageManager
const framework = React.useMemo(
@@ -117,12 +179,85 @@ export function ProjectForm({
const command = commands[packageManager]
const applyCommands = React.useMemo(() => {
const presetFlag = ` --preset ${presetCode}`
const onlyFlag =
applyMode === "theme"
? " --only theme"
: applyMode === "font"
? " --only font"
: ""
const flags = `${presetFlag}${onlyFlag}`
return IS_LOCAL_DEV
? {
pnpm: `shadcn apply${flags}`,
npm: `shadcn apply${flags}`,
yarn: `shadcn apply${flags}`,
bun: `shadcn apply${flags}`,
}
: {
pnpm: `pnpm dlx shadcn${SHADCN_VERSION} apply${flags}`,
npm: `npx shadcn${SHADCN_VERSION} apply${flags}`,
yarn: `yarn dlx shadcn${SHADCN_VERSION} apply${flags}`,
bun: `bunx --bun shadcn${SHADCN_VERSION} apply${flags}`,
}
}, [applyMode, presetCode])
const applyCommand = applyCommands[packageManager]
const themeConfig = React.useMemo<DesignSystemConfig>(() => {
const isRadiusLocked = params.style === "lyra" || params.style === "sera"
return {
...DEFAULT_CONFIG,
base: params.base,
style: params.style,
baseColor: params.baseColor,
theme: params.theme,
chartColor: params.chartColor,
iconLibrary: params.iconLibrary,
font: params.font,
fontHeading: params.fontHeading,
menuAccent: params.menuAccent,
menuColor: params.menuColor,
radius: isRadiusLocked ? "none" : params.radius,
template: params.template,
rtl: params.rtl,
pointer: params.pointer,
}
}, [
params.base,
params.baseColor,
params.chartColor,
params.font,
params.fontHeading,
params.iconLibrary,
params.menuAccent,
params.menuColor,
params.pointer,
params.radius,
params.rtl,
params.style,
params.template,
params.theme,
])
const themeCss = React.useMemo(() => {
const theme = buildThemeForPreset(themeConfig)
if (!theme.cssVars) {
return ""
}
return formatThemeCss(theme.cssVars)
}, [themeConfig])
React.useEffect(() => {
if (hasCopied) {
const timer = setTimeout(() => setHasCopied(false), 2000)
if (copiedTarget) {
const timer = setTimeout(() => setCopiedTarget(null), 2000)
return () => clearTimeout(timer)
}
}, [hasCopied])
}, [copiedTarget])
const handleCopy = React.useCallback(() => {
const properties: Record<string, string> = {
@@ -135,159 +270,316 @@ export function ProjectForm({
name: "copy_npm_command",
properties,
})
setHasCopied(true)
setCopiedTarget("command")
}, [command, params.template])
const handleCopyApply = React.useCallback(() => {
copyToClipboardWithMeta(applyCommand, {
name: "copy_apply_command",
properties: {
command: applyCommand,
applyMode,
},
})
setCopiedTarget("apply")
}, [applyCommand, applyMode])
const handleCopyTheme = React.useCallback(() => {
copyToClipboardWithMeta(themeCss, {
name: "copy_theme_code",
properties: {
preset: presetCode,
baseColor: params.baseColor,
theme: params.theme,
format: "css",
},
})
setCopiedTarget("theme")
}, [params.baseColor, params.theme, presetCode, themeCss])
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger render={<Button className={cn(className)} />}>
Create Project
Get Code
</DialogTrigger>
<DialogContent className="dark no-scrollbar max-h-[calc(100svh-2rem)] overflow-y-auto rounded-2xl p-6 shadow-xl **:data-[slot=field-separator]:h-2 sm:max-w-sm">
<DialogHeader>
<DialogTitle>Create Project</DialogTitle>
<DialogDescription>
Pick a template and configure your project.
</DialogDescription>
</DialogHeader>
<div>
<FieldGroup>
<FieldSeparator className="-mx-6" />
<Field className="-mt-2 gap-3">
<FieldLabel>Template</FieldLabel>
<TemplateGrid template={params.template} setParams={setParams} />
</Field>
<FieldSeparator className="-mx-6" />
<Field className="-mt-2">
<FieldLabel>Base</FieldLabel>
<BaseGrid base={params.base} setParams={setParams} />
</Field>
<FieldSeparator className="-mx-6" />
<FieldSet>
<FieldLegend variant="label" className="sr-only">
Options
</FieldLegend>
<Field orientation="horizontal">
<FieldLabel htmlFor="pointer">
<HugeiconsIcon
icon={HandPointingRight04Icon}
className="size-4 -rotate-90"
/>
Use pointer on buttons
</FieldLabel>
<Switch
id="pointer"
checked={params.pointer}
onCheckedChange={(checked) =>
setParams({ pointer: checked === true })
}
/>
</Field>
<FieldSeparator className="-mx-6" />
<Field
orientation="horizontal"
data-disabled={hasMonorepo ? undefined : "true"}
>
<FieldLabel htmlFor="monorepo">
<span
className="size-4 text-neutral-100 [&_svg]:size-4 [&_svg]:fill-current"
dangerouslySetInnerHTML={{
__html: TURBOREPO_LOGO,
}}
/>
Create a monorepo
</FieldLabel>
<Switch
id="monorepo"
checked={params.template?.endsWith("-monorepo") ?? false}
disabled={!hasMonorepo}
onCheckedChange={(checked) => {
const framework = getFramework(params.template ?? "next")
setParams({
template: getTemplateValue(
framework,
checked === true
) as typeof params.template,
})
}}
/>
</Field>
<FieldSeparator className="-mx-6" />
<Field orientation="horizontal">
<FieldLabel htmlFor="rtl">
<HugeiconsIcon icon={Globe02Icon} className="size-4" />
Enable RTL support
</FieldLabel>
<Switch
id="rtl"
checked={params.rtl}
onCheckedChange={(checked) =>
setParams({ rtl: checked === true })
}
/>
</Field>
</FieldSet>
</FieldGroup>
</div>
<DialogFooter className="-mx-6 -mb-6 min-w-0">
<div className="flex w-full min-w-0 flex-col gap-3">
<Tabs
value={packageManager}
onValueChange={(value) => {
setConfig((prev) => ({
...prev,
packageManager: value as PackageManager,
}))
}}
className="min-w-0 gap-0 overflow-hidden rounded-xl border-0 ring-1 ring-border"
<DialogContent className="dark top-[64px] no-scrollbar flex max-h-[calc(100svh-2rem)] translate-y-0 flex-col rounded-2xl p-0 shadow-xl **:data-[slot=dialog-close]:top-4.5 **:data-[slot=dialog-close]:right-4 **:data-[slot=field-separator]:h-2 sm:max-w-md">
<div className="flex min-w-0 flex-1 flex-col gap-0 overflow-hidden rounded-2xl">
<DialogHeader className="border-b px-6 py-5">
<ToggleGroup
value={[activeTab]}
onValueChange={(values) =>
setActiveTab((values[0] as typeof activeTab) ?? "new-project")
}
aria-label="Project type"
spacing={2}
className="**:data-[slot=toggle-group-item]:data-pressed:bg-neutral-700/70"
>
<div className="flex items-center gap-2 py-1 pr-1.5 pl-1">
<TabsList className="bg-transparent font-mono">
{PACKAGE_MANAGERS.map((manager) => {
return (
<TabsTrigger
key={manager}
value={manager}
className="py-0 leading-none data-[state=active]:shadow-none"
<ToggleGroupItem value="new-project">New Project</ToggleGroupItem>
<ToggleGroupItem value="existing-project">
Existing Project
</ToggleGroupItem>
<ToggleGroupItem value="theme">Theme</ToggleGroupItem>
</ToggleGroup>
</DialogHeader>
{activeTab === "new-project" && (
<div className="no-scrollbar overflow-y-auto">
<FieldGroup className="px-6 py-4">
<Field className="gap-3">
<FieldLabel>Template</FieldLabel>
<TemplateGrid
template={params.template}
setParams={setParams}
/>
</Field>
<FieldSeparator className="-mx-6" />
<Field>
<FieldLabel>Base</FieldLabel>
<BaseGrid base={params.base} setParams={setParams} />
</Field>
<FieldSeparator className="-mx-6" />
<FieldSet>
<FieldLegend variant="label" className="sr-only">
Options
</FieldLegend>
<Field orientation="horizontal">
<FieldLabel htmlFor="pointer">
<HugeiconsIcon
icon={HandPointingRight04Icon}
className="size-4 -rotate-90"
/>
Use pointer on buttons
</FieldLabel>
<Switch
id="pointer"
checked={params.pointer}
onCheckedChange={(checked) =>
setParams({ pointer: checked === true })
}
/>
</Field>
<FieldSeparator className="-mx-6" />
<Field
orientation="horizontal"
data-disabled={hasMonorepo ? undefined : "true"}
>
<FieldLabel htmlFor="monorepo">
<span
className="size-4 text-neutral-100 [&_svg]:size-4 [&_svg]:fill-current"
dangerouslySetInnerHTML={{
__html: TURBOREPO_LOGO,
}}
/>
Create a monorepo
</FieldLabel>
<Switch
id="monorepo"
checked={params.template?.endsWith("-monorepo") ?? false}
disabled={!hasMonorepo}
onCheckedChange={(checked) => {
const framework = getFramework(
params.template ?? "next"
)
setParams({
template: getTemplateValue(
framework,
checked === true
) as typeof params.template,
})
}}
/>
</Field>
<FieldSeparator className="-mx-6" />
<Field orientation="horizontal">
<FieldLabel htmlFor="rtl">
<HugeiconsIcon icon={Globe02Icon} className="size-4" />
Enable RTL support
</FieldLabel>
<Switch
id="rtl"
checked={params.rtl}
onCheckedChange={(checked) =>
setParams({ rtl: checked === true })
}
/>
</Field>
</FieldSet>
</FieldGroup>
<DialogFooter className="m-0 min-w-0 p-6">
<div className="flex w-full min-w-0 flex-col gap-3">
<Tabs
value={packageManager}
onValueChange={(value) => {
setConfig((prev) => ({
...prev,
packageManager: value as PackageManager,
}))
}}
className="min-w-0 gap-0 overflow-hidden rounded-xl border-0 ring-1 ring-border"
>
<div className="flex items-center gap-2 py-1 pr-1.5 pl-1">
<TabsList className="bg-transparent font-mono">
{PACKAGE_MANAGERS.map((manager) => {
return (
<TabsTrigger
key={manager}
value={manager}
className="py-0 leading-none data-[state=active]:shadow-none"
>
{manager}
</TabsTrigger>
)
})}
</TabsList>
<Button
size="icon-sm"
variant="ghost"
className="ml-auto"
onClick={handleCopy}
>
{manager}
</TabsTrigger>
)
})}
</TabsList>
<Button
size="icon-sm"
variant="ghost"
className="ml-auto"
onClick={handleCopy}
>
{hasCopied ? (
<HugeiconsIcon icon={Tick02Icon} />
) : (
<HugeiconsIcon icon={Copy01Icon} />
)}
<span className="sr-only">Copy command</span>
</Button>
</div>
{Object.entries(commands).map(([key, cmd]) => {
return (
<TabsContent key={key} value={key}>
<div className="relative overflow-hidden border-t bg-popover p-3">
<div className="no-scrollbar overflow-x-auto">
<code className="font-mono text-sm whitespace-nowrap">
{cmd}
</code>
</div>
{copiedTarget === "command" ? (
<HugeiconsIcon icon={Tick02Icon} />
) : (
<HugeiconsIcon icon={Copy01Icon} />
)}
<span className="sr-only">Copy command</span>
</Button>
</div>
</TabsContent>
)
})}
</Tabs>
<Button onClick={handleCopy} className="h-9 w-full">
{hasCopied ? "Copied" : "Copy Command"}
</Button>
</div>
</DialogFooter>
{Object.entries(commands).map(([key, cmd]) => {
return (
<TabsContent key={key} value={key}>
<div className="relative overflow-hidden border-t bg-popover p-3">
<div className="no-scrollbar overflow-x-auto">
<code className="font-mono text-sm whitespace-nowrap">
{cmd}
</code>
</div>
</div>
</TabsContent>
)
})}
</Tabs>
<Button onClick={handleCopy} className="h-9 w-full">
{copiedTarget === "command" ? "Copied" : "Copy Command"}
</Button>
</div>
</DialogFooter>
</div>
)}
{activeTab === "existing-project" && (
<div className="no-scrollbar overflow-y-auto">
<FieldGroup className="px-6 py-4">
<FieldSet className="gap-3">
<FieldLegend variant="label">Apply Preset</FieldLegend>
<FieldDescription>
Pick which parts of the preset to apply.
</FieldDescription>
<ApplyModeGrid mode={applyMode} setMode={setApplyMode} />
</FieldSet>
</FieldGroup>
<DialogFooter className="m-0 min-w-0 p-6">
<div className="flex w-full min-w-0 flex-col gap-3">
<Tabs
value={packageManager}
onValueChange={(value) => {
setConfig((prev) => ({
...prev,
packageManager: value as PackageManager,
}))
}}
className="min-w-0 gap-0 overflow-hidden rounded-xl border-0 ring-1 ring-border"
>
<div className="flex items-center gap-2 py-1 pr-1.5 pl-1">
<TabsList className="bg-transparent font-mono">
{PACKAGE_MANAGERS.map((manager) => {
return (
<TabsTrigger
key={manager}
value={manager}
className="py-0 leading-none data-[state=active]:shadow-none"
>
{manager}
</TabsTrigger>
)
})}
</TabsList>
<Button
size="icon-sm"
variant="ghost"
className="ml-auto"
onClick={handleCopyApply}
>
{copiedTarget === "apply" ? (
<HugeiconsIcon icon={Tick02Icon} />
) : (
<HugeiconsIcon icon={Copy01Icon} />
)}
<span className="sr-only">Copy command</span>
</Button>
</div>
{Object.entries(applyCommands).map(([key, cmd]) => {
return (
<TabsContent key={key} value={key}>
<div className="relative overflow-hidden border-t bg-popover p-3">
<div className="no-scrollbar overflow-x-auto">
<code className="font-mono text-sm whitespace-nowrap">
{cmd}
</code>
</div>
</div>
</TabsContent>
)
})}
</Tabs>
<Button onClick={handleCopyApply} className="h-9 w-full">
{copiedTarget === "apply" ? "Copied" : "Copy Command"}
</Button>
</div>
</DialogFooter>
</div>
)}
{activeTab === "theme" && (
<div className="no-scrollbar overflow-y-auto">
<FieldGroup className="min-w-0 px-6 py-4">
<FieldSet className="min-w-0 gap-3">
<FieldLegend variant="label">Theme Tokens</FieldLegend>
<FieldDescription>
Copy the CSS variables for this preset.
</FieldDescription>
<div className="w-full min-w-0 overflow-hidden rounded-xl border-0 ring-1 ring-border">
<div className="flex items-center gap-2 py-1 pr-1.5 pl-3">
<div className="min-w-0 truncate font-mono text-sm text-muted-foreground">
globals.css
</div>
<Button
size="icon-sm"
variant="ghost"
className="ml-auto"
onClick={handleCopyTheme}
>
{copiedTarget === "theme" ? (
<HugeiconsIcon icon={Tick02Icon} />
) : (
<HugeiconsIcon icon={Copy01Icon} />
)}
<span className="sr-only">Copy theme</span>
</Button>
</div>
<div className="relative no-scrollbar max-h-[45svh] overflow-auto border-t bg-popover p-3">
<pre className="min-w-max font-mono leading-normal whitespace-pre">
<code>{themeCss}</code>
</pre>
</div>
</div>
</FieldSet>
</FieldGroup>
<DialogFooter className="m-0 min-w-0 p-6">
<Button onClick={handleCopyTheme} className="h-9 w-full">
{copiedTarget === "theme" ? "Copied" : "Copy Theme"}
</Button>
</DialogFooter>
</div>
)}
</div>
</DialogContent>
</Dialog>
)
@@ -352,6 +644,34 @@ const TemplateGrid = React.memo(function TemplateGrid({
)
})
const ApplyModeGrid = React.memo(function ApplyModeGrid({
mode,
setMode,
}: {
mode: ApplyMode
setMode: (mode: ApplyMode) => void
}) {
return (
<RadioGroup
value={mode}
onValueChange={(value) => setMode(value as ApplyMode)}
aria-label="Apply"
>
{APPLY_MODES.map((option) => (
<FieldLabel key={option.value} htmlFor={`apply-${option.value}`}>
<Field orientation="horizontal">
<RadioGroupItem value={option.value} id={`apply-${option.value}`} />
<FieldContent>
<FieldTitle>{option.title}</FieldTitle>
<FieldDescription>{option.description}</FieldDescription>
</FieldContent>
</Field>
</FieldLabel>
))}
</RadioGroup>
)
})
const BaseGrid = React.memo(function BaseGrid({
base,
setParams,

View File

@@ -2,7 +2,7 @@
import * as React from "react"
import { PRESETS, type Style, type StyleName } from "@/registry/config"
import { type Style, type StyleName } from "@/registry/config"
import { LockButton } from "@/app/(app)/create/components/lock-button"
import {
Picker,
@@ -53,24 +53,7 @@ export function StylePicker({
<PickerRadioGroup
value={currentStyle?.name}
onValueChange={(value) => {
const styleName = value as StyleName
const preset = PRESETS.find(
(p) => p.base === params.base && p.style === styleName
)
setParams({
style: styleName,
...(preset && {
baseColor: preset.baseColor,
theme: preset.theme,
chartColor: preset.chartColor,
iconLibrary: preset.iconLibrary,
font: preset.font,
fontHeading: preset.fontHeading,
menuAccent: preset.menuAccent,
menuColor: preset.menuColor,
radius: preset.radius,
}),
})
setParams({ style: value as StyleName })
}}
>
<PickerGroup>

View File

@@ -156,7 +156,7 @@ The `Field` family is designed for composing accessible forms. A typical field i
## Form
See the [Form](/docs/forms) documentation for building forms with the `Field` component and [React Hook Form](/docs/forms/react-hook-form) or [Tanstack Form](/docs/forms/tanstack-form).
See the [Form](/docs/forms) documentation for building forms with the `Field` component and [React Hook Form](/docs/forms/react-hook-form), [Tanstack Form](/docs/forms/tanstack-form), or [Formisch](/docs/forms/formisch).
## Examples

View File

@@ -129,3 +129,9 @@ To enable RTL support in shadcn/ui, see the [RTL configuration guide](/docs/rtl)
## API Reference
See the [Base UI Toggle Group](https://base-ui.com/react/components/toggle-group#api-reference) documentation.
## Changelog
### 2026-05-17 Default Spacing
Changed the default `spacing` from `0` to `2` so toggle groups render with space between items by default. Use `spacing={0}` for connected items.

View File

@@ -156,7 +156,7 @@ The `Field` family is designed for composing accessible forms. A typical field i
## Form
See the [Form](/docs/forms) documentation for building forms with the `Field` component and [React Hook Form](/docs/forms/react-hook-form) or [Tanstack Form](/docs/forms/tanstack-form).
See the [Form](/docs/forms) documentation for building forms with the `Field` component and [React Hook Form](/docs/forms/react-hook-form), [Tanstack Form](/docs/forms/tanstack-form), or [Formisch](/docs/forms/formisch).
## Examples

View File

@@ -129,3 +129,9 @@ To enable RTL support in shadcn/ui, see the [RTL configuration guide](/docs/rtl)
## API Reference
See the [Radix Toggle Group](https://www.radix-ui.com/docs/primitives/components/toggle-group#api-reference) documentation.
## Changelog
### 2026-05-17 Default Spacing
Changed the default `spacing` from `0` to `2` so toggle groups render with space between items by default. Use `spacing={0}` for connected items.

View File

@@ -0,0 +1,669 @@
---
title: Formisch
description: Build forms in React using Formisch and Valibot.
links:
doc: https://formisch.dev
---
import { InfoIcon } from "lucide-react"
This guide covers building forms with [Formisch](https://formisch.dev), the lightweight, schema-first, and fully type-safe form library for React. We'll create forms with the `<Field />` component, validate them with Valibot schemas, handle errors, and ensure accessibility.
## Demo
We'll build the following form. It has a simple text input and a textarea. On submit, we'll validate the form data and display any errors.
<Callout icon={<InfoIcon />}>
**Note:** For the purpose of this demo, we have intentionally disabled browser
validation to show how schema validation and form errors work in Formisch. It
is recommended to add basic browser validation in your production code.
</Callout>
<ComponentPreview
name="form-formisch-demo"
className="sm:[&_.preview]:h-[700px]"
chromeLessOnMobile
/>
## Approach
This form leverages Formisch for headless, schema-first form handling. We'll build our form using the `<Field />` component, which gives you **complete flexibility over the markup and styling**.
- Uses Formisch's `useForm` hook for form state management.
- `<Form />` component to wrap the native `<form>` element with submit handling.
- `<Field />` render-prop component for controlled inputs.
- Schema validation using [Valibot](https://valibot.dev).
- Type-safe field paths inferred from the schema.
## Form Methods
Formisch exposes form operations as **top-level functions** rather than methods on a form object. Import only what you need:
```ts
import { getInput, insert, reset, submit } from "@formisch/react"
```
Every method follows the same signature: the **first parameter is always the form store**, and the **second parameter (if necessary) is always a config object**.
```ts
// Read a field value
const email = getInput(form, { path: ["email"] })
// Reset the form with new initial values
reset(form, { initialInput: { email: "", password: "" } })
// Move an item in a field array
move(form, { path: ["items"], from: 0, to: 3 })
```
This design keeps the API flexible and consistent across all methods. You'll see the same `(form, config)` shape used throughout this guide for reading state (`getInput`, `getErrors`), writing state (`setInput`, `setErrors`), form control (`submit`, `validate`, `focus`), and array operations (`insert`, `remove`, `move`, `swap`, `replace`). See the [full methods reference](https://formisch.dev/react/guides/form-methods) for details.
## Anatomy
Here's a basic example of a form using the `<Field />` component from Formisch and the shadcn `<Field />` component.
```tsx showLineNumbers {3-21}
<Form of={form} onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["title"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-title">Bug Title</FieldLabel>
<Input
{...field.props}
id="form-title"
value={field.input}
aria-invalid={field.errors !== null}
placeholder="Login button not working on mobile"
autoComplete="off"
/>
<FieldDescription>
Provide a concise title for your bug report.
</FieldDescription>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</Field>
)}
</FormischField>
</FieldGroup>
</Form>
```
<Callout icon={<InfoIcon />}>
**Note:** Formisch ships its own `Field` component. To avoid a name clash with
the shadcn `Field`, the examples below import the Formisch one as
`FormischField` and keep the shadcn `Field` under its original name. In your
own code you can alias either side — just be consistent.
</Callout>
## Form
### Create a form schema
We'll start by defining the shape of our form using a Valibot schema. Formisch infers all input and output types directly from this schema.
```tsx showLineNumbers title="form.tsx"
import * as v from "valibot"
const FormSchema = v.object({
title: v.pipe(
v.string(),
v.minLength(5, "Bug title must be at least 5 characters."),
v.maxLength(32, "Bug title must be at most 32 characters.")
),
description: v.pipe(
v.string(),
v.minLength(20, "Description must be at least 20 characters."),
v.maxLength(100, "Description must be at most 100 characters.")
),
})
```
### Set up the form
Next, we'll use the `useForm` hook from Formisch to create our form instance. The schema is passed directly to `useForm` — there is no resolver step.
```tsx showLineNumbers title="form.tsx" {1-2,21-25}
import { Form, Field as FormischField, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import * as v from "valibot"
const FormSchema = v.object({
title: v.pipe(
v.string(),
v.minLength(5, "Bug title must be at least 5 characters."),
v.maxLength(32, "Bug title must be at most 32 characters.")
),
description: v.pipe(
v.string(),
v.minLength(20, "Description must be at least 20 characters."),
v.maxLength(100, "Description must be at most 100 characters.")
),
})
export function BugReportForm() {
const form = useForm({
schema: FormSchema,
initialInput: {
title: "",
description: "",
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
// Do something with the validated form values.
console.log(output)
}
return (
<Form of={form} onSubmit={handleSubmit}>
{/* ... */}
{/* Build the form here */}
{/* ... */}
</Form>
)
}
```
The `<Form />` component wraps a native `<form>` element. It calls `event.preventDefault()`, runs validation, and only invokes `onSubmit` when the data is valid. The `output` you receive is fully typed from the schema.
### Build the form
We can now build the form using the `<Field />` component from Formisch and the shadcn `<Field />` component.
<ComponentSource
src="/registry/new-york-v4/examples/form-formisch-demo.tsx"
title="form.tsx"
/>
### Done
That's it. You now have a fully accessible form with client-side validation.
When you submit the form, the `handleSubmit` function will be called with the validated form data. If the form data is invalid, Formisch will populate `field.errors` for each invalid field and the UI will display them.
## Validation
### Client-side Validation
Formisch validates your form data using the Valibot schema you pass to `useForm`. There is no resolver — the schema is the single source of truth for both runtime validation and static types.
```tsx showLineNumbers title="form.tsx" {1,3-6,11}
import { useForm } from "@formisch/react"
const FormSchema = v.object({
title: v.string(),
description: v.optional(v.string()),
})
export function ExampleForm() {
const form = useForm({
schema: FormSchema,
initialInput: {
title: "",
description: "",
},
})
}
```
### Validation Modes
Formisch separates the **first** validation from **subsequent** validations. You configure them with the `validate` and `revalidate` options on `useForm`.
```tsx showLineNumbers title="form.tsx" {3-4}
const form = useForm({
schema: FormSchema,
validate: "blur",
revalidate: "input",
})
```
| Option | Value | Description |
| ------------ | ----------- | --------------------------------------------------------------- |
| `validate` | `"submit"` | Validate on form submission (default). |
| `validate` | `"blur"` | Validate when a field loses focus. |
| `validate` | `"input"` | Validate on every input change. |
| `validate` | `"initial"` | Validate immediately on form creation. |
| `revalidate` | `"input"` | Revalidate on every input change after the first run (default). |
| `revalidate` | `"blur"` | Revalidate on blur after the first run. |
| `revalidate` | `"submit"` | Revalidate only on form submission. |
## Displaying Errors
Display errors next to the field using `<FieldError />`. Formisch returns errors as an array of strings, so map them to the shape `<FieldError />` expects. For styling and accessibility:
- Add the `data-invalid` prop to the `<Field />` component.
- Add the `aria-invalid` prop to the form control such as `<Input />`, `<SelectTrigger />`, `<Checkbox />`, etc.
```tsx showLineNumbers title="form.tsx" {3,10,12-14}
<FormischField of={form} path={["email"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-email">Email</FieldLabel>
<Input
{...field.props}
id="form-email"
value={field.input}
type="email"
aria-invalid={field.errors !== null}
/>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</Field>
)}
</FormischField>
```
## Working with Different Field Types
Formisch exposes two ways to bind a field to an element:
- **Native HTML elements** (like `<Input />` and `<Textarea />`) — spread `field.props` and provide `value={field.input}`. Formisch wires up `name`, `ref`, `onChange`, `onBlur`, and `onFocus` for you.
- **Component-library inputs** (like Radix-based `<Select />`, `<Checkbox />`, `<RadioGroup />`, `<Switch />`) — read the value from `field.input` and call `field.onChange(value)` to update it.
### Input
- For input fields, spread `field.props` and provide `value={field.input}`.
- To show errors, add the `aria-invalid` prop to the `<Input />` component and the `data-invalid` prop to the `<Field />` component.
<ComponentPreview
name="form-formisch-input"
className="sm:[&_.preview]:h-[700px]"
chromeLessOnMobile
/>
```tsx showLineNumbers title="form.tsx" {5-8}
<FormischField of={form} path={["username"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-username">Username</FieldLabel>
<Input
{...field.props}
id="form-username"
value={field.input}
aria-invalid={field.errors !== null}
/>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</Field>
)}
</FormischField>
```
### Textarea
- For textarea fields, spread `field.props` and provide `value={field.input}`.
- To show errors, add the `aria-invalid` prop to the `<Textarea />` component and the `data-invalid` prop to the `<Field />` component.
<ComponentPreview
name="form-formisch-textarea"
className="sm:[&_.preview]:h-[700px]"
chromeLessOnMobile
/>
```tsx showLineNumbers title="form.tsx" {7-10}
<FormischField of={form} path={["about"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-about">More about you</FieldLabel>
<Textarea
{...field.props}
id="form-about"
value={field.input}
aria-invalid={field.errors !== null}
placeholder="I'm a software engineer..."
className="min-h-[120px]"
/>
<FieldDescription>
Tell us more about yourself. This will be used to help us personalize
your experience.
</FieldDescription>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</Field>
)}
</FormischField>
```
### Select
- For select components, read `field.input` and call `field.onChange` from `<Select />`'s `onValueChange`.
- To show errors, add the `aria-invalid` prop to the `<SelectTrigger />` component and the `data-invalid` prop to the `<Field />` component.
<ComponentPreview
name="form-formisch-select"
className="sm:[&_.preview]:h-[500px]"
chromeLessOnMobile
/>
```tsx showLineNumbers title="form.tsx" {15-19}
<FormischField of={form} path={["language"]}>
{(field) => (
<Field orientation="responsive" data-invalid={field.errors !== null}>
<FieldContent>
<FieldLabel htmlFor="form-language">Spoken Language</FieldLabel>
<FieldDescription>
For best results, select the language you speak.
</FieldDescription>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</FieldContent>
<Select value={field.input} onValueChange={field.onChange}>
<SelectTrigger
id="form-language"
aria-invalid={field.errors !== null}
className="min-w-[120px]"
>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent position="item-aligned">
<SelectItem value="auto">Auto</SelectItem>
<SelectItem value="en">English</SelectItem>
</SelectContent>
</Select>
</Field>
)}
</FormischField>
```
### Checkbox
- For checkbox arrays, read `field.input` and update it from `onCheckedChange` using `field.onChange`.
- To show errors, add the `aria-invalid` prop to the `<Checkbox />` component and the `data-invalid` prop to the `<Field />` component.
- Remember to add `data-slot="checkbox-group"` to the `<FieldGroup />` component for proper styling and spacing.
<ComponentPreview
name="form-formisch-checkbox"
className="sm:[&_.preview]:h-[700px]"
chromeLessOnMobile
/>
```tsx showLineNumbers title="form.tsx" {16,19-25}
<FormischField of={form} path={["tasks"]}>
{(field) => (
<FieldSet>
<FieldLegend variant="label">Tasks</FieldLegend>
<FieldDescription>
Get notified when tasks you&apos;ve created have updates.
</FieldDescription>
<FieldGroup data-slot="checkbox-group">
{tasks.map((task) => (
<Field
key={task.id}
orientation="horizontal"
data-invalid={field.errors !== null}
>
<Checkbox
id={`form-checkbox-${task.id}`}
aria-invalid={field.errors !== null}
checked={field.input?.includes(task.id) ?? false}
onCheckedChange={(checked) => {
const current = field.input ?? []
field.onChange(
checked === true
? [...current, task.id]
: current.filter((value) => value !== task.id)
)
}}
/>
<FieldLabel
htmlFor={`form-checkbox-${task.id}`}
className="font-normal"
>
{task.label}
</FieldLabel>
</Field>
))}
</FieldGroup>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</FieldSet>
)}
</FormischField>
```
### Radio Group
- For radio groups, read `field.input` and call `field.onChange` from `onValueChange`.
- To show errors, add the `aria-invalid` prop to the `<RadioGroupItem />` component and the `data-invalid` prop to the `<Field />` component.
<ComponentPreview
name="form-formisch-radiogroup"
className="sm:[&_.preview]:h-[700px]"
chromeLessOnMobile
/>
```tsx showLineNumbers title="form.tsx" {9-13,21}
<FormischField of={form} path={["plan"]}>
{(field) => (
<FieldSet>
<FieldLegend>Plan</FieldLegend>
<FieldDescription>
You can upgrade or downgrade your plan at any time.
</FieldDescription>
<RadioGroup value={field.input} onValueChange={field.onChange}>
{plans.map((plan) => (
<FieldLabel key={plan.id} htmlFor={`form-radiogroup-${plan.id}`}>
<Field
orientation="horizontal"
data-invalid={field.errors !== null}
>
<FieldContent>
<FieldTitle>{plan.title}</FieldTitle>
<FieldDescription>{plan.description}</FieldDescription>
</FieldContent>
<RadioGroupItem
value={plan.id}
id={`form-radiogroup-${plan.id}`}
aria-invalid={field.errors !== null}
/>
</Field>
</FieldLabel>
))}
</RadioGroup>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</FieldSet>
)}
</FormischField>
```
### Switch
- For switches, read `field.input` and call `field.onChange` from `onCheckedChange`.
- To show errors, add the `aria-invalid` prop to the `<Switch />` component and the `data-invalid` prop to the `<Field />` component.
<ComponentPreview
name="form-formisch-switch"
className="sm:[&_.preview]:h-[500px]"
chromeLessOnMobile
/>
```tsx showLineNumbers title="form.tsx" {15-19}
<FormischField of={form} path={["twoFactor"]}>
{(field) => (
<Field orientation="horizontal" data-invalid={field.errors !== null}>
<FieldContent>
<FieldLabel htmlFor="form-twoFactor">
Multi-factor authentication
</FieldLabel>
<FieldDescription>
Enable multi-factor authentication to secure your account.
</FieldDescription>
{field.errors && (
<FieldError errors={field.errors.map((message) => ({ message }))} />
)}
</FieldContent>
<Switch
id="form-twoFactor"
checked={field.input ?? false}
onCheckedChange={field.onChange}
aria-invalid={field.errors !== null}
/>
</Field>
)}
</FormischField>
```
### Complex Forms
Here is an example of a more complex form with multiple fields and validation.
<ComponentPreview
name="form-formisch-complex"
className="sm:[&_.preview]:h-[1300px]"
chromeLessOnMobile
/>
## Resetting the Form
Formisch exposes a top-level `reset` function. Pass the form store to reset it to its initial input.
```tsx showLineNumbers
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
```
You can also reset to new initial values, or reset while keeping the user's current input:
```tsx showLineNumbers
// Reset to a fresh set of initial values
reset(form, { initialInput: { title: "", description: "" } })
// Sync the baseline to new server data, but keep the user's edits
reset(form, { initialInput: serverData, keepInput: true })
```
## Array Fields
Formisch provides a `<FieldArray />` component and a set of helper functions for managing dynamic array fields. Use it whenever you need to add, remove, or reorder items.
<ComponentPreview
name="form-formisch-array"
className="sm:[&_.preview]:h-[700px]"
chromeLessOnMobile
/>
### Using FieldArray
`<FieldArray />` follows the same render-prop pattern as `<Field />`. Its `items` array contains a stable key per item that you should use as the React `key`.
```tsx showLineNumbers title="form.tsx" {1,7-22}
import {
Field as FormischField,
FieldArray,
insert,
remove,
} from "@formisch/react"
export function ExampleForm() {
// ... form config
return (
<FieldArray of={form} path={["emails"]}>
{(fieldArray) => (
<FieldGroup className="gap-4">
{fieldArray.items.map((item, index) => (
<FormischField
key={item}
of={form}
path={["emails", index, "address"]}
>
{(field) => /* ... */}
</FormischField>
))}
</FieldGroup>
)}
</FieldArray>
)
}
```
### Array Field Structure
Wrap your array fields in a `<FieldSet />` with a `<FieldLegend />` and `<FieldDescription />`.
```tsx showLineNumbers title="form.tsx"
<FieldSet className="gap-4">
<FieldLegend variant="label">Email Addresses</FieldLegend>
<FieldDescription>
Add up to 5 email addresses where we can contact you.
</FieldDescription>
<FieldGroup className="gap-4">{/* Array items go here */}</FieldGroup>
</FieldSet>
```
### Adding Items
Use the `insert` function to add new items to the array. By default new items are appended to the end. You can also pass an `at` index to insert at a specific position.
```tsx showLineNumbers title="form.tsx"
<Button
type="button"
variant="outline"
size="sm"
onClick={() =>
insert(form, { path: ["emails"], initialInput: { address: "" } })
}
disabled={fieldArray.items.length >= 5}
>
Add Email Address
</Button>
```
### Removing Items
Use the `remove` function with an `at` index to remove items from the array.
```tsx showLineNumbers title="form.tsx"
import { remove } from "@formisch/react"
{
fieldArray.items.length > 1 && (
<InputGroupAddon align="inline-end">
<InputGroupButton
type="button"
variant="ghost"
size="icon-xs"
onClick={() => remove(form, { path: ["emails"], at: index })}
aria-label={`Remove email ${index + 1}`}
>
<XIcon />
</InputGroupButton>
</InputGroupAddon>
)
}
```
Formisch also exposes `move`, `swap`, and `replace` for reordering and replacing items. They follow the same `(form, config)` signature.
### Array Validation
Use Valibot's `array` and pipeline validators to constrain array fields.
```tsx showLineNumbers title="form.tsx"
const FormSchema = v.object({
emails: v.pipe(
v.array(
v.object({
address: v.pipe(
v.string(),
v.nonEmpty("Enter an email address."),
v.email("Enter a valid email address.")
),
})
),
v.minLength(1, "Add at least one email address."),
v.maxLength(5, "You can add up to 5 email addresses.")
),
})
```

View File

@@ -25,6 +25,24 @@ Start by selecting your framework. Then follow the instructions to learn how to
</svg>
<p className="mt-2 font-medium">TanStack Form</p>
</LinkedCard>
<LinkedCard href="/docs/forms/formisch">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
className="size-10"
fill="currentColor"
>
<path
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="20"
d="M90.9 291.7 49 299.4v125.5l193.3 63.8 221-63.8V304.7l-62-13.7m-352.2 8.6 193.2 53.6V488m221-183.2-221.1 48.4m-171-67.7 147.5 47 212.7-48.3L437 76 271.9 25 72.1 66.3Zm1-219.3L220.5 118l-1.8 214m1.8-214.3 216.2-41.6m-84.4 159.6-.9 1.9c-5.2 11.3-16.2 34.8-33.5 35.5h-1a22 22 0 0 1-9.7-2.2 26 26 0 0 1-7.4-5.5c-4.3-4.4-7.4-10-10-14.5q-1.2-2.4-2.7-4.7a54 54 0 0 0 21.7 4.1 74 74 0 0 0 23.6-4 70 70 0 0 0 11.4-5 64 64 0 0 0 8.5-5.6M247.6 168c19.5-20.8 34.8-12.3 36-18.4 1.5-7-12.2-7.7-22.2-2.6s-18 17.7-13.8 21m101.2-33.2c1.6 2.6 8.7-1.6 19.5-1.2s15.8 5.7 18 4.1-4.6-14.7-17.1-15.2-22 9.7-20.4 12.3m21.2 16.3c9.4.6 17.1 12.8 16.1 27.6s-10.4 25.9-19.8 25.2-17.1-12.9-16.1-27.7 10.4-25.8 19.8-25.1m-97.4 19c11 1.2 19 15.1 17.4 30.5s-12.3 27.4-23.4 26.2-19-15-17.4-30.5 12.3-27.3 23.4-26.2"
/>
</svg>
<p className="mt-2 font-medium">Formisch</p>
</LinkedCard>
<LinkedCard href="#" className="border border-dashed bg-transparent">
<svg
role="img"

View File

@@ -1,3 +1,3 @@
{
"pages": ["react-hook-form", "tanstack-form"]
"pages": ["react-hook-form", "tanstack-form", "formisch"]
}

View File

@@ -21,6 +21,7 @@ const eventSchema = z.object({
"copy_create_share_url",
"copy_registry_add_command",
"copy_preset_command",
"copy_apply_command",
]),
// declare type AllowedPropertyValues = string | number | boolean | null
properties: z

View File

@@ -28,6 +28,7 @@
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@faker-js/faker": "^10.1.0",
"@formisch/react": "^0.4.4",
"@hookform/resolvers": "^3.10.0",
"@hugeicons/core-free-icons": "^1.2.1",
"@hugeicons/react": "^1.1.1",
@@ -82,6 +83,7 @@
"swr": "^2.3.6",
"tailwind-merge": "^3.0.1",
"ts-morph": "26.0.0",
"valibot": "^1.4.0",
"vaul": "1.1.2",
"zod": "^3.25.76"
},

View File

@@ -124,6 +124,7 @@
- [Forms Overview](https://ui.shadcn.com/docs/forms): Guide to building forms with shadcn/ui.
- [React Hook Form](https://ui.shadcn.com/docs/forms/react-hook-form): Using shadcn/ui with React Hook Form.
- [TanStack Form](https://ui.shadcn.com/docs/forms/tanstack-form): Using shadcn/ui with TanStack Form.
- [Formisch](https://ui.shadcn.com/docs/forms/formisch): Using shadcn/ui with Formisch.
- [Forms - Next.js](https://ui.shadcn.com/docs/forms/next): Building forms in Next.js with Server Actions.
## Advanced

View File

@@ -137,6 +137,12 @@
"url": "https://boldkit.dev/r/{name}.json",
"description": "Neubrutalism component library with 43 components, 42 SVG shapes, thick borders, and hard shadows. Supports React, Vue, and Nuxt. Built on shadcn/ui."
},
{
"name": "@bklit",
"homepage": "https://ui.bklit.com",
"url": "https://ui.bklit.com/r/{name}.json",
"description": "Open-source composable chart components for React — line, area, bar, pie, radar, maps, and more. Built with Visx, Motion, and shadcn/ui."
},
{
"name": "@bundui",
"homepage": "https://bundui.io",
@@ -179,6 +185,12 @@
"url": "https://commercn.com/r/{name}.json",
"description": "Shadcn UI Blocks for Ecommerce websites"
},
{
"name": "@corr",
"homepage": "https://ui.corr.sh",
"url": "https://ui.corr.sh/r/{name}.json",
"description": "A collection of shadcn-based React components, charts, animated components, and blocks built over time."
},
{
"name": "@coss",
"homepage": "https://coss.com/ui",
@@ -289,7 +301,7 @@
},
{
"name": "@glasscn",
"homepage": "https://glasscn.vercel.app/",
"homepage": "https://glasscn-components.vercel.app/",
"url": "https://glasscn-components.vercel.app/r/{name}.json",
"description": "A shadcn-compatible registry of glassmorphism components inspired by Apple"
},
@@ -447,7 +459,7 @@
"name": "@ncdai",
"homepage": "https://chanhdai.com/components",
"url": "https://chanhdai.com/r/{name}.json",
"description": "A collection of reusable components."
"description": "Pixel-perfect, uniquely crafted."
},
{
"name": "@nteract",
@@ -509,6 +521,12 @@
"url": "https://gsap.pacekit.dev/r/{name}.json",
"description": "Animated GSAP components crafted for smooth interaction and rich detail."
},
{
"name": "@paddle",
"homepage": "https://developer.paddle.com/",
"url": "https://developer.paddle.com/r/{name}.json",
"description": "Drop-in components for building checkouts, pricing pages, and subscription management screens using Paddle Billing."
},
{
"name": "@pastecn",
"homepage": "https://pastecn.com",
@@ -755,6 +773,12 @@
"url": "https://onboarding-tour.vercel.app/r/{name}.json",
"description": "A component for building onboarding tours. Designed to integrate with shadcn/ui."
},
{
"name": "@trophy-ui",
"homepage": "https://ui.trophy.so",
"url": "https://ui.trophy.so/r/{name}.json",
"description": "Open-source gamification UI components for streaks, achievements, leaderboards, points, and more. Built on shadcn/ui and Tailwind CSS."
},
{
"name": "@uitripled",
"homepage": "https://ui.tripled.work",
@@ -1138,5 +1162,17 @@
"homepage": "https://framecn.vercel.app",
"url": "https://framecn.vercel.app/r/{name}.json",
"description": "Beautiful videos, made simple. Ready to use, customizable video components for React."
},
{
"name": "@turbopills-ui",
"homepage": "https://www.turbopills.com/ui/docs",
"url": "https://ui.turbopills.com/r/{name}.json",
"description": "Beautiful, accessible, and customizable React components for your telehealth applications."
},
{
"name": "@wensity",
"homepage": "https://wensity.com",
"url": "https://raw.githubusercontent.com/ksparth12/wensity-shadcn-registry/main/{name}.json",
"description": "Motion-rich React components for AI interfaces, SaaS blocks, and cinematic interactions. Free Wensity components only."
}
]

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-luma/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-luma/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-luma/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-3xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-luma/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-luma/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-3xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-lyra/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-lyra/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-lyra/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-none data-[size=sm]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-lyra/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-lyra/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-none data-[size=sm]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-maia/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-maia/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-maia/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-4xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-maia/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-maia/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-4xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-mira/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-mira/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-mira/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[size=sm]:rounded-[min(var(--radius-md),8px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-mira/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-mira/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[size=sm]:rounded-[min(var(--radius-md),8px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-nova/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-nova/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-nova/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-nova/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-nova/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-sera/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-sera/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-sera/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-6 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-4 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-4 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none data-[state=on]:bg-muted data-[state=on]:text-foreground group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-sera/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-sera/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-6 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-4 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-4 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none data-[state=on]:bg-muted data-[state=on]:text-foreground group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/base-vega/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-vega/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-vega/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=0]:data-[variant=outline]:shadow-xs data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Toggle as TogglePrimitive } from \"@base-ui/react/toggle\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"@base-ui/react/toggle-group\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@/registry/base-vega/lib/utils\"\nimport { toggleVariants } from \"@/registry/base-vega/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: ToggleGroupPrimitive.Props &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=0]:data-[variant=outline]:shadow-xs data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <TogglePrimitive\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </TogglePrimitive>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form-formisch-input",
"dependencies": [
"@formisch/react",
"valibot"
],
"registryDependencies": [
"field",
"input",
"button",
"card"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-input.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Form, Field as FormischField, reset, useForm } from \"@formisch/react\"\nimport type { SubmitHandler } from \"@formisch/react\"\nimport { toast } from \"sonner\"\nimport * as v from \"valibot\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/new-york-v4/ui/card\"\nimport {\n Field,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n} from \"@/registry/new-york-v4/ui/field\"\nimport { Input } from \"@/registry/new-york-v4/ui/input\"\n\nconst FormSchema = v.object({\n username: v.pipe(\n v.string(),\n v.minLength(3, \"Username must be at least 3 characters.\"),\n v.maxLength(10, \"Username must be at most 10 characters.\"),\n v.regex(\n /^[a-zA-Z0-9_]+$/,\n \"Username can only contain letters, numbers, and underscores.\"\n )\n ),\n})\n\nexport default function FormFormischInput() {\n const form = useForm({\n schema: FormSchema,\n initialInput: {\n username: \"\",\n },\n })\n\n const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {\n toast(\"You submitted the following values:\", {\n description: (\n <pre className=\"mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground\">\n <code>{JSON.stringify(output, null, 2)}</code>\n </pre>\n ),\n position: \"bottom-right\",\n classNames: {\n content: \"flex flex-col gap-2\",\n },\n style: {\n \"--border-radius\": \"calc(var(--radius) + 4px)\",\n } as React.CSSProperties,\n })\n }\n\n return (\n <Card className=\"w-full sm:max-w-md\">\n <CardHeader>\n <CardTitle>Profile Settings</CardTitle>\n <CardDescription>\n Update your profile information below.\n </CardDescription>\n </CardHeader>\n <CardContent>\n <Form of={form} id=\"form-formisch-input\" onSubmit={handleSubmit}>\n <FieldGroup>\n <FormischField of={form} path={[\"username\"]}>\n {(field) => (\n <Field data-invalid={field.errors !== null}>\n <FieldLabel htmlFor=\"form-formisch-input-username\">\n Username\n </FieldLabel>\n <Input\n {...field.props}\n id=\"form-formisch-input-username\"\n value={field.input ?? \"\"}\n aria-invalid={field.errors !== null}\n placeholder=\"shadcn\"\n autoComplete=\"username\"\n />\n <FieldDescription>\n This is your public display name. Must be between 3 and 10\n characters. Must only contain letters, numbers, and\n underscores.\n </FieldDescription>\n {field.errors && (\n <FieldError\n errors={field.errors.map((message) => ({ message }))}\n />\n )}\n </Field>\n )}\n </FormischField>\n </FieldGroup>\n </Form>\n </CardContent>\n <CardFooter>\n <Field orientation=\"horizontal\">\n <Button type=\"button\" variant=\"outline\" onClick={() => reset(form)}>\n Reset\n </Button>\n <Button type=\"submit\" form=\"form-formisch-input\">\n Save\n </Button>\n </Field>\n </CardFooter>\n </Card>\n )\n}\n",
"type": "registry:example"
}
],
"type": "registry:example"
}

View File

@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form-formisch-radiogroup",
"dependencies": [
"@formisch/react",
"valibot"
],
"registryDependencies": [
"field",
"radio-group",
"button",
"card"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-radiogroup.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Form, Field as FormischField, reset, useForm } from \"@formisch/react\"\nimport type { SubmitHandler } from \"@formisch/react\"\nimport { toast } from \"sonner\"\nimport * as v from \"valibot\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/new-york-v4/ui/card\"\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n FieldLegend,\n FieldSet,\n FieldTitle,\n} from \"@/registry/new-york-v4/ui/field\"\nimport {\n RadioGroup,\n RadioGroupItem,\n} from \"@/registry/new-york-v4/ui/radio-group\"\n\nconst plans = [\n {\n id: \"starter\",\n title: \"Starter (100K tokens/month)\",\n description: \"For everyday use with basic features.\",\n },\n {\n id: \"pro\",\n title: \"Pro (1M tokens/month)\",\n description: \"For advanced AI usage with more features.\",\n },\n {\n id: \"enterprise\",\n title: \"Enterprise (Unlimited tokens)\",\n description: \"For large teams and heavy usage.\",\n },\n] as const\n\nconst FormSchema = v.object({\n plan: v.pipe(\n v.string(),\n v.minLength(1, \"You must select a subscription plan to continue.\")\n ),\n})\n\nexport default function FormFormischRadioGroup() {\n const form = useForm({\n schema: FormSchema,\n initialInput: {\n plan: \"\",\n },\n })\n\n const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {\n toast(\"You submitted the following values:\", {\n description: (\n <pre className=\"mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground\">\n <code>{JSON.stringify(output, null, 2)}</code>\n </pre>\n ),\n position: \"bottom-right\",\n classNames: {\n content: \"flex flex-col gap-2\",\n },\n style: {\n \"--border-radius\": \"calc(var(--radius) + 4px)\",\n } as React.CSSProperties,\n })\n }\n\n return (\n <Card className=\"w-full sm:max-w-md\">\n <CardHeader>\n <CardTitle>Subscription Plan</CardTitle>\n <CardDescription>\n See pricing and features for each plan.\n </CardDescription>\n </CardHeader>\n <CardContent>\n <Form of={form} id=\"form-formisch-radiogroup\" onSubmit={handleSubmit}>\n <FieldGroup>\n <FormischField of={form} path={[\"plan\"]}>\n {(field) => (\n <FieldSet data-invalid={field.errors !== null}>\n <FieldLegend>Plan</FieldLegend>\n <FieldDescription>\n You can upgrade or downgrade your plan at any time.\n </FieldDescription>\n <RadioGroup\n value={field.input ?? \"\"}\n onValueChange={(value) => field.onChange(value)}\n aria-invalid={field.errors !== null}\n >\n {plans.map((plan) => (\n <FieldLabel\n key={plan.id}\n htmlFor={`form-formisch-radiogroup-${plan.id}`}\n >\n <Field\n orientation=\"horizontal\"\n data-invalid={field.errors !== null}\n >\n <FieldContent>\n <FieldTitle>{plan.title}</FieldTitle>\n <FieldDescription>\n {plan.description}\n </FieldDescription>\n </FieldContent>\n <RadioGroupItem\n value={plan.id}\n id={`form-formisch-radiogroup-${plan.id}`}\n aria-invalid={field.errors !== null}\n />\n </Field>\n </FieldLabel>\n ))}\n </RadioGroup>\n {field.errors && (\n <FieldError\n errors={field.errors.map((message) => ({ message }))}\n />\n )}\n </FieldSet>\n )}\n </FormischField>\n </FieldGroup>\n </Form>\n </CardContent>\n <CardFooter>\n <Field orientation=\"horizontal\">\n <Button type=\"button\" variant=\"outline\" onClick={() => reset(form)}>\n Reset\n </Button>\n <Button type=\"submit\" form=\"form-formisch-radiogroup\">\n Save\n </Button>\n </Field>\n </CardFooter>\n </Card>\n )\n}\n",
"type": "registry:example"
}
],
"type": "registry:example"
}

View File

@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form-formisch-select",
"dependencies": [
"@formisch/react",
"valibot"
],
"registryDependencies": [
"field",
"select",
"button",
"card"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-select.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Form, Field as FormischField, reset, useForm } from \"@formisch/react\"\nimport type { SubmitHandler } from \"@formisch/react\"\nimport { toast } from \"sonner\"\nimport * as v from \"valibot\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/new-york-v4/ui/card\"\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n} from \"@/registry/new-york-v4/ui/field\"\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n} from \"@/registry/new-york-v4/ui/select\"\n\nconst spokenLanguages = [\n { label: \"English\", value: \"en\" },\n { label: \"Spanish\", value: \"es\" },\n { label: \"French\", value: \"fr\" },\n { label: \"German\", value: \"de\" },\n { label: \"Italian\", value: \"it\" },\n { label: \"Chinese\", value: \"zh\" },\n { label: \"Japanese\", value: \"ja\" },\n] as const\n\nconst FormSchema = v.object({\n language: v.pipe(\n v.string(),\n v.minLength(1, \"Please select your spoken language.\"),\n v.check(\n (value) => value !== \"auto\",\n \"Auto-detection is not allowed. Please select a specific language.\"\n )\n ),\n})\n\nexport default function FormFormischSelect() {\n const form = useForm({\n schema: FormSchema,\n initialInput: {\n language: \"\",\n },\n })\n\n const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {\n toast(\"You submitted the following values:\", {\n description: (\n <pre className=\"mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground\">\n <code>{JSON.stringify(output, null, 2)}</code>\n </pre>\n ),\n position: \"bottom-right\",\n classNames: {\n content: \"flex flex-col gap-2\",\n },\n style: {\n \"--border-radius\": \"calc(var(--radius) + 4px)\",\n } as React.CSSProperties,\n })\n }\n\n return (\n <Card className=\"w-full sm:max-w-lg\">\n <CardHeader>\n <CardTitle>Language Preferences</CardTitle>\n <CardDescription>\n Select your preferred spoken language.\n </CardDescription>\n </CardHeader>\n <CardContent>\n <Form of={form} id=\"form-formisch-select\" onSubmit={handleSubmit}>\n <FieldGroup>\n <FormischField of={form} path={[\"language\"]}>\n {(field) => (\n <Field\n orientation=\"responsive\"\n data-invalid={field.errors !== null}\n >\n <FieldContent>\n <FieldLabel htmlFor=\"form-formisch-select-language\">\n Spoken Language\n </FieldLabel>\n <FieldDescription>\n For best results, select the language you speak.\n </FieldDescription>\n {field.errors && (\n <FieldError\n errors={field.errors.map((message) => ({ message }))}\n />\n )}\n </FieldContent>\n <Select\n value={field.input ?? \"\"}\n onValueChange={(value) => field.onChange(value)}\n >\n <SelectTrigger\n id=\"form-formisch-select-language\"\n aria-invalid={field.errors !== null}\n className=\"min-w-[120px]\"\n >\n <SelectValue placeholder=\"Select\" />\n </SelectTrigger>\n <SelectContent position=\"item-aligned\">\n <SelectItem value=\"auto\">Auto</SelectItem>\n <SelectSeparator />\n {spokenLanguages.map((language) => (\n <SelectItem key={language.value} value={language.value}>\n {language.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </Field>\n )}\n </FormischField>\n </FieldGroup>\n </Form>\n </CardContent>\n <CardFooter>\n <Field orientation=\"horizontal\">\n <Button type=\"button\" variant=\"outline\" onClick={() => reset(form)}>\n Reset\n </Button>\n <Button type=\"submit\" form=\"form-formisch-select\">\n Save\n </Button>\n </Field>\n </CardFooter>\n </Card>\n )\n}\n",
"type": "registry:example"
}
],
"type": "registry:example"
}

View File

@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form-formisch-switch",
"dependencies": [
"@formisch/react",
"valibot"
],
"registryDependencies": [
"field",
"switch",
"button",
"card"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-switch.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Form, Field as FormischField, reset, useForm } from \"@formisch/react\"\nimport type { SubmitHandler } from \"@formisch/react\"\nimport { toast } from \"sonner\"\nimport * as v from \"valibot\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/new-york-v4/ui/card\"\nimport {\n Field,\n FieldContent,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n} from \"@/registry/new-york-v4/ui/field\"\nimport { Switch } from \"@/registry/new-york-v4/ui/switch\"\n\nconst FormSchema = v.object({\n twoFactor: v.pipe(\n v.boolean(),\n v.check(\n (value) => value === true,\n \"It is highly recommended to enable two-factor authentication.\"\n )\n ),\n})\n\nexport default function FormFormischSwitch() {\n const form = useForm({\n schema: FormSchema,\n initialInput: {\n twoFactor: false,\n },\n })\n\n const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {\n toast(\"You submitted the following values:\", {\n description: (\n <pre className=\"mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground\">\n <code>{JSON.stringify(output, null, 2)}</code>\n </pre>\n ),\n position: \"bottom-right\",\n classNames: {\n content: \"flex flex-col gap-2\",\n },\n style: {\n \"--border-radius\": \"calc(var(--radius) + 4px)\",\n } as React.CSSProperties,\n })\n }\n\n return (\n <Card className=\"w-full sm:max-w-md\">\n <CardHeader>\n <CardTitle>Security Settings</CardTitle>\n <CardDescription>\n Manage your account security preferences.\n </CardDescription>\n </CardHeader>\n <CardContent>\n <Form of={form} id=\"form-formisch-switch\" onSubmit={handleSubmit}>\n <FieldGroup>\n <FormischField of={form} path={[\"twoFactor\"]}>\n {(field) => (\n <Field\n orientation=\"horizontal\"\n data-invalid={field.errors !== null}\n >\n <FieldContent>\n <FieldLabel htmlFor=\"form-formisch-switch-twoFactor\">\n Multi-factor authentication\n </FieldLabel>\n <FieldDescription>\n Enable multi-factor authentication to secure your account.\n </FieldDescription>\n {field.errors && (\n <FieldError\n errors={field.errors.map((message) => ({ message }))}\n />\n )}\n </FieldContent>\n <Switch\n id=\"form-formisch-switch-twoFactor\"\n checked={field.input ?? false}\n onCheckedChange={(checked) => field.onChange(checked)}\n aria-invalid={field.errors !== null}\n />\n </Field>\n )}\n </FormischField>\n </FieldGroup>\n </Form>\n </CardContent>\n <CardFooter>\n <Field orientation=\"horizontal\">\n <Button type=\"button\" variant=\"outline\" onClick={() => reset(form)}>\n Reset\n </Button>\n <Button type=\"submit\" form=\"form-formisch-switch\">\n Save\n </Button>\n </Field>\n </CardFooter>\n </Card>\n )\n}\n",
"type": "registry:example"
}
],
"type": "registry:example"
}

View File

@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "form-formisch-textarea",
"dependencies": [
"@formisch/react",
"valibot"
],
"registryDependencies": [
"field",
"textarea",
"button",
"card"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-textarea.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Form, Field as FormischField, reset, useForm } from \"@formisch/react\"\nimport type { SubmitHandler } from \"@formisch/react\"\nimport { toast } from \"sonner\"\nimport * as v from \"valibot\"\n\nimport { Button } from \"@/registry/new-york-v4/ui/button\"\nimport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/registry/new-york-v4/ui/card\"\nimport {\n Field,\n FieldDescription,\n FieldError,\n FieldGroup,\n FieldLabel,\n} from \"@/registry/new-york-v4/ui/field\"\nimport { Textarea } from \"@/registry/new-york-v4/ui/textarea\"\n\nconst FormSchema = v.object({\n about: v.pipe(\n v.string(),\n v.minLength(10, \"Please provide at least 10 characters.\"),\n v.maxLength(200, \"Please keep it under 200 characters.\")\n ),\n})\n\nexport default function FormFormischTextarea() {\n const form = useForm({\n schema: FormSchema,\n initialInput: {\n about: \"\",\n },\n })\n\n const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {\n toast(\"You submitted the following values:\", {\n description: (\n <pre className=\"mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground\">\n <code>{JSON.stringify(output, null, 2)}</code>\n </pre>\n ),\n position: \"bottom-right\",\n classNames: {\n content: \"flex flex-col gap-2\",\n },\n style: {\n \"--border-radius\": \"calc(var(--radius) + 4px)\",\n } as React.CSSProperties,\n })\n }\n\n return (\n <Card className=\"w-full sm:max-w-md\">\n <CardHeader>\n <CardTitle>Personalization</CardTitle>\n <CardDescription>\n Customize your experience by telling us more about yourself.\n </CardDescription>\n </CardHeader>\n <CardContent>\n <Form of={form} id=\"form-formisch-textarea\" onSubmit={handleSubmit}>\n <FieldGroup>\n <FormischField of={form} path={[\"about\"]}>\n {(field) => (\n <Field data-invalid={field.errors !== null}>\n <FieldLabel htmlFor=\"form-formisch-textarea-about\">\n More about you\n </FieldLabel>\n <Textarea\n {...field.props}\n id=\"form-formisch-textarea-about\"\n value={field.input ?? \"\"}\n aria-invalid={field.errors !== null}\n placeholder=\"I'm a software engineer...\"\n className=\"min-h-[120px]\"\n />\n <FieldDescription>\n Tell us more about yourself. This will be used to help us\n personalize your experience.\n </FieldDescription>\n {field.errors && (\n <FieldError\n errors={field.errors.map((message) => ({ message }))}\n />\n )}\n </Field>\n )}\n </FormischField>\n </FieldGroup>\n </Form>\n </CardContent>\n <CardFooter>\n <Field orientation=\"horizontal\">\n <Button type=\"button\" variant=\"outline\" onClick={() => reset(form)}>\n Reset\n </Button>\n <Button type=\"submit\" form=\"form-formisch-textarea\">\n Save\n </Button>\n </Field>\n </CardFooter>\n </Card>\n )\n}\n",
"type": "registry:example"
}
],
"type": "registry:example"
}

View File

@@ -4011,6 +4011,134 @@
],
"type": "registry:example"
},
{
"name": "form-formisch-demo",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": [
"field",
"input",
"input-group",
"button",
"card"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-demo.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-input",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": ["field", "input", "button", "card"],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-input.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-textarea",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": ["field", "textarea", "button", "card"],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-textarea.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-select",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": ["field", "select", "button", "card"],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-select.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-checkbox",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": ["field", "checkbox", "button", "card"],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-checkbox.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-radiogroup",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": ["field", "radio-group", "button", "card"],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-radiogroup.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-switch",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": ["field", "switch", "button", "card"],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-switch.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-array",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": [
"field",
"input",
"input-group",
"button",
"card"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-array.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "form-formisch-complex",
"dependencies": ["@formisch/react", "valibot"],
"registryDependencies": [
"field",
"button",
"card",
"checkbox",
"radio-group",
"select",
"switch"
],
"files": [
{
"path": "registry/new-york-v4/examples/form-formisch-complex.tsx",
"type": "registry:example"
}
],
"type": "registry:example"
},
{
"name": "drawer-dialog",
"registryDependencies": ["drawer", "dialog"],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/radix-luma/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-luma/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-luma/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-3xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-luma/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-luma/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-3xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/radix-lyra/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-lyra/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-lyra/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-none data-[size=sm]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-lyra/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-lyra/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-none data-[size=sm]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/radix-maia/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-maia/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-maia/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-4xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-maia/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-maia/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-4xl data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-3 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-2.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-2.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-3xl group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-3xl group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-3xl group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-3xl data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/radix-mira/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-mira/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-mira/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[size=sm]:rounded-[min(var(--radius-md),8px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-mira/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-mira/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[size=sm]:rounded-[min(var(--radius-md),8px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/radix-nova/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-nova/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-nova/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-nova/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-nova/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/radix-sera/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-sera/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-sera/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-6 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-4 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-4 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none data-[state=on]:bg-muted data-[state=on]:text-foreground group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-sera/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-sera/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] data-[spacing=0]:data-[variant=outline]:rounded-none data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-6 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-4 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-4 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none data-[state=on]:bg-muted data-[state=on]:text-foreground group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7,7 +7,7 @@
"files": [
{
"path": "registry/radix-vega/ui/toggle-group.tsx",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-vega/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-vega/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 0,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 0,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=0]:data-[variant=outline]:shadow-xs data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"content": "\"use client\"\n\nimport * as React from \"react\"\nimport { type VariantProps } from \"class-variance-authority\"\nimport { ToggleGroup as ToggleGroupPrimitive } from \"radix-ui\"\n\nimport { cn } from \"@/registry/radix-vega/lib/utils\"\nimport { toggleVariants } from \"@/registry/radix-vega/ui/toggle\"\n\nconst ToggleGroupContext = React.createContext<\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }\n>({\n size: \"default\",\n variant: \"default\",\n spacing: 2,\n orientation: \"horizontal\",\n})\n\nfunction ToggleGroup({\n className,\n variant,\n size,\n spacing = 2,\n orientation = \"horizontal\",\n children,\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &\n VariantProps<typeof toggleVariants> & {\n spacing?: number\n orientation?: \"horizontal\" | \"vertical\"\n }) {\n return (\n <ToggleGroupPrimitive.Root\n data-slot=\"toggle-group\"\n data-variant={variant}\n data-size={size}\n data-spacing={spacing}\n data-orientation={orientation}\n style={{ \"--gap\": spacing } as React.CSSProperties}\n className={cn(\n \"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=0]:data-[variant=outline]:shadow-xs data-vertical:flex-col data-vertical:items-stretch\",\n className\n )}\n {...props}\n >\n <ToggleGroupContext.Provider\n value={{ variant, size, spacing, orientation }}\n >\n {children}\n </ToggleGroupContext.Provider>\n </ToggleGroupPrimitive.Root>\n )\n}\n\nfunction ToggleGroupItem({\n className,\n children,\n variant = \"default\",\n size = \"default\",\n ...props\n}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &\n VariantProps<typeof toggleVariants>) {\n const context = React.useContext(ToggleGroupContext)\n\n return (\n <ToggleGroupPrimitive.Item\n data-slot=\"toggle-group-item\"\n data-variant={context.variant || variant}\n data-size={context.size || size}\n data-spacing={context.spacing}\n className={cn(\n \"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t\",\n toggleVariants({\n variant: context.variant || variant,\n size: context.size || size,\n }),\n className\n )}\n {...props}\n >\n {children}\n </ToggleGroupPrimitive.Item>\n )\n}\n\nexport { ToggleGroup, ToggleGroupItem }\n",
"type": "registry:ui"
}
],

View File

@@ -7431,6 +7431,257 @@ export const Index: Record<string, Record<string, any>> = {
categories: undefined,
meta: undefined,
},
"form-formisch-demo": {
name: "form-formisch-demo",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "input", "input-group", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-demo.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-demo"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-demo"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-input": {
name: "form-formisch-input",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "input", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-input.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-input"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-input"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-textarea": {
name: "form-formisch-textarea",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "textarea", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-textarea.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-textarea"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-textarea"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-select": {
name: "form-formisch-select",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "select", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-select.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-select"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-select"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-checkbox": {
name: "form-formisch-checkbox",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "checkbox", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-checkbox.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-checkbox"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-checkbox"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-radiogroup": {
name: "form-formisch-radiogroup",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "radio-group", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-radiogroup.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-radiogroup"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-radiogroup"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-switch": {
name: "form-formisch-switch",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "switch", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-switch.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-switch"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-switch"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-array": {
name: "form-formisch-array",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: ["field", "input", "input-group", "button", "card"],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-array.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-array"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-array"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"form-formisch-complex": {
name: "form-formisch-complex",
title: "undefined",
description: "",
type: "registry:example",
registryDependencies: [
"field",
"button",
"card",
"checkbox",
"radio-group",
"select",
"switch",
],
files: [
{
path: "registry/new-york-v4/examples/form-formisch-complex.tsx",
type: "registry:example",
target: "",
},
],
component: React.lazy(async () => {
const mod = await import(
"@/registry/new-york-v4/examples/form-formisch-complex"
)
const exportName =
Object.keys(mod).find(
(key) =>
typeof mod[key] === "function" || typeof mod[key] === "object"
) || "form-formisch-complex"
return { default: mod.default || mod[exportName] }
}),
categories: undefined,
meta: undefined,
},
"drawer-dialog": {
name: "drawer-dialog",
title: "undefined",

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -4,6 +4,7 @@ import publicSchema from "../public/schema.json"
import {
buildPartialRegistryBase,
buildRegistryBase,
buildThemeForPreset,
DEFAULT_CONFIG,
designSystemConfigSchema,
parseRegistryBaseParts,
@@ -152,6 +153,31 @@ describe("buildRegistryBase", () => {
})
})
describe("buildThemeForPreset", () => {
it("builds a copyable registry theme item", () => {
const result = buildThemeForPreset({
...DEFAULT_CONFIG,
baseColor: "taupe",
theme: "taupe",
chartColor: "taupe",
menuAccent: "bold",
radius: "large",
})
expect(result).toMatchObject({
$schema: "https://ui.shadcn.com/schema/registry-item.json",
name: "taupe-taupe",
type: "registry:theme",
})
expect(result.cssVars?.light?.radius).toBe("0.875rem")
expect(result.cssVars?.light?.accent).toBe(result.cssVars?.light?.primary)
expect(result.cssVars?.dark?.accent).toBe(result.cssVars?.dark?.primary)
expect(result.cssVars?.light?.background).toBeDefined()
expect(result.cssVars?.dark?.background).toBeDefined()
expect(result.css).toBeUndefined()
})
})
describe("parseRegistryBaseParts", () => {
it("returns undefined parts when only is omitted", () => {
expect(parseRegistryBaseParts(null)).toEqual({

View File

@@ -3,7 +3,7 @@ import {
type IconLibrary,
type IconLibraryName,
} from "shadcn/icons"
import { type RegistryItem } from "shadcn/schema"
import { registryItemSchema, type RegistryItem } from "shadcn/schema"
import { z } from "zod"
import { BASE_COLORS, type BaseColor } from "@/registry/base-colors"
@@ -13,6 +13,7 @@ import { STYLES, type Style } from "@/registry/styles"
import { THEMES, type Theme } from "@/registry/themes"
const SHADCN_VERSION = "latest"
const DEFAULT_RADIUS_VALUE = "0.625rem"
export { BASES, type Base }
export { STYLES, type Style }
@@ -577,6 +578,28 @@ export function buildRegistryTheme(config: DesignSystemConfig) {
}
}
export function buildThemeForPreset(config: DesignSystemConfig) {
const registryTheme = buildRegistryTheme(config)
const radius = RADII.find((r) => r.name === config.radius)
const radiusValue =
config.radius === "default"
? (registryTheme.cssVars?.light?.radius ?? DEFAULT_RADIUS_VALUE)
: (radius?.value ?? registryTheme.cssVars?.light?.radius)
return registryItemSchema.parse({
$schema: "https://ui.shadcn.com/schema/registry-item.json",
name: registryTheme.name,
type: "registry:theme",
cssVars: {
...registryTheme.cssVars,
light: {
...registryTheme.cssVars.light,
...(radiusValue && { radius: radiusValue }),
},
},
})
}
// Builds a registry:base item from a design system config.
export function buildRegistryBase(config: DesignSystemConfig) {
const baseItem = getBase(config.base)

View File

@@ -160,6 +160,13 @@
"description": "Neubrutalism component library with 43 components, 42 SVG shapes, thick borders, and hard shadows. Supports React, Vue, and Nuxt. Built on shadcn/ui.",
"logo": "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' fill='none'><rect x='5' y='5' width='90' height='90' fill='var(--background)' stroke='var(--foreground)' stroke-width='6'/><rect x='20' y='20' width='25' height='25' fill='var(--foreground)'/><rect x='55' y='20' width='25' height='25' fill='var(--foreground)'/><rect x='20' y='55' width='25' height='25' fill='var(--foreground)'/><rect x='55' y='55' width='25' height='25' fill='var(--foreground)'/></svg>"
},
{
"name": "@bklit",
"homepage": "https://ui.bklit.com",
"url": "https://ui.bklit.com/r/{name}.json",
"description": "Open-source composable chart components for React — line, area, bar, pie, radar, maps, and more. Built with Visx, Motion, and shadcn/ui.",
"logo": "<svg class='h-auto max-h-full w-full max-w-full text-foreground' fill='none' viewBox='0 0 112 179' xmlns='http://www.w3.org/2000/svg'><title>Bklit Logo</title><path d='M11.082 88.6498C11.082 88.6498 24.4884 66.4873 55.407 66.4873C84.2319 66.4873 99.732 88.6498 99.732 88.6498C99.732 88.6498 86.1801 110.813 55.407 110.812C26.7612 110.812 11.082 88.6498 11.082 88.6498Z' fill='url(#bklit-paint0_radial_375_479)'></path><g style='mix-blend-mode:normal'><path d='M11.082 88.6498C11.082 88.6498 24.4884 66.4873 55.407 66.4873C84.2319 66.4873 99.732 88.6498 99.732 88.6498C99.732 88.6498 86.1801 110.813 55.407 110.812C26.7612 110.812 11.082 88.6498 11.082 88.6498Z' fill='url(#bklit-paint1_radial)'></path></g><g style='mix-blend-mode:normal'><path d='M110.812 121.894C110.812 148.67 90.0415 177.3 55.4062 177.3V110.812C55.4062 110.812 83.2175 112.19 99.7312 88.6499C108.153 99.9149 110.812 111.675 110.812 121.894Z' fill='url(#bklit-paint2_linear)'></path></g><g style='mix-blend-mode:normal'><path d='M0 121.894C0 148.67 20.771 177.3 55.4062 177.3V110.812C55.4062 110.812 29.4726 112.262 11.0813 88.6499C2.65942 99.9149 0 111.675 0 121.894Z' fill='url(#bklit-paint3_linear)'></path></g><g style='mix-blend-mode:normal'><path d='M0 55.4061C0 28.6295 20.771 0 55.4063 0L55.4063 66.4874C55.4063 66.4874 27.595 65.1102 11.0813 88.6499C2.65942 77.3849 0 65.6252 0 55.4061Z' fill='url(#bklit-paint4_linear)'></path></g><g style='mix-blend-mode:normal'><path d='M110.812 55.4062C110.813 28.6295 90.0415 0 55.4063 0L55.4063 66.4874C55.4063 66.4874 81.5638 65.1362 99.7312 88.6499C108.153 77.3849 110.812 65.6252 110.812 55.4062Z' fill='url(#bklit-paint5_linear)'></path></g><defs><radialGradient id='bklit-paint0_radial_375_479' cx='50%' cy='50%' r='50%' gradientUnits='objectBoundingBox'><stop offset='0' stop-color='currentColor'></stop><stop offset='1' stop-color='currentColor' stop-opacity='0.75'></stop></radialGradient><linearGradient gradientUnits='objectBoundingBox' id='bklit-paint1_radial' x1='0' x2='1' y1='0' y2='0'><stop offset='0' stop-color='rgba(255, 255, 255)'></stop><stop offset='0.5' stop-color='rgba(255, 255, 255, 0.8)'></stop><stop offset='1' stop-color='rgba(255, 255, 255)'></stop></linearGradient><linearGradient gradientUnits='userSpaceOnUse' id='bklit-paint2_linear' x1='55.4062' x2='110.813' y1='177.3' y2='66.6642'><stop stop-color='currentColor'></stop><stop offset='1' stop-color='rgba(255, 255, 255, 0.5)'></stop></linearGradient><linearGradient gradientUnits='userSpaceOnUse' id='bklit-paint3_linear' x1='55.4063' x2='0' y1='177.3' y2='66.6642'><stop stop-color='currentColor' stop-opacity='0'></stop><stop offset='0.745192' stop-color='rgba(120,120,120, 0.5)'></stop></linearGradient><linearGradient gradientUnits='userSpaceOnUse' id='bklit-paint4_linear' x1='55.4063' x2='0' y1='0' y2='110.636'><stop stop-color='currentColor'></stop><stop offset='1' stop-color='rgba(255, 255, 255, 0.5)'></stop></linearGradient><linearGradient gradientUnits='userSpaceOnUse' id='bklit-paint5_linear' x1='55.4063' x2='110.813' y1='0' y2='110.636'><stop stop-color='currentColor' stop-opacity='0'></stop><stop offset='0.75' stop-color='rgba(120,120,120, 0.5)'></stop></linearGradient></defs></svg>"
},
{
"name": "@bundui",
"homepage": "https://bundui.io",
@@ -209,6 +216,13 @@
"description": "Shadcn UI Blocks for Ecommerce websites",
"logo": "<svg width='400' height='400' viewBox='0 0 298 347' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M149 13L291.028 95V259L149 341L6.97183 259V95L149 13Z' fill='var(--background)'/><path d='M132.82 9.99006L21.54 77.1701C11.62 83.1601 5.5 94.2301 5.5 106.21V240.56C5.5 252.54 11.61 263.61 21.54 269.6L132.82 336.78C142.74 342.77 154.97 342.77 164.9 336.78L276.18 269.6C286.1 263.61 292.22 252.54 292.22 240.56V106.21C292.22 94.2301 286.11 83.1601 276.18 77.1701L164.89 9.99006C154.97 4.00006 142.74 4.00006 132.81 9.99006H132.82ZM132.82 313.69L40.67 258.06C30.75 252.07 24.63 241 24.63 229.02V117.76C24.63 105.78 30.74 94.7101 40.67 88.7201L132.82 33.0901C142.74 27.1001 154.97 27.1001 164.9 33.0901L257.05 88.7201C266.97 94.7101 273.09 105.78 273.09 117.76V229.02C273.09 241 266.98 252.07 257.05 258.06L164.9 313.69C154.98 319.68 142.75 319.68 132.82 313.69Z' fill='var(--foreground)' stroke='var(--foreground)' stroke-width='11' stroke-miterlimit='10'/><path d='M66.38 239.42C61.04 239.61 55.96 236.91 53.12 232.38C48.73 225.37 50.87 216.09 57.88 211.71L223.9 107.87C226.13 106.47 228.7 105.69 231.31 105.6C236.65 105.41 241.73 108.11 244.57 112.64C246.7 116.04 247.37 120.06 246.47 123.96C245.57 127.86 243.2 131.18 239.81 133.31L73.79 237.15C71.56 238.55 68.99 239.33 66.38 239.42Z' fill='var(--foreground)'/><path d='M226.632 112.041L60.4685 215.992C55.8285 218.895 54.4202 225.01 57.323 229.65L57.4184 229.802C60.3212 234.442 66.4358 235.85 71.0757 232.948L237.239 128.996C241.879 126.094 243.287 119.979 240.384 115.339L240.289 115.187C237.386 110.547 231.271 109.138 226.632 112.041Z' fill='var(--foreground)'/><path d='M138.74 271.19C133.86 271.23 129.3 268.48 126.83 264.01C124.45 259.71 124.28 254.47 126.53 249.83C127.72 247.38 129.63 245.42 131.87 244.03L214.37 192.67C216.44 191.38 218.8 190.69 221.19 190.67C226.07 190.63 230.63 193.39 233.1 197.85C234.95 201.19 235.46 205.1 234.55 208.86C233.63 212.61 231.39 215.76 228.24 217.72L145.57 269.19C143.5 270.48 141.14 271.17 138.75 271.19H138.74Z' fill='var(--foreground)'/><path d='M130.8 261.64L130.72 261.5C128.19 256.93 129.63 251.05 133.92 248.38L216.76 196.81C221.06 194.13 226.59 195.67 229.13 200.24L229.21 200.38C231.74 204.95 230.3 210.83 226.01 213.5L143.17 265.07C138.87 267.75 133.34 266.21 130.8"
},
{
"name": "@corr",
"homepage": "https://ui.corr.sh",
"url": "https://ui.corr.sh/r/{name}.json",
"description": "A collection of shadcn-based React components, charts, animated components, and blocks built over time.",
"logo": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\"><rect fill=\"#111111\" x=\"6\" y=\"6\" width=\"52\" height=\"52\" rx=\"14\"/><path fill=\"none\" stroke=\"#ffffff\" stroke-width=\"4.5\" stroke-linejoin=\"round\" stroke-linecap=\"round\" d=\"M32 17 45 24.5v15L32 47 19 39.5v-15L32 17Z\"/><path fill=\"none\" stroke=\"#ffffff\" stroke-width=\"4.5\" stroke-linejoin=\"round\" stroke-linecap=\"round\" d=\"M19 24.5 32 32l13-7.5\"/><path fill=\"none\" stroke=\"#ffffff\" stroke-width=\"4.5\" stroke-linejoin=\"round\" stroke-linecap=\"round\" d=\"M32 32v15\"/></svg>"
},
{
"name": "@coss",
"homepage": "https://coss.com/ui",
@@ -338,7 +352,7 @@
},
{
"name": "@glasscn",
"homepage": "https://glasscn.vercel.app/",
"homepage": "https://glasscn-components.vercel.app/",
"url": "https://glasscn-components.vercel.app/r/{name}.json",
"description": "A shadcn-compatible registry of glassmorphism components inspired by Apple",
"logo": "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80' fill='none'><rect x='0.5' y='0.5' width='51' height='51' rx='13.5' fill='var(--background)' fill-opacity='.08' stroke='var(--foreground)' stroke-opacity='.2'/><rect x='14.5' y='14.5' width='51' height='51' rx='13.5' fill='var(--background)' fill-opacity='.12' stroke='var(--foreground)' stroke-opacity='.3'/><rect x='28.5' y='28.5' width='51' height='51' rx='13.5' fill='var(--background)' fill-opacity='.18' stroke='var(--foreground)' stroke-opacity='.45'/><line x1='34' y1='36' x2='34' y2='56' stroke='var(--foreground)' stroke-opacity='.35' stroke-width='1.5' stroke-linecap='round'/></svg>"
@@ -595,6 +609,13 @@
"description": "Animated GSAP components crafted for smooth interaction and rich detail.",
"logo": "<svg xmlns='http://www.w3.org/2000/svg' width='512' height='512' fill='none' viewBox='0 0 512 512'><g clip-path='url(#clip0_832_289)'><path fill='var(--foregorund)' d='M256 0C51.2 0 0 51.2 0 256s51.2 256 256 256 256-51.2 256-256S460.8 0 256 0'/><path stroke='var(--background)' stroke-width='24' d='M381.631 89.102 156.848 226.934c-1.77 1.101-3.439 2.653-4.844 4.505s-2.5 3.94-3.177 6.062c-.673 2.062-.875 4.084-.584 5.83.288 1.658 1.077 2.946 2.257 3.686l98.286 32.478-122.2 137.576a7.9 7.9 0 0 0-1.306 2.72c-.234.895-.22 1.742.04 2.404q.369 1.032 1.341 1.214c.64.125 1.387-.039 2.127-.468l224.795-137.85c1.77-1.102 3.438-2.654 4.844-4.506 1.405-1.852 2.5-3.94 3.177-6.061.666-2.057.864-4.072.571-5.813-.282-1.663-1.066-2.957-2.245-3.704l-87.131-44.529L383.845 94.953a7.9 7.9 0 0 0 1.306-2.72c.233-.895.219-1.741-.04-2.404-.257-.632-.727-1.058-1.341-1.214-.641-.125-1.4.058-2.139.487Z'/></g></svg>"
},
{
"name": "@paddle",
"homepage": "https://developer.paddle.com/",
"url": "https://developer.paddle.com/r/{name}.json",
"description": "Drop-in components for building checkouts, pricing pages, and subscription management screens using Paddle Billing.",
"logo": "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32' fill='none'><path fill='var(--foreground)' d='M28 0H4C1.79086 0 0 1.79086 0 4V28C0 30.2091 1.79086 32 4 32H28C30.2091 32 32 30.2091 32 28V4C32 1.79086 30.2091 0 28 0Z'/><path fill='var(--background)' d='M26 15.0174V16.9696C24.8052 16.9699 23.6222 17.2038 22.5186 17.6579C21.415 18.1119 20.4124 18.7773 19.5682 19.616C18.7239 20.4546 18.0546 21.4501 17.5985 22.5455C17.1423 23.641 16.9082 24.8148 16.9097 26H15.0903C15.0897 23.6086 14.1318 21.3154 12.4272 19.6244C10.7225 17.9334 8.41072 16.9832 6 16.9826V15.0304C7.19478 15.0301 8.37779 14.7962 9.48141 14.3421C10.585 13.8881 11.5876 13.2227 12.4318 12.384C13.2761 11.5453 13.9454 10.5499 14.4015 9.45445C14.8577 8.35904 15.0917 7.18519 15.0903 6H16.9097C16.9103 8.39137 17.8682 10.6846 19.5728 12.3756C21.2775 14.0666 23.5893 15.0168 26 15.0174Z'/></svg>"
},
{
"name": "@pastecn",
"homepage": "https://pastecn.com",
@@ -882,6 +903,13 @@
"description": "A component for building onboarding tours. Designed to integrate with shadcn/ui.",
"logo": "<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' class='lucide lucide-binoculars-icon lucide-binoculars fill-none!'><path d='M10 10h4'/><path d='M19 7V4a1 1 0 0 0-1-1h-2a1 1 0 0 0-1 1v3'/><path d='M20 21a2 2 0 0 0 2-2v-3.851c0-1.39-2-2.962-2-4.829V8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v11a2 2 0 0 0 2 2z'/><path d='M 22 16 L 2 16'/><path d='M4 21a2 2 0 0 1-2-2v-3.851c0-1.39 2-2.962 2-4.829V8a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v11a2 2 0 0 1-2 2z'/><path d='M9 7V4a1 1 0 0 0-1-1H6a1 1 0 0 0-1 1v3'/></svg>"
},
{
"name": "@trophy-ui",
"homepage": "https://ui.trophy.so",
"url": "https://ui.trophy.so/r/{name}.json",
"description": "Open-source gamification UI components for streaks, achievements, leaderboards, points, and more. Built on shadcn/ui and Tailwind CSS.",
"logo": "<svg width='32' height='51' viewBox='0 0 40 64' fill='none' xmlns='http://www.w3.org/2000/svg'><rect x='32' y='70.75' width='13' height='15' transform='rotate(180 28 64.75)' fill='var(--foreground)'/><rect x='32' y='55.75' width='13' height='13' transform='rotate(180 28 49.75)' fill='var(--foreground)'/><rect x='19' y='55.75' width='15' height='13' transform='rotate(180 15 49.75)' fill='var(--foreground)'/><rect x='53.5' y='49.25' width='13' height='15' transform='rotate(180 49.5 43.25)' fill='var(--foreground)'/><rect x='53.5' y='34.25' width='13' height='13' transform='rotate(180 49.5 28.25)' fill='var(--foreground)'/><rect x='40.5' y='34.25' width='15' height='13' transform='rotate(180 36.5 28.25)' fill='var(--foreground)'/></svg>"
},
{
"name": "@uitripled",
"homepage": "https://ui.tripled.work",
@@ -1329,5 +1357,19 @@
"url": "https://framecn.vercel.app/r/{name}.json",
"description": "Beautiful videos, made simple. Ready to use, customizable video components for React.",
"logo": "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><rect width='32' height='32' rx='4'/><g stroke='currentColor' fill='none' stroke-linecap='round' stroke-linejoin='round'><path d='M6.72 4.36v2.36M27.96 25.6H25.6m-4.13 0H13.8c-3.337 0-5.007 0-6.043-1.037S6.72 21.857 6.72 18.52v-7.67M25.6 27.96v-11.8c0-4.45 0-6.675-1.383-8.057S20.61 6.72 16.16 6.72H4.36' stroke-width='1.77'/><path d='m21.824 16.16-5.664 5.664M20.691 9.93 9.93 20.69' stroke-width='2.266'/></g></svg>"
},
{
"name": "@turbopills-ui",
"homepage": "https://www.turbopills.com/ui/docs",
"url": "https://ui.turbopills.com/r/{name}.json",
"description": "Beautiful, accessible, and customizable React components for your telehealth applications.",
"logo": "<svg xmlns='http://www.w3.org/2000/svg' version='1.0' width='597.000000pt' height='525.000000pt' viewBox='0 0 597.000000 525.000000' preserveAspectRatio='xMidYMid meet'><g transform='translate(0.000000,525.000000) scale(0.100000,-0.100000)' fill='var(--foreground, currentColor)' stroke='none'><path d='M1816 5223 l-1809 -3 6 -108 c24 -383 241 -748 579 -969 113 -74 205 -118 338 -162 178 -58 237 -64 678 -70 l404 -6 14 33 c56 136 213 367 364 538 283 320 747 605 1163 716 48 12 87 26 87 30 0 5 -3 7 -7 6 -5 -2 -822 -4 -1817 -5z'/><path d='M4630 5209 c-983 -101 -1863 -612 -2297 -1334 -249 -414 -394 -925 -462 -1625 -28 -287 -29 -310 -19 -304 4 3 8 12 8 20 0 19 87 192 163 324 413 718 1068 1274 1797 1522 228 78 466 118 703 118 506 0 811 110 1079 389 135 139 217 274 283 462 43 123 60 208 70 342 l7 97 -623 -1 c-343 -1 -662 -6 -709 -10z'/><path d='M3876 3584 c-339 -138 -733 -410 -1041 -719 -347 -347 -596 -694 -840 -1170 -188 -367 -356 -851 -435 -1250 -31 -157 -60 -344 -60 -389 l0 -39 183 7 c224 8 336 20 497 52 676 135 1179 580 1326 1174 19 74 56 257 83 405 94 515 222 1211 256 1390 50 268 97 545 93 555 -2 4 -29 -2 -62 -16z'/></g></svg>"
},
{
"name": "@wensity",
"homepage": "https://wensity.com",
"url": "https://raw.githubusercontent.com/ksparth12/wensity-shadcn-registry/main/{name}.json",
"description": "Motion-rich React components for AI interfaces, SaaS blocks, and cinematic interactions. Free Wensity components only.",
"logo": "<svg width='512' height='512' viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'><rect width='512' height='512' rx='112' fill='var(--foreground)'/><path d='M112 152h58l36 160 43-160h49l43 160 36-160h58l-62 208h-62l-38-141-38 141h-62L112 152Z' fill='var(--background)'/></svg>"
}
]
]

View File

@@ -1241,6 +1241,122 @@ export const examples: Registry["items"] = [
],
dependencies: ["@tanstack/react-form", "zod"],
},
{
name: "form-formisch-demo",
type: "registry:example",
registryDependencies: ["field", "input", "input-group", "button", "card"],
files: [
{
path: "examples/form-formisch-demo.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-input",
type: "registry:example",
registryDependencies: ["field", "input", "button", "card"],
files: [
{
path: "examples/form-formisch-input.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-textarea",
type: "registry:example",
registryDependencies: ["field", "textarea", "button", "card"],
files: [
{
path: "examples/form-formisch-textarea.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-select",
type: "registry:example",
registryDependencies: ["field", "select", "button", "card"],
files: [
{
path: "examples/form-formisch-select.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-checkbox",
type: "registry:example",
registryDependencies: ["field", "checkbox", "button", "card"],
files: [
{
path: "examples/form-formisch-checkbox.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-radiogroup",
type: "registry:example",
registryDependencies: ["field", "radio-group", "button", "card"],
files: [
{
path: "examples/form-formisch-radiogroup.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-switch",
type: "registry:example",
registryDependencies: ["field", "switch", "button", "card"],
files: [
{
path: "examples/form-formisch-switch.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-array",
type: "registry:example",
registryDependencies: ["field", "input", "input-group", "button", "card"],
files: [
{
path: "examples/form-formisch-array.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "form-formisch-complex",
type: "registry:example",
registryDependencies: [
"field",
"button",
"card",
"checkbox",
"radio-group",
"select",
"switch",
],
files: [
{
path: "examples/form-formisch-complex.tsx",
type: "registry:example",
},
],
dependencies: ["@formisch/react", "valibot"],
},
{
name: "drawer-dialog",
type: "registry:example",

View File

@@ -0,0 +1,190 @@
"use client"
import * as React from "react"
import {
FieldArray,
Form,
Field as FormischField,
insert,
remove,
reset,
useForm,
} from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { XIcon } from "lucide-react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLegend,
FieldSet,
} from "@/registry/new-york-v4/ui/field"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@/registry/new-york-v4/ui/input-group"
const FormSchema = v.object({
emails: v.pipe(
v.array(
v.object({
address: v.pipe(
v.string(),
v.nonEmpty("Enter an email address."),
v.email("Enter a valid email address.")
),
})
),
v.minLength(1, "Add at least one email address."),
v.maxLength(5, "You can add up to 5 email addresses.")
),
})
export default function FormFormischArray() {
const form = useForm({
schema: FormSchema,
initialInput: {
emails: [{ address: "" }, { address: "" }],
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader className="border-b">
<CardTitle>Contact Emails</CardTitle>
<CardDescription>Manage your contact email addresses.</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-array" onSubmit={handleSubmit}>
<FieldArray of={form} path={["emails"]}>
{(fieldArray) => (
<FieldSet className="gap-4">
<FieldLegend variant="label">Email Addresses</FieldLegend>
<FieldDescription>
Add up to 5 email addresses where we can contact you.
</FieldDescription>
<FieldGroup className="gap-4">
{fieldArray.items.map((item, index) => (
<FormischField
key={item}
of={form}
path={["emails", index, "address"]}
>
{(field) => (
<Field
orientation="horizontal"
data-invalid={field.errors !== null}
>
<FieldContent>
<InputGroup>
<InputGroupInput
{...field.props}
id={`form-formisch-array-email-${index}`}
value={field.input ?? ""}
aria-invalid={field.errors !== null}
placeholder="name@example.com"
type="email"
autoComplete="email"
/>
{fieldArray.items.length > 1 && (
<InputGroupAddon align="inline-end">
<InputGroupButton
type="button"
variant="ghost"
size="icon-xs"
onClick={() =>
remove(form, {
path: ["emails"],
at: index,
})
}
aria-label={`Remove email ${index + 1}`}
>
<XIcon />
</InputGroupButton>
</InputGroupAddon>
)}
</InputGroup>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({
message,
}))}
/>
)}
</FieldContent>
</Field>
)}
</FormischField>
))}
<Button
type="button"
variant="outline"
size="sm"
onClick={() =>
insert(form, {
path: ["emails"],
initialInput: { address: "" },
})
}
disabled={fieldArray.items.length >= 5}
>
Add Email Address
</Button>
</FieldGroup>
{fieldArray.errors && (
<FieldError
errors={fieldArray.errors.map((message) => ({ message }))}
/>
)}
</FieldSet>
)}
</FieldArray>
</Form>
</CardContent>
<CardFooter className="border-t">
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-array">
Save
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,190 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
} from "@/registry/new-york-v4/ui/field"
const tasks = [
{
id: "push",
label: "Push notifications",
},
{
id: "email",
label: "Email notifications",
},
] as const
const FormSchema = v.object({
responses: v.boolean(),
tasks: v.pipe(
v.array(v.string()),
v.minLength(1, "Please select at least one notification type."),
v.check(
(value) => value.every((task) => tasks.some((t) => t.id === task)),
"Invalid notification type selected."
)
),
})
export default function FormFormischCheckbox() {
const form = useForm({
schema: FormSchema,
initialInput: {
responses: true,
tasks: [],
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader>
<CardTitle>Notifications</CardTitle>
<CardDescription>Manage your notification preferences.</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-checkbox" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["responses"]}>
{(field) => (
<div>
<FieldSet data-invalid={field.errors !== null}>
<FieldLegend variant="label">Responses</FieldLegend>
<FieldDescription>
Get notified for requests that take time, like research or
image generation.
</FieldDescription>
<FieldGroup data-slot="checkbox-group">
<Field orientation="horizontal">
<Checkbox
id="form-formisch-checkbox-responses"
checked={field.input ?? false}
onCheckedChange={(checked) =>
field.onChange(checked === true)
}
disabled
/>
<FieldLabel
htmlFor="form-formisch-checkbox-responses"
className="font-normal"
>
Push notifications
</FieldLabel>
</Field>
</FieldGroup>
</FieldSet>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</div>
)}
</FormischField>
<FieldSeparator />
<FormischField of={form} path={["tasks"]}>
{(field) => (
<FieldGroup>
<FieldSet data-invalid={field.errors !== null}>
<FieldLegend variant="label">Tasks</FieldLegend>
<FieldDescription>
Get notified when tasks you&apos;ve created have updates.
</FieldDescription>
<FieldGroup data-slot="checkbox-group">
{tasks.map((task) => {
const current = field.input ?? []
return (
<Field
key={task.id}
orientation="horizontal"
data-invalid={field.errors !== null}
>
<Checkbox
id={`form-formisch-checkbox-${task.id}`}
aria-invalid={field.errors !== null}
checked={current.includes(task.id)}
onCheckedChange={(checked) => {
field.onChange(
checked === true
? [...current, task.id]
: current.filter(
(value) => value !== task.id
)
)
}}
/>
<FieldLabel
htmlFor={`form-formisch-checkbox-${task.id}`}
className="font-normal"
>
{task.label}
</FieldLabel>
</Field>
)
})}
</FieldGroup>
</FieldSet>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</FieldGroup>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-checkbox">
Save
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,305 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
import {
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
FieldTitle,
} from "@/registry/new-york-v4/ui/field"
import {
RadioGroup,
RadioGroupItem,
} from "@/registry/new-york-v4/ui/radio-group"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/registry/new-york-v4/ui/select"
import { Switch } from "@/registry/new-york-v4/ui/switch"
const addons = [
{
id: "analytics",
title: "Analytics",
description: "Advanced analytics and reporting",
},
{
id: "backup",
title: "Backup",
description: "Automated daily backups",
},
{
id: "support",
title: "Priority Support",
description: "24/7 premium customer support",
},
] as const
const FormSchema = v.object({
plan: v.pipe(
v.string(),
v.minLength(1, "Please select a subscription plan"),
v.check(
(value) => value === "basic" || value === "pro",
"Invalid plan selection. Please choose Basic or Pro"
)
),
billingPeriod: v.pipe(
v.string(),
v.minLength(1, "Please select a billing period")
),
addons: v.pipe(
v.array(v.string()),
v.minLength(1, "Please select at least one add-on"),
v.maxLength(3, "You can select up to 3 add-ons"),
v.check(
(value) => value.every((addon) => addons.some((a) => a.id === addon)),
"You selected an invalid add-on"
)
),
emailNotifications: v.boolean(),
})
export default function FormFormischComplex() {
const form = useForm({
schema: FormSchema,
initialInput: {
plan: "basic",
billingPeriod: "",
addons: [],
emailNotifications: false,
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full max-w-sm">
<CardHeader className="border-b">
<CardTitle>You&apos;re almost there!</CardTitle>
<CardDescription>
Choose your subscription plan and billing period.
</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-complex" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["plan"]}>
{(field) => (
<FieldSet data-invalid={field.errors !== null}>
<FieldLegend variant="label">Subscription Plan</FieldLegend>
<FieldDescription>
Choose your subscription plan.
</FieldDescription>
<RadioGroup
value={field.input ?? ""}
onValueChange={(value) => field.onChange(value)}
aria-invalid={field.errors !== null}
>
<FieldLabel htmlFor="form-formisch-complex-basic">
<Field orientation="horizontal">
<FieldContent>
<FieldTitle>Basic</FieldTitle>
<FieldDescription>
For individuals and small teams
</FieldDescription>
</FieldContent>
<RadioGroupItem
value="basic"
id="form-formisch-complex-basic"
/>
</Field>
</FieldLabel>
<FieldLabel htmlFor="form-formisch-complex-pro">
<Field orientation="horizontal">
<FieldContent>
<FieldTitle>Pro</FieldTitle>
<FieldDescription>
For businesses with higher demands
</FieldDescription>
</FieldContent>
<RadioGroupItem
value="pro"
id="form-formisch-complex-pro"
/>
</Field>
</FieldLabel>
</RadioGroup>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</FieldSet>
)}
</FormischField>
<FieldSeparator />
<FormischField of={form} path={["billingPeriod"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-formisch-complex-billingPeriod">
Billing Period
</FieldLabel>
<Select
value={field.input ?? ""}
onValueChange={(value) => field.onChange(value)}
>
<SelectTrigger
id="form-formisch-complex-billingPeriod"
aria-invalid={field.errors !== null}
>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent>
<SelectItem value="monthly">Monthly</SelectItem>
<SelectItem value="yearly">Yearly</SelectItem>
</SelectContent>
</Select>
<FieldDescription>
Choose how often you want to be billed.
</FieldDescription>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</Field>
)}
</FormischField>
<FieldSeparator />
<FormischField of={form} path={["addons"]}>
{(field) => {
const current = field.input ?? []
return (
<FieldSet>
<FieldLegend>Add-ons</FieldLegend>
<FieldDescription>
Select additional features you&apos;d like to include.
</FieldDescription>
<FieldGroup data-slot="checkbox-group">
{addons.map((addon) => (
<Field
key={addon.id}
orientation="horizontal"
data-invalid={field.errors !== null}
>
<Checkbox
id={`form-formisch-complex-${addon.id}`}
aria-invalid={field.errors !== null}
checked={current.includes(addon.id)}
onCheckedChange={(checked) => {
field.onChange(
checked === true
? [...current, addon.id]
: current.filter(
(value) => value !== addon.id
)
)
}}
/>
<FieldContent>
<FieldLabel
htmlFor={`form-formisch-complex-${addon.id}`}
>
{addon.title}
</FieldLabel>
<FieldDescription>
{addon.description}
</FieldDescription>
</FieldContent>
</Field>
))}
</FieldGroup>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</FieldSet>
)
}}
</FormischField>
<FieldSeparator />
<FormischField of={form} path={["emailNotifications"]}>
{(field) => (
<Field
orientation="horizontal"
data-invalid={field.errors !== null}
>
<FieldContent>
<FieldLabel htmlFor="form-formisch-complex-emailNotifications">
Email Notifications
</FieldLabel>
<FieldDescription>
Receive email updates about your subscription
</FieldDescription>
</FieldContent>
<Switch
id="form-formisch-complex-emailNotifications"
checked={field.input ?? false}
onCheckedChange={(checked) => field.onChange(checked)}
aria-invalid={field.errors !== null}
/>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</Field>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter className="border-t">
<Field>
<Button type="submit" form="form-formisch-complex">
Save Preferences
</Button>
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,154 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/registry/new-york-v4/ui/field"
import { Input } from "@/registry/new-york-v4/ui/input"
import {
InputGroup,
InputGroupAddon,
InputGroupText,
InputGroupTextarea,
} from "@/registry/new-york-v4/ui/input-group"
const FormSchema = v.object({
title: v.pipe(
v.string(),
v.minLength(5, "Bug title must be at least 5 characters."),
v.maxLength(32, "Bug title must be at most 32 characters.")
),
description: v.pipe(
v.string(),
v.minLength(20, "Description must be at least 20 characters."),
v.maxLength(100, "Description must be at most 100 characters.")
),
})
export default function BugReportForm() {
const form = useForm({
schema: FormSchema,
initialInput: {
title: "",
description: "",
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader>
<CardTitle>Bug Report</CardTitle>
<CardDescription>
Help us improve by reporting bugs you encounter.
</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-demo" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["title"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-formisch-demo-title">
Bug Title
</FieldLabel>
<Input
{...field.props}
id="form-formisch-demo-title"
value={field.input ?? ""}
aria-invalid={field.errors !== null}
placeholder="Login button not working on mobile"
autoComplete="off"
/>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</Field>
)}
</FormischField>
<FormischField of={form} path={["description"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-formisch-demo-description">
Description
</FieldLabel>
<InputGroup>
<InputGroupTextarea
{...field.props}
id="form-formisch-demo-description"
value={field.input ?? ""}
placeholder="I'm having an issue with the login button on mobile."
rows={6}
className="min-h-24 resize-none"
aria-invalid={field.errors !== null}
/>
<InputGroupAddon align="block-end">
<InputGroupText className="tabular-nums">
{(field.input ?? "").length}/100 characters
</InputGroupText>
</InputGroupAddon>
</InputGroup>
<FieldDescription>
Include steps to reproduce, expected behavior, and what
actually happened.
</FieldDescription>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</Field>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-demo">
Submit
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,117 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/registry/new-york-v4/ui/field"
import { Input } from "@/registry/new-york-v4/ui/input"
const FormSchema = v.object({
username: v.pipe(
v.string(),
v.minLength(3, "Username must be at least 3 characters."),
v.maxLength(10, "Username must be at most 10 characters."),
v.regex(
/^[a-zA-Z0-9_]+$/,
"Username can only contain letters, numbers, and underscores."
)
),
})
export default function FormFormischInput() {
const form = useForm({
schema: FormSchema,
initialInput: {
username: "",
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader>
<CardTitle>Profile Settings</CardTitle>
<CardDescription>
Update your profile information below.
</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-input" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["username"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-formisch-input-username">
Username
</FieldLabel>
<Input
{...field.props}
id="form-formisch-input-username"
value={field.input ?? ""}
aria-invalid={field.errors !== null}
placeholder="shadcn"
autoComplete="username"
/>
<FieldDescription>
This is your public display name. Must be between 3 and 10
characters. Must only contain letters, numbers, and
underscores.
</FieldDescription>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</Field>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-input">
Save
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,154 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSet,
FieldTitle,
} from "@/registry/new-york-v4/ui/field"
import {
RadioGroup,
RadioGroupItem,
} from "@/registry/new-york-v4/ui/radio-group"
const plans = [
{
id: "starter",
title: "Starter (100K tokens/month)",
description: "For everyday use with basic features.",
},
{
id: "pro",
title: "Pro (1M tokens/month)",
description: "For advanced AI usage with more features.",
},
{
id: "enterprise",
title: "Enterprise (Unlimited tokens)",
description: "For large teams and heavy usage.",
},
] as const
const FormSchema = v.object({
plan: v.pipe(
v.string(),
v.minLength(1, "You must select a subscription plan to continue.")
),
})
export default function FormFormischRadioGroup() {
const form = useForm({
schema: FormSchema,
initialInput: {
plan: "",
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader>
<CardTitle>Subscription Plan</CardTitle>
<CardDescription>
See pricing and features for each plan.
</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-radiogroup" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["plan"]}>
{(field) => (
<FieldSet data-invalid={field.errors !== null}>
<FieldLegend>Plan</FieldLegend>
<FieldDescription>
You can upgrade or downgrade your plan at any time.
</FieldDescription>
<RadioGroup
value={field.input ?? ""}
onValueChange={(value) => field.onChange(value)}
aria-invalid={field.errors !== null}
>
{plans.map((plan) => (
<FieldLabel
key={plan.id}
htmlFor={`form-formisch-radiogroup-${plan.id}`}
>
<Field
orientation="horizontal"
data-invalid={field.errors !== null}
>
<FieldContent>
<FieldTitle>{plan.title}</FieldTitle>
<FieldDescription>
{plan.description}
</FieldDescription>
</FieldContent>
<RadioGroupItem
value={plan.id}
id={`form-formisch-radiogroup-${plan.id}`}
aria-invalid={field.errors !== null}
/>
</Field>
</FieldLabel>
))}
</RadioGroup>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</FieldSet>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-radiogroup">
Save
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,150 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/registry/new-york-v4/ui/field"
import {
Select,
SelectContent,
SelectItem,
SelectSeparator,
SelectTrigger,
SelectValue,
} from "@/registry/new-york-v4/ui/select"
const spokenLanguages = [
{ label: "English", value: "en" },
{ label: "Spanish", value: "es" },
{ label: "French", value: "fr" },
{ label: "German", value: "de" },
{ label: "Italian", value: "it" },
{ label: "Chinese", value: "zh" },
{ label: "Japanese", value: "ja" },
] as const
const FormSchema = v.object({
language: v.pipe(
v.string(),
v.minLength(1, "Please select your spoken language."),
v.check(
(value) => value !== "auto",
"Auto-detection is not allowed. Please select a specific language."
)
),
})
export default function FormFormischSelect() {
const form = useForm({
schema: FormSchema,
initialInput: {
language: "",
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-lg">
<CardHeader>
<CardTitle>Language Preferences</CardTitle>
<CardDescription>
Select your preferred spoken language.
</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-select" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["language"]}>
{(field) => (
<Field
orientation="responsive"
data-invalid={field.errors !== null}
>
<FieldContent>
<FieldLabel htmlFor="form-formisch-select-language">
Spoken Language
</FieldLabel>
<FieldDescription>
For best results, select the language you speak.
</FieldDescription>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</FieldContent>
<Select
value={field.input ?? ""}
onValueChange={(value) => field.onChange(value)}
>
<SelectTrigger
id="form-formisch-select-language"
aria-invalid={field.errors !== null}
className="min-w-[120px]"
>
<SelectValue placeholder="Select" />
</SelectTrigger>
<SelectContent position="item-aligned">
<SelectItem value="auto">Auto</SelectItem>
<SelectSeparator />
{spokenLanguages.map((language) => (
<SelectItem key={language.value} value={language.value}>
{language.label}
</SelectItem>
))}
</SelectContent>
</Select>
</Field>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-select">
Save
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,117 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
Field,
FieldContent,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/registry/new-york-v4/ui/field"
import { Switch } from "@/registry/new-york-v4/ui/switch"
const FormSchema = v.object({
twoFactor: v.pipe(
v.boolean(),
v.check(
(value) => value === true,
"It is highly recommended to enable two-factor authentication."
)
),
})
export default function FormFormischSwitch() {
const form = useForm({
schema: FormSchema,
initialInput: {
twoFactor: false,
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader>
<CardTitle>Security Settings</CardTitle>
<CardDescription>
Manage your account security preferences.
</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-switch" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["twoFactor"]}>
{(field) => (
<Field
orientation="horizontal"
data-invalid={field.errors !== null}
>
<FieldContent>
<FieldLabel htmlFor="form-formisch-switch-twoFactor">
Multi-factor authentication
</FieldLabel>
<FieldDescription>
Enable multi-factor authentication to secure your account.
</FieldDescription>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</FieldContent>
<Switch
id="form-formisch-switch-twoFactor"
checked={field.input ?? false}
onCheckedChange={(checked) => field.onChange(checked)}
aria-invalid={field.errors !== null}
/>
</Field>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-switch">
Save
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,112 @@
"use client"
import * as React from "react"
import { Form, Field as FormischField, reset, useForm } from "@formisch/react"
import type { SubmitHandler } from "@formisch/react"
import { toast } from "sonner"
import * as v from "valibot"
import { Button } from "@/registry/new-york-v4/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/registry/new-york-v4/ui/card"
import {
Field,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
} from "@/registry/new-york-v4/ui/field"
import { Textarea } from "@/registry/new-york-v4/ui/textarea"
const FormSchema = v.object({
about: v.pipe(
v.string(),
v.minLength(10, "Please provide at least 10 characters."),
v.maxLength(200, "Please keep it under 200 characters.")
),
})
export default function FormFormischTextarea() {
const form = useForm({
schema: FormSchema,
initialInput: {
about: "",
},
})
const handleSubmit: SubmitHandler<typeof FormSchema> = (output) => {
toast("You submitted the following values:", {
description: (
<pre className="mt-2 w-[320px] overflow-x-auto rounded-md bg-code p-4 text-code-foreground">
<code>{JSON.stringify(output, null, 2)}</code>
</pre>
),
position: "bottom-right",
classNames: {
content: "flex flex-col gap-2",
},
style: {
"--border-radius": "calc(var(--radius) + 4px)",
} as React.CSSProperties,
})
}
return (
<Card className="w-full sm:max-w-md">
<CardHeader>
<CardTitle>Personalization</CardTitle>
<CardDescription>
Customize your experience by telling us more about yourself.
</CardDescription>
</CardHeader>
<CardContent>
<Form of={form} id="form-formisch-textarea" onSubmit={handleSubmit}>
<FieldGroup>
<FormischField of={form} path={["about"]}>
{(field) => (
<Field data-invalid={field.errors !== null}>
<FieldLabel htmlFor="form-formisch-textarea-about">
More about you
</FieldLabel>
<Textarea
{...field.props}
id="form-formisch-textarea-about"
value={field.input ?? ""}
aria-invalid={field.errors !== null}
placeholder="I'm a software engineer..."
className="min-h-[120px]"
/>
<FieldDescription>
Tell us more about yourself. This will be used to help us
personalize your experience.
</FieldDescription>
{field.errors && (
<FieldError
errors={field.errors.map((message) => ({ message }))}
/>
)}
</Field>
)}
</FormischField>
</FieldGroup>
</Form>
</CardContent>
<CardFooter>
<Field orientation="horizontal">
<Button type="button" variant="outline" onClick={() => reset(form)}>
Reset
</Button>
<Button type="submit" form="form-formisch-textarea">
Save
</Button>
</Field>
</CardFooter>
</Card>
)
}

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -24,7 +24,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -15,7 +15,7 @@ const ToggleGroupContext = React.createContext<
>({
size: "default",
variant: "default",
spacing: 0,
spacing: 2,
orientation: "horizontal",
})
@@ -23,7 +23,7 @@ function ToggleGroup({
className,
variant,
size,
spacing = 0,
spacing = 2,
orientation = "horizontal",
children,
...props

View File

@@ -1027,162 +1027,4 @@ function Menu({ className, ...props }: React.ComponentProps<"div">) {
"
`)
})
it("applies styles to cn-* classes in object properties (toastOptions pattern)", async () => {
const source = `import * as React from "react"
import { Toaster as Sonner } from "sonner"
const Toaster = ({ ...props }) => {
return (
<Sonner
toastOptions={{
classNames: {
toast: "cn-toast",
},
}}
{...props}
/>
)
}
`
const styleMap: StyleMap = {
"cn-toast": "rounded-2xl",
}
const result = await applyTransform(source, styleMap)
expect(result).toMatchInlineSnapshot(`
"import * as React from "react"
import { Toaster as Sonner } from "sonner"
const Toaster = ({ ...props }) => {
return (
<Sonner
toastOptions={{
classNames: {
toast: "rounded-2xl",
},
}}
{...props}
/>
)
}
"
`)
})
it("applies styles to cn-* classes in deeply nested object properties", async () => {
const source = `import * as React from "react"
const config = {
options: {
classNames: {
wrapper: "cn-wrapper existing-class",
inner: "cn-inner",
},
},
}
`
const styleMap: StyleMap = {
"cn-wrapper": "flex flex-col",
"cn-inner": "p-4 rounded-lg",
}
const result = await applyTransform(source, styleMap)
expect(result).toMatchInlineSnapshot(`
"import * as React from "react"
const config = {
options: {
classNames: {
wrapper: "flex flex-col existing-class",
inner: "p-4 rounded-lg",
},
},
}
"
`)
})
it("removes cn-* classes from object properties when not in styleMap", async () => {
const source = `import * as React from "react"
const config = {
classNames: {
item: "cn-unknown-class existing-class",
},
}
`
const styleMap: StyleMap = {}
const result = await applyTransform(source, styleMap)
expect(result).toMatchInlineSnapshot(`
"import * as React from "react"
const config = {
classNames: {
item: "existing-class",
},
}
"
`)
})
it("preserves allowlisted cn-* classes in object properties", async () => {
const source = `import * as React from "react"
const config = {
classNames: {
target: "cn-menu-target",
},
}
`
const styleMap: StyleMap = {
"cn-menu-target": "z-50 origin-top",
}
const result = await applyTransform(source, styleMap)
expect(result).toMatchInlineSnapshot(`
"import * as React from "react"
const config = {
classNames: {
target: "cn-menu-target",
},
}
"
`)
})
it("handles multiple cn-* classes in object property values", async () => {
const source = `import * as React from "react"
const options = {
toast: "cn-toast cn-toast-content extra-class",
}
`
const styleMap: StyleMap = {
"cn-toast": "rounded-lg",
"cn-toast-content": "p-4",
}
const result = await applyTransform(source, styleMap)
expect(result).toMatchInlineSnapshot(`
"import * as React from "react"
const options = {
toast: "rounded-lg p-4 extra-class",
}
"
`)
})
})

View File

@@ -41,7 +41,6 @@ export const transformStyleMap: TransformerStyle<SourceFile> = async ({
applyToCvaCalls(sourceFile, styleMap, matchedClasses)
applyToClassNameAttributes(sourceFile, styleMap, matchedClasses)
applyToMergePropsCalls(sourceFile, styleMap, matchedClasses)
applyToAllStringLiterals(sourceFile, styleMap)
return sourceFile
}
@@ -289,6 +288,11 @@ function extractCnClasses(str: string) {
return Array.from(matches, (match) => match[0])
}
function extractCnClass(str: string) {
const classes = extractCnClasses(str)
return classes[0] ?? null
}
function removeCnClasses(str: string) {
return str
.replace(/\bcn-[\w-]+\b/g, (match) => {
@@ -602,69 +606,3 @@ function applyClassesToCnCall(
cnCall.replaceWithText(`cn(${updatedArguments.join(", ")})`)
}
}
function isInAlreadyProcessedContext(node: Node): boolean {
let current = node.getParent()
while (current) {
if (Node.isCallExpression(current)) {
const expression = current.getExpression()
if (Node.isIdentifier(expression)) {
const name = expression.getText()
if (name === "cn" || name === "cva") {
return true
}
}
}
if (Node.isJsxAttribute(current)) {
const attrName = current.getNameNode().getText()
if (attrName === "className") {
return true
}
}
current = current.getParent()
}
return false
}
function applyToAllStringLiterals(sourceFile: SourceFile, styleMap: StyleMap) {
sourceFile.forEachDescendant((node) => {
if (!isStringLiteralLike(node)) {
return
}
if (isInAlreadyProcessedContext(node)) {
return
}
const stringValue = node.getLiteralText()
const cnClasses = extractCnClasses(stringValue)
if (cnClasses.length === 0) {
return
}
// Skip allowlisted classes — they are handled at CLI install time.
const classesToInline = cnClasses.filter(
(cnClass) => !ALLOWLIST.has(cnClass)
)
const tailwindClassesToApply = classesToInline
.map((cnClass) => styleMap[cnClass])
.filter((classes): classes is string => Boolean(classes))
if (tailwindClassesToApply.length > 0) {
const mergedClasses = tailwindClassesToApply.join(" ")
const updated = removeCnClasses(mergeClasses(mergedClasses, stringValue))
node.setLiteralValue(updated)
} else {
const updated = removeCnClasses(stringValue)
if (updated !== stringValue) {
node.setLiteralValue(updated)
}
}
})
}

37
pnpm-lock.yaml generated
View File

@@ -139,6 +139,9 @@ importers:
'@faker-js/faker':
specifier: ^10.1.0
version: 10.1.0
'@formisch/react':
specifier: ^0.4.4
version: 0.4.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(valibot@1.4.0(typescript@5.9.2))
'@hookform/resolvers':
specifier: ^3.10.0
version: 3.10.0(react-hook-form@7.62.0(react@19.2.3))
@@ -301,6 +304,9 @@ importers:
ts-morph:
specifier: 26.0.0
version: 26.0.0
valibot:
specifier: ^1.4.0
version: 1.4.0(typescript@5.9.2)
vaul:
specifier: 1.1.2
version: 1.1.2(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
@@ -1586,6 +1592,17 @@ packages:
'@formatjs/intl-localematcher@0.6.2':
resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==}
'@formisch/react@0.4.4':
resolution: {integrity: sha512-7BjxZgWlyn7Xl0g1hi3MmelgPrmWzbonwr0biC+4ffsOXSIvUSsJVWhDqGgy8xltP598YbTaTyKjSZ5tpEbcYg==}
peerDependencies:
react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
typescript: '>=5'
valibot: ^1.0.0
peerDependenciesMeta:
typescript:
optional: true
'@hapi/hoek@9.3.0':
resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==}
@@ -8048,6 +8065,14 @@ packages:
util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
valibot@1.4.0:
resolution: {integrity: sha512-iC/x7fVcSyOwlm/VSt7RlHnzNGLGvR9GnxdifUeWoCJo0q4ZZvrVkIHC6faTlkxG47I2Y4UrFquPuVHCrOnrLg==}
peerDependencies:
typescript: '>=5'
peerDependenciesMeta:
typescript:
optional: true
validate-npm-package-name@3.0.0:
resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==}
@@ -9331,6 +9356,14 @@ snapshots:
dependencies:
tslib: 2.8.1
'@formisch/react@0.4.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.2)(valibot@1.4.0(typescript@5.9.2))':
dependencies:
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
valibot: 1.4.0(typescript@5.9.2)
optionalDependencies:
typescript: 5.9.2
'@hapi/hoek@9.3.0': {}
'@hapi/topo@5.1.0':
@@ -16736,6 +16769,10 @@ snapshots:
util-deprecate@1.0.2: {}
valibot@1.4.0(typescript@5.9.2):
optionalDependencies:
typescript: 5.9.2
validate-npm-package-name@3.0.0:
dependencies:
builtins: 1.0.3