mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-07-07 14:08:47 +00:00
Compare commits
13 Commits
fix/cn-cla
...
revert-106
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9f57eb0e8 | ||
|
|
d7066f4a2d | ||
|
|
5274de83d6 | ||
|
|
7e4dac7f31 | ||
|
|
28122dba18 | ||
|
|
93cde61946 | ||
|
|
c2dc06a99c | ||
|
|
c9930b7fda | ||
|
|
d1149454a8 | ||
|
|
36139f6200 | ||
|
|
15ac1be92b | ||
|
|
8ca30ed32c | ||
|
|
e2605bc7c2 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"shadcn": patch
|
||||
---
|
||||
|
||||
handle cn-\* class transformation in non-className contexts
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
669
apps/v4/content/docs/forms/formisch.mdx
Normal file
669
apps/v4/content/docs/forms/formisch.mdx
Normal 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'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.")
|
||||
),
|
||||
})
|
||||
```
|
||||
@@ -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"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"pages": ["react-hook-form", "tanstack-form"]
|
||||
"pages": ["react-hook-form", "tanstack-form", "formisch"]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
23
apps/v4/public/r/styles/new-york-v4/form-formisch-array.json
Normal file
23
apps/v4/public/r/styles/new-york-v4/form-formisch-array.json
Normal file
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
23
apps/v4/public/r/styles/new-york-v4/form-formisch-demo.json
Normal file
23
apps/v4/public/r/styles/new-york-v4/form-formisch-demo.json
Normal file
File diff suppressed because one or more lines are too long
22
apps/v4/public/r/styles/new-york-v4/form-formisch-input.json
Normal file
22
apps/v4/public/r/styles/new-york-v4/form-formisch-input.json
Normal 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -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",
|
||||
|
||||
190
apps/v4/registry/new-york-v4/examples/form-formisch-array.tsx
Normal file
190
apps/v4/registry/new-york-v4/examples/form-formisch-array.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
190
apps/v4/registry/new-york-v4/examples/form-formisch-checkbox.tsx
Normal file
190
apps/v4/registry/new-york-v4/examples/form-formisch-checkbox.tsx
Normal 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'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>
|
||||
)
|
||||
}
|
||||
305
apps/v4/registry/new-york-v4/examples/form-formisch-complex.tsx
Normal file
305
apps/v4/registry/new-york-v4/examples/form-formisch-complex.tsx
Normal 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'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'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>
|
||||
)
|
||||
}
|
||||
154
apps/v4/registry/new-york-v4/examples/form-formisch-demo.tsx
Normal file
154
apps/v4/registry/new-york-v4/examples/form-formisch-demo.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
117
apps/v4/registry/new-york-v4/examples/form-formisch-input.tsx
Normal file
117
apps/v4/registry/new-york-v4/examples/form-formisch-input.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
150
apps/v4/registry/new-york-v4/examples/form-formisch-select.tsx
Normal file
150
apps/v4/registry/new-york-v4/examples/form-formisch-select.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
117
apps/v4/registry/new-york-v4/examples/form-formisch-switch.tsx
Normal file
117
apps/v4/registry/new-york-v4/examples/form-formisch-switch.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
112
apps/v4/registry/new-york-v4/examples/form-formisch-textarea.tsx
Normal file
112
apps/v4/registry/new-york-v4/examples/form-formisch-textarea.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
"
|
||||
`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -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
37
pnpm-lock.yaml
generated
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user