) {
+ return (
+
+
+ Create an account
+
+ Enter your information below to create your account
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-01/page.tsx b/apps/ui/src/registry/bases/base/blocks/signup-01/page.tsx
new file mode 100644
index 0000000..f217385
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-01/page.tsx
@@ -0,0 +1,11 @@
+import { SignupForm } from "@/registry/bases/base/blocks/signup-01/components/signup-form"
+
+export default function Page() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-02/components/signup-form.tsx b/apps/ui/src/registry/bases/base/blocks/signup-02/components/signup-form.tsx
new file mode 100644
index 0000000..652289c
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-02/components/signup-form.tsx
@@ -0,0 +1,92 @@
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-02/page.tsx b/apps/ui/src/registry/bases/base/blocks/signup-02/page.tsx
new file mode 100644
index 0000000..55dce58
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-02/page.tsx
@@ -0,0 +1,39 @@
+
+import { SignupForm } from "@/registry/bases/base/blocks/signup-02/components/signup-form"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SignupPage() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-03/components/signup-form.tsx b/apps/ui/src/registry/bases/base/blocks/signup-03/components/signup-form.tsx
new file mode 100644
index 0000000..ecbb41e
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-03/components/signup-form.tsx
@@ -0,0 +1,80 @@
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+
+
+ Create your account
+
+ Enter your email below to create your account
+
+
+
+
+
+
+
+ By clicking continue, you agree to our Terms of Service {" "}
+ and Privacy Policy .
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-03/page.tsx b/apps/ui/src/registry/bases/base/blocks/signup-03/page.tsx
new file mode 100644
index 0000000..fae4c8a
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-03/page.tsx
@@ -0,0 +1,26 @@
+
+import { SignupForm } from "@/registry/bases/base/blocks/signup-03/components/signup-form"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SignupPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-04/components/signup-form.tsx b/apps/ui/src/registry/bases/base/blocks/signup-04/components/signup-form.tsx
new file mode 100644
index 0000000..8791e3d
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-04/components/signup-form.tsx
@@ -0,0 +1,114 @@
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Card, CardContent } from "@/registry/bases/base/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-04/page.tsx b/apps/ui/src/registry/bases/base/blocks/signup-04/page.tsx
new file mode 100644
index 0000000..ec7ded9
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-04/page.tsx
@@ -0,0 +1,11 @@
+import { SignupForm } from "@/registry/bases/base/blocks/signup-04/components/signup-form"
+
+export default function SignupPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-05/components/signup-form.tsx b/apps/ui/src/registry/bases/base/blocks/signup-05/components/signup-form.tsx
new file mode 100644
index 0000000..095763e
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-05/components/signup-form.tsx
@@ -0,0 +1,85 @@
+
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/blocks/signup-05/page.tsx b/apps/ui/src/registry/bases/base/blocks/signup-05/page.tsx
new file mode 100644
index 0000000..f8612f2
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/blocks/signup-05/page.tsx
@@ -0,0 +1,11 @@
+import { SignupForm } from "@/registry/bases/base/blocks/signup-05/components/signup-form"
+
+export default function SignupPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/components/_registry.ts b/apps/ui/src/registry/bases/base/components/_registry.ts
new file mode 100644
index 0000000..f0b6741
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/components/_registry.ts
@@ -0,0 +1,15 @@
+import { type Registry } from "shadcn/schema"
+
+export const components: Registry["items"] = [
+ {
+ name: "example",
+ title: "Example",
+ type: "registry:component",
+ files: [
+ {
+ path: "components/example.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/bases/base/components/example.tsx b/apps/ui/src/registry/bases/base/components/example.tsx
new file mode 100644
index 0000000..05c4b3e
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/components/example.tsx
@@ -0,0 +1,56 @@
+import { cn } from "@/registry/bases/base/lib/utils"
+
+function ExampleWrapper({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function Example({
+ title,
+ children,
+ className,
+ containerClassName,
+ ...props
+}: React.ComponentProps<"div"> & {
+ title?: string
+ containerClassName?: string
+}) {
+ return (
+
+ {title && (
+
+ {title}
+
+ )}
+
+ {children}
+
+
+ )
+}
+
+export { ExampleWrapper, Example }
diff --git a/apps/ui/src/registry/bases/base/examples/_registry.ts b/apps/ui/src/registry/bases/base/examples/_registry.ts
new file mode 100644
index 0000000..93ea9a2
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/_registry.ts
@@ -0,0 +1,908 @@
+import { type Registry } from "shadcn/schema"
+
+export const examples: Registry["items"] = [
+ {
+ name: "accordion-example",
+ title: "Accordion",
+ type: "registry:example",
+ registryDependencies: ["accordion", "button", "card", "example"],
+ files: [
+ {
+ path: "examples/accordion-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "alert-example",
+ title: "Alert",
+ type: "registry:example",
+ registryDependencies: ["alert", "badge", "example"],
+ files: [
+ {
+ path: "examples/alert-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "alert-dialog-example",
+ title: "Alert Dialog",
+ type: "registry:example",
+ registryDependencies: ["alert-dialog", "button", "dialog", "example"],
+ files: [
+ {
+ path: "examples/alert-dialog-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "aspect-ratio-example",
+ title: "Aspect Ratio",
+ type: "registry:example",
+ registryDependencies: ["aspect-ratio", "example"],
+ files: [
+ {
+ path: "examples/aspect-ratio-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "avatar-example",
+ title: "Avatar",
+ type: "registry:example",
+ registryDependencies: ["avatar", "button", "empty", "example"],
+ files: [
+ {
+ path: "examples/avatar-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "badge-example",
+ title: "Badge",
+ type: "registry:example",
+ registryDependencies: ["badge", "spinner", "example"],
+ files: [
+ {
+ path: "examples/badge-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb-example",
+ title: "Breadcrumb",
+ type: "registry:example",
+ registryDependencies: ["breadcrumb", "dropdown-menu", "example"],
+ files: [
+ {
+ path: "examples/breadcrumb-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-example",
+ title: "Button",
+ type: "registry:example",
+ registryDependencies: ["button", "example"],
+ files: [
+ {
+ path: "examples/button-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-example",
+ title: "Button Group",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "button-group",
+ "dropdown-menu",
+ "field",
+ "input",
+ "input-group",
+ "label",
+ "popover",
+ "select",
+ "tooltip",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/button-group-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "calendar-example",
+ title: "Calendar",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "calendar",
+ "card",
+ "field",
+ "input",
+ "label",
+ "popover",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/calendar-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "card-example",
+ title: "Card",
+ type: "registry:example",
+ registryDependencies: [
+ "avatar",
+ "button",
+ "card",
+ "field",
+ "input",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/card-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "carousel-example",
+ title: "Carousel",
+ type: "registry:example",
+ registryDependencies: ["card", "carousel", "example"],
+ files: [
+ {
+ path: "examples/carousel-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "chart-example",
+ title: "Chart",
+ type: "registry:example",
+ registryDependencies: ["chart", "card", "example"],
+ files: [
+ {
+ path: "examples/chart-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "checkbox-example",
+ title: "Checkbox",
+ type: "registry:example",
+ registryDependencies: ["checkbox", "field", "table", "example"],
+ files: [
+ {
+ path: "examples/checkbox-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "collapsible-example",
+ title: "Collapsible",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "card",
+ "collapsible",
+ "field",
+ "input",
+ "tabs",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/collapsible-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "combobox-example",
+ title: "Combobox",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "card",
+ "combobox",
+ "dialog",
+ "field",
+ "input",
+ "input-group",
+ "item",
+ "select",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/combobox-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "command-example",
+ title: "Command",
+ type: "registry:example",
+ registryDependencies: ["button", "command", "example"],
+ files: [
+ {
+ path: "examples/command-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "context-menu-example",
+ title: "Context Menu",
+ type: "registry:example",
+ registryDependencies: ["button", "context-menu", "dialog", "example"],
+ files: [
+ {
+ path: "examples/context-menu-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "dialog-example",
+ title: "Dialog",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "checkbox",
+ "dialog",
+ "field",
+ "input",
+ "input-group",
+ "kbd",
+ "native-select",
+ "select",
+ "switch",
+ "tabs",
+ "textarea",
+ "tooltip",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/dialog-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "drawer-example",
+ title: "Drawer",
+ type: "registry:example",
+ registryDependencies: ["drawer", "example"],
+ files: [
+ {
+ path: "examples/drawer-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "dropdown-menu-example",
+ title: "Dropdown Menu",
+ type: "registry:example",
+ registryDependencies: [
+ "avatar",
+ "button",
+ "dialog",
+ "dropdown-menu",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/dropdown-menu-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-example",
+ title: "Empty",
+ type: "registry:example",
+ registryDependencies: ["button", "empty", "input-group", "kbd", "example"],
+ files: [
+ {
+ path: "examples/empty-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-example",
+ title: "Field",
+ type: "registry:example",
+ registryDependencies: [
+ "badge",
+ "checkbox",
+ "field",
+ "input",
+ "input-otp",
+ "native-select",
+ "radio-group",
+ "select",
+ "slider",
+ "switch",
+ "textarea",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/field-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "hover-card-example",
+ title: "Hover Card",
+ type: "registry:example",
+ registryDependencies: ["button", "dialog", "hover-card", "example"],
+ files: [
+ {
+ path: "examples/hover-card-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-example",
+ title: "Input",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "field",
+ "input",
+ "native-select",
+ "select",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/input-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-example",
+ title: "Input Group",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "button-group",
+ "card",
+ "dropdown-menu",
+ "field",
+ "input",
+ "input-group",
+ "kbd",
+ "popover",
+ "spinner",
+ "textarea",
+ "tooltip",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/input-group-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-otp-example",
+ title: "Input OTP",
+ type: "registry:example",
+ registryDependencies: ["button", "card", "field", "input-otp", "example"],
+ files: [
+ {
+ path: "examples/input-otp-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-example",
+ title: "Item",
+ type: "registry:example",
+ registryDependencies: ["button", "item", "example"],
+ files: [
+ {
+ path: "examples/item-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "kbd-example",
+ title: "Kbd",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "input-group",
+ "kbd",
+ "tooltip",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/kbd-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "label-example",
+ title: "Label",
+ type: "registry:example",
+ registryDependencies: [
+ "checkbox",
+ "field",
+ "input",
+ "label",
+ "textarea",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/label-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "menubar-example",
+ title: "Menubar",
+ type: "registry:example",
+ registryDependencies: ["button", "dialog", "menubar", "example"],
+ files: [
+ {
+ path: "examples/menubar-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "native-select-example",
+ title: "Native Select",
+ type: "registry:example",
+ registryDependencies: ["field", "native-select", "example"],
+ files: [
+ {
+ path: "examples/native-select-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "navigation-menu-example",
+ title: "Navigation Menu",
+ type: "registry:example",
+ registryDependencies: ["button", "dialog", "navigation-menu", "example"],
+ files: [
+ {
+ path: "examples/navigation-menu-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "pagination-example",
+ title: "Pagination",
+ type: "registry:example",
+ registryDependencies: ["field", "pagination", "select", "example"],
+ files: [
+ {
+ path: "examples/pagination-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "popover-example",
+ title: "Popover",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "dialog",
+ "field",
+ "input",
+ "popover",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/popover-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "progress-example",
+ title: "Progress",
+ type: "registry:example",
+ registryDependencies: ["field", "item", "progress", "slider", "example"],
+ files: [
+ {
+ path: "examples/progress-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "radio-group-example",
+ title: "Radio Group",
+ type: "registry:example",
+ registryDependencies: ["field", "radio-group", "example"],
+ files: [
+ {
+ path: "examples/radio-group-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "resizable-example",
+ title: "Resizable",
+ type: "registry:example",
+ registryDependencies: ["resizable", "example"],
+ files: [
+ {
+ path: "examples/resizable-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "scroll-area-example",
+ title: "Scroll Area",
+ type: "registry:example",
+ registryDependencies: ["scroll-area", "separator", "example"],
+ files: [
+ {
+ path: "examples/scroll-area-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "select-example",
+ title: "Select",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "dialog",
+ "field",
+ "input",
+ "item",
+ "native-select",
+ "select",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/select-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "separator-example",
+ title: "Separator",
+ type: "registry:example",
+ registryDependencies: ["separator", "example"],
+ files: [
+ {
+ path: "examples/separator-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sheet-example",
+ title: "Sheet",
+ type: "registry:example",
+ registryDependencies: ["button", "field", "input", "sheet", "example"],
+ files: [
+ {
+ path: "examples/sheet-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sidebar-example",
+ title: "Sidebar",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "dropdown-menu",
+ "item",
+ "label",
+ "sidebar",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/sidebar-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sidebar-icon-example",
+ title: "Sidebar (Icon)",
+ type: "registry:example",
+ registryDependencies: [
+ "avatar",
+ "button",
+ "collapsible",
+ "dropdown-menu",
+ "item",
+ "sidebar",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/sidebar-icon-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sidebar-inset-example",
+ title: "Sidebar (Inset)",
+ type: "registry:example",
+ registryDependencies: ["collapsible", "sidebar", "example"],
+ files: [
+ {
+ path: "examples/sidebar-inset-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sidebar-floating-example",
+ title: "Sidebar (Floating)",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "card",
+ "dropdown-menu",
+ "field",
+ "item",
+ "sidebar",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/sidebar-floating-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "skeleton-example",
+ title: "Skeleton",
+ type: "registry:example",
+ registryDependencies: ["skeleton", "example"],
+ files: [
+ {
+ path: "examples/skeleton-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "slider-example",
+ title: "Slider",
+ type: "registry:example",
+ registryDependencies: ["label", "slider", "example"],
+ files: [
+ {
+ path: "examples/slider-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sonner-example",
+ title: "Sonner",
+ type: "registry:example",
+ registryDependencies: ["sonner", "example"],
+ files: [
+ {
+ path: "examples/sonner-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-example",
+ title: "Spinner",
+ type: "registry:example",
+ registryDependencies: [
+ "badge",
+ "button",
+ "empty",
+ "field",
+ "input-group",
+ "spinner",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/spinner-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "switch-example",
+ title: "Switch",
+ type: "registry:example",
+ registryDependencies: ["field", "label", "switch", "example"],
+ files: [
+ {
+ path: "examples/switch-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "table-example",
+ title: "Table",
+ type: "registry:example",
+ registryDependencies: [
+ "button",
+ "dropdown-menu",
+ "input",
+ "select",
+ "table",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/table-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "tabs-example",
+ title: "Tabs",
+ type: "registry:example",
+ registryDependencies: ["button", "dropdown-menu", "tabs", "example"],
+ files: [
+ {
+ path: "examples/tabs-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "textarea-example",
+ title: "Textarea",
+ type: "registry:example",
+ registryDependencies: ["field", "textarea", "example"],
+ files: [
+ {
+ path: "examples/textarea-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-example",
+ title: "Toggle",
+ type: "registry:example",
+ registryDependencies: ["toggle", "example"],
+ files: [
+ {
+ path: "examples/toggle-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-example",
+ title: "Toggle Group",
+ type: "registry:example",
+ registryDependencies: ["input", "select", "toggle-group", "example"],
+ files: [
+ {
+ path: "examples/toggle-group-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "tooltip-example",
+ title: "Tooltip",
+ type: "registry:example",
+ registryDependencies: ["button", "kbd", "tooltip", "example"],
+ files: [
+ {
+ path: "examples/tooltip-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "demo",
+ title: "Demo",
+ type: "registry:example",
+ registryDependencies: [
+ "alert-dialog",
+ "badge",
+ "button",
+ "button-group",
+ "card",
+ "checkbox",
+ "dropdown-menu",
+ "field",
+ "input-group",
+ "item",
+ "radio-group",
+ "slider",
+ "switch",
+ "textarea",
+ ],
+ files: [
+ {
+ path: "examples/demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "component-example",
+ title: "Example",
+ type: "registry:example",
+ registryDependencies: [
+ "alert-dialog",
+ "badge",
+ "button",
+ "card",
+ "combobox",
+ "dropdown-menu",
+ "field",
+ "input",
+ "select",
+ "textarea",
+ "example",
+ ],
+ files: [
+ {
+ path: "examples/component-example.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/bases/base/examples/accordion-example.tsx b/apps/ui/src/registry/bases/base/examples/accordion-example.tsx
new file mode 100644
index 0000000..43689dc
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/accordion-example.tsx
@@ -0,0 +1,336 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/registry/bases/base/ui/accordion"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function AccordionExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function AccordionBasic() {
+ const items = [
+ {
+ value: "item-1",
+ trigger: "Is it accessible?",
+ content: "Yes. It adheres to the WAI-ARIA design pattern.",
+ },
+ {
+ value: "item-2",
+ trigger: "Is it styled?",
+ content:
+ "Yes. It comes with default styles that matches the other components' aesthetic.",
+ },
+ {
+ value: "item-3",
+ trigger: "Is it animated?",
+ content:
+ "Yes. It's animated by default, but you can disable it if you prefer.",
+ },
+ ]
+
+ return (
+
+
+ {items.map((item) => (
+
+ {item.trigger}
+ {item.content}
+
+ ))}
+
+
+ )
+}
+
+function AccordionMultiple() {
+ const items = [
+ {
+ value: "item-1",
+ trigger:
+ "What are the key considerations when implementing a comprehensive enterprise-level authentication system?",
+ content:
+ "Implementing a robust enterprise authentication system requires careful consideration of multiple factors. This includes secure password hashing and storage, multi-factor authentication (MFA) implementation, session management, OAuth2 and SSO integration, regular security audits, rate limiting to prevent brute force attacks, and maintaining detailed audit logs. Additionally, you'll need to consider scalability, performance impact, and compliance with relevant data protection regulations such as GDPR or HIPAA.",
+ },
+ {
+ value: "item-2",
+ trigger:
+ "How does modern distributed system architecture handle eventual consistency and data synchronization across multiple regions?",
+ content:
+ "Modern distributed systems employ various strategies to maintain data consistency across regions. This often involves using techniques like CRDT (Conflict-Free Replicated Data Types), vector clocks, and gossip protocols. Systems might implement event sourcing patterns, utilize message queues for asynchronous updates, and employ sophisticated conflict resolution strategies. Popular solutions like Amazon's DynamoDB and Google's Spanner demonstrate different approaches to solving these challenges, balancing between consistency, availability, and partition tolerance as described in the CAP theorem.",
+ },
+ ]
+
+ return (
+
+
+ {items.map((item) => (
+
+ {item.trigger}
+ {item.content}
+
+ ))}
+
+
+ )
+}
+
+function AccordionWithBorders() {
+ const items = [
+ {
+ value: "billing",
+ trigger: "How does billing work?",
+ content:
+ "We offer monthly and annual subscription plans. Billing is charged at the beginning of each cycle, and you can cancel anytime. All plans include automatic backups, 24/7 support, and unlimited team members. There are no hidden fees or setup costs.",
+ },
+ {
+ value: "security",
+ trigger: "Is my data secure?",
+ content:
+ "Yes. We use end-to-end encryption, SOC 2 Type II compliance, and regular third-party security audits. All data is encrypted at rest and in transit using industry-standard protocols. We also offer optional two-factor authentication and single sign-on for enterprise customers.",
+ },
+ {
+ value: "integration",
+ trigger: "What integrations do you support?",
+ content: (
+ <>
+
+ We integrate with 500+ popular tools including Slack, Zapier,
+ Salesforce, HubSpot, and more. You can also build custom
+ integrations using our REST API and webhooks.{" "}
+
+
+ Our API documentation includes code examples in 10+ programming
+ languages.
+
+ >
+ ),
+ },
+ ]
+
+ return (
+
+
+ {items.map((item) => (
+
+
+ {item.trigger}
+
+
+ {item.content}
+
+
+ ))}
+
+
+ )
+}
+
+function AccordionInCard() {
+ const items = [
+ {
+ value: "plans",
+ trigger: "What subscription plans do you offer?",
+ content: (
+ <>
+
+ We offer three subscription tiers: Starter ($9/month), Professional
+ ($29/month), and Enterprise ($99/month). Each plan includes
+ increasing storage limits, API access, priority support, and team
+ collaboration features.
+
+
+ Annual billing is available with a 20% discount. All
+ plans include a 14-day free trial with no credit card required.
+
+
+ View plans
+
+
+ >
+ ),
+ },
+ {
+ value: "billing",
+ trigger: "How does billing work?",
+ content: (
+ <>
+
+ Billing occurs automatically at the start of each billing cycle. We
+ accept all major credit cards, PayPal, and ACH transfers for
+ enterprise customers.
+
+
+ You'll receive an invoice via email after each payment. You can
+ update your payment method or billing information anytime in your
+ account settings. Failed payments will trigger automated retry
+ attempts and email notifications.
+
+ >
+ ),
+ },
+ {
+ value: "upgrade",
+ trigger: "Can I upgrade or downgrade my plan?",
+ content: (
+ <>
+
+ Yes, you can change your plan at any time. When upgrading,
+ you'll be charged a prorated amount for the remainder of your
+ billing cycle and immediately gain access to new features.
+
+
+ When downgrading, the change takes effect at the end of your current
+ billing period, and you'll retain access to premium features
+ until then. No refunds are provided for downgrades.
+
+ >
+ ),
+ },
+ {
+ value: "cancel",
+ trigger: "How do I cancel my subscription?",
+ content: (
+ <>
+
+ You can cancel your subscription anytime from your account settings.
+ There are no cancellation fees or penalties. Your access will
+ continue until the end of your current billing period.
+
+
+ After cancellation, your data is retained for 30 days in case you
+ want to reactivate. You can export all your data before or after
+ canceling. We'd love to hear your feedback about why
+ you're leaving.
+
+ >
+ ),
+ },
+ {
+ value: "refund",
+ trigger: "What is your refund policy?",
+ content: (
+ <>
+
+ We offer a 30-day money-back guarantee for new subscriptions. If
+ you're not satisfied within the first 30 days, contact our
+ support team for a full refund.
+
+
+ After 30 days, we don't provide refunds for partial billing
+ periods, but you can cancel anytime to avoid future charges.
+ Enterprise customers have custom refund terms outlined in their
+ contracts.
+
+ >
+ ),
+ },
+ ]
+
+ return (
+
+
+
+ Subscription & Billing
+
+ Common questions about your account, plans, and payments
+
+
+
+
+ {items.map((item) => (
+
+ {item.trigger}
+ {item.content}
+
+ ))}
+
+
+
+
+ )
+}
+
+function AccordionWithDisabled() {
+ const items = [
+ {
+ value: "item-1",
+ trigger: "Can I access my account history?",
+ content:
+ "Yes, you can view your complete account history including all transactions, plan changes, and support tickets in the Account History section of your dashboard.",
+ disabled: false,
+ },
+ {
+ value: "item-2",
+ trigger: "Premium feature information",
+ content:
+ "This section contains information about premium features. Upgrade your plan to access this content.",
+ disabled: true,
+ },
+ {
+ value: "item-3",
+ trigger: "How do I update my email address?",
+ content:
+ "You can update your email address in your account settings. You'll receive a verification email at your new address to confirm the change.",
+ disabled: false,
+ },
+ ]
+
+ return (
+
+
+ {items.map((item) => (
+
+
+ {item.trigger}
+
+
+ {item.content}
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/alert-dialog-example.tsx b/apps/ui/src/registry/bases/base/examples/alert-dialog-example.tsx
new file mode 100644
index 0000000..0183b73
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/alert-dialog-example.tsx
@@ -0,0 +1,230 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogMedia,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/registry/bases/base/ui/alert-dialog"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function AlertDialogExample() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function AlertDialogBasic() {
+ return (
+
+
+ Default}
+ />
+
+
+ Are you absolutely sure?
+
+ This action cannot be undone. This will permanently delete your
+ account and remove your data from our servers.
+
+
+
+ Cancel
+ Continue
+
+
+
+
+ )
+}
+
+function AlertDialogSmall() {
+ return (
+
+
+ Small} />
+
+
+ Allow accessory to connect?
+
+ Do you want to allow the USB accessory to connect to this device?
+
+
+
+ Don't allow
+ Allow
+
+
+
+
+ )
+}
+
+function AlertDialogWithMedia() {
+ return (
+
+
+ Default (Media)}
+ />
+
+
+
+
+
+ Are you absolutely sure?
+
+ This will permanently delete your account and remove your data
+ from our servers.
+
+
+
+ Cancel
+ Continue
+
+
+
+
+ )
+}
+
+function AlertDialogSmallWithMedia() {
+ return (
+
+
+ Small (Media)}
+ />
+
+
+
+
+
+
+ Allow accessory to connect?
+
+ Do you want to allow the USB accessory to connect to this device?
+
+
+
+ Don't allow
+ Allow
+
+
+
+
+ )
+}
+
+function AlertDialogDestructive() {
+ return (
+
+
+ Delete Chat}
+ />
+
+
+
+
+
+ Delete chat?
+
+ This will permanently delete this chat conversation. View{" "}
+ Settings delete any memories saved during this
+ chat.
+
+
+
+ Cancel
+ Delete
+
+
+
+
+ )
+}
+
+function AlertDialogInDialog() {
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Alert Dialog Example
+
+ Click the button below to open an alert dialog.
+
+
+
+
+ }>
+ Open Alert Dialog
+
+
+
+ Are you absolutely sure?
+
+ This action cannot be undone. This will permanently delete
+ your account and remove your data from our servers.
+
+
+
+ Cancel
+ Continue
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/alert-example.tsx b/apps/ui/src/registry/bases/base/examples/alert-example.tsx
new file mode 100644
index 0000000..4dd0742
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/alert-example.tsx
@@ -0,0 +1,227 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Alert,
+ AlertAction,
+ AlertDescription,
+ AlertTitle,
+} from "@/registry/bases/base/ui/alert"
+import { Badge } from "@/registry/bases/base/ui/badge"
+import { Button } from "@/registry/bases/base/ui/button"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function AlertExample() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function AlertExample1() {
+ return (
+
+
+
+ Success! Your changes have been saved.
+
+
+ Success! Your changes have been saved.
+
+ This is an alert with title and description.
+
+
+
+
+ This one has a description only. No title. No icon.
+
+
+
+
+ )
+}
+
+function AlertExample2() {
+ return (
+
+
+
+
+
+ Let's try one with icon, title and a link .
+
+
+
+
+
+ This one has an icon and a description only. No title.{" "}
+ But it has a link and a second link .
+
+
+
+
+
+ Success! Your changes have been saved
+
+ This is an alert with icon, title and description.
+
+
+
+
+
+ This is a very long alert title that demonstrates how the component
+ handles extended text content and potentially wraps across multiple
+ lines
+
+
+
+
+
+ This is a very long alert description that demonstrates how the
+ component handles extended text content and potentially wraps across
+ multiple lines
+
+
+
+
+
+ This is an extremely long alert title that spans multiple lines to
+ demonstrate how the component handles very lengthy headings while
+ maintaining readability and proper text wrapping behavior
+
+
+ This is an equally long description that contains detailed
+ information about the alert. It shows how the component can
+ accommodate extensive content while preserving proper spacing,
+ alignment, and readability across different screen sizes and
+ viewport widths. This helps ensure the user experience remains
+ consistent regardless of the content length.
+
+
+
+
+ )
+}
+
+function AlertExample3() {
+ return (
+
+
+
+
+ Something went wrong!
+
+ Your session has expired. Please log in again.
+
+
+
+
+ Unable to process your payment.
+
+
+ Please verify your billing information and try
+ again.
+
+
+ Check your card details
+ Ensure sufficient funds
+ Verify billing address
+
+
+
+
+
+ )
+}
+
+function AlertExample4() {
+ return (
+
+
+
+
+ The selected emails have been marked as spam.
+
+ Undo
+
+
+
+
+ The selected emails have been marked as spam.
+
+ This is a very long alert title that demonstrates how the component
+ handles extended text content.
+
+
+ Badge
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/aspect-ratio-example.tsx b/apps/ui/src/registry/bases/base/examples/aspect-ratio-example.tsx
new file mode 100644
index 0000000..0f19f7b
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/aspect-ratio-example.tsx
@@ -0,0 +1,90 @@
+import Image from "next/image"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { AspectRatio } from "@/registry/bases/base/ui/aspect-ratio"
+
+export default function AspectRatioExample() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function AspectRatio16x9() {
+ return (
+
+
+
+
+
+ )
+}
+
+function AspectRatio1x1() {
+ return (
+
+
+
+
+
+ )
+}
+
+function AspectRatio9x16() {
+ return (
+
+
+
+
+
+ )
+}
+
+function AspectRatio21x9() {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/avatar-example.tsx b/apps/ui/src/registry/bases/base/examples/avatar-example.tsx
new file mode 100644
index 0000000..e02f454
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/avatar-example.tsx
@@ -0,0 +1,511 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Avatar,
+ AvatarBadge,
+ AvatarFallback,
+ AvatarGroup,
+ AvatarGroupCount,
+ AvatarImage,
+} from "@/registry/bases/base/ui/avatar"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/bases/base/ui/empty"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function AvatarExample() {
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function AvatarSizes() {
+ return (
+
+
+
+
+ CN
+
+
+
+ CN
+
+
+
+ CN
+
+
+
+
+ CN
+
+
+ CN
+
+
+ CN
+
+
+
+ )
+}
+
+function AvatarWithBadge() {
+ return (
+
+
+
+
+ JZ
+
+
+
+
+ JZ
+
+
+
+
+ JZ
+
+
+
+
+
+ JZ
+
+
+
+ JZ
+
+
+
+ JZ
+
+
+
+
+ )
+}
+
+function AvatarWithBadgeIcon() {
+ return (
+
+
+
+
+ PP
+
+
+
+
+
+
+ PP
+
+
+
+
+
+
+ PP
+
+
+
+
+
+
+
+ PP
+
+
+
+
+
+ PP
+
+
+
+
+
+ PP
+
+
+
+
+
+
+ )
+}
+
+function AvatarGroupExample() {
+ return (
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+ )
+}
+
+function AvatarGroupWithCount() {
+ return (
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+ +3
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+ +3
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+ +3
+
+
+ )
+}
+
+function AvatarGroupWithIconCount() {
+ return (
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+
+
+
+ )
+}
+
+function AvatarInEmpty() {
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+
+
+
+ No Team Members
+
+ Invite your team to collaborate on this project.
+
+
+
+
+
+ Invite Members
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/badge-example.tsx b/apps/ui/src/registry/bases/base/examples/badge-example.tsx
new file mode 100644
index 0000000..4a972dc
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/badge-example.tsx
@@ -0,0 +1,339 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Badge } from "@/registry/bases/base/ui/badge"
+import { Spinner } from "@/registry/bases/base/ui/spinner"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function BadgeExample() {
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function BadgeVariants() {
+ return (
+
+
+ Default
+ Secondary
+ Destructive
+ Outline
+ Ghost
+ Link
+
+
+ )
+}
+
+function BadgeWithIconLeft() {
+ return (
+
+
+
+
+ Default
+
+
+
+ Secondary
+
+
+
+ Destructive
+
+
+
+ Outline
+
+
+
+ Ghost
+
+
+
+ Link
+
+
+
+ )
+}
+
+function BadgeWithIconRight() {
+ return (
+
+
+
+ Default
+
+
+
+ Secondary
+
+
+
+ Destructive
+
+
+
+ Outline
+
+
+
+ Ghost
+
+
+
+ Link
+
+
+
+
+ )
+}
+
+function BadgeWithSpinner() {
+ return (
+
+
+
+
+ Default
+
+
+
+ Secondary
+
+
+
+ Destructive
+
+
+
+ Outline
+
+
+
+ Ghost
+
+
+
+ Link
+
+
+
+ )
+}
+
+function BadgeAsLink() {
+ return (
+
+
+
+ Link{" "}
+
+
+ }
+ />
+
+ Link{" "}
+
+
+ }
+ />
+
+ Link{" "}
+
+
+ }
+ />
+
+ Link{" "}
+
+
+ }
+ />
+
+
+ )
+}
+
+function BadgeLongText() {
+ return (
+
+
+
+ A badge with a lot of text to see how it wraps
+
+
+
+ )
+}
+
+function BadgeCustomColors() {
+ return (
+
+
+
+ Blue
+
+
+ Green
+
+
+ Sky
+
+
+ Purple
+
+
+ Blue
+
+
+ Green
+
+
+ Sky
+
+
+ Purple
+
+
+ Red
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/breadcrumb-example.tsx b/apps/ui/src/registry/bases/base/examples/breadcrumb-example.tsx
new file mode 100644
index 0000000..3a84bcd
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/breadcrumb-example.tsx
@@ -0,0 +1,118 @@
+import Link from "next/link"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Breadcrumb,
+ BreadcrumbEllipsis,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/bases/base/ui/breadcrumb"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+
+export default function BreadcrumbExample() {
+ return (
+
+
+
+
+
+ )
+}
+
+function BreadcrumbBasic() {
+ return (
+
+
+
+
+ Home
+
+
+
+ Components
+
+
+
+ Breadcrumb
+
+
+
+
+ )
+}
+
+function BreadcrumbWithDropdown() {
+ return (
+
+
+
+
+ Home
+
+
+
+
+ }
+ >
+
+ Toggle menu
+
+
+ Documentation
+ Themes
+ GitHub
+
+
+
+
+
+ Components
+
+
+
+ Breadcrumb
+
+
+
+
+ )
+}
+
+function BreadcrumbWithLink() {
+ return (
+
+
+
+
+ Home} />
+
+
+
+
+
+
+
+ Components} />
+
+
+
+ Breadcrumb
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/button-example.tsx b/apps/ui/src/registry/bases/base/examples/button-example.tsx
new file mode 100644
index 0000000..c4b9af7
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/button-example.tsx
@@ -0,0 +1,1001 @@
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button, buttonVariants } from "@/registry/bases/base/ui/button"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ButtonExample() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonVariantsAndSizes() {
+ return (
+
+
+ Default
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+ Link
+
+
+
+ Default
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+ Link
+
+
+
+ Default
+ Secondary
+ Outline
+ Ghost
+ Destructive
+ Link
+
+
+ Default
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+ Link
+
+
+
+ )
+}
+
+function ButtonIconRight() {
+ return (
+
+
+
+ Default{" "}
+
+
+
+ Secondary{" "}
+
+
+
+ Outline{" "}
+
+
+
+ Ghost{" "}
+
+
+
+ Destructive{" "}
+
+
+
+ Link{" "}
+
+
+
+
+
+ Default
+
+
+
+ Secondary{" "}
+
+
+
+ Outline{" "}
+
+
+
+ Ghost{" "}
+
+
+
+ Destructive{" "}
+
+
+
+ Link{" "}
+
+
+
+
+
+ Default{" "}
+
+
+
+ Secondary{" "}
+
+
+
+ Outline{" "}
+
+
+
+ Ghost{" "}
+
+
+
+ Destructive{" "}
+
+
+
+ Link{" "}
+
+
+
+
+
+ Default{" "}
+
+
+
+ Secondary{" "}
+
+
+
+ Outline{" "}
+
+
+
+ Ghost{" "}
+
+
+
+ Destructive{" "}
+
+
+
+ Link{" "}
+
+
+
+
+ )
+}
+
+function ButtonIconLeft() {
+ return (
+
+
+
+ {" "}
+ Default
+
+
+ {" "}
+ Secondary
+
+
+ {" "}
+ Outline
+
+
+ {" "}
+ Ghost
+
+
+ {" "}
+ Destructive
+
+
+ {" "}
+ Link
+
+
+
+
+ {" "}
+ Default
+
+
+ {" "}
+ Secondary
+
+
+ {" "}
+ Outline
+
+
+ {" "}
+ Ghost
+
+
+ {" "}
+ Destructive
+
+
+ {" "}
+ Link
+
+
+
+
+ {" "}
+ Default
+
+
+ {" "}
+ Secondary
+
+
+ {" "}
+ Outline
+
+
+ {" "}
+ Ghost
+
+
+ {" "}
+ Destructive
+
+
+ {" "}
+ Link
+
+
+
+
+ {" "}
+ Default
+
+
+ {" "}
+ Secondary
+
+
+ {" "}
+ Outline
+
+
+ {" "}
+ Ghost
+
+
+ {" "}
+ Destructive
+
+
+ {" "}
+ Link
+
+
+
+ )
+}
+
+function ButtonIconOnly() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonExamples() {
+ return (
+
+
+
+ Cancel
+
+ Submit{" "}
+
+
+
+
+ Delete
+
+
+
+
+
+ Link
+
+
+
+ )
+}
+
+function ButtonInvalidStates() {
+ return (
+
+
+
+ Default
+
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+ Link
+
+
+
+
+ Default
+
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+ Link
+
+
+
+ Default
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+ Link
+
+
+
+
+ Default
+
+
+ Secondary
+
+
+ Outline
+
+
+ Ghost
+
+
+ Destructive
+
+
+ Link
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/button-group-example.tsx b/apps/ui/src/registry/bases/base/examples/button-group-example.tsx
new file mode 100644
index 0000000..a27dd2a
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/button-group-example.tsx
@@ -0,0 +1,752 @@
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ ButtonGroup,
+ ButtonGroupText,
+} from "@/registry/bases/base/ui/button-group"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import { Field, FieldGroup } from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/bases/base/ui/input-group"
+import { Label } from "@/registry/bases/base/ui/label"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/bases/base/ui/tooltip"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ButtonGroupExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupBasic() {
+ return (
+
+
+
+ Button
+ Another Button
+
+
+
+ )
+}
+
+function ButtonGroupWithInput() {
+ return (
+
+
+
+ Button
+
+
+
+
+ Button
+
+
+
+ )
+}
+
+function ButtonGroupWithText() {
+ return (
+
+
+
+ Text
+ Another Button
+
+
+ }>
+ GPU Size
+
+
+
+
+
+ )
+}
+
+function ButtonGroupWithDropdown() {
+ return (
+
+
+
+ Update
+
+ }
+ >
+
+
+
+ Disable
+
+ Uninstall
+
+
+
+
+
+ Follow
+
+ }
+ >
+
+
+
+
+
+
+ Mute Conversation
+
+
+
+ Mark as Read
+
+
+
+ Report Conversation
+
+
+
+ Block User
+
+
+
+ Share Conversation
+
+
+
+ Copy Conversation
+
+
+
+
+
+
+ Delete Conversation
+
+
+
+
+
+
+
+ )
+}
+
+const currencyItems = [
+ { label: "$", value: "$" },
+ { label: "€", value: "€" },
+ { label: "£", value: "£" },
+]
+
+function ButtonGroupWithSelect() {
+ return (
+
+
+ Amount
+
+
+
+
+
+
+
+ {currencyItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupWithIcons() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupWithInputGroup() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupWithFields() {
+ return (
+
+
+
+ Width
+
+
+
+
+ W
+
+
+ px
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupWithLike() {
+ return (
+
+
+
+ {" "}
+ Like
+
+ }
+ nativeButton={false}
+ >
+ 1.2K
+
+
+
+ )
+}
+
+const durationItems = [
+ { label: "Hours", value: "hours" },
+ { label: "Days", value: "days" },
+ { label: "Weeks", value: "weeks" },
+]
+
+function ButtonGroupWithSelectAndInput() {
+ return (
+
+
+
+
+
+
+
+
+ {durationItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupNested() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ }>
+
+
+ Voice Mode
+
+
+
+
+
+ )
+}
+
+function ButtonGroupPagination() {
+ return (
+
+
+
+
+ Previous
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+ Next
+
+
+
+
+ )
+}
+
+function ButtonGroupPaginationSplit() {
+ return (
+
+
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupNavigation() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupTextAlignment() {
+ return (
+
+
+ Text Alignment
+
+
+ Left
+
+
+ Center
+
+
+ Right
+
+
+ Justify
+
+
+
+
+ )
+}
+
+function ButtonGroupVertical() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ButtonGroupVerticalNested() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/calendar-example.tsx b/apps/ui/src/registry/bases/base/examples/calendar-example.tsx
new file mode 100644
index 0000000..848ed53
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/calendar-example.tsx
@@ -0,0 +1,541 @@
+
+import * as React from "react"
+import { addDays, format } from "date-fns"
+import { type DateRange } from "react-day-picker"
+import { es } from "react-day-picker/locale"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Calendar, CalendarDayButton } from "@/registry/bases/base/ui/calendar"
+import { Card, CardContent, CardFooter } from "@/registry/bases/base/ui/card"
+import { Field, FieldGroup, FieldLabel } from "@/registry/bases/base/ui/field"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/bases/base/ui/input-group"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/bases/base/ui/popover"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function CalendarExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function CalendarInCard() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function CalendarInPopover() {
+ return (
+
+
+ }
+ >
+
+ Open Calendar
+
+
+
+
+
+
+ )
+}
+
+function CalendarSingle() {
+ const [date, setDate] = React.useState(
+ new Date(new Date().getFullYear(), new Date().getMonth(), 12)
+ )
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function CalendarMultiple() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function CalendarRange() {
+ const [dateRange, setDateRange] = React.useState({
+ from: new Date(new Date().getFullYear(), 0, 12),
+ to: addDays(new Date(new Date().getFullYear(), 0, 12), 30),
+ })
+
+ return (
+
+
+
+
+ date > new Date() || date < new Date("1900-01-01")
+ }
+ />
+
+
+
+ )
+}
+
+function CalendarRangeMultipleMonths() {
+ const [range, setRange] = React.useState({
+ from: new Date(new Date().getFullYear(), 3, 12),
+ to: addDays(new Date(new Date().getFullYear(), 3, 12), 60),
+ })
+
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function CalendarBookedDates() {
+ const [date, setDate] = React.useState(
+ new Date(new Date().getFullYear(), 1, 3)
+ )
+ const bookedDates = Array.from(
+ { length: 15 },
+ (_, i) => new Date(new Date().getFullYear(), 1, 12 + i)
+ )
+
+ return (
+
+
+
+ button]:line-through opacity-100",
+ }}
+ />
+
+
+
+ )
+}
+
+function CalendarWithTime() {
+ const [date, setDate] = React.useState(
+ new Date(new Date().getFullYear(), new Date().getMonth(), 12)
+ )
+
+ return (
+
+
+
+
+
+
+
+
+ Start Time
+
+
+
+
+
+
+
+
+ End Time
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function CalendarCustomDays() {
+ const [range, setRange] = React.useState({
+ from: new Date(new Date().getFullYear(), 11, 8),
+ to: addDays(new Date(new Date().getFullYear(), 11, 8), 10),
+ })
+
+ return (
+
+
+
+ {
+ return date.toLocaleString("default", { month: "long" })
+ },
+ }}
+ components={{
+ DayButton: ({ children, modifiers, day, ...props }) => {
+ const isWeekend =
+ day.date.getDay() === 0 || day.date.getDay() === 6
+
+ return (
+
+ {children}
+ {!modifiers.outside && (
+ {isWeekend ? "$120" : "$100"}
+ )}
+
+ )
+ },
+ }}
+ />
+
+
+
+ )
+}
+
+function CalendarWithPresets() {
+ const [date, setDate] = React.useState(
+ new Date(new Date().getFullYear(), 1, 12)
+ )
+ const [currentMonth, setCurrentMonth] = React.useState(
+ new Date(new Date().getFullYear(), new Date().getMonth(), 1)
+ )
+
+ return (
+
+
+
+
+
+
+ {[
+ { label: "Today", value: 0 },
+ { label: "Tomorrow", value: 1 },
+ { label: "In 3 days", value: 3 },
+ { label: "In a week", value: 7 },
+ { label: "In 2 weeks", value: 14 },
+ ].map((preset) => (
+ {
+ const newDate = addDays(new Date(), preset.value)
+ setDate(newDate)
+ setCurrentMonth(
+ new Date(newDate.getFullYear(), newDate.getMonth(), 1)
+ )
+ }}
+ >
+ {preset.label}
+
+ ))}
+
+
+
+ )
+}
+
+function DatePickerSimple() {
+ const [date, setDate] = React.useState()
+
+ return (
+
+
+ Date
+
+
+ }
+ >
+
+ {date ? format(date, "PPP") : Pick a date }
+
+
+
+
+
+
+
+ )
+}
+
+function DatePickerWithRange() {
+ const [date, setDate] = React.useState({
+ from: new Date(new Date().getFullYear(), 0, 20),
+ to: addDays(new Date(new Date().getFullYear(), 0, 20), 20),
+ })
+
+ return (
+
+
+ Date Picker Range
+
+
+ }
+ >
+
+ {date?.from ? (
+ date.to ? (
+ <>
+ {format(date.from, "LLL dd, y")} -{" "}
+ {format(date.to, "LLL dd, y")}
+ >
+ ) : (
+ format(date.from, "LLL dd, y")
+ )
+ ) : (
+ Pick a date
+ )}
+
+
+
+
+
+
+
+ )
+}
+
+function DataPickerWithDropdowns() {
+ const [date, setDate] = React.useState()
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+
+
+ Date
+
+
+ }
+ >
+ {date ? format(date, "PPP") : Pick a date }
+
+
+
+
+
+ setOpen(false)}
+ >
+ Done
+
+
+
+
+
+
+ )
+}
+
+function CalendarWeekNumbers() {
+ const [date, setDate] = React.useState(
+ new Date(new Date().getFullYear(), 1, 3)
+ )
+
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/card-example.tsx b/apps/ui/src/registry/bases/base/examples/card-example.tsx
new file mode 100644
index 0000000..2d5314e
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/card-example.tsx
@@ -0,0 +1,367 @@
+import Image from "next/image"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarGroup,
+ AvatarGroupCount,
+ AvatarImage,
+} from "@/registry/bases/base/ui/avatar"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Card,
+ CardAction,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import { Field, FieldGroup, FieldLabel } from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function CardExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function CardLogin() {
+ return (
+
+
+
+ Login to your account
+
+ Enter your email below to login to your account
+
+
+
+
+
+
+
+ Login
+
+
+ Login with Google
+
+
+
+
+
+ )
+}
+
+function CardMeetingNotes() {
+ return (
+
+
+
+ Meeting Notes
+
+ Transcript from the meeting with the client.
+
+
+
+
+ Transcribe
+
+
+
+
+
+ Client requested dashboard redesign with focus on mobile
+ responsiveness.
+
+
+ New analytics widgets for daily/weekly metrics
+ Simplified navigation menu
+ Dark mode support
+ Timeline: 6 weeks
+ Follow-up meeting scheduled for next Tuesday
+
+
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+ +8
+
+
+
+
+ )
+}
+
+function CardWithImage() {
+ return (
+
+
+
+
+
+ Beautiful Landscape
+
+ A stunning view that captures the essence of natural beauty.
+
+
+
+
+
+ Button
+
+
+
+
+ )
+}
+
+function CardWithImageSmall() {
+ return (
+
+
+
+
+
+ Beautiful Landscape
+
+ A stunning view that captures the essence of natural beauty.
+
+
+
+
+
+ Button
+
+
+
+
+ )
+}
+
+function CardHeaderWithBorder() {
+ return (
+
+
+
+ Header with Border
+
+ This is a card with a header that has a bottom border.
+
+
+
+
+ The header has a border-b class applied, creating a visual
+ separation between the header and content sections.
+
+
+
+
+ )
+}
+
+function CardFooterWithBorder() {
+ return (
+
+
+
+
+ The footer has a border-t class applied, creating a visual
+ separation between the content and footer sections.
+
+
+
+
+ Footer with Border
+
+
+
+
+ )
+}
+
+function CardDefault() {
+ return (
+
+
+
+ Default Card
+
+ This card uses the default size variant.
+
+
+
+
+ The card component supports a size prop that defaults to
+ "default" for standard spacing and sizing.
+
+
+
+
+ Action
+
+
+
+
+ )
+}
+
+function CardSmall() {
+ return (
+
+
+
+ Small Card
+
+ This card uses the small size variant.
+
+
+
+
+ The card component supports a size prop that can be set to
+ "sm" for a more compact appearance.
+
+
+
+
+ Action
+
+
+
+
+ )
+}
+
+function CardHeaderWithBorderSmall() {
+ return (
+
+
+
+ Header with Border
+
+ This is a small card with a header that has a bottom border.
+
+
+
+
+ The header has a border-b class applied, creating a visual
+ separation between the header and content sections.
+
+
+
+
+ )
+}
+
+function CardFooterWithBorderSmall() {
+ return (
+
+
+
+
+ The footer has a border-t class applied, creating a visual
+ separation between the content and footer sections.
+
+
+
+
+ Footer with Border
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/carousel-example.tsx b/apps/ui/src/registry/bases/base/examples/carousel-example.tsx
new file mode 100644
index 0000000..0a501ad
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/carousel-example.tsx
@@ -0,0 +1,99 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Card, CardContent } from "@/registry/bases/base/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/registry/bases/base/ui/carousel"
+
+export default function CarouselExample() {
+ return (
+
+
+
+
+
+ )
+}
+
+function CarouselBasic() {
+ return (
+
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+
+ )
+}
+
+function CarouselMultiple() {
+ return (
+
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+
+ )
+}
+
+function CarouselWithGap() {
+ return (
+
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/chart-example.tsx b/apps/ui/src/registry/bases/base/examples/chart-example.tsx
new file mode 100644
index 0000000..3e53d55
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/chart-example.tsx
@@ -0,0 +1,581 @@
+
+import * as React from "react"
+import {
+ Area,
+ AreaChart,
+ Bar,
+ BarChart,
+ CartesianGrid,
+ Label,
+ Line,
+ LineChart,
+ Pie,
+ PieChart,
+ PolarAngleAxis,
+ PolarGrid,
+ PolarRadiusAxis,
+ Radar,
+ RadarChart,
+ RadialBar,
+ RadialBarChart,
+ XAxis,
+} from "recharts"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/bases/base/ui/chart"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+const areaChartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const areaChartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export default function ChartExample() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function ChartAreaExample() {
+ return (
+
+
+
+ Area Chart
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month{" "}
+
+
+
+ January - June 2024
+
+
+
+
+
+
+ )
+}
+
+const barChartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const barChartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+function ChartBarExample() {
+ return (
+
+
+
+ Bar Chart - Multiple
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month{" "}
+
+
+
+ Showing total visitors for the last 6 months
+
+
+
+
+ )
+}
+
+const lineChartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const lineChartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+function ChartLineExample() {
+ return (
+
+
+
+ Line Chart - Multiple
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ } />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month{" "}
+
+
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ )
+}
+
+const pieChartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 287, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 190, fill: "var(--color-other)" },
+]
+
+const pieChartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+function ChartPieExample() {
+ const totalVisitors = React.useMemo(() => {
+ return pieChartData.reduce((acc, curr) => acc + curr.visitors, 0)
+ }, [])
+
+ return (
+
+
+
+ Pie Chart - Donut with Text
+ January - June 2024
+
+
+
+
+ }
+ />
+
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {totalVisitors.toLocaleString()}
+
+
+ Visitors
+
+
+ )
+ }
+ }}
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month{" "}
+
+
+
+ Showing total visitors for the last 6 months
+
+
+
+
+ )
+}
+
+const radarChartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const radarChartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+function ChartRadarExample() {
+ return (
+
+
+
+ Radar Chart - Multiple
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month{" "}
+
+
+
+ January - June 2024
+
+
+
+
+ )
+}
+
+const radialChartData = [
+ { browser: "safari", visitors: 1260, fill: "var(--color-safari)" },
+]
+
+const radialChartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+function ChartRadialExample() {
+ return (
+
+
+
+ Radial Chart - Shape
+ January - June 2024
+
+
+
+
+
+
+
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {radialChartData[0].visitors.toLocaleString()}
+
+
+ Visitors
+
+
+ )
+ }
+ }}
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month{" "}
+
+
+
+ Showing total visitors for the last 6 months
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/checkbox-example.tsx b/apps/ui/src/registry/bases/base/examples/checkbox-example.tsx
new file mode 100644
index 0000000..b26c287
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/checkbox-example.tsx
@@ -0,0 +1,259 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Checkbox } from "@/registry/bases/base/ui/checkbox"
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldTitle,
+} from "@/registry/bases/base/ui/field"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/registry/bases/base/ui/table"
+
+export default function CheckboxExample() {
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function CheckboxBasic() {
+ return (
+
+
+
+ Accept terms and conditions
+
+
+ )
+}
+
+function CheckboxWithDescription() {
+ return (
+
+
+
+
+ Accept terms and conditions
+
+ By clicking this checkbox, you agree to the terms and conditions.
+
+
+
+
+ )
+}
+
+function CheckboxInvalid() {
+ return (
+
+
+
+ Accept terms and conditions
+
+
+ )
+}
+
+function CheckboxDisabled() {
+ return (
+
+
+
+ Enable notifications
+
+
+ )
+}
+
+function CheckboxWithTitle() {
+ return (
+
+
+
+
+
+
+ Enable notifications
+
+ You can enable or disable notifications at any time.
+
+
+
+
+
+
+
+
+ Enable notifications
+
+ You can enable or disable notifications at any time.
+
+
+
+
+
+
+ )
+}
+
+const tableData = [
+ {
+ id: "1",
+ name: "Sarah Chen",
+ email: "sarah.chen@example.com",
+ role: "Admin",
+ },
+ {
+ id: "2",
+ name: "Marcus Rodriguez",
+ email: "marcus.rodriguez@example.com",
+ role: "User",
+ },
+ {
+ id: "3",
+ name: "Priya Patel",
+ email: "priya.patel@example.com",
+ role: "User",
+ },
+ {
+ id: "4",
+ name: "David Kim",
+ email: "david.kim@example.com",
+ role: "Editor",
+ },
+]
+
+function CheckboxInTable() {
+ const [selectedRows, setSelectedRows] = React.useState>(
+ new Set(["1"])
+ )
+
+ const selectAll = selectedRows.size === tableData.length
+
+ const handleSelectAll = (checked: boolean) => {
+ if (checked) {
+ setSelectedRows(new Set(tableData.map((row) => row.id)))
+ } else {
+ setSelectedRows(new Set())
+ }
+ }
+
+ const handleSelectRow = (id: string, checked: boolean) => {
+ const newSelected = new Set(selectedRows)
+ if (checked) {
+ newSelected.add(id)
+ } else {
+ newSelected.delete(id)
+ }
+ setSelectedRows(newSelected)
+ }
+
+ return (
+
+
+
+
+
+
+
+ Name
+ Email
+ Role
+
+
+
+ {tableData.map((row) => (
+
+
+
+ handleSelectRow(row.id, checked === true)
+ }
+ />
+
+ {row.name}
+ {row.email}
+ {row.role}
+
+ ))}
+
+
+
+ )
+}
+
+function CheckboxGroup() {
+ return (
+
+
+ Show these items on the desktop:
+
+
+
+ Hard disks
+
+
+
+
+
+ External disks
+
+
+
+
+
+ CDs, DVDs, and iPods
+
+
+
+
+
+ Connected servers
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/collapsible-example.tsx b/apps/ui/src/registry/bases/base/examples/collapsible-example.tsx
new file mode 100644
index 0000000..4c14a4c
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/collapsible-example.tsx
@@ -0,0 +1,240 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/bases/base/ui/collapsible"
+import { Field, FieldGroup, FieldLabel } from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import { Tabs, TabsList, TabsTrigger } from "@/registry/bases/base/ui/tabs"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function CollapsibleExample() {
+ return (
+
+
+
+
+ )
+}
+
+type FileTreeItem = { name: string } | { name: string; items: FileTreeItem[] }
+
+function CollapsibleFileTree() {
+ const fileTree: FileTreeItem[] = [
+ {
+ name: "components",
+ items: [
+ {
+ name: "ui",
+ items: [
+ { name: "button.tsx" },
+ { name: "card.tsx" },
+ { name: "dialog.tsx" },
+ { name: "input.tsx" },
+ { name: "select.tsx" },
+ { name: "table.tsx" },
+ ],
+ },
+ { name: "login-form.tsx" },
+ { name: "register-form.tsx" },
+ ],
+ },
+ {
+ name: "lib",
+ items: [{ name: "utils.ts" }, { name: "cn.ts" }, { name: "api.ts" }],
+ },
+ {
+ name: "hooks",
+ items: [
+ { name: "use-media-query.ts" },
+ { name: "use-debounce.ts" },
+ { name: "use-local-storage.ts" },
+ ],
+ },
+ {
+ name: "types",
+ items: [{ name: "index.d.ts" }, { name: "api.d.ts" }],
+ },
+ {
+ name: "public",
+ items: [
+ { name: "favicon.ico" },
+ { name: "logo.svg" },
+ { name: "images" },
+ ],
+ },
+ { name: "app.tsx" },
+ { name: "layout.tsx" },
+ { name: "globals.css" },
+ { name: "package.json" },
+ { name: "tsconfig.json" },
+ { name: "README.md" },
+ { name: ".gitignore" },
+ ]
+
+ const renderItem = (fileItem: FileTreeItem) => {
+ if ("items" in fileItem) {
+ return (
+
+
+ }
+ >
+
+
+ {fileItem.name}
+
+
+
+ {fileItem.items.map((child) => renderItem(child))}
+
+
+
+ )
+ }
+ return (
+
+
+ {fileItem.name}
+
+ )
+ }
+
+ return (
+
+
+
+
+
+ Explorer
+ Outline
+
+
+
+
+
+ {fileTree.map((item) => renderItem(item))}
+
+
+
+
+ )
+}
+
+function CollapsibleSettings() {
+ const [isOpen, setIsOpen] = React.useState(false)
+
+ return (
+
+
+
+ Radius
+
+ Set the corner radius of the element.
+
+
+
+
+
+
+
+ Radius X
+
+
+
+
+
+ Radius Y
+
+
+
+
+
+
+ Radius X
+
+
+
+
+
+ Radius Y
+
+
+
+
+
+ }
+ >
+ {isOpen ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/combobox-example.tsx b/apps/ui/src/registry/bases/base/examples/combobox-example.tsx
new file mode 100644
index 0000000..3f4b454
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/combobox-example.tsx
@@ -0,0 +1,1320 @@
+
+import * as React from "react"
+import { toast } from "sonner"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Card, CardContent, CardFooter } from "@/registry/bases/base/ui/card"
+import {
+ Combobox,
+ ComboboxChip,
+ ComboboxChips,
+ ComboboxChipsInput,
+ ComboboxCollection,
+ ComboboxContent,
+ ComboboxEmpty,
+ ComboboxGroup,
+ ComboboxInput,
+ ComboboxItem,
+ ComboboxLabel,
+ ComboboxList,
+ ComboboxSeparator,
+ ComboboxTrigger,
+ ComboboxValue,
+ useComboboxAnchor,
+} from "@/registry/bases/base/ui/combobox"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import {
+ Field,
+ FieldDescription,
+ FieldError,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/bases/base/ui/input-group"
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ComboboxExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const frameworks = [
+ "Next.js",
+ "SvelteKit",
+ "Nuxt.js",
+ "Remix",
+ "Astro",
+] as const
+
+const countries = [
+ { code: "", value: "", continent: "", label: "Select country" },
+ { code: "af", value: "afghanistan", label: "Afghanistan", continent: "Asia" },
+ { code: "al", value: "albania", label: "Albania", continent: "Europe" },
+ { code: "dz", value: "algeria", label: "Algeria", continent: "Africa" },
+ { code: "ad", value: "andorra", label: "Andorra", continent: "Europe" },
+ { code: "ao", value: "angola", label: "Angola", continent: "Africa" },
+ {
+ code: "ar",
+ value: "argentina",
+ label: "Argentina",
+ continent: "South America",
+ },
+ { code: "am", value: "armenia", label: "Armenia", continent: "Asia" },
+ { code: "au", value: "australia", label: "Australia", continent: "Oceania" },
+ { code: "at", value: "austria", label: "Austria", continent: "Europe" },
+ { code: "az", value: "azerbaijan", label: "Azerbaijan", continent: "Asia" },
+ {
+ code: "bs",
+ value: "bahamas",
+ label: "Bahamas",
+ continent: "North America",
+ },
+ { code: "bh", value: "bahrain", label: "Bahrain", continent: "Asia" },
+ { code: "bd", value: "bangladesh", label: "Bangladesh", continent: "Asia" },
+ {
+ code: "bb",
+ value: "barbados",
+ label: "Barbados",
+ continent: "North America",
+ },
+ { code: "by", value: "belarus", label: "Belarus", continent: "Europe" },
+ { code: "be", value: "belgium", label: "Belgium", continent: "Europe" },
+ { code: "bz", value: "belize", label: "Belize", continent: "North America" },
+ { code: "bj", value: "benin", label: "Benin", continent: "Africa" },
+ { code: "bt", value: "bhutan", label: "Bhutan", continent: "Asia" },
+ {
+ code: "bo",
+ value: "bolivia",
+ label: "Bolivia",
+ continent: "South America",
+ },
+ {
+ code: "ba",
+ value: "bosnia-and-herzegovina",
+ label: "Bosnia and Herzegovina",
+ continent: "Europe",
+ },
+ { code: "bw", value: "botswana", label: "Botswana", continent: "Africa" },
+ { code: "br", value: "brazil", label: "Brazil", continent: "South America" },
+ { code: "bn", value: "brunei", label: "Brunei", continent: "Asia" },
+ { code: "bg", value: "bulgaria", label: "Bulgaria", continent: "Europe" },
+ {
+ code: "bf",
+ value: "burkina-faso",
+ label: "Burkina Faso",
+ continent: "Africa",
+ },
+ { code: "bi", value: "burundi", label: "Burundi", continent: "Africa" },
+ { code: "kh", value: "cambodia", label: "Cambodia", continent: "Asia" },
+ { code: "cm", value: "cameroon", label: "Cameroon", continent: "Africa" },
+ { code: "ca", value: "canada", label: "Canada", continent: "North America" },
+ { code: "cv", value: "cape-verde", label: "Cape Verde", continent: "Africa" },
+ {
+ code: "cf",
+ value: "central-african-republic",
+ label: "Central African Republic",
+ continent: "Africa",
+ },
+ { code: "td", value: "chad", label: "Chad", continent: "Africa" },
+ { code: "cl", value: "chile", label: "Chile", continent: "South America" },
+ { code: "cn", value: "china", label: "China", continent: "Asia" },
+ {
+ code: "co",
+ value: "colombia",
+ label: "Colombia",
+ continent: "South America",
+ },
+ { code: "km", value: "comoros", label: "Comoros", continent: "Africa" },
+ { code: "cg", value: "congo", label: "Congo", continent: "Africa" },
+ {
+ code: "cr",
+ value: "costa-rica",
+ label: "Costa Rica",
+ continent: "North America",
+ },
+ { code: "hr", value: "croatia", label: "Croatia", continent: "Europe" },
+ { code: "cu", value: "cuba", label: "Cuba", continent: "North America" },
+ { code: "cy", value: "cyprus", label: "Cyprus", continent: "Asia" },
+ {
+ code: "cz",
+ value: "czech-republic",
+ label: "Czech Republic",
+ continent: "Europe",
+ },
+ { code: "dk", value: "denmark", label: "Denmark", continent: "Europe" },
+ { code: "dj", value: "djibouti", label: "Djibouti", continent: "Africa" },
+ {
+ code: "dm",
+ value: "dominica",
+ label: "Dominica",
+ continent: "North America",
+ },
+ {
+ code: "do",
+ value: "dominican-republic",
+ label: "Dominican Republic",
+ continent: "North America",
+ },
+ {
+ code: "ec",
+ value: "ecuador",
+ label: "Ecuador",
+ continent: "South America",
+ },
+ { code: "eg", value: "egypt", label: "Egypt", continent: "Africa" },
+ {
+ code: "sv",
+ value: "el-salvador",
+ label: "El Salvador",
+ continent: "North America",
+ },
+ {
+ code: "gq",
+ value: "equatorial-guinea",
+ label: "Equatorial Guinea",
+ continent: "Africa",
+ },
+ { code: "er", value: "eritrea", label: "Eritrea", continent: "Africa" },
+ { code: "ee", value: "estonia", label: "Estonia", continent: "Europe" },
+ { code: "et", value: "ethiopia", label: "Ethiopia", continent: "Africa" },
+ { code: "fj", value: "fiji", label: "Fiji", continent: "Oceania" },
+ { code: "fi", value: "finland", label: "Finland", continent: "Europe" },
+ { code: "fr", value: "france", label: "France", continent: "Europe" },
+ { code: "ga", value: "gabon", label: "Gabon", continent: "Africa" },
+ { code: "gm", value: "gambia", label: "Gambia", continent: "Africa" },
+ { code: "ge", value: "georgia", label: "Georgia", continent: "Asia" },
+ { code: "de", value: "germany", label: "Germany", continent: "Europe" },
+ { code: "gh", value: "ghana", label: "Ghana", continent: "Africa" },
+ { code: "gr", value: "greece", label: "Greece", continent: "Europe" },
+ {
+ code: "gd",
+ value: "grenada",
+ label: "Grenada",
+ continent: "North America",
+ },
+ {
+ code: "gt",
+ value: "guatemala",
+ label: "Guatemala",
+ continent: "North America",
+ },
+ { code: "gn", value: "guinea", label: "Guinea", continent: "Africa" },
+ {
+ code: "gw",
+ value: "guinea-bissau",
+ label: "Guinea-Bissau",
+ continent: "Africa",
+ },
+ { code: "gy", value: "guyana", label: "Guyana", continent: "South America" },
+ { code: "ht", value: "haiti", label: "Haiti", continent: "North America" },
+ {
+ code: "hn",
+ value: "honduras",
+ label: "Honduras",
+ continent: "North America",
+ },
+ { code: "hu", value: "hungary", label: "Hungary", continent: "Europe" },
+ { code: "is", value: "iceland", label: "Iceland", continent: "Europe" },
+ { code: "in", value: "india", label: "India", continent: "Asia" },
+ { code: "id", value: "indonesia", label: "Indonesia", continent: "Asia" },
+ { code: "ir", value: "iran", label: "Iran", continent: "Asia" },
+ { code: "iq", value: "iraq", label: "Iraq", continent: "Asia" },
+ { code: "ie", value: "ireland", label: "Ireland", continent: "Europe" },
+ { code: "il", value: "israel", label: "Israel", continent: "Asia" },
+ { code: "it", value: "italy", label: "Italy", continent: "Europe" },
+ {
+ code: "jm",
+ value: "jamaica",
+ label: "Jamaica",
+ continent: "North America",
+ },
+ { code: "jp", value: "japan", label: "Japan", continent: "Asia" },
+ { code: "jo", value: "jordan", label: "Jordan", continent: "Asia" },
+ { code: "kz", value: "kazakhstan", label: "Kazakhstan", continent: "Asia" },
+ { code: "ke", value: "kenya", label: "Kenya", continent: "Africa" },
+ { code: "kw", value: "kuwait", label: "Kuwait", continent: "Asia" },
+ { code: "kg", value: "kyrgyzstan", label: "Kyrgyzstan", continent: "Asia" },
+ { code: "la", value: "laos", label: "Laos", continent: "Asia" },
+ { code: "lv", value: "latvia", label: "Latvia", continent: "Europe" },
+ { code: "lb", value: "lebanon", label: "Lebanon", continent: "Asia" },
+ { code: "ls", value: "lesotho", label: "Lesotho", continent: "Africa" },
+ { code: "lr", value: "liberia", label: "Liberia", continent: "Africa" },
+ { code: "ly", value: "libya", label: "Libya", continent: "Africa" },
+ {
+ code: "li",
+ value: "liechtenstein",
+ label: "Liechtenstein",
+ continent: "Europe",
+ },
+ { code: "lt", value: "lithuania", label: "Lithuania", continent: "Europe" },
+ { code: "lu", value: "luxembourg", label: "Luxembourg", continent: "Europe" },
+ { code: "mg", value: "madagascar", label: "Madagascar", continent: "Africa" },
+ { code: "mw", value: "malawi", label: "Malawi", continent: "Africa" },
+ { code: "my", value: "malaysia", label: "Malaysia", continent: "Asia" },
+ { code: "mv", value: "maldives", label: "Maldives", continent: "Asia" },
+ { code: "ml", value: "mali", label: "Mali", continent: "Africa" },
+ { code: "mt", value: "malta", label: "Malta", continent: "Europe" },
+ {
+ code: "mh",
+ value: "marshall-islands",
+ label: "Marshall Islands",
+ continent: "Oceania",
+ },
+ { code: "mr", value: "mauritania", label: "Mauritania", continent: "Africa" },
+ { code: "mu", value: "mauritius", label: "Mauritius", continent: "Africa" },
+ { code: "mx", value: "mexico", label: "Mexico", continent: "North America" },
+ {
+ code: "fm",
+ value: "micronesia",
+ label: "Micronesia",
+ continent: "Oceania",
+ },
+ { code: "md", value: "moldova", label: "Moldova", continent: "Europe" },
+ { code: "mc", value: "monaco", label: "Monaco", continent: "Europe" },
+ { code: "mn", value: "mongolia", label: "Mongolia", continent: "Asia" },
+ { code: "me", value: "montenegro", label: "Montenegro", continent: "Europe" },
+ { code: "ma", value: "morocco", label: "Morocco", continent: "Africa" },
+ { code: "mz", value: "mozambique", label: "Mozambique", continent: "Africa" },
+ { code: "mm", value: "myanmar", label: "Myanmar", continent: "Asia" },
+ { code: "na", value: "namibia", label: "Namibia", continent: "Africa" },
+ { code: "nr", value: "nauru", label: "Nauru", continent: "Oceania" },
+ { code: "np", value: "nepal", label: "Nepal", continent: "Asia" },
+ {
+ code: "nl",
+ value: "netherlands",
+ label: "Netherlands",
+ continent: "Europe",
+ },
+ {
+ code: "nz",
+ value: "new-zealand",
+ label: "New Zealand",
+ continent: "Oceania",
+ },
+ {
+ code: "ni",
+ value: "nicaragua",
+ label: "Nicaragua",
+ continent: "North America",
+ },
+ { code: "ne", value: "niger", label: "Niger", continent: "Africa" },
+ { code: "ng", value: "nigeria", label: "Nigeria", continent: "Africa" },
+ { code: "kp", value: "north-korea", label: "North Korea", continent: "Asia" },
+ {
+ code: "mk",
+ value: "north-macedonia",
+ label: "North Macedonia",
+ continent: "Europe",
+ },
+ { code: "no", value: "norway", label: "Norway", continent: "Europe" },
+ { code: "om", value: "oman", label: "Oman", continent: "Asia" },
+ { code: "pk", value: "pakistan", label: "Pakistan", continent: "Asia" },
+ { code: "pw", value: "palau", label: "Palau", continent: "Oceania" },
+ { code: "ps", value: "palestine", label: "Palestine", continent: "Asia" },
+ { code: "pa", value: "panama", label: "Panama", continent: "North America" },
+ {
+ code: "pg",
+ value: "papua-new-guinea",
+ label: "Papua New Guinea",
+ continent: "Oceania",
+ },
+ {
+ code: "py",
+ value: "paraguay",
+ label: "Paraguay",
+ continent: "South America",
+ },
+ { code: "pe", value: "peru", label: "Peru", continent: "South America" },
+ { code: "ph", value: "philippines", label: "Philippines", continent: "Asia" },
+ { code: "pl", value: "poland", label: "Poland", continent: "Europe" },
+ { code: "pt", value: "portugal", label: "Portugal", continent: "Europe" },
+ { code: "qa", value: "qatar", label: "Qatar", continent: "Asia" },
+ { code: "ro", value: "romania", label: "Romania", continent: "Europe" },
+ { code: "ru", value: "russia", label: "Russia", continent: "Europe" },
+ { code: "rw", value: "rwanda", label: "Rwanda", continent: "Africa" },
+ { code: "ws", value: "samoa", label: "Samoa", continent: "Oceania" },
+ { code: "sm", value: "san-marino", label: "San Marino", continent: "Europe" },
+ {
+ code: "sa",
+ value: "saudi-arabia",
+ label: "Saudi Arabia",
+ continent: "Asia",
+ },
+ { code: "sn", value: "senegal", label: "Senegal", continent: "Africa" },
+ { code: "rs", value: "serbia", label: "Serbia", continent: "Europe" },
+ { code: "sc", value: "seychelles", label: "Seychelles", continent: "Africa" },
+ {
+ code: "sl",
+ value: "sierra-leone",
+ label: "Sierra Leone",
+ continent: "Africa",
+ },
+ { code: "sg", value: "singapore", label: "Singapore", continent: "Asia" },
+ { code: "sk", value: "slovakia", label: "Slovakia", continent: "Europe" },
+ { code: "si", value: "slovenia", label: "Slovenia", continent: "Europe" },
+ {
+ code: "sb",
+ value: "solomon-islands",
+ label: "Solomon Islands",
+ continent: "Oceania",
+ },
+ { code: "so", value: "somalia", label: "Somalia", continent: "Africa" },
+ {
+ code: "za",
+ value: "south-africa",
+ label: "South Africa",
+ continent: "Africa",
+ },
+ { code: "kr", value: "south-korea", label: "South Korea", continent: "Asia" },
+ {
+ code: "ss",
+ value: "south-sudan",
+ label: "South Sudan",
+ continent: "Africa",
+ },
+ { code: "es", value: "spain", label: "Spain", continent: "Europe" },
+ { code: "lk", value: "sri-lanka", label: "Sri Lanka", continent: "Asia" },
+ { code: "sd", value: "sudan", label: "Sudan", continent: "Africa" },
+ {
+ code: "sr",
+ value: "suriname",
+ label: "Suriname",
+ continent: "South America",
+ },
+ { code: "se", value: "sweden", label: "Sweden", continent: "Europe" },
+ {
+ code: "ch",
+ value: "switzerland",
+ label: "Switzerland",
+ continent: "Europe",
+ },
+ { code: "sy", value: "syria", label: "Syria", continent: "Asia" },
+ { code: "tw", value: "taiwan", label: "Taiwan", continent: "Asia" },
+ { code: "tj", value: "tajikistan", label: "Tajikistan", continent: "Asia" },
+ { code: "tz", value: "tanzania", label: "Tanzania", continent: "Africa" },
+ { code: "th", value: "thailand", label: "Thailand", continent: "Asia" },
+ { code: "tl", value: "timor-leste", label: "Timor-Leste", continent: "Asia" },
+ { code: "tg", value: "togo", label: "Togo", continent: "Africa" },
+ { code: "to", value: "tonga", label: "Tonga", continent: "Oceania" },
+ {
+ code: "tt",
+ value: "trinidad-and-tobago",
+ label: "Trinidad and Tobago",
+ continent: "North America",
+ },
+ { code: "tn", value: "tunisia", label: "Tunisia", continent: "Africa" },
+ { code: "tr", value: "turkey", label: "Turkey", continent: "Asia" },
+ {
+ code: "tm",
+ value: "turkmenistan",
+ label: "Turkmenistan",
+ continent: "Asia",
+ },
+ { code: "tv", value: "tuvalu", label: "Tuvalu", continent: "Oceania" },
+ { code: "ug", value: "uganda", label: "Uganda", continent: "Africa" },
+ { code: "ua", value: "ukraine", label: "Ukraine", continent: "Europe" },
+ {
+ code: "ae",
+ value: "united-arab-emirates",
+ label: "United Arab Emirates",
+ continent: "Asia",
+ },
+ {
+ code: "gb",
+ value: "united-kingdom",
+ label: "United Kingdom",
+ continent: "Europe",
+ },
+ {
+ code: "us",
+ value: "united-states",
+ label: "United States",
+ continent: "North America",
+ },
+ {
+ code: "uy",
+ value: "uruguay",
+ label: "Uruguay",
+ continent: "South America",
+ },
+ { code: "uz", value: "uzbekistan", label: "Uzbekistan", continent: "Asia" },
+ { code: "vu", value: "vanuatu", label: "Vanuatu", continent: "Oceania" },
+ {
+ code: "va",
+ value: "vatican-city",
+ label: "Vatican City",
+ continent: "Europe",
+ },
+ {
+ code: "ve",
+ value: "venezuela",
+ label: "Venezuela",
+ continent: "South America",
+ },
+ { code: "vn", value: "vietnam", label: "Vietnam", continent: "Asia" },
+ { code: "ye", value: "yemen", label: "Yemen", continent: "Asia" },
+ { code: "zm", value: "zambia", label: "Zambia", continent: "Africa" },
+ { code: "zw", value: "zimbabwe", label: "Zimbabwe", continent: "Africa" },
+]
+
+const timezones = [
+ {
+ value: "Americas",
+ items: [
+ "(GMT-5) New York",
+ "(GMT-8) Los Angeles",
+ "(GMT-6) Chicago",
+ "(GMT-5) Toronto",
+ "(GMT-8) Vancouver",
+ "(GMT-3) São Paulo",
+ ],
+ },
+ {
+ value: "Europe",
+ items: [
+ "(GMT+0) London",
+ "(GMT+1) Paris",
+ "(GMT+1) Berlin",
+ "(GMT+1) Rome",
+ "(GMT+1) Madrid",
+ "(GMT+1) Amsterdam",
+ ],
+ },
+ {
+ value: "Asia/Pacific",
+ items: [
+ "(GMT+9) Tokyo",
+ "(GMT+8) Shanghai",
+ "(GMT+8) Singapore",
+ "(GMT+4) Dubai",
+ "(GMT+11) Sydney",
+ "(GMT+9) Seoul",
+ ],
+ },
+] as const
+
+function ComboboxBasic() {
+ return (
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxSides() {
+ return (
+
+
+ {(
+ [
+ "inline-start",
+ "left",
+ "top",
+ "bottom",
+ "right",
+ "inline-end",
+ ] as const
+ ).map((side) => (
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+ ))}
+
+
+ )
+}
+
+function ComboboxDisabled() {
+ return (
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+const disabledFrameworks = ["Nuxt.js", "Remix"]
+
+function ComboboxDisabledItems() {
+ return (
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxInvalid() {
+ return (
+
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+
+ Framework
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+ Please select a valid framework.
+
+
+
+
+ )
+}
+
+function ComboboxWithClear() {
+ return (
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxWithGroups() {
+ return (
+
+
+
+
+ No timezones found.
+
+ {(group) => (
+
+ {group.value}
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxWithGroupsAndSeparator() {
+ return (
+
+
+
+
+ No timezones found.
+
+ {(group) => (
+
+ {group.value}
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxWithForm() {
+ const handleSubmit = (event: React.FormEvent) => {
+ event.preventDefault()
+ const formData = new FormData(event.target as HTMLFormElement)
+ const framework = formData.get("framework") as string
+ toast(`You selected ${framework} as your framework.`)
+ }
+
+ return (
+
+
+
+
+
+
+
+ Submit
+
+
+
+
+ )
+}
+
+const largeListItems = Array.from({ length: 100 }, (_, i) => `Item ${i + 1}`)
+
+function ComboboxLargeList() {
+ return (
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxAutoHighlight() {
+ return (
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboxboxInputAddon() {
+ return (
+
+
+
+
+
+
+
+
+ No timezones found.
+
+ {(group) => (
+
+ {group.value}
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxInPopup() {
+ return (
+
+
+
+ }
+ >
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item.label}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxMultiple() {
+ const anchor = useComboboxAnchor()
+
+ return (
+
+
+
+
+ {(values) => (
+
+ {values.map((value: string) => (
+ {value}
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxMultipleDisabled() {
+ const anchor = useComboboxAnchor()
+
+ return (
+
+
+
+
+ {(values) => (
+
+ {values.map((value: string) => (
+ {value}
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxMultipleInvalid() {
+ const anchor1 = useComboboxAnchor()
+ const anchor2 = useComboboxAnchor()
+
+ return (
+
+
+
+
+
+ {(values) => (
+
+ {values.map((value: string) => (
+ {value}
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+
+ Frameworks
+
+
+
+
+ {(values) => (
+
+ {values.map((value: string) => (
+ {value}
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ Please select at least one framework.
+
+
+
+
+
+ )
+}
+
+function ComboboxMultipleNoRemove() {
+ const anchor = useComboboxAnchor()
+
+ return (
+
+
+
+
+ {(values) => (
+
+ {values.map((value: string) => (
+
+ {value}
+
+ ))}
+
+
+ )}
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxWithCustomItems() {
+ return (
+
+ country.code !== "")}
+ itemToStringValue={(country: (typeof countries)[number]) =>
+ country.label
+ }
+ >
+
+
+ No countries found.
+
+ {(country) => (
+
+ -
+
+
+ {country.label}
+
+
+ {country.continent} ({country.code})
+
+
+
+
+ )}
+
+
+
+
+ )
+}
+
+function ComboboxInDialog() {
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Select Framework
+
+ Choose your preferred framework from the list below.
+
+
+
+
+ Framework
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+
+ setOpen(false)}
+ >
+ Cancel
+
+ {
+ toast("Framework selected.")
+ setOpen(false)
+ }}
+ >
+ Confirm
+
+
+
+
+
+ )
+}
+
+const items = [
+ {
+ label: "Select a framework",
+ value: null,
+ },
+ {
+ label: "React",
+ value: "react",
+ },
+ {
+ label: "Vue",
+ value: "vue",
+ },
+ {
+ label: "Angular",
+ value: "angular",
+ },
+ {
+ label: "Svelte",
+ value: "svelte",
+ },
+ {
+ label: "Solid",
+ value: "solid",
+ },
+ {
+ label: "Preact",
+ value: "preact",
+ },
+ {
+ label: "Next.js",
+ value: "next.js",
+ },
+]
+
+function ComboboxWithOtherInputs() {
+ return (
+
+
+
+
+ No items found.
+
+ {(item) => (
+
+ {item}
+
+ )}
+
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ Select a framework
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/command-example.tsx b/apps/ui/src/registry/bases/base/examples/command-example.tsx
new file mode 100644
index 0000000..7368835
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/command-example.tsx
@@ -0,0 +1,589 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Card, CardContent } from "@/registry/bases/base/ui/card"
+import {
+ Command,
+ CommandDialog,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+ CommandSeparator,
+ CommandShortcut,
+} from "@/registry/bases/base/ui/command"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function CommandExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function CommandInline() {
+ return (
+
+
+
+
+
+
+ No results found.
+
+
+
+ Calendar
+
+
+
+ Search Emoji
+
+
+
+ Calculator
+
+
+
+
+
+
+ Profile
+ ⌘P
+
+
+
+ Billing
+ ⌘B
+
+
+
+ Settings
+ ⌘S
+
+
+
+
+
+
+
+ )
+}
+
+function CommandBasic() {
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+ setOpen(true)}
+ variant="outline"
+ className="w-fit"
+ >
+ Open Menu
+
+
+
+
+
+ No results found.
+
+ Calendar
+ Search Emoji
+ Calculator
+
+
+
+
+
+
+ )
+}
+
+function CommandWithShortcuts() {
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+ setOpen(true)}
+ variant="outline"
+ className="w-fit"
+ >
+ Open Menu
+
+
+
+
+
+ No results found.
+
+
+
+ Profile
+ ⌘P
+
+
+
+ Billing
+ ⌘B
+
+
+
+ Settings
+ ⌘S
+
+
+
+
+
+
+
+ )
+}
+
+function CommandWithGroups() {
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+ setOpen(true)}
+ variant="outline"
+ className="w-fit"
+ >
+ Open Menu
+
+
+
+
+
+ No results found.
+
+
+
+ Calendar
+
+
+
+ Search Emoji
+
+
+
+ Calculator
+
+
+
+
+
+
+ Profile
+ ⌘P
+
+
+
+ Billing
+ ⌘B
+
+
+
+ Settings
+ ⌘S
+
+
+
+
+
+
+
+ )
+}
+
+function CommandManyItems() {
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+ setOpen(true)}
+ variant="outline"
+ className="w-fit"
+ >
+ Open Menu
+
+
+
+
+
+ No results found.
+
+
+
+ Home
+ ⌘H
+
+
+
+ Inbox
+ ⌘I
+
+
+
+ Documents
+ ⌘D
+
+
+
+ Folders
+ ⌘F
+
+
+
+
+
+
+ New File
+ ⌘N
+
+
+
+ New Folder
+ ⇧⌘N
+
+
+
+ Copy
+ ⌘C
+
+
+
+ Cut
+ ⌘X
+
+
+
+ Paste
+ ⌘V
+
+
+
+ Delete
+ ⌫
+
+
+
+
+
+
+ Grid View
+
+
+
+ List View
+
+
+
+ Zoom In
+ ⌘+
+
+
+
+ Zoom Out
+ ⌘-
+
+
+
+
+
+
+ Profile
+ ⌘P
+
+
+
+ Billing
+ ⌘B
+
+
+
+ Settings
+ ⌘S
+
+
+
+ Notifications
+
+
+
+ Help & Support
+
+
+
+
+
+
+ Calculator
+
+
+
+ Calendar
+
+
+
+ Image Editor
+
+
+
+ Code Editor
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/component-example.tsx b/apps/ui/src/registry/bases/base/examples/component-example.tsx
new file mode 100644
index 0000000..e885aa0
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/component-example.tsx
@@ -0,0 +1,662 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogMedia,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/registry/bases/base/ui/alert-dialog"
+import { Badge } from "@/registry/bases/base/ui/badge"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Card,
+ CardAction,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import {
+ Combobox,
+ ComboboxContent,
+ ComboboxEmpty,
+ ComboboxInput,
+ ComboboxItem,
+ ComboboxList,
+} from "@/registry/bases/base/ui/combobox"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuPortal,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import { Field, FieldGroup, FieldLabel } from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import { Textarea } from "@/registry/bases/base/ui/textarea"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export function ComponentExample() {
+ return (
+
+
+
+
+ )
+}
+
+function CardExample() {
+ return (
+
+
+
+
+
+ Observability Plus is replacing Monitoring
+
+ Switch to the improved way to explore your data, with natural
+ language. Monitoring will no longer be available on the Pro plan in
+ November, 2025
+
+
+
+
+ }>
+
+ Show Dialog
+
+
+
+
+
+
+ Allow accessory to connect?
+
+ Do you want to allow the USB accessory to connect to this
+ device?
+
+
+
+ Don't allow
+ Allow
+
+
+
+
+ Warning
+
+
+
+
+ )
+}
+
+const frameworks = [
+ "Next.js",
+ "SvelteKit",
+ "Nuxt.js",
+ "Remix",
+ "Astro",
+] as const
+
+const roleItems = [
+ { label: "Developer", value: "developer" },
+ { label: "Designer", value: "designer" },
+ { label: "Manager", value: "manager" },
+ { label: "Other", value: "other" },
+]
+
+function FormExample() {
+ const [notifications, setNotifications] = React.useState({
+ email: true,
+ sms: false,
+ push: true,
+ })
+ const [theme, setTheme] = React.useState("light")
+
+ return (
+
+
+
+ User Information
+ Please fill in your details below
+
+
+ }
+ >
+
+ More options
+
+
+
+ File
+
+
+ New File
+ ⌘N
+
+
+
+ New Folder
+ ⇧⌘N
+
+
+
+
+ Open Recent
+
+
+
+
+ Recent Projects
+
+
+ Project Alpha
+
+
+
+ Project Beta
+
+
+
+
+ More Projects
+
+
+
+
+
+ Project Gamma
+
+
+
+ Project Delta
+
+
+
+
+
+
+
+
+
+ Browse...
+
+
+
+
+
+
+
+
+ Save
+ ⌘S
+
+
+
+ Export
+ ⇧⌘E
+
+
+
+
+ View
+
+ setNotifications({
+ ...notifications,
+ email: checked === true,
+ })
+ }
+ >
+
+ Show Sidebar
+
+
+ setNotifications({
+ ...notifications,
+ sms: checked === true,
+ })
+ }
+ >
+
+ Show Status Bar
+
+
+
+
+ Theme
+
+
+
+
+ Appearance
+
+
+
+ Light
+
+
+
+ Dark
+
+
+
+ System
+
+
+
+
+
+
+
+
+
+ Account
+
+
+ Profile
+ ⇧⌘P
+
+
+
+ Billing
+
+
+
+
+ Settings
+
+
+
+
+ Preferences
+
+
+ Keyboard Shortcuts
+
+
+
+ Language
+
+
+
+
+ Notifications
+
+
+
+
+
+ Notification Types
+
+
+ setNotifications({
+ ...notifications,
+ push: checked === true,
+ })
+ }
+ >
+
+ Push Notifications
+
+
+ setNotifications({
+ ...notifications,
+ email: checked === true,
+ })
+ }
+ >
+
+ Email Notifications
+
+
+
+
+
+
+
+
+
+
+ Privacy & Security
+
+
+
+
+
+
+
+
+
+
+ Help & Support
+
+
+
+ Documentation
+
+
+
+
+
+
+ Sign Out
+ ⇧⌘Q
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/context-menu-example.tsx b/apps/ui/src/registry/bases/base/examples/context-menu-example.tsx
new file mode 100644
index 0000000..9e7f27e
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/context-menu-example.tsx
@@ -0,0 +1,596 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ ContextMenu,
+ ContextMenuCheckboxItem,
+ ContextMenuContent,
+ ContextMenuGroup,
+ ContextMenuItem,
+ ContextMenuLabel,
+ ContextMenuRadioGroup,
+ ContextMenuRadioItem,
+ ContextMenuSeparator,
+ ContextMenuShortcut,
+ ContextMenuSub,
+ ContextMenuSubContent,
+ ContextMenuSubTrigger,
+ ContextMenuTrigger,
+} from "@/registry/bases/base/ui/context-menu"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ContextMenuExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ContextMenuBasic() {
+ return (
+
+
+
+ Right click here
+
+
+
+ Back
+ Forward
+ Reload
+
+
+
+
+ )
+}
+
+function ContextMenuWithIcons() {
+ return (
+
+
+
+ Right click here
+
+
+
+
+
+ Copy
+
+
+
+ Cut
+
+
+
+ Paste
+
+
+
+
+
+
+ Delete
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithShortcuts() {
+ return (
+
+
+
+ Right click here
+
+
+
+
+ Back
+ ⌘[
+
+
+ Forward
+ ⌘]
+
+
+ Reload
+ ⌘R
+
+
+
+
+
+ Save
+ ⌘S
+
+
+ Save As...
+ ⇧⌘S
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithSubmenu() {
+ return (
+
+
+
+ Right click here
+
+
+
+
+ Copy
+ ⌘C
+
+
+ Cut
+ ⌘X
+
+
+
+ More Tools
+
+
+ Save Page...
+ Create Shortcut...
+ Name Window...
+
+
+
+ Developer Tools
+
+
+
+ Delete
+
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithGroups() {
+ return (
+
+
+
+ Right click here
+
+
+
+ File
+
+ New File
+ ⌘N
+
+
+ Open File
+ ⌘O
+
+
+ Save
+ ⌘S
+
+
+
+
+ Edit
+
+ Undo
+ ⌘Z
+
+
+ Redo
+ ⇧⌘Z
+
+
+
+
+
+ Cut
+ ⌘X
+
+
+ Copy
+ ⌘C
+
+
+ Paste
+ ⌘V
+
+
+
+
+
+ Delete
+ ⌫
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithCheckboxes() {
+ return (
+
+
+
+ Right click here
+
+
+
+
+ Show Bookmarks Bar
+
+ Show Full URLs
+
+ Show Developer Tools
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithRadio() {
+ const [user, setUser] = React.useState("pedro")
+ const [theme, setTheme] = React.useState("light")
+
+ return (
+
+
+
+ Right click here
+
+
+
+ People
+
+
+ Pedro Duarte
+
+
+ Colm Tuite
+
+
+
+
+
+ Theme
+
+ Light
+ Dark
+ System
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithDestructive() {
+ return (
+
+
+
+ Right click here
+
+
+
+
+
+ Edit
+
+
+
+ Share
+
+
+
+
+
+
+ Archive
+
+
+
+ Delete
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithSides() {
+ return (
+
+
+ {(
+ [
+ "inline-start",
+ "left",
+ "top",
+ "bottom",
+ "right",
+ "inline-end",
+ ] as const
+ ).map((side) => (
+
+
+ {side.replace("-", " ")}
+
+
+
+ Back
+ Forward
+ Reload
+
+
+
+ ))}
+
+
+ )
+}
+
+function ContextMenuInDialog() {
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Context Menu Example
+
+ Right click on the area below to see the context menu.
+
+
+
+
+ Right click here
+
+
+
+
+
+ Copy
+
+
+
+ Cut
+
+
+
+ Paste
+
+
+
+
+ More Options
+
+
+ Save Page...
+ Create Shortcut...
+ Name Window...
+
+
+
+ Developer Tools
+
+
+
+
+
+
+
+ Delete
+
+
+
+
+
+
+
+ )
+}
+
+function ContextMenuWithInset() {
+ const [showBookmarks, setShowBookmarks] = React.useState(true)
+ const [showUrls, setShowUrls] = React.useState(false)
+ const [theme, setTheme] = React.useState("system")
+
+ return (
+
+
+
+ Right click here
+
+
+
+ Actions
+
+
+ Copy
+
+
+
+ Cut
+
+ Paste
+
+
+
+ Appearance
+
+ Bookmarks
+
+
+ Full URLs
+
+
+
+
+ Theme
+
+
+ Light
+
+
+ Dark
+
+
+ System
+
+
+
+
+
+ More Options
+
+
+ Save Page...
+ Create Shortcut...
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/demo.tsx b/apps/ui/src/registry/bases/base/examples/demo.tsx
new file mode 100644
index 0000000..e41e39c
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/demo.tsx
@@ -0,0 +1,413 @@
+
+import * as React from "react"
+
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/registry/bases/base/ui/alert-dialog"
+import { Badge } from "@/registry/bases/base/ui/badge"
+import { Button } from "@/registry/bases/base/ui/button"
+import { ButtonGroup } from "@/registry/bases/base/ui/button-group"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import { Checkbox } from "@/registry/bases/base/ui/checkbox"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import { Field, FieldGroup } from "@/registry/bases/base/ui/field"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+ InputGroupText,
+} from "@/registry/bases/base/ui/input-group"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import {
+ RadioGroup,
+ RadioGroupItem,
+} from "@/registry/bases/base/ui/radio-group"
+import { Slider } from "@/registry/bases/base/ui/slider"
+import { Switch } from "@/registry/bases/base/ui/switch"
+import { Textarea } from "@/registry/bases/base/ui/textarea"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export function Demo() {
+ const [sliderValue, setSliderValue] = React.useState([500])
+ const handleSliderValueChange = React.useCallback(
+ (value: number | readonly number[]) => {
+ if (typeof value === "number") {
+ setSliderValue([value])
+ } else {
+ setSliderValue([...value])
+ }
+ },
+ []
+ )
+
+ return (
+
+
+
+
+
+ Style Overview
+
+ Designers love packing quirky glyphs into test phrases. This is
+ a preview of the typography styles.
+
+
+
+
+ {[
+ "--background",
+ "--foreground",
+ "--primary",
+ "--secondary",
+ "--muted",
+ "--accent",
+ "--border",
+ "--chart-1",
+ "--chart-2",
+ "--chart-3",
+ "--chart-4",
+ "--chart-5",
+ ].map((variant) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Button
+ Secondary
+ Outline
+ Ghost
+
+
-
+
+ Two-factor authentication
+
+ Verify via email or phone number.
+
+
+
+
+ Enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Badge
+ Secondary
+ Outline
+
+
+
+
+
+
+
+
+
+
+
+
+ }>
+ Alert Dialog
+ Dialog
+
+
+
+
+ Allow accessory to connect?
+
+
+ Do you want to allow the USB accessory to connect to
+ this device and your data?
+
+
+
+ Don't allow
+ Allow
+
+
+
+
+ Button Group
+
+ }
+ >
+
+
+
+
+ Quick Actions
+ Mute Conversation
+ Mark as Read
+ Block User
+
+
+
+ Conversation
+ Share Conversation
+ Copy Conversation
+ Report Conversation
+
+
+
+
+ Delete Conversation
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/dialog-example.tsx b/apps/ui/src/registry/bases/base/examples/dialog-example.tsx
new file mode 100644
index 0000000..ec3d5f0
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/dialog-example.tsx
@@ -0,0 +1,600 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Checkbox } from "@/registry/bases/base/ui/checkbox"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+ FieldSet,
+ FieldTitle,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/registry/bases/base/ui/input-group"
+import { Kbd } from "@/registry/bases/base/ui/kbd"
+import {
+ NativeSelect,
+ NativeSelectOption,
+} from "@/registry/bases/base/ui/native-select"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import { Switch } from "@/registry/bases/base/ui/switch"
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from "@/registry/bases/base/ui/tabs"
+import { Textarea } from "@/registry/bases/base/ui/textarea"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/bases/base/ui/tooltip"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function DialogExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function DialogWithForm() {
+ return (
+
+
+
+
+
+ )
+}
+
+function DialogScrollableContent() {
+ return (
+
+
+ }>
+ Scrollable Content
+
+
+
+ Scrollable Content
+
+ This is a dialog with scrollable content.
+
+
+
+ {Array.from({ length: 10 }).map((_, index) => (
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
+ nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
+ in reprehenderit in voluptate velit esse cillum dolore eu fugiat
+ nulla pariatur. Excepteur sint occaecat cupidatat non proident,
+ sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+ ))}
+
+
+
+
+ )
+}
+
+function DialogWithStickyFooter() {
+ return (
+
+
+ }>
+ Sticky Footer
+
+
+
+ Scrollable Content
+
+ This is a dialog with scrollable content.
+
+
+
+ {Array.from({ length: 10 }).map((_, index) => (
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
+ nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
+ in reprehenderit in voluptate velit esse cillum dolore eu fugiat
+ nulla pariatur. Excepteur sint occaecat cupidatat non proident,
+ sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+ ))}
+
+
+ }>
+ Close
+
+
+
+
+
+ )
+}
+
+function DialogNoCloseButton() {
+ return (
+
+
+ }>
+ No Close Button
+
+
+
+ No Close Button
+
+ This dialog doesn't have a close button in the top-right
+ corner.
+
+
+
+ }>
+ Close
+
+
+
+
+
+ )
+}
+
+const spokenLanguages = [
+ { label: "Auto", value: "auto" },
+ { label: "English", value: "en" },
+ { label: "Spanish", value: "es" },
+ { label: "French", value: "fr" },
+ { label: "German", value: "de" },
+ { label: "Italian", value: "it" },
+ { label: "Portuguese", value: "pt" },
+ { label: "Russian", value: "ru" },
+ { label: "Chinese", value: "zh" },
+ { label: "Japanese", value: "ja" },
+ { label: "Korean", value: "ko" },
+ { label: "Arabic", value: "ar" },
+ { label: "Hindi", value: "hi" },
+ { label: "Bengali", value: "bn" },
+ { label: "Telugu", value: "te" },
+ { label: "Marathi", value: "mr" },
+ { label: "Kannada", value: "kn" },
+ { label: "Malayalam", value: "ml" },
+]
+
+const voices = [
+ { label: "Samantha", value: "samantha" },
+ { label: "Alex", value: "alex" },
+ { label: "Fred", value: "fred" },
+ { label: "Victoria", value: "victoria" },
+ { label: "Tom", value: "tom" },
+ { label: "Karen", value: "karen" },
+ { label: "Sam", value: "sam" },
+ { label: "Daniel", value: "daniel" },
+]
+
+const themes = [
+ { label: "Light", value: "light" },
+ { label: "Dark", value: "dark" },
+ { label: "System", value: "system" },
+]
+
+const accents = [
+ { label: "Default", value: "default" },
+ { label: "Red", value: "red" },
+ { label: "Blue", value: "blue" },
+ { label: "Green", value: "green" },
+ { label: "Purple", value: "purple" },
+ { label: "Pink", value: "pink" },
+]
+
+function DialogChatSettings() {
+ const [tab, setTab] = React.useState("general")
+ const [theme, setTheme] = React.useState("system")
+ const [accentColor, setAccentColor] = React.useState("default")
+ const [spokenLanguage, setSpokenLanguage] = React.useState("en")
+ const [voice, setVoice] = React.useState("samantha")
+
+ return (
+
+
+ }>
+ Chat Settings
+
+
+
+ Chat Settings
+
+ Customize your chat settings: theme, accent color, spoken
+ language, voice, personality, and custom instructions.
+
+
+
+
setTab(e.target.value)}
+ className="w-full md:hidden"
+ >
+ General
+
+ Notifications
+
+
+ Personalization
+
+ Security
+
+
+
+ General
+ Notifications
+
+ Personalization
+
+ Security
+
+
+
+
+
+
+ Theme
+ setTheme(value as string)}
+ >
+
+
+
+
+
+ {themes.map((theme) => (
+
+ {theme.label}
+
+ ))}
+
+
+
+
+
+
+
+ Accent Color
+
+
+ setAccentColor(value as string)
+ }
+ >
+
+
+
+
+
+ {accents.map((accent) => (
+
+ {accent.label}
+
+ ))}
+
+
+
+
+
+
+
+
+ Spoken Language
+
+
+ For best results, select the language you mainly
+ speak. If it's not listed, it may still be
+ supported via auto-detection.
+
+
+
+ setSpokenLanguage(value as string)
+ }
+ >
+
+
+
+
+
+ {spokenLanguages.map((language) => (
+
+ {language.label}
+
+ ))}
+
+
+
+
+
+
+ Voice
+ setVoice(value as string)}
+ >
+
+
+
+
+
+ {voices.map((voice) => (
+
+ {voice.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ Responses
+
+ Get notified when ChatGPT responds to requests that take
+ time, like research or image generation.
+
+
+
+
+
+ Push notifications
+
+
+
+
+
+
+ Tasks
+
+ Get notified when tasks you've created have
+ updates. Manage tasks
+
+
+
+
+
+ Push notifications
+
+
+
+
+
+ Email notifications
+
+
+
+
+
+
+
+
+
+ Nickname
+
+
+
+
+ }
+ >
+
+
+
+ Used to identify you in the chat. N
+
+
+
+
+
+
+
+
+ More about you
+
+ Tell us more about yourself. This will be used to help
+ us personalize your experience.
+
+
+
+
+
+
+
+
+
+ Enable customizations
+
+
+ Enable customizations to make ChatGPT more
+ personalized.
+
+
+
+
+
+
+
+
+
+
+
+
+ Multi-factor authentication
+
+
+ Enable multi-factor authentication to secure your
+ account. If you do not have a two-factor
+ authentication device, you can use a one-time code
+ sent to your email.
+
+
+
+
+
+
+
+ Log out
+
+ Log out of your account on this device.
+
+
+
+ Log Out
+
+
+
+
+
+ Log out of all devices
+
+ This will log you out of all devices, including the
+ current session. It may take up to 30 minutes for the
+ changes to take effect.
+
+
+
+ Log Out All
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/drawer-example.tsx b/apps/ui/src/registry/bases/base/examples/drawer-example.tsx
new file mode 100644
index 0000000..06861e4
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/drawer-example.tsx
@@ -0,0 +1,121 @@
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/registry/bases/base/ui/drawer"
+
+export default function DrawerExample() {
+ return (
+
+
+
+
+ )
+}
+
+const DRAWER_SIDES = ["top", "right", "bottom", "left"] as const
+
+function DrawerWithSides() {
+ return (
+
+
+ {DRAWER_SIDES.map((side) => (
+
+
+
+ {side}
+
+
+
+
+ Move Goal
+
+ Set your daily activity goal.
+
+
+
+ {Array.from({ length: 10 }).map((_, index) => (
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
+ do eiusmod tempor incididunt ut labore et dolore magna
+ aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ ullamco laboris nisi ut aliquip ex ea commodo consequat.
+ Duis aute irure dolor in reprehenderit in voluptate velit
+ esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+ occaecat cupidatat non proident, sunt in culpa qui officia
+ deserunt mollit anim id est laborum.
+
+ ))}
+
+
+ Submit
+
+ Cancel
+
+
+
+
+ ))}
+
+
+ )
+}
+
+function DrawerScrollableContent() {
+ return (
+
+
+
+ Scrollable Content
+
+
+
+ Move Goal
+ Set your daily activity goal.
+
+
+ {Array.from({ length: 10 }).map((_, index) => (
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do
+ eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
+ enim ad minim veniam, quis nostrud exercitation ullamco laboris
+ nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
+ in reprehenderit in voluptate velit esse cillum dolore eu fugiat
+ nulla pariatur. Excepteur sint occaecat cupidatat non proident,
+ sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+ ))}
+
+
+ Submit
+
+ Cancel
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/dropdown-menu-example.tsx b/apps/ui/src/registry/bases/base/examples/dropdown-menu-example.tsx
new file mode 100644
index 0000000..3c10540
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/dropdown-menu-example.tsx
@@ -0,0 +1,992 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/bases/base/ui/avatar"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuPortal,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function DropdownMenuExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function DropdownMenuBasic() {
+ return (
+
+
+ }
+ >
+ Open
+
+
+
+ My Account
+ Profile
+ Billing
+ Settings
+
+
+ GitHub
+ Support
+ API
+
+
+
+ )
+}
+
+function DropdownMenuSides() {
+ return (
+
+
+ {(
+ [
+ "inline-start",
+ "left",
+ "top",
+ "bottom",
+ "right",
+ "inline-end",
+ ] as const
+ ).map((side) => (
+
+ }
+ >
+ {side.replace("-", " ")}
+
+
+
+ Profile
+ Billing
+ Settings
+
+
+
+ ))}
+
+
+ )
+}
+
+function DropdownMenuWithIcons() {
+ return (
+
+
+ }
+ >
+ Open
+
+
+
+
+ Profile
+
+
+
+ Billing
+
+
+
+ Settings
+
+
+
+
+ Log out
+
+
+
+
+ )
+}
+
+function DropdownMenuWithShortcuts() {
+ return (
+
+
+ }
+ >
+ Open
+
+
+
+ My Account
+
+ Profile
+ ⇧⌘P
+
+
+ Billing
+ ⌘B
+
+
+ Settings
+ ⌘S
+
+
+ Keyboard shortcuts
+ ⌘K
+
+
+
+
+ Log out
+ ⇧⌘Q
+
+
+
+
+ )
+}
+
+function DropdownMenuWithSubmenu() {
+ return (
+
+
+ }
+ >
+ Open
+
+
+
+ Team
+
+ Invite users
+
+
+ Email
+ Message
+
+ More...
+
+
+
+
+ New Team
+ ⌘+T
+
+
+
+
+
+ )
+}
+
+function DropdownMenuWithCheckboxes() {
+ const [showStatusBar, setShowStatusBar] = React.useState(true)
+ const [showActivityBar, setShowActivityBar] = React.useState(false)
+ const [showPanel, setShowPanel] = React.useState(false)
+
+ return (
+
+
+ }
+ >
+ Checkboxes
+
+
+
+ Appearance
+
+
+ Status Bar
+
+
+
+ Activity Bar
+
+
+
+ Panel
+
+
+
+
+
+ )
+}
+
+function DropdownMenuWithRadio() {
+ const [position, setPosition] = React.useState("bottom")
+
+ return (
+
+
+ }
+ >
+ Radio Group
+
+
+
+ Panel Position
+
+
+
+ Top
+
+
+
+ Bottom
+
+
+
+ Right
+
+
+
+
+
+
+ )
+}
+
+function DropdownMenuWithCheckboxesIcons() {
+ const [notifications, setNotifications] = React.useState({
+ email: true,
+ sms: false,
+ push: true,
+ })
+
+ return (
+
+
+ }
+ >
+ Notifications
+
+
+
+ Notification Preferences
+
+ setNotifications({ ...notifications, email: checked === true })
+ }
+ >
+
+ Email notifications
+
+
+ setNotifications({ ...notifications, sms: checked === true })
+ }
+ >
+
+ SMS notifications
+
+
+ setNotifications({ ...notifications, push: checked === true })
+ }
+ >
+
+ Push notifications
+
+
+
+
+
+ )
+}
+
+function DropdownMenuWithRadioIcons() {
+ const [paymentMethod, setPaymentMethod] = React.useState("card")
+
+ return (
+
+
+ }
+ >
+ Payment Method
+
+
+
+ Select Payment Method
+
+
+
+ Credit Card
+
+
+
+ PayPal
+
+
+
+ Bank Transfer
+
+
+
+
+
+
+ )
+}
+
+function DropdownMenuWithDestructive() {
+ return (
+
+
+ }
+ >
+ Actions
+
+
+
+
+ Edit
+
+
+
+ Share
+
+
+
+
+ Archive
+
+
+
+ Delete
+
+
+
+
+ )
+}
+
+function DropdownMenuWithAvatar() {
+ const menuContent = (
+ <>
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Sign Out
+
+ >
+ )
+
+ return (
+
+
+
+
+ }
+ >
+
+
+ CN
+
+
+ shadcn
+
+ shadcn@example.com
+
+
+
+
+
+ {menuContent}
+
+
+
+
+ }
+ >
+
+
+ LR
+
+
+
+ {menuContent}
+
+
+
+
+ )
+}
+
+function DropdownMenuInDialog() {
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Dropdown Menu Example
+
+ Click the button below to see the dropdown menu.
+
+
+
+ }
+ >
+ Open Menu
+
+
+
+
+ Copy
+
+
+
+ Cut
+
+
+
+ Paste
+
+
+
+ More Options
+
+
+ Save Page...
+ Create Shortcut...
+ Name Window...
+
+ Developer Tools
+
+
+
+
+
+
+ Delete
+
+
+
+
+
+
+ )
+}
+
+function DropdownMenuWithInset() {
+ const [showBookmarks, setShowBookmarks] = React.useState(true)
+ const [showUrls, setShowUrls] = React.useState(false)
+ const [theme, setTheme] = React.useState("system")
+
+ return (
+
+
+ }
+ >
+ Open
+
+
+
+ Actions
+
+
+ Copy
+
+
+
+ Cut
+
+ Paste
+
+
+
+ Appearance
+
+ Bookmarks
+
+
+ Full URLs
+
+
+
+
+ Theme
+
+
+ Light
+
+
+ Dark
+
+
+ System
+
+
+
+
+
+ More Options
+
+
+
+ Save Page...
+ Create Shortcut...
+
+
+
+
+
+
+
+ )
+}
+
+function DropdownMenuComplex() {
+ const [showSidebar, setShowSidebar] = React.useState(true)
+ const [showStatusBar, setShowStatusBar] = React.useState(false)
+
+ return (
+
+
+ }
+ >
+ Complex Menu
+
+
+
+ My Account
+
+
+ Profile
+ ⇧⌘P
+
+
+
+ Billing
+ ⌘B
+
+
+
+ Settings
+ ⌘S
+
+
+
+
+ View
+
+
+ Sidebar
+
+
+
+ Status Bar
+
+
+
+
+
+
+
+ Invite Users
+
+
+
+
+
+
+ Email
+
+
+
+ Message
+
+
+
+
+
+
+ More...
+
+
+
+
+
+
+
+
+
+
+ Support
+
+
+
+ Sign Out
+ ⇧⌘Q
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/empty-example.tsx b/apps/ui/src/registry/bases/base/examples/empty-example.tsx
new file mode 100644
index 0000000..fae9047
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/empty-example.tsx
@@ -0,0 +1,263 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/bases/base/ui/empty"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/bases/base/ui/input-group"
+import { Kbd } from "@/registry/bases/base/ui/kbd"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function EmptyExample() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function EmptyBasic() {
+ return (
+
+
+
+ No projects yet
+
+ You haven't created any projects yet. Get started by creating
+ your first project.
+
+
+
+
+ } nativeButton={false}>
+ Create project
+
+ Import project
+
+ }
+ className="text-muted-foreground"
+ nativeButton={false}
+ >
+ Learn more{" "}
+
+
+
+
+
+ )
+}
+
+function EmptyWithMutedBackground() {
+ return (
+
+
+
+ No results found
+
+ No results found for your search. Try adjusting your search terms.
+
+
+
+ Try again
+ }
+ className="text-muted-foreground"
+ nativeButton={false}
+ >
+ Learn more{" "}
+
+
+
+
+
+ )
+}
+
+function EmptyWithBorder() {
+ return (
+
+
+
+ 404 - Not Found
+
+ The page you're looking for doesn't exist. Try searching
+ for what you need below.
+
+
+
+
+
+
+
+
+
+ /
+
+
+
+ Need help? Contact support
+
+
+
+
+ )
+}
+
+function EmptyWithIcon() {
+ return (
+
+
+
+
+
+
+ Nothing to see here
+
+ No posts have been created yet. Get started by{" "}
+ creating your first post .
+
+
+
+
+
+ New Post
+
+
+
+
+ )
+}
+
+function EmptyWithMutedBackgroundAlt() {
+ return (
+
+
+
+ 404 - Not Found
+
+ The page you're looking for doesn't exist. Try searching
+ for what you need below.
+
+
+
+
+
+
+
+
+
+ /
+
+
+
+ Need help? Contact support
+
+
+
+
+ )
+}
+
+function EmptyInCard() {
+ return (
+
+
+
+
+
+
+ No projects yet
+
+ You haven't created any projects yet. Get started by creating
+ your first project.
+
+
+
+
+ } nativeButton={false}>
+ Create project
+
+ Import project
+
+ }
+ className="text-muted-foreground"
+ nativeButton={false}
+ >
+ Learn more{" "}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/field-example.tsx b/apps/ui/src/registry/bases/base/examples/field-example.tsx
new file mode 100644
index 0000000..43a5a03
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/field-example.tsx
@@ -0,0 +1,1018 @@
+
+import { useState } from "react"
+import { REGEXP_ONLY_DIGITS } from "input-otp"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Badge } from "@/registry/bases/base/ui/badge"
+import { Checkbox } from "@/registry/bases/base/ui/checkbox"
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldLegend,
+ FieldSet,
+ FieldTitle,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ InputOTP,
+ InputOTPGroup,
+ InputOTPSeparator,
+ InputOTPSlot,
+} from "@/registry/bases/base/ui/input-otp"
+import {
+ NativeSelect,
+ NativeSelectOptGroup,
+ NativeSelectOption,
+} from "@/registry/bases/base/ui/native-select"
+import {
+ RadioGroup,
+ RadioGroupItem,
+} from "@/registry/bases/base/ui/radio-group"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import { Slider } from "@/registry/bases/base/ui/slider"
+import { Switch } from "@/registry/bases/base/ui/switch"
+import { Textarea } from "@/registry/bases/base/ui/textarea"
+
+export default function FieldExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputFields() {
+ return (
+
+
+
+ Basic Input
+
+
+
+
+ Input with Description
+
+
+
+ Choose a unique username for your account.
+
+
+
+ Email Address
+
+ We'll never share your email with anyone.
+
+
+
+
+
+ Required Field *
+
+
+ This field must be filled out.
+
+
+ Disabled Input
+
+ This field is currently disabled.
+
+
+
+ Input with Badge{" "}
+
+ Recommended
+
+
+
+
+
+ Invalid Input
+
+
+ This field contains validation errors.
+
+
+
+ Disabled Field
+
+ This field is currently disabled.
+
+
+
+ )
+}
+
+function TextareaFields() {
+ return (
+
+
+
+ Basic Textarea
+
+
+
+ Comments
+
+ Maximum 500 characters allowed.
+
+
+ Bio
+
+ Tell us about yourself in a few sentences.
+
+
+
+
+ Message
+
+
+ Enter your message so it is long enough to test the layout.
+
+
+
+ Invalid Textarea
+
+
+ This field contains validation errors.
+
+
+
+
+ Disabled Field
+
+
+ This field is currently disabled.
+
+
+
+ )
+}
+
+function SelectFields() {
+ const basicItems = [
+ { label: "Choose an option", value: null },
+ { label: "Option 1", value: "option1" },
+ { label: "Option 2", value: "option2" },
+ { label: "Option 3", value: "option3" },
+ ]
+ const countryItems = [
+ { label: "Select your country", value: null },
+ { label: "United States", value: "us" },
+ { label: "United Kingdom", value: "uk" },
+ { label: "Canada", value: "ca" },
+ ]
+ const timezoneItems = [
+ { label: "Select timezone", value: null },
+ { label: "UTC", value: "utc" },
+ { label: "Eastern Time", value: "est" },
+ { label: "Pacific Time", value: "pst" },
+ ]
+ const invalidItems = [
+ { label: "This field has an error", value: null },
+ { label: "Option 1", value: "option1" },
+ { label: "Option 2", value: "option2" },
+ { label: "Option 3", value: "option3" },
+ ]
+ const disabledItems = [
+ { label: "Cannot select", value: null },
+ { label: "Option 1", value: "option1" },
+ { label: "Option 2", value: "option2" },
+ { label: "Option 3", value: "option3" },
+ ]
+
+ return (
+
+
+
+ Basic Select
+
+
+
+
+
+
+ {basicItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+ Country
+
+
+
+
+
+
+ {countryItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ Select the country where you currently reside.
+
+
+
+ Timezone
+
+ Choose your local timezone for accurate scheduling.
+
+
+
+
+
+
+
+ {timezoneItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+ Invalid Select
+
+
+
+
+
+
+ {invalidItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ This field contains validation errors.
+
+
+
+
+ Disabled Field
+
+
+
+
+
+
+
+ {disabledItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+ This field is currently disabled.
+
+
+
+ )
+}
+
+function NativeSelectFields() {
+ return (
+
+
+
+
+ Basic Native Select
+
+
+ Choose an option
+ Option 1
+ Option 2
+ Option 3
+
+
+
+ Country
+
+
+ Select your country
+
+ United States
+ United Kingdom
+ Canada
+
+
+ Select the country where you currently reside.
+
+
+
+ Timezone
+
+ Choose your local timezone for accurate scheduling.
+
+
+ Select timezone
+ UTC
+ Eastern Time
+ Pacific Time
+
+
+
+
+ Grouped Options
+
+
+ Select a region
+
+ United States
+ Canada
+ Mexico
+
+
+ United Kingdom
+ France
+ Germany
+
+
+
+ Native select with grouped options using optgroup.
+
+
+
+
+ Invalid Native Select
+
+
+
+ This field has an error
+
+ Option 1
+ Option 2
+ Option 3
+
+
+ This field contains validation errors.
+
+
+
+
+ Disabled Field
+
+
+ Cannot select
+ Option 1
+ Option 2
+ Option 3
+
+ This field is currently disabled.
+
+
+
+ )
+}
+
+function CheckboxFields() {
+ return (
+
+
+
+
+
+ I agree to the terms and conditions
+
+
+
+
+ Accept terms and conditions
+
+
+
+
+
+
+
+ Subscribe to newsletter
+
+
+ Receive weekly updates about new features and promotions.
+
+
+
+
+
+
+
+ Enable Touch ID
+
+ Enable Touch ID to quickly unlock your device.
+
+
+
+
+
+ Preferences
+
+ Select all that apply to customize your experience.
+
+
+
+
+ Dark mode
+
+
+
+ Compact view
+
+
+
+
+ Enable notifications
+
+
+
+
+
+
+ Invalid checkbox
+
+
+
+
+ Disabled checkbox
+
+
+
+
+ )
+}
+
+function RadioFields() {
+ return (
+
+
+
+ Subscription Plan
+
+
+
+ Free Plan
+
+
+
+ Pro Plan
+
+
+
+ Enterprise
+
+
+
+
+ Battery Level
+
+ Choose your preferred battery level.
+
+
+
+
+ High
+
+
+
+ Medium
+
+
+
+ Low
+
+
+
+
+
+
+
+ Enable Touch ID
+
+ Enable Touch ID to quickly unlock your device.
+
+
+
+
+
+
+
+
+
+ Enable Touch ID
+
+ Enable Touch ID to quickly unlock your device.
+
+
+
+
+
+
+ Invalid Radio Group
+
+
+
+
+ Invalid Option 1
+
+
+
+
+
+ Invalid Option 2
+
+
+
+
+
+ Disabled Radio Group
+
+
+
+
+ Disabled Option 1
+
+
+
+
+
+ Disabled Option 2
+
+
+
+
+
+
+ )
+}
+
+function SwitchFields() {
+ return (
+
+
+
+
+ Airplane Mode
+
+ Turn on airplane mode to disable all connections.
+
+
+
+
+
+ Dark Mode
+
+
+
+
+
+ Marketing Emails
+
+ Receive emails about new products, features, and more.
+
+
+
+
+ Privacy Settings
+ Manage your privacy preferences.
+
+
+
+ Make profile visible to others
+
+
+
+
+
+ Show email on profile
+
+
+
+
+
+ Invalid Switch
+
+ This switch has validation errors.
+
+
+
+
+
+
+
+ Disabled Switch
+
+
+ This switch is currently disabled.
+
+
+
+
+
+
+ )
+}
+
+function SliderFields() {
+ const [volume, setVolume] = useState([50])
+ const [brightness, setBrightness] = useState([75])
+ const [temperature, setTemperature] = useState([0.3, 0.7])
+ const [priceRange, setPriceRange] = useState([25, 75])
+ const [colorBalance, setColorBalance] = useState([10, 20, 70])
+
+ return (
+
+
+
+ Volume
+ setVolume(value as number[])}
+ max={100}
+ step={1}
+ />
+
+
+ Screen Brightness
+ setBrightness(value as number[])}
+ max={100}
+ step={5}
+ />
+
+ Current brightness: {brightness[0]}%
+
+
+
+ Video Quality
+
+ Higher quality uses more bandwidth.
+
+
+
+
+
+ Temperature Range
+
+ setTemperature(value as number[])}
+ min={0}
+ max={1}
+ step={0.1}
+ />
+
+ Range: {temperature[0].toFixed(1)} - {temperature[1].toFixed(1)}
+
+
+
+ Price Range
+ setPriceRange(value as number[])}
+ max={100}
+ step={5}
+ />
+
+ ${priceRange[0]} - ${priceRange[1]}
+
+
+
+ Color Balance
+ setColorBalance(value as number[])}
+ max={100}
+ step={10}
+ />
+
+ Red: {colorBalance[0]}%, Green: {colorBalance[1]}%, Blue:{" "}
+ {colorBalance[2]}%
+
+
+
+ Invalid Slider
+
+
+ This slider has validation errors.
+
+
+
+
+ Disabled Slider
+
+
+
+ This slider is currently disabled.
+
+
+
+
+ )
+}
+
+function InputOTPFields() {
+ const [value, setValue] = useState("")
+ const [pinValue, setPinValue] = useState("")
+
+ return (
+
+
+
+ Verification Code
+
+
+
+
+
+
+
+
+
+
+
+
+ Enter OTP
+
+
+
+
+
+
+
+
+
+
+
+ Enter the 6-digit code sent to your email.
+
+
+
+
+ Two-Factor Authentication
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enter the code from your authenticator app.
+
+
+
+ PIN Code
+
+
+
+
+
+
+
+
+
+ Enter your 4-digit PIN (numbers only).
+
+
+
+ Invalid OTP
+
+
+
+
+
+
+
+
+
+
+
+ This OTP field contains validation errors.
+
+
+
+ Disabled OTP
+
+
+
+
+
+
+
+
+
+
+
+ This OTP field is currently disabled.
+
+
+
+
+ )
+}
+
+function HorizontalFields() {
+ const basicItems = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Orange", value: "orange" },
+ ]
+
+ return (
+
+
+
+
+ Username
+ Enter your preferred username.
+
+
+
+
+
+ Bio
+
+ Write a short description about yourself.
+
+
+
+
+
+
+
+ Email Notifications
+
+
+ Receive email updates about your account.
+
+
+
+
+
+
+ Favorite Fruit
+ Choose your favorite fruit.
+
+
+
+
+
+
+
+ {basicItems.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+ Country
+ Select your country.
+
+
+ Select a country
+ United States
+ United Kingdom
+ Canada
+
+
+
+
+ Volume
+ Adjust the volume level.
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/hover-card-example.tsx b/apps/ui/src/registry/bases/base/examples/hover-card-example.tsx
new file mode 100644
index 0000000..5f037de
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/hover-card-example.tsx
@@ -0,0 +1,103 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@/registry/bases/base/ui/hover-card"
+
+export default function HoverCardExample() {
+ return (
+
+
+
+
+ )
+}
+
+const HOVER_CARD_SIDES = [
+ "inline-start",
+ "left",
+ "top",
+ "bottom",
+ "right",
+ "inline-end",
+] as const
+
+function HoverCardSides() {
+ return (
+
+
+ {HOVER_CARD_SIDES.map((side) => (
+
+ }
+ >
+ {side.replace("-", " ")}
+
+
+
+
Hover Card
+
+ This hover card appears on the {side.replace("-", " ")} side
+ of the trigger.
+
+
+
+
+ ))}
+
+
+ )
+}
+
+function HoverCardInDialog() {
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Hover Card Example
+
+ Hover over the button below to see the hover card.
+
+
+
+ }
+ >
+ Hover me
+
+
+
+
Hover Card
+
+ This hover card appears inside a dialog. Hover over the button
+ to see it.
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/input-example.tsx b/apps/ui/src/registry/bases/base/examples/input-example.tsx
new file mode 100644
index 0000000..8bc098c
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/input-example.tsx
@@ -0,0 +1,268 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ NativeSelect,
+ NativeSelectOption,
+} from "@/registry/bases/base/ui/native-select"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+
+export default function InputExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputBasic() {
+ return (
+
+
+
+ )
+}
+
+function InputInvalid() {
+ return (
+
+
+
+ )
+}
+
+function InputWithLabel() {
+ return (
+
+
+ Email
+
+
+
+ )
+}
+
+function InputWithDescription() {
+ return (
+
+
+ Username
+
+
+ Choose a unique username for your account.
+
+
+
+ )
+}
+
+function InputDisabled() {
+ return (
+
+
+ Email
+
+
+
+ )
+}
+
+function InputTypes() {
+ return (
+
+
+
+ Password
+
+
+
+ Phone
+
+
+
+ URL
+
+
+
+ Search
+
+
+
+ Number
+
+
+
+ Date
+
+
+
+ Time
+
+
+
+ File
+
+
+
+
+ )
+}
+
+function InputWithSelect() {
+ return (
+
+
+
+
+
+
+
+
+
+ USD
+ EUR
+ GBP
+
+
+
+
+
+ )
+}
+
+function InputWithButton() {
+ return (
+
+
+
+ Search
+
+
+ )
+}
+
+function InputWithNativeSelect() {
+ return (
+
+
+
+
+ +1
+ +44
+ +46
+
+
+
+ )
+}
+
+function InputForm() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/input-group-example.tsx b/apps/ui/src/registry/bases/base/examples/input-group-example.tsx
new file mode 100644
index 0000000..4ff0f6d
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/input-group-example.tsx
@@ -0,0 +1,910 @@
+
+import { useState } from "react"
+import { toast } from "sonner"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ ButtonGroup,
+ ButtonGroupText,
+} from "@/registry/bases/base/ui/button-group"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+ InputGroupText,
+ InputGroupTextarea,
+} from "@/registry/bases/base/ui/input-group"
+import { Kbd, KbdGroup } from "@/registry/bases/base/ui/kbd"
+import {
+ Popover,
+ PopoverContent,
+ PopoverDescription,
+ PopoverHeader,
+ PopoverTitle,
+ PopoverTrigger,
+} from "@/registry/bases/base/ui/popover"
+import { Spinner } from "@/registry/bases/base/ui/spinner"
+import { Textarea } from "@/registry/bases/base/ui/textarea"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/bases/base/ui/tooltip"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function InputGroupExample() {
+ const [country, setCountry] = useState("+1")
+
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputGroupBasic() {
+ return (
+
+
+
+
+ Default (No Input Group)
+
+
+
+
+ Input Group
+
+
+
+
+
+ Disabled
+
+
+
+
+
+ Invalid
+
+
+
+
+
+
+ )
+}
+
+function InputGroupWithAddons() {
+ return (
+
+
+
+
+ Addon (inline-start)
+
+
+
+
+
+
+
+
+
+
+ Addon (inline-end)
+
+
+
+
+
+
+
+
+
+
+ Addon (inline-start and inline-end)
+
+
+
+
+
+
+
+
+
+
+
+
+ Addon (block-start)
+
+
+
+ First Name
+
+
+
+
+
+ Addon (block-end)
+
+
+
+ 20/240 characters
+
+
+
+
+
+ Multiple Icons
+
+
+
+
+ toast("Copied to clipboard")}
+ >
+
+
+
+
+
+
+
+
+
+ Description
+
+
+
+
+
+
+
+ This is a description of the input group.
+
+
+
+ Label
+
+
+ Label
+
+
+
+
+
+
+ (optional)
+
+
+
+
+
+ )
+}
+
+function InputGroupWithButtons() {
+ return (
+
+
+
+ Button
+
+
+
+ Default
+
+
+
+
+
+ Outline
+
+
+
+
+
+ Secondary
+
+
+
+
+
+ Button
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputGroupWithTooltip({
+ country,
+ setCountry,
+}: {
+ country: string
+ setCountry: (value: string) => void
+}) {
+ return (
+
+
+
+ Tooltip
+
+
+
+
+
+ }
+ >
+
+
+ This is content in a tooltip.
+
+
+
+
+ This is a description of the input group.
+
+
+
+ Dropdown
+
+
+
+
+
+ }
+ >
+ {country}{" "}
+
+
+
+ setCountry("+1")}>
+ +1
+
+ setCountry("+44")}>
+ +44
+
+ setCountry("+46")}>
+ +46
+
+
+
+
+
+
+ This is a description of the input group.
+
+
+
+ Popover
+
+
+ } nativeButton={false}>
+
+
+
+
+
+
+ Your connection is not secure.
+
+ You should not enter any sensitive information on this site.
+
+
+
+
+
+ https://
+
+
+
+ toast("Added to favorites")}
+ >
+
+
+
+
+
+ This is a description of the input group.
+
+
+
+ Button Group
+
+ https://
+
+
+
+
+
+
+ .com
+
+
+ This is a description of the input group.
+
+
+
+
+ )
+}
+
+function InputGroupWithKbd() {
+ return (
+
+
+
+ Input Group with Kbd
+
+
+
+ ⌘K
+
+
+
+
+
+ ⌘K
+
+
+
+
+ Ask AI
+
+ Tab
+
+
+
+
+
+
+
+
+
+ Ctrl
+ C
+
+
+
+
+
+ Username
+
+
+
+
+
+
+
+
+
+ This username is available.
+
+
+
+
+
+
+
+ 12 results
+
+
+
+
+
+
+ Disabled
+
+
+
+ First Name
+
+
+
+
+
+
+
+
+ Last Name
+
+
+
+
+
+
+
+
+
+
+ Loading ("data-disabled="true")
+
+
+
+
+
+
+
+
+ This is a description of the input group.
+
+
+
+
+ )
+}
+
+function InputGroupInCard() {
+ return (
+
+
+
+ Card with Input Group
+ This is a card with an input group.
+
+
+
+
+ Email Address
+
+
+
+
+
+
+
+
+ Website URL
+
+
+ https://
+
+
+
+
+
+
+
+
+
+ Feedback & Comments
+
+
+
+
+ 0/500 characters
+
+
+
+
+
+
+ Cancel
+ Submit
+
+
+
+ )
+}
+
+function InputGroupTextareaExamples() {
+ return (
+
+
+
+
+ Default Textarea (No Input Group)
+
+
+
+
+
+ Input Group
+
+
+
+
+
+ This is a description of the input group.
+
+
+
+ Invalid
+
+
+
+
+ This is a description of the input group.
+
+
+
+ Disabled
+
+
+
+
+ This is a description of the input group.
+
+
+
+ Addon (block-start)
+
+
+
+ Ask, Search or Chat...
+
+
+
+
+ This is a description of the input group.
+
+
+
+
+ Addon (block-end)
+
+
+
+
+ 0/280 characters
+
+
+ Send
+
+
+
+
+
+ Addon (Buttons)
+
+
+
+
+ Cancel
+
+
+ Post Comment
+
+
+
+
+
+ Code Editor
+
+
+
+
+
+ script.js
+
+
+
+
+
+
+
+
+
+ Line 1, Column 1
+ JavaScript
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/input-otp-example.tsx b/apps/ui/src/registry/bases/base/examples/input-otp-example.tsx
new file mode 100644
index 0000000..6638b4b
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/input-otp-example.tsx
@@ -0,0 +1,291 @@
+
+import * as React from "react"
+import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldError,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import {
+ InputOTP,
+ InputOTPGroup,
+ InputOTPSeparator,
+ InputOTPSlot,
+} from "@/registry/bases/base/ui/input-otp"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function InputOTPExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPSimple() {
+ return (
+
+
+ Simple
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPPattern() {
+ return (
+
+
+ Digits Only
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPWithSeparator() {
+ const [value, setValue] = React.useState("123456")
+
+ return (
+
+
+ With Separator
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPAlphanumeric() {
+ return (
+
+
+ Alphanumeric
+ Accepts both letters and numbers.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPDisabled() {
+ return (
+
+
+ Disabled
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPFourDigits() {
+ return (
+
+
+ 4 Digits
+ Common pattern for PIN codes.
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPInvalid() {
+ const [value, setValue] = React.useState("000000")
+
+ return (
+
+
+ Invalid State
+
+ Example showing the invalid error state.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function InputOTPForm() {
+ return (
+
+
+
+ Verify your login
+
+ Enter the verification code we sent to your email address:{" "}
+ m@example.com .
+
+
+
+
+
+
+
+ Verify
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/item-example.tsx b/apps/ui/src/registry/bases/base/examples/item-example.tsx
new file mode 100644
index 0000000..8fe2d7b
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/item-example.tsx
@@ -0,0 +1,2027 @@
+import Image from "next/image"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemFooter,
+ ItemGroup,
+ ItemHeader,
+ ItemMedia,
+ ItemSeparator,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ItemExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function DefaultVariantItems() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+ Action
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function OutlineVariantItems() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+ Action
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function MutedVariantItems() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+ Action
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function DefaultVariantItemsSmall() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+
+ Action
+
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+
+ Action
+
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function OutlineVariantItemsSmall() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+
+ Action
+
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+
+ Action
+
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function MutedVariantItemsSmall() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+
+ Action
+
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+
+ Action
+
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function DefaultVariantItemsExtraSmall() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+
+ Action
+
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+
+ Action
+
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function OutlineVariantItemsExtraSmall() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+
+ Action
+
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+
+ Action
+
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function MutedVariantItemsExtraSmall() {
+ return (
+
+ -
+
+ Title Only
+
+
+ -
+
+ Title + Button
+
+
+
+ Action
+
+
+
+ -
+
+ Title + Description
+
+ This is a description that provides additional context.
+
+
+
+ -
+
+ Title + Description + Button
+
+ This item includes a title, description, and action button.
+
+
+
+
+ Action
+
+
+
+ -
+
+
+
+
+ Media + Title
+
+
+ -
+
+
+
+
+ Media + Title + Button
+
+
+ Action
+
+
+ -
+
+
+
+
+ Media + Title + Description
+
+ This item includes media, title, and description.
+
+
+
+ -
+
+
+
+
+ Media + Title + Description + Button
+
+ Complete item with all components: media, title, description, and
+ button.
+
+
+
+ Action
+
+
+ -
+
+ Multiple Actions
+
+ Item with multiple action buttons in the actions area.
+
+
+
+
+ Cancel
+
+ Confirm
+
+
+
+ )
+}
+
+function DefaultLinkItems() {
+ return (
+
+
+ }>
+
+ Title Only (Link)
+
+
+ }>
+
+ Title + Description (Link)
+
+ Clickable item with title and description.
+
+
+
+ }>
+
+
+
+
+ Media + Title (Link)
+
+
+ }>
+
+
+
+
+ Media + Title + Description (Link)
+
+ Complete link item with media, title, and description.
+
+
+
+ }>
+
+ With Actions (Link)
+
+ Link item that also has action buttons.
+
+
+
+
+ Share
+
+
+
+
+
+ )
+}
+
+function OutlineLinkItems() {
+ return (
+
+
+ }>
+
+ Title Only (Link)
+
+
+ }>
+
+ Title + Description (Link)
+
+ Clickable item with title and description.
+
+
+
+ }>
+
+
+
+
+ Media + Title (Link)
+
+
+ }>
+
+
+
+
+ Media + Title + Description (Link)
+
+ Complete link item with media, title, and description.
+
+
+
+ }>
+
+ With Actions (Link)
+
+ Link item that also has action buttons.
+
+
+
+
+ Share
+
+
+
+
+
+ )
+}
+
+function MutedLinkItems() {
+ return (
+
+
+ }>
+
+ Title Only (Link)
+
+
+ }>
+
+ Title + Description (Link)
+
+ Clickable item with title and description.
+
+
+
+ }>
+
+
+
+
+ Media + Title (Link)
+
+
+ }>
+
+
+
+
+ Media + Title + Description (Link)
+
+ Complete link item with media, title, and description.
+
+
+
+ }>
+
+ With Actions (Link)
+
+ Link item that also has action buttons.
+
+
+
+
+ Share
+
+
+
+
+
+ )
+}
+
+function DefaultItemGroup() {
+ return (
+
+
+ -
+
+ Item 1
+ First item in the group.
+
+
+ -
+
+ Item 2
+ Second item in the group.
+
+
+ -
+
+ Item 3
+ Third item in the group.
+
+
+
+
+ )
+}
+
+function OutlineItemGroup() {
+ return (
+
+
+ -
+
+
+
+
+ Item 1
+ First item with icon.
+
+
+ -
+
+
+
+
+ Item 2
+ Second item with icon.
+
+
+ -
+
+
+
+
+ Item 3
+ Third item with icon.
+
+
+
+
+ )
+}
+
+function MutedItemGroup() {
+ return (
+
+
+ -
+
+ Item 1
+ First item in muted group.
+
+
+
+ Action
+
+
+
+ -
+
+ Item 2
+ Second item in muted group.
+
+
+
+ Action
+
+
+
+ -
+
+ Item 3
+ Third item in muted group.
+
+
+
+ Action
+
+
+
+
+
+ )
+}
+
+function ItemSeparatorExample() {
+ return (
+
+
+ -
+
+
+
+
+ Inbox
+ View all incoming messages.
+
+
+
+ -
+
+
+
+
+ Sent
+ View all sent messages.
+
+
+
+ -
+
+
+
+
+ Drafts
+ View all draft messages.
+
+
+
+ -
+
+
+
+
+ Archive
+ View archived messages.
+
+
+
+
+ )
+}
+
+function ItemHeaderExamples() {
+ return (
+
+ -
+
+ Design System
+
+
+ Component Library
+
+ A comprehensive collection of reusable UI components for building
+ consistent interfaces.
+
+
+
+ -
+
+ Marketing
+
+
+ Campaign Analytics
+
+ Track performance metrics and engagement rates across all marketing
+ channels.
+
+
+
+ -
+
+ Engineering
+
+
+ API Documentation
+
+ Complete reference guide for all available endpoints and
+ authentication methods.
+
+
+
+
+ )
+}
+
+function ItemFooterExamples() {
+ return (
+
+ -
+
+ Quarterly Report Q4 2024
+
+ Financial overview including revenue, expenses, and growth metrics
+ for the fourth quarter.
+
+
+
+
+ Last updated 2 hours ago
+
+
+
+ -
+
+ User Research Findings
+
+ Insights from interviews and surveys conducted with 50+ users across
+ different demographics.
+
+
+
+
+ Created by Sarah Chen
+
+
+
+ -
+
+ Product Roadmap
+
+ Planned features and improvements scheduled for the next three
+ months.
+
+
+
+ 12 comments
+
+
+
+ )
+}
+
+function ItemHeaderAndFooterExamples() {
+ return (
+
+ -
+
+ Team Project
+
+
+ Website Redesign
+
+ Complete overhaul of the company website with modern design
+ principles and improved user experience.
+
+
+
+
+ Updated 5 minutes ago
+
+
+
+ -
+
+ Client Work
+
+
+ Mobile App Development
+
+ Building a cross-platform mobile application for iOS and Android
+ with React Native.
+
+
+
+
+ Status: In Progress
+
+
+
+ -
+
+ Documentation
+
+
+ API Integration Guide
+
+ Step-by-step instructions for integrating third-party APIs with
+ authentication and error handling.
+
+
+
+
+ Category: Technical • 3 attachments
+
+
+
+
+ )
+}
+
+function DefaultVariantItemsWithImage() {
+ return (
+
+ -
+
+
+
+
+ Project Dashboard
+
+ Overview of project settings and configuration.
+
+
+
+ -
+
+
+
+
+ Document
+ A document with metadata displayed.
+
+
+
+ View
+
+
+
+ -
+
+
+
+
+ File Attachment
+
+ Complete file with image, title, description, and action button.
+
+
+
+ Download
+
+
+
+ )
+}
+
+function OutlineVariantItemsWithImage() {
+ return (
+
+ -
+
+
+
+
+ Project Dashboard
+
+ Overview of project settings and configuration.
+
+
+
+ -
+
+
+
+
+ Document
+ A document with metadata displayed.
+
+
+
+ View
+
+
+
+ -
+
+
+
+
+ File Attachment
+
+ Complete file with image, title, description, and action button.
+
+
+
+ Download
+
+
+
+ )
+}
+
+function OutlineVariantItemsWithImageSmall() {
+ return (
+
+ -
+
+
+
+
+ Project Dashboard
+
+ Overview of project settings and configuration.
+
+
+
+ -
+
+
+
+
+ Document
+ A document with metadata displayed.
+
+
+
+ View
+
+
+
+ -
+
+
+
+
+ File Attachment
+
+ Complete file with image, title, description, and action button.
+
+
+
+ Download
+
+
+
+ )
+}
+
+function OutlineVariantItemsWithImageExtraSmall() {
+ return (
+
+ -
+
+
+
+
+ Project Dashboard
+
+
+ -
+
+
+
+
+ Document
+
+
+
+ View
+
+
+
+ -
+
+
+
+
+ File Attachment
+
+
+ Download
+
+
+
+ )
+}
+
+function MutedVariantItemsWithImage() {
+ return (
+
+ -
+
+
+
+
+ Project Dashboard
+
+ Overview of project settings and configuration.
+
+
+
+ -
+
+
+
+
+ Document
+ A document with metadata displayed.
+
+
+
+ View
+
+
+
+ -
+
+
+
+
+ File Attachment
+
+ Complete file with image, title, description, and action button.
+
+
+
+ Download
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/kbd-example.tsx b/apps/ui/src/registry/bases/base/examples/kbd-example.tsx
new file mode 100644
index 0000000..e831ea1
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/kbd-example.tsx
@@ -0,0 +1,192 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/bases/base/ui/input-group"
+import { Kbd, KbdGroup } from "@/registry/bases/base/ui/kbd"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/bases/base/ui/tooltip"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function KbdExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function KbdBasic() {
+ return (
+
+
+ Ctrl
+ ⌘K
+ Ctrl + B
+
+
+ )
+}
+
+function KbdModifierKeys() {
+ return (
+
+
+ ⌘
+ C
+
+
+ )
+}
+
+function KbdGroupExample() {
+ return (
+
+
+ Ctrl
+ Shift
+ P
+
+
+ )
+}
+
+function KbdArrowKeys() {
+ return (
+
+
+ ↑
+ ↓
+ ←
+ →
+
+
+ )
+}
+
+function KbdWithIcons() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function KbdWithIconsAndText() {
+ return (
+
+
+
+
+ Left
+
+
+
+ Voice Enabled
+
+
+
+ )
+}
+
+function KbdInInputGroup() {
+ return (
+
+
+
+
+ Space
+
+
+
+ )
+}
+
+function KbdInTooltip() {
+ return (
+
+
+ }>
+
+
+
+
+ Save Changes S
+
+
+
+
+ )
+}
+
+function KbdWithSamp() {
+ return (
+
+
+ File
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/label-example.tsx b/apps/ui/src/registry/bases/base/examples/label-example.tsx
new file mode 100644
index 0000000..18fc4b8
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/label-example.tsx
@@ -0,0 +1,64 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Checkbox } from "@/registry/bases/base/ui/checkbox"
+import { Field } from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import { Label } from "@/registry/bases/base/ui/label"
+import { Textarea } from "@/registry/bases/base/ui/textarea"
+
+export default function LabelExample() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function LabelWithCheckbox() {
+ return (
+
+
+
+ Accept terms and conditions
+
+
+ )
+}
+
+function LabelWithInput() {
+ return (
+
+
+ Username
+
+
+
+ )
+}
+
+function LabelDisabled() {
+ return (
+
+
+ Disabled
+
+
+
+ )
+}
+
+function LabelWithTextarea() {
+ return (
+
+
+ Message
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/menubar-example.tsx b/apps/ui/src/registry/bases/base/examples/menubar-example.tsx
new file mode 100644
index 0000000..14f478e
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/menubar-example.tsx
@@ -0,0 +1,773 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import {
+ Menubar,
+ MenubarCheckboxItem,
+ MenubarContent,
+ MenubarGroup,
+ MenubarItem,
+ MenubarLabel,
+ MenubarMenu,
+ MenubarRadioGroup,
+ MenubarRadioItem,
+ MenubarSeparator,
+ MenubarShortcut,
+ MenubarSub,
+ MenubarSubContent,
+ MenubarSubTrigger,
+ MenubarTrigger,
+} from "@/registry/bases/base/ui/menubar"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function MenubarExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function MenubarBasic() {
+ return (
+
+
+
+ File
+
+
+ New Tab ⌘T
+
+
+ New Window ⌘N
+
+ New Incognito Window
+
+
+ Print... ⌘P
+
+
+
+
+ Edit
+
+
+ Undo ⌘Z
+
+
+ Redo ⇧⌘Z
+
+
+ Cut
+ Copy
+ Paste
+
+
+
+
+ )
+}
+
+function MenubarSides() {
+ return (
+
+
+ {(
+ [
+ "inline-start",
+ "left",
+ "top",
+ "bottom",
+ "right",
+ "inline-end",
+ ] as const
+ ).map((side) => (
+
+
+
+ {side.replace("-", " ")}
+
+
+
+ New Tab
+ New Window
+ New Incognito Window
+
+
+
+
+ ))}
+
+
+ )
+}
+
+function MenubarWithSubmenu() {
+ return (
+
+
+
+ File
+
+
+ Share
+
+ Email link
+ Messages
+ Notes
+
+
+
+
+ Print... ⌘P
+
+
+
+
+ Edit
+
+
+ Undo ⌘Z
+
+
+ Redo ⇧⌘Z
+
+
+
+ Find
+
+ Find...
+ Find Next
+ Find Previous
+
+
+
+ Cut
+ Copy
+ Paste
+
+
+
+
+ )
+}
+
+function MenubarWithCheckboxes() {
+ return (
+
+
+
+ View
+
+ Always Show Bookmarks Bar
+
+ Always Show Full URLs
+
+
+
+ Reload ⌘R
+
+
+ Force Reload ⇧⌘R
+
+
+
+
+ Format
+
+ Strikethrough
+ Code
+ Superscript
+
+
+
+
+ )
+}
+
+function MenubarWithRadio() {
+ const [user, setUser] = React.useState("benoit")
+ const [theme, setTheme] = React.useState("system")
+
+ return (
+
+
+
+ Profiles
+
+
+ Andy
+ Benoit
+ Luis
+
+
+ Edit...
+ Add Profile...
+
+
+
+ Theme
+
+
+ Light
+ Dark
+ System
+
+
+
+
+
+ )
+}
+
+function MenubarWithIcons() {
+ return (
+
+
+
+ File
+
+
+
+ New File ⌘N
+
+
+
+ Open Folder
+
+
+
+
+ Save ⌘S
+
+
+
+
+ More
+
+
+
+
+ Settings
+
+
+
+ Help
+
+
+
+
+ Delete
+
+
+
+
+
+
+ )
+}
+
+function MenubarWithShortcuts() {
+ return (
+
+
+
+ File
+
+
+ New Tab ⌘T
+
+
+ New Window ⌘N
+
+
+ Print... ⌘P
+
+
+
+
+ Edit
+
+
+ Undo ⌘Z
+
+
+ Redo ⇧⌘Z
+
+
+
+ Cut ⌘X
+
+
+ Copy ⌘C
+
+
+ Paste ⌘V
+
+
+
+
+
+ )
+}
+
+function MenubarFormat() {
+ return (
+
+
+
+ Format
+
+
+
+ Bold ⌘B
+
+
+
+ Italic ⌘I
+
+
+
+ Underline ⌘U
+
+
+ Strikethrough
+ Code
+
+
+
+ View
+
+ Show Ruler
+ Show Grid
+
+ Zoom In
+ Zoom Out
+
+
+
+
+ )
+}
+
+function MenubarInsert() {
+ return (
+
+
+
+ Insert
+
+
+
+
+ Media
+
+
+ Image
+ Video
+ Audio
+
+
+
+
+
+ Link ⌘K
+
+
+
+ Table
+
+
+
+
+ Tools
+
+
+
+ Find & Replace ⌘F
+
+
+
+ Spell Check
+
+
+
+
+
+ )
+}
+
+function MenubarDestructive() {
+ return (
+
+
+
+ File
+
+
+
+ New File ⌘N
+
+
+
+ Open Folder
+
+
+
+
+ Delete File ⌘⌫
+
+
+
+
+ Account
+
+
+
+ Profile
+
+
+
+ Settings
+
+
+
+
+ Sign out
+
+
+
+
+ Delete
+
+
+
+
+
+ )
+}
+
+function MenubarInDialog() {
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Menubar Example
+
+ Use the menubar below to see the menu options.
+
+
+
+
+ File
+
+
+
+ Copy
+
+
+
+ Cut
+
+
+
+ Paste
+
+
+
+ More Options
+
+ Save Page...
+ Create Shortcut...
+ Name Window...
+
+ Developer Tools
+
+
+
+
+
+ Delete
+
+
+
+
+ Edit
+
+
+ Undo ⌘Z
+
+
+ Redo ⇧⌘Z
+
+
+
+
+
+
+
+ )
+}
+
+function MenubarWithInset() {
+ const [showBookmarks, setShowBookmarks] = React.useState(true)
+ const [showUrls, setShowUrls] = React.useState(false)
+ const [theme, setTheme] = React.useState("system")
+
+ return (
+
+
+
+ View
+
+
+ Actions
+
+
+ Copy
+
+
+
+ Cut
+
+ Paste
+
+
+
+ Appearance
+
+ Bookmarks
+
+
+ Full URLs
+
+
+
+
+ Theme
+
+
+ Light
+
+
+ Dark
+
+
+ System
+
+
+
+
+
+ More Options
+
+
+ Save Page...
+ Create Shortcut...
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/native-select-example.tsx b/apps/ui/src/registry/bases/base/examples/native-select-example.tsx
new file mode 100644
index 0000000..fc625f0
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/native-select-example.tsx
@@ -0,0 +1,129 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Field,
+ FieldDescription,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import {
+ NativeSelect,
+ NativeSelectOptGroup,
+ NativeSelectOption,
+} from "@/registry/bases/base/ui/native-select"
+
+export default function NativeSelectExample() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function NativeSelectBasic() {
+ return (
+
+
+ Select a fruit
+ Apple
+ Banana
+ Blueberry
+
+ Grapes
+
+ Pineapple
+
+
+ )
+}
+
+function NativeSelectWithGroups() {
+ return (
+
+
+ Select a food
+
+ Apple
+ Banana
+ Blueberry
+
+
+ Carrot
+ Broccoli
+ Spinach
+
+
+
+ )
+}
+
+function NativeSelectSizes() {
+ return (
+
+
+
+ Select a fruit
+ Apple
+ Banana
+ Blueberry
+
+
+ Select a fruit
+ Apple
+ Banana
+ Blueberry
+
+
+
+ )
+}
+
+function NativeSelectWithField() {
+ return (
+
+
+ Country
+
+ Select a country
+ United States
+ United Kingdom
+ Canada
+ Australia
+
+ Select your country of residence.
+
+
+ )
+}
+
+function NativeSelectDisabled() {
+ return (
+
+
+ Disabled
+ Apple
+ Banana
+ Blueberry
+
+
+ )
+}
+
+function NativeSelectInvalid() {
+ return (
+
+
+ Error state
+ Apple
+ Banana
+ Blueberry
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/navigation-menu-example.tsx b/apps/ui/src/registry/bases/base/examples/navigation-menu-example.tsx
new file mode 100644
index 0000000..6d22afa
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/navigation-menu-example.tsx
@@ -0,0 +1,184 @@
+
+import * as React from "react"
+import Link from "next/link"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ NavigationMenu,
+ NavigationMenuContent,
+ NavigationMenuItem,
+ NavigationMenuLink,
+ NavigationMenuList,
+ NavigationMenuTrigger,
+ navigationMenuTriggerStyle,
+} from "@/registry/bases/base/ui/navigation-menu"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+const components: { title: string; href: string; description: string }[] = [
+ {
+ title: "Alert Dialog",
+ href: "/docs/primitives/alert-dialog",
+ description:
+ "A modal dialog that interrupts the user with important content and expects a response.",
+ },
+ {
+ title: "Hover Card",
+ href: "/docs/primitives/hover-card",
+ description:
+ "For sighted users to preview content available behind a link.",
+ },
+ {
+ title: "Progress",
+ href: "/docs/primitives/progress",
+ description:
+ "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
+ },
+ {
+ title: "Scroll-area",
+ href: "/docs/primitives/scroll-area",
+ description: "Visually or semantically separates content.",
+ },
+ {
+ title: "Tabs",
+ href: "/docs/primitives/tabs",
+ description:
+ "A set of layered sections of content—known as tab panels—that are displayed one at a time.",
+ },
+ {
+ title: "Tooltip",
+ href: "/docs/primitives/tooltip",
+ description:
+ "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
+ },
+]
+
+export default function NavigationMenuExample() {
+ return (
+
+
+
+ )
+}
+
+function NavigationMenuBasic() {
+ return (
+
+
+
+
+ Getting started
+
+
+
+ Re-usable components built with Tailwind CSS.
+
+
+ How to install dependencies and structure your app.
+
+
+ Styles for headings, paragraphs, lists...etc
+
+
+
+
+
+ Components
+
+
+ {components.map((component) => (
+
+ {component.description}
+
+ ))}
+
+
+
+
+ With Icon
+
+
+
+
+ }
+ >
+
+ Backlog
+
+
+ }
+ >
+
+ To Do
+
+
+ }
+ >
+
+ Done
+
+
+
+
+
+
+ }
+ className={navigationMenuTriggerStyle()}
+ >
+ Documentation
+
+
+
+
+
+ )
+}
+
+function ListItem({
+ title,
+ children,
+ href,
+ ...props
+}: React.ComponentPropsWithoutRef<"li"> & { href: string }) {
+ return (
+
+ }>
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/pagination-example.tsx b/apps/ui/src/registry/bases/base/examples/pagination-example.tsx
new file mode 100644
index 0000000..60ccc41
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/pagination-example.tsx
@@ -0,0 +1,127 @@
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Field, FieldLabel } from "@/registry/bases/base/ui/field"
+import {
+ Pagination,
+ PaginationContent,
+ PaginationEllipsis,
+ PaginationItem,
+ PaginationLink,
+ PaginationNext,
+ PaginationPrevious,
+} from "@/registry/bases/base/ui/pagination"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+
+export default function PaginationExample() {
+ return (
+
+
+
+
+
+ )
+}
+
+function PaginationBasic() {
+ return (
+
+
+
+
+
+
+
+ 1
+
+
+
+ 2
+
+
+
+ 3
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function PaginationSimple() {
+ return (
+
+
+
+
+ 1
+
+
+
+ 2
+
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+
+
+ )
+}
+
+function PaginationIconsOnly() {
+ return (
+
+
+
+ Rows per page
+
+
+
+
+
+
+ 10
+ 25
+ 50
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/popover-example.tsx b/apps/ui/src/registry/bases/base/examples/popover-example.tsx
new file mode 100644
index 0000000..23c027f
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/popover-example.tsx
@@ -0,0 +1,199 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import { Field, FieldGroup, FieldLabel } from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ Popover,
+ PopoverContent,
+ PopoverDescription,
+ PopoverHeader,
+ PopoverTitle,
+ PopoverTrigger,
+} from "@/registry/bases/base/ui/popover"
+
+export default function PopoverExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function PopoverBasic() {
+ return (
+
+
+ }>
+ Open Popover
+
+
+
+ Dimensions
+
+ Set the dimensions for the layer.
+
+
+
+
+
+ )
+}
+
+function PopoverSides() {
+ return (
+
+
+
+ {(["inline-start", "left", "top"] as const).map((side) => (
+
+
+ }
+ >
+ {side.replace("-", " ")}
+
+
+ Popover on {side.replace("-", " ")}
+
+
+ ))}
+
+
+ {(["bottom", "right", "inline-end"] as const).map((side) => (
+
+
+ }
+ >
+ {side.replace("-", " ")}
+
+
+ Popover on {side.replace("-", " ")}
+
+
+ ))}
+
+
+
+ )
+}
+
+function PopoverWithForm() {
+ return (
+
+
+ }>
+ Open Popover
+
+
+
+ Dimensions
+
+ Set the dimensions for the layer.
+
+
+
+
+
+ Width
+
+
+
+
+
+ Height
+
+
+
+
+
+
+
+ )
+}
+
+function PopoverAlignments() {
+ return (
+
+
+
+ }>
+ Start
+
+
+ Aligned to start
+
+
+
+ }>
+ Center
+
+
+ Aligned to center
+
+
+
+ }>
+ End
+
+
+ Aligned to end
+
+
+
+
+ )
+}
+
+function PopoverInDialog() {
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Popover Example
+
+ Click the button below to see the popover.
+
+
+
+ }
+ >
+ Open Popover
+
+
+
+ Popover in Dialog
+
+ This popover appears inside a dialog. Click the button to open
+ it.
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/progress-example.tsx b/apps/ui/src/registry/bases/base/examples/progress-example.tsx
new file mode 100644
index 0000000..0504b28
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/progress-example.tsx
@@ -0,0 +1,141 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemGroup,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import {
+ Progress,
+ ProgressLabel,
+ ProgressValue,
+} from "@/registry/bases/base/ui/progress"
+import { Slider } from "@/registry/bases/base/ui/slider"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ProgressExample() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function ProgressValues() {
+ return (
+
+
+
+ )
+}
+
+function ProgressWithLabel() {
+ return (
+
+
+ Upload progress
+
+
+
+ )
+}
+
+function ProgressControlled() {
+ const [value, setValue] = React.useState(50)
+
+ return (
+
+
+
+
setValue(value as number)}
+ min={0}
+ max={100}
+ step={1}
+ />
+
+
+ )
+}
+
+function FileUploadList() {
+ const files = React.useMemo(
+ () => [
+ {
+ id: "1",
+ name: "document.pdf",
+ progress: 45,
+ timeRemaining: "2m 30s",
+ },
+ {
+ id: "2",
+ name: "presentation.pptx",
+ progress: 78,
+ timeRemaining: "45s",
+ },
+ {
+ id: "3",
+ name: "spreadsheet.xlsx",
+ progress: 12,
+ timeRemaining: "5m 12s",
+ },
+ {
+ id: "4",
+ name: "image.jpg",
+ progress: 100,
+ timeRemaining: "Complete",
+ },
+ ],
+ []
+ )
+
+ return (
+
+
+ {files.map((file) => (
+ -
+
+
+
+
+ {file.name}
+
+
+
+
+
+
+ {file.timeRemaining}
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/radio-group-example.tsx b/apps/ui/src/registry/bases/base/examples/radio-group-example.tsx
new file mode 100644
index 0000000..c0d11c9
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/radio-group-example.tsx
@@ -0,0 +1,222 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldLabel,
+ FieldLegend,
+ FieldSet,
+} from "@/registry/bases/base/ui/field"
+import {
+ RadioGroup,
+ RadioGroupItem,
+} from "@/registry/bases/base/ui/radio-group"
+
+export default function RadioGroupExample() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function RadioGroupBasic() {
+ return (
+
+
+
+
+
+ Default
+
+
+
+
+
+ Comfortable
+
+
+
+
+
+ Compact
+
+
+
+
+ )
+}
+
+function RadioGroupWithDescriptions() {
+ return (
+
+
+
+
+
+ Plus
+
+ For individuals and small teams
+
+
+
+
+
+
+
+
+ Pro
+ For growing businesses
+
+
+
+
+
+
+
+ Enterprise
+
+ For large teams and enterprises
+
+
+
+
+
+
+
+ )
+}
+
+function RadioGroupWithFieldSet() {
+ return (
+
+
+ Battery Level
+
+ Choose your preferred battery level.
+
+
+
+
+
+ High
+
+
+
+
+
+ Medium
+
+
+
+
+
+ Low
+
+
+
+
+
+ )
+}
+
+function RadioGroupGrid() {
+ return (
+
+
+
+
+
+ Small
+
+
+
+
+
+ Medium
+
+
+
+
+
+ Large
+
+
+
+
+
+ X-Large
+
+
+
+
+ )
+}
+
+function RadioGroupDisabled() {
+ return (
+
+
+
+
+
+ Option 1
+
+
+
+
+
+ Option 2
+
+
+
+
+
+ Option 3
+
+
+
+
+ )
+}
+
+function RadioGroupInvalid() {
+ return (
+
+
+ Notification Preferences
+
+ Choose how you want to receive notifications.
+
+
+
+
+
+ Email only
+
+
+
+
+
+ SMS only
+
+
+
+
+
+ Both Email & SMS
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/resizable-example.tsx b/apps/ui/src/registry/bases/base/examples/resizable-example.tsx
new file mode 100644
index 0000000..86725f0
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/resizable-example.tsx
@@ -0,0 +1,157 @@
+
+import * as React from "react"
+import type { Layout } from "react-resizable-panels"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ ResizableHandle,
+ ResizablePanel,
+ ResizablePanelGroup,
+} from "@/registry/bases/base/ui/resizable"
+
+export default function ResizableExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function ResizableHorizontal() {
+ return (
+
+
+
+
+ Sidebar
+
+
+
+
+
+ Content
+
+
+
+
+ )
+}
+
+function ResizableVertical() {
+ return (
+
+
+
+
+ Header
+
+
+
+
+
+ Content
+
+
+
+
+ )
+}
+
+function ResizableWithHandle() {
+ return (
+
+
+
+
+ Sidebar
+
+
+
+
+
+ Content
+
+
+
+
+ )
+}
+
+function ResizableNested() {
+ return (
+
+
+
+
+ One
+
+
+
+
+
+
+
+ Two
+
+
+
+
+
+ Three
+
+
+
+
+
+
+ )
+}
+
+function ResizableControlled() {
+ const [layout, setLayout] = React.useState({})
+
+ return (
+
+
+
+
+
+ {Math.round(layout.left ?? 30)}%
+
+
+
+
+
+
+
+ {Math.round(layout.right ?? 70)}%
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/scroll-area-example.tsx b/apps/ui/src/registry/bases/base/examples/scroll-area-example.tsx
new file mode 100644
index 0000000..ae41902
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/scroll-area-example.tsx
@@ -0,0 +1,86 @@
+import * as React from "react"
+import Image from "next/image"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { ScrollArea, ScrollBar } from "@/registry/bases/base/ui/scroll-area"
+import { Separator } from "@/registry/bases/base/ui/separator"
+
+const tags = Array.from({ length: 50 }).map(
+ (_, i, a) => `v1.2.0-beta.${a.length - i}`
+)
+
+const works = [
+ {
+ artist: "Ornella Binni",
+ art: "https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80",
+ },
+ {
+ artist: "Tom Byrom",
+ art: "https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80",
+ },
+ {
+ artist: "Vladimir Malyav",
+ art: "https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80",
+ },
+] as const
+
+export default function ScrollAreaExample() {
+ return (
+
+
+
+
+ )
+}
+
+function ScrollAreaVertical() {
+ return (
+
+
+
+
Tags
+ {tags.map((tag) => (
+
+ {tag}
+
+
+ ))}
+
+
+
+ )
+}
+
+function ScrollAreaHorizontal() {
+ return (
+
+
+
+ {works.map((artwork) => (
+
+
+
+
+
+ Photo by{" "}
+
+ {artwork.artist}
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/select-example.tsx b/apps/ui/src/registry/bases/base/examples/select-example.tsx
new file mode 100644
index 0000000..a30caf3
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/select-example.tsx
@@ -0,0 +1,722 @@
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/bases/base/ui/dialog"
+import {
+ Field,
+ FieldDescription,
+ FieldError,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import {
+ NativeSelect,
+ NativeSelectOption,
+} from "@/registry/bases/base/ui/native-select"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SelectExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function SelectBasic() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ { label: "Grapes", value: "grapes" },
+ { label: "Pineapple", value: "pineapple" },
+ ]
+ return (
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ )
+}
+
+function SelectSides() {
+ const items = [
+ { label: "Select", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ ]
+ return (
+
+
+ {(
+ [
+ "inline-start",
+ "left",
+ "top",
+ "bottom",
+ "right",
+ "inline-end",
+ ] as const
+ ).map((side) => (
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+ ))}
+
+
+ )
+}
+
+function SelectWithIcons() {
+ const items = [
+ {
+ label: (
+ <>
+
+ Chart Type
+ >
+ ),
+ value: null,
+ },
+ {
+ label: (
+ <>
+
+ Line
+ >
+ ),
+ value: "line",
+ },
+ {
+ label: (
+ <>
+
+ Bar
+ >
+ ),
+ value: "bar",
+ },
+ {
+ label: (
+ <>
+
+ Pie
+ >
+ ),
+ value: "pie",
+ },
+ ]
+ return (
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+ )
+}
+
+function SelectWithGroups() {
+ const fruits = [
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ ]
+ const vegetables = [
+ { label: "Carrot", value: "carrot" },
+ { label: "Broccoli", value: "broccoli" },
+ { label: "Spinach", value: "spinach" },
+ ]
+ const allItems = [
+ { label: "Select a fruit", value: null },
+ ...fruits,
+ ...vegetables,
+ ]
+ return (
+
+
+
+
+
+
+
+ Fruits
+ {fruits.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+ Vegetables
+ {vegetables.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ )
+}
+
+function SelectLargeList() {
+ const items = [
+ { label: "Select an item", value: null },
+ ...Array.from({ length: 100 }).map((_, i) => ({
+ label: `Item ${i}`,
+ value: `item-${i}`,
+ })),
+ ]
+ return (
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ )
+}
+
+function SelectSizes() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ ]
+ return (
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+ )
+}
+
+function SelectWithButton() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ ]
+ return (
+
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ Submit
+
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+ Submit
+
+
+
+ )
+}
+
+function SelectItemAligned() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ { label: "Grapes", value: "grapes", disabled: true },
+ { label: "Pineapple", value: "pineapple" },
+ ]
+ return (
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ )
+}
+
+function SelectWithField() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ { label: "Grapes", value: "grapes" },
+ { label: "Pineapple", value: "pineapple" },
+ ]
+ return (
+
+
+ Favorite Fruit
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ Choose your favorite fruit from the list.
+
+
+
+ )
+}
+
+function SelectInvalid() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ { label: "Grapes", value: "grapes" },
+ { label: "Pineapple", value: "pineapple" },
+ ]
+ return (
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ Favorite Fruit
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+
+ )
+}
+
+function SelectInline() {
+ const items = [
+ { label: "Filter", value: null },
+ { label: "All", value: "all" },
+ { label: "Active", value: "active" },
+ { label: "Inactive", value: "inactive" },
+ ]
+ return (
+
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ Sort by
+ Name
+ Date
+ Status
+
+
+
+ )
+}
+
+function SelectDisabled() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ { label: "Grapes", value: "grapes", disabled: true },
+ { label: "Pineapple", value: "pineapple" },
+ ]
+ return (
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ )
+}
+
+const plans = [
+ {
+ name: "Starter",
+ description: "Perfect for individuals getting started.",
+ },
+ {
+ name: "Professional",
+ description: "Ideal for growing teams and businesses.",
+ },
+ {
+ name: "Enterprise",
+ description: "Advanced features for large organizations.",
+ },
+]
+
+function SelectPlan() {
+ return (
+
+ plan.name}
+ >
+
+
+ {(value: (typeof plans)[number]) => }
+
+
+
+
+ {plans.map((plan) => (
+
+
+
+ ))}
+
+
+
+
+ )
+}
+
+function SelectPlanItem({ plan }: { plan: (typeof plans)[number] }) {
+ return (
+ -
+
+ {plan.name}
+
+ {plan.description}
+
+
+
+ )
+}
+
+function SelectMultiple() {
+ const items = [
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ { label: "Grapes", value: "grapes" },
+ { label: "Pineapple", value: "pineapple" },
+ { label: "Strawberry", value: "strawberry" },
+ { label: "Watermelon", value: "watermelon" },
+ ]
+ return (
+
+
+
+
+ {(value: string[]) => {
+ if (value.length === 0) {
+ return "Select fruits"
+ }
+ if (value.length === 1) {
+ return items.find((item) => item.value === value[0])?.label
+ }
+ return `${value.length} fruits selected`
+ }}
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+ )
+}
+
+function SelectInDialog() {
+ const items = [
+ { label: "Select a fruit", value: null },
+ { label: "Apple", value: "apple" },
+ { label: "Banana", value: "banana" },
+ { label: "Blueberry", value: "blueberry" },
+ { label: "Grapes", value: "grapes" },
+ { label: "Pineapple", value: "pineapple" },
+ ]
+ return (
+
+
+ }>
+ Open Dialog
+
+
+
+ Select Example
+
+ Use the select below to choose a fruit.
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/separator-example.tsx b/apps/ui/src/registry/bases/base/examples/separator-example.tsx
new file mode 100644
index 0000000..c703724
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/separator-example.tsx
@@ -0,0 +1,100 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Separator } from "@/registry/bases/base/ui/separator"
+
+export default function SeparatorExample() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function SeparatorHorizontal() {
+ return (
+
+
+
+
shadcn/ui
+
+ The Foundation for your Design System
+
+
+
+
+ A set of beautifully designed components that you can customize,
+ extend, and build on.
+
+
+
+ )
+}
+
+function SeparatorVertical() {
+ return (
+
+
+
Blog
+
+
Docs
+
+
Source
+
+
+ )
+}
+
+function SeparatorVerticalMenu() {
+ return (
+
+
+
+ Settings
+
+ Manage preferences
+
+
+
+
+ Account
+
+ Profile & security
+
+
+
+
+ Help
+ Support & docs
+
+
+
+ )
+}
+
+function SeparatorInList() {
+ return (
+
+
+
+ Item 1
+ Value 1
+
+
+
+ Item 2
+ Value 2
+
+
+
+ Item 3
+ Value 3
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/sheet-example.tsx b/apps/ui/src/registry/bases/base/examples/sheet-example.tsx
new file mode 100644
index 0000000..83fcd3c
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/sheet-example.tsx
@@ -0,0 +1,138 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Field, FieldGroup, FieldLabel } from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ Sheet,
+ SheetClose,
+ SheetContent,
+ SheetDescription,
+ SheetFooter,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/registry/bases/base/ui/sheet"
+
+export default function SheetExample() {
+ return (
+
+
+
+
+
+ )
+}
+
+function SheetWithForm() {
+ return (
+
+
+ }>Open
+
+
+ Edit profile
+
+ Make changes to your profile here. Click save when you're
+ done.
+
+
+
+
+
+ Name
+
+
+
+ Username
+
+
+
+
+
+ Save changes
+ }>Close
+
+
+
+
+ )
+}
+
+function SheetNoCloseButton() {
+ return (
+
+
+ }>
+ No Close Button
+
+
+
+ No Close Button
+
+ This sheet doesn't have a close button in the top-right
+ corner. You can only close it using the button below.
+
+
+
+
+
+ )
+}
+
+const SHEET_SIDES = ["top", "right", "bottom", "left"] as const
+
+function SheetWithSides() {
+ return (
+
+
+ {SHEET_SIDES.map((side) => (
+
+ }
+ >
+ {side}
+
+
+
+ Edit profile
+
+ Make changes to your profile here. Click save when you're
+ done.
+
+
+
+ {Array.from({ length: 10 }).map((_, index) => (
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
+ do eiusmod tempor incididunt ut labore et dolore magna
+ aliqua. Ut enim ad minim veniam, quis nostrud exercitation
+ ullamco laboris nisi ut aliquip ex ea commodo consequat.
+ Duis aute irure dolor in reprehenderit in voluptate velit
+ esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
+ occaecat cupidatat non proident, sunt in culpa qui officia
+ deserunt mollit anim id est laborum.
+
+ ))}
+
+
+ Save changes
+ }>
+ Cancel
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/sidebar-example.tsx b/apps/ui/src/registry/bases/base/examples/sidebar-example.tsx
new file mode 100644
index 0000000..200c96f
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/sidebar-example.tsx
@@ -0,0 +1,285 @@
+
+import * as React from "react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import { Label } from "@/registry/bases/base/ui/label"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarHeader,
+ SidebarInput,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarRail,
+ SidebarTrigger,
+} from "@/registry/bases/base/ui/sidebar"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SidebarExample() {
+ const data = {
+ versions: ["1.0.1", "1.1.0-alpha", "2.0.0-beta1"],
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ ],
+ }
+
+ const [selectedVersion, setSelectedVersion] = React.useState(data.versions[0])
+
+ return (
+
+
+
+
+
+
+
+ }
+ >
+ -
+
+ Documentation
+ v{selectedVersion}
+
+
+
+
+
+
+
+ {data.versions.map((version) => (
+ setSelectedVersion(version)}
+ >
+ v{version}{" "}
+ {version === selectedVersion && (
+
+ )}
+
+ ))}
+
+
+
+
+
+
+
+ {data.navMain.map((item) => (
+
+ {item.title}
+
+
+ {item.items.map((subItem) => (
+
+ }
+ isActive={subItem.isActive}
+ >
+ {subItem.title}
+
+
+ ))}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/sidebar-floating-example.tsx b/apps/ui/src/registry/bases/base/examples/sidebar-floating-example.tsx
new file mode 100644
index 0000000..30f5846
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/sidebar-floating-example.tsx
@@ -0,0 +1,274 @@
+
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/bases/base/ui/card"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import { Field } from "@/registry/bases/base/ui/field"
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarGroup,
+ SidebarHeader,
+ SidebarInput,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarRail,
+ SidebarTrigger,
+} from "@/registry/bases/base/ui/sidebar"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SidebarFloatingExample() {
+ const data = {
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ ],
+ }
+
+ return (
+
+
+
+
+
+ }>
+ -
+
+ Documentation
+ v1.0.0
+
+
+
+
+
+
+
+
+
+ {data.navMain.map((item) => (
+
+
+
+ }
+ >
+ {item.title}{" "}
+
+
+ {item.items?.length ? (
+
+
+ {item.items.map((subItem) => (
+ }
+ key={subItem.title}
+ >
+ {subItem.title}
+
+ ))}
+
+
+ ) : null}
+
+
+ ))}
+
+
+
+
+
+
+
+
+ Subscribe to our newsletter
+
+
+ Opt-in to receive updates and news about the sidebar.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/sidebar-icon-example.tsx b/apps/ui/src/registry/bases/base/examples/sidebar-icon-example.tsx
new file mode 100644
index 0000000..7b915b7
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/sidebar-icon-example.tsx
@@ -0,0 +1,460 @@
+
+import * as React from "react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/bases/base/ui/avatar"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/bases/base/ui/collapsible"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/bases/base/ui/item"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarHeader,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+ SidebarRail,
+ SidebarTrigger,
+} from "@/registry/bases/base/ui/sidebar"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SidebarIconExample() {
+ const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ teams: [
+ {
+ name: "Acme Inc",
+ plan: "Enterprise",
+ },
+ {
+ name: "Acme Corp.",
+ plan: "Startup",
+ },
+ {
+ name: "Evil Corp.",
+ plan: "Free",
+ },
+ ],
+ navMain: [
+ {
+ title: "Playground",
+ url: "#",
+ icon: (
+
+ ),
+ isActive: true,
+ items: [
+ {
+ title: "History",
+ url: "#",
+ },
+ {
+ title: "Starred",
+ url: "#",
+ },
+ {
+ title: "Settings",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Models",
+ url: "#",
+ icon: (
+
+ ),
+ items: [
+ {
+ title: "Genesis",
+ url: "#",
+ },
+ {
+ title: "Explorer",
+ url: "#",
+ },
+ {
+ title: "Quantum",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Documentation",
+ url: "#",
+ icon: (
+
+ ),
+ items: [
+ {
+ title: "Introduction",
+ url: "#",
+ },
+ {
+ title: "Get Started",
+ url: "#",
+ },
+ {
+ title: "Tutorials",
+ url: "#",
+ },
+ {
+ title: "Changelog",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: (
+
+ ),
+ items: [
+ {
+ title: "General",
+ url: "#",
+ },
+ {
+ title: "Team",
+ url: "#",
+ },
+ {
+ title: "Billing",
+ url: "#",
+ },
+ {
+ title: "Limits",
+ url: "#",
+ },
+ ],
+ },
+ ],
+ projects: [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ ],
+ }
+
+ const [activeTeam, setActiveTeam] = React.useState(data.teams[0])
+
+ return (
+
+
+
+
+
+
+
+ }
+ >
+ }
+ nativeButton={false}
+ className="size-8"
+ >
+
+
+
+
+
+
+
+
+ {activeTeam.name}
+
+ {activeTeam.plan}
+
+
+
+
+
+ Teams
+
+
+ {data.teams.map((team) => (
+ setActiveTeam(team)}
+ >
+ {team.name}
+
+ ))}
+
+
+
+
+
+
+
+
+ Platform
+
+ {data.navMain.map((item) => (
+
+
+ }
+ >
+ {item.icon}
+ {item.title}
+
+
+
+
+ {item.items?.map((subItem) => (
+
+ }
+ >
+ {subItem.title}
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+ Projects
+
+ {data.projects.map((item) => (
+
+ }>
+ {item.icon}
+ {item.name}
+
+
+ ))}
+
+
+
+
+
+
+
+
+ }
+ >
+
+
+ CN
+
+
+
+ {data.user.name}
+
+ {data.user.email}
+
+
+
+
+
+
+ -
+
+
+
+ CN
+
+
+
+ {data.user.name}
+ {data.user.email}
+
+
+
+
+
+
+ Account
+ Billing
+ Settings
+
+
+
+ Log out
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/sidebar-inset-example.tsx b/apps/ui/src/registry/bases/base/examples/sidebar-inset-example.tsx
new file mode 100644
index 0000000..ac28ddd
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/sidebar-inset-example.tsx
@@ -0,0 +1,286 @@
+
+import * as React from "react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/bases/base/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+ SidebarRail,
+ SidebarTrigger,
+} from "@/registry/bases/base/ui/sidebar"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SidebarInsetExample() {
+ const data = {
+ navMain: [
+ {
+ title: "Dashboard",
+ url: "#",
+ icon: (
+
+ ),
+ isActive: true,
+ items: [
+ {
+ title: "Overview",
+ url: "#",
+ },
+ {
+ title: "Analytics",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Analytics",
+ url: "#",
+ icon: (
+
+ ),
+ items: [
+ {
+ title: "Reports",
+ url: "#",
+ },
+ {
+ title: "Metrics",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Orders",
+ url: "#",
+ icon: (
+
+ ),
+ items: [
+ {
+ title: "All Orders",
+ url: "#",
+ },
+ {
+ title: "Pending",
+ url: "#",
+ },
+ {
+ title: "Completed",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Products",
+ url: "#",
+ icon: (
+
+ ),
+ items: [
+ {
+ title: "All Products",
+ url: "#",
+ },
+ {
+ title: "Categories",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Invoices",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ {
+ title: "Customers",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ ],
+ navSecondary: [
+ {
+ title: "Support",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ {
+ title: "Feedback",
+ url: "#",
+ icon: (
+
+ ),
+ },
+ ],
+ }
+
+ return (
+
+
+
+
+ Dashboard
+
+ {data.navMain.map((item) => (
+ }
+ >
+ }
+ isActive={item.isActive}
+ >
+ {item.icon}
+ {item.title}
+
+ {item.items?.length ? (
+ <>
+
+ }
+ >
+
+ Toggle
+
+
+
+ {item.items.map((subItem) => (
+
+ }
+ >
+ {subItem.title}
+
+
+ ))}
+
+
+ >
+ ) : null}
+
+ ))}
+
+
+
+
+
+ {data.navSecondary.map((item) => (
+
+ } size="sm">
+ {item.icon}
+ {item.title}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/skeleton-example.tsx b/apps/ui/src/registry/bases/base/examples/skeleton-example.tsx
new file mode 100644
index 0000000..1f56f9e
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/skeleton-example.tsx
@@ -0,0 +1,102 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Card, CardContent, CardHeader } from "@/registry/bases/base/ui/card"
+import { Skeleton } from "@/registry/bases/base/ui/skeleton"
+
+export default function SkeletonExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function SkeletonAvatar() {
+ return (
+
+
+
+ )
+}
+
+function SkeletonCard() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function SkeletonText() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function SkeletonForm() {
+ return (
+
+
+
+ )
+}
+
+function SkeletonTable() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/slider-example.tsx b/apps/ui/src/registry/bases/base/examples/slider-example.tsx
new file mode 100644
index 0000000..fb9d1bb
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/slider-example.tsx
@@ -0,0 +1,102 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Label } from "@/registry/bases/base/ui/label"
+import { Slider } from "@/registry/bases/base/ui/slider"
+
+export default function SliderExample() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+function SliderBasic() {
+ return (
+
+
+
+ )
+}
+
+function SliderRange() {
+ return (
+
+
+
+ )
+}
+
+function SliderMultiple() {
+ return (
+
+
+
+ )
+}
+
+function SliderVertical() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function SliderControlled() {
+ const [value, setValue] = React.useState([0.3, 0.7])
+
+ return (
+
+
+
+ Temperature
+
+ {value.join(", ")}
+
+
+
setValue(value as number[])}
+ min={0}
+ max={1}
+ step={0.1}
+ />
+
+
+ )
+}
+
+function SliderDisabled() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/sonner-example.tsx b/apps/ui/src/registry/bases/base/examples/sonner-example.tsx
new file mode 100644
index 0000000..c4e4f3f
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/sonner-example.tsx
@@ -0,0 +1,49 @@
+
+import { toast } from "sonner"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+
+export default function SonnerExample() {
+ return (
+
+
+
+
+ )
+}
+
+function SonnerBasic() {
+ return (
+
+ toast("Event has been created")}
+ variant="outline"
+ className="w-fit"
+ >
+ Show Toast
+
+
+ )
+}
+
+function SonnerWithDescription() {
+ return (
+
+
+ toast("Event has been created", {
+ description: "Monday, January 3rd at 6:00pm",
+ })
+ }
+ variant="outline"
+ className="w-fit"
+ >
+ Show Toast
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/spinner-example.tsx b/apps/ui/src/registry/bases/base/examples/spinner-example.tsx
new file mode 100644
index 0000000..0afb0f2
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/spinner-example.tsx
@@ -0,0 +1,150 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Badge } from "@/registry/bases/base/ui/badge"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/bases/base/ui/empty"
+import { Field, FieldLabel } from "@/registry/bases/base/ui/field"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/bases/base/ui/input-group"
+import { Spinner } from "@/registry/bases/base/ui/spinner"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function SpinnerExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function SpinnerBasic() {
+ return (
+
+
+
+
+
+
+ )
+}
+
+function SpinnerInButtons() {
+ return (
+
+
+
+ Submit
+
+
+ Disabled
+
+
+ Outline
+
+
+
+ Loading...
+
+
+
+ )
+}
+
+function SpinnerInBadges() {
+ return (
+
+
+
+
+ Badge
+
+
+
+ Badge
+
+
+
+ Badge
+
+
+
+ Badge
+
+
+
+ )
+}
+
+function SpinnerInInputGroup() {
+ return (
+
+
+ Input Group
+
+
+
+
+
+
+
+
+ )
+}
+
+function SpinnerInEmpty() {
+ return (
+
+
+
+
+
+
+ No projects yet
+
+ You haven't created any projects yet. Get started by creating
+ your first project.
+
+
+
+
+ } nativeButton={false}>
+ Create project
+
+ Import project
+
+ }
+ nativeButton={false}
+ className="text-muted-foreground"
+ >
+ Learn more{" "}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/switch-example.tsx b/apps/ui/src/registry/bases/base/examples/switch-example.tsx
new file mode 100644
index 0000000..a4b6863
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/switch-example.tsx
@@ -0,0 +1,102 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldLabel,
+ FieldTitle,
+} from "@/registry/bases/base/ui/field"
+import { Label } from "@/registry/bases/base/ui/label"
+import { Switch } from "@/registry/bases/base/ui/switch"
+
+export default function SwitchExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function SwitchBasic() {
+ return (
+
+
+
+ Airplane Mode
+
+
+ )
+}
+
+function SwitchWithLabel() {
+ return (
+
+
+
+ Bluetooth
+
+
+ )
+}
+
+function SwitchWithDescription() {
+ return (
+
+
+
+
+ Share across devices
+
+ Focus is shared across devices, and turns off when you leave the
+ app.
+
+
+
+
+
+
+ )
+}
+
+function SwitchDisabled() {
+ return (
+
+
+
+
+
+ Disabled (Unchecked)
+
+
+
+
+ Disabled (Checked)
+
+
+
+ )
+}
+
+function SwitchSizes() {
+ return (
+
+
+
+
+ Small
+
+
+
+ Default
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/table-example.tsx b/apps/ui/src/registry/bases/base/examples/table-example.tsx
new file mode 100644
index 0000000..53527e2
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/table-example.tsx
@@ -0,0 +1,483 @@
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import {
+ Table,
+ TableBody,
+ TableCaption,
+ TableCell,
+ TableFooter,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/registry/bases/base/ui/table"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+const invoices = [
+ {
+ invoice: "INV001",
+ paymentStatus: "Paid",
+ totalAmount: "$250.00",
+ paymentMethod: "Credit Card",
+ },
+ {
+ invoice: "INV002",
+ paymentStatus: "Pending",
+ totalAmount: "$150.00",
+ paymentMethod: "PayPal",
+ },
+ {
+ invoice: "INV003",
+ paymentStatus: "Unpaid",
+ totalAmount: "$350.00",
+ paymentMethod: "Bank Transfer",
+ },
+ {
+ invoice: "INV004",
+ paymentStatus: "Paid",
+ totalAmount: "$450.00",
+ paymentMethod: "Credit Card",
+ },
+ {
+ invoice: "INV005",
+ paymentStatus: "Paid",
+ totalAmount: "$550.00",
+ paymentMethod: "PayPal",
+ },
+ {
+ invoice: "INV006",
+ paymentStatus: "Pending",
+ totalAmount: "$200.00",
+ paymentMethod: "Bank Transfer",
+ },
+ {
+ invoice: "INV007",
+ paymentStatus: "Unpaid",
+ totalAmount: "$300.00",
+ paymentMethod: "Credit Card",
+ },
+]
+
+export default function TableExample() {
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function TableBasic() {
+ return (
+
+
+ A list of your recent invoices.
+
+
+ Invoice
+ Status
+ Method
+ Amount
+
+
+
+ {invoices.slice(0, 3).map((invoice) => (
+
+ {invoice.invoice}
+ {invoice.paymentStatus}
+ {invoice.paymentMethod}
+
+ {invoice.totalAmount}
+
+
+ ))}
+
+
+
+ )
+}
+
+function TableWithFooter() {
+ return (
+
+
+ A list of your recent invoices.
+
+
+ Invoice
+ Status
+ Method
+ Amount
+
+
+
+ {invoices.slice(0, 3).map((invoice) => (
+
+ {invoice.invoice}
+ {invoice.paymentStatus}
+ {invoice.paymentMethod}
+
+ {invoice.totalAmount}
+
+
+ ))}
+
+
+
+ Total
+ $2,500.00
+
+
+
+
+ )
+}
+
+function TableSimple() {
+ return (
+
+
+
+
+ Name
+ Email
+ Role
+
+
+
+
+ Sarah Chen
+ sarah.chen@acme.com
+ Admin
+
+
+ Marc Rodriguez
+ marcus.rodriguez@acme.com
+ User
+
+
+ Emily Watson
+ emily.watson@acme.com
+ User
+
+
+
+
+ )
+}
+
+function TableWithBadges() {
+ return (
+
+
+
+
+ Task
+ Status
+ Priority
+
+
+
+
+ Design homepage
+
+
+ Completed
+
+
+
+
+ High
+
+
+
+
+ Implement API
+
+
+ In Progress
+
+
+
+
+ Medium
+
+
+
+
+ Write tests
+
+
+ Pending
+
+
+
+
+ Low
+
+
+
+
+
+
+ )
+}
+
+function TableWithActions() {
+ return (
+
+
+
+
+ Product
+ Price
+ Actions
+
+
+
+
+ Wireless Mouse
+ $29.99
+
+
+
+ }
+ >
+
+ Open menu
+
+
+ Edit
+ Duplicate
+
+
+ Delete
+
+
+
+
+
+
+ Mechanical Keyboard
+ $129.99
+
+
+
+ }
+ >
+
+ Open menu
+
+
+ Edit
+ Duplicate
+
+
+ Delete
+
+
+
+
+
+
+ USB-C Hub
+ $49.99
+
+
+
+ }
+ >
+
+ Open menu
+
+
+ Edit
+ Duplicate
+
+
+ Delete
+
+
+
+
+
+
+
+
+ )
+}
+
+const people = [
+ { value: "sarah", label: "Sarah Chen" },
+ { value: "marcus", label: "Marc Rodriguez" },
+ { value: "emily", label: "Emily Watson" },
+ { value: "david", label: "David Kim" },
+]
+
+const tasks = [
+ {
+ task: "Design homepage",
+ assignee: "sarah",
+ status: "In Progress",
+ },
+ {
+ task: "Implement API",
+ assignee: "marcus",
+ status: "Pending",
+ },
+ {
+ task: "Write tests",
+ assignee: "emily",
+ status: "Not Started",
+ },
+]
+
+function TableWithSelect() {
+ return (
+
+
+
+
+ Task
+ Assignee
+ Status
+
+
+
+ {tasks.map((item) => (
+
+ {item.task}
+
+ person.value === item.assignee
+ )}
+ itemToStringValue={(item) => {
+ return item.value
+ }}
+ >
+
+
+
+
+
+ {people.map((person) => (
+
+ {person.label}
+
+ ))}
+
+
+
+
+ {item.status}
+
+ ))}
+
+
+
+ )
+}
+
+function TableWithInput() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/tabs-example.tsx b/apps/ui/src/registry/bases/base/examples/tabs-example.tsx
new file mode 100644
index 0000000..9854218
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/tabs-example.tsx
@@ -0,0 +1,359 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/bases/base/ui/dropdown-menu"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from "@/registry/bases/base/ui/tabs"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function TabsExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function TabsBasic() {
+ return (
+
+
+
+ Home
+ Settings
+
+
+
+ )
+}
+
+function TabsLine() {
+ return (
+
+
+
+ Overview
+ Analytics
+ Reports
+
+
+
+ )
+}
+
+function TabsVariantsComparison() {
+ return (
+
+
+
+
+ Overview
+ Analytics
+
+
+
+
+ Overview
+ Analytics
+
+
+
+
+ )
+}
+
+function TabsDisabled() {
+ return (
+
+
+
+ Home
+
+ Disabled
+
+
+
+
+ )
+}
+
+function TabsWithIcons() {
+ return (
+
+
+
+
+
+ Preview
+
+
+
+ Code
+
+
+
+
+ )
+}
+
+function TabsIconOnly() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function TabsMultiple() {
+ return (
+
+
+
+ Overview
+ Analytics
+ Reports
+ Settings
+
+
+
+ )
+}
+
+function TabsWithContent() {
+ return (
+
+
+
+ Account
+ Password
+ Notifications
+
+
+
+ Manage your account preferences and profile information.
+
+
+ Update your password to keep your account secure.
+
+
+ Configure how you receive notifications and alerts.
+
+
+
+
+ )
+}
+
+function TabsLineWithContent() {
+ return (
+
+
+
+ Account
+ Password
+ Notifications
+
+
+
+ Manage your account preferences and profile information.
+
+
+ Update your password to keep your account secure.
+
+
+ Configure how you receive notifications and alerts.
+
+
+
+
+ )
+}
+
+function TabsLineDisabled() {
+ return (
+
+
+
+ Overview
+ Analytics
+
+ Reports
+
+
+
+
+ )
+}
+
+function TabsWithDropdown() {
+ return (
+
+
+
+
+ Overview
+ Analytics
+ Reports
+
+
+ }
+ >
+
+ More options
+
+
+ Settings
+ Export
+
+ Archive
+
+
+
+
+
+
+ View your dashboard metrics and key performance indicators.
+
+
+ Detailed analytics and insights about your data.
+
+
+ Generate and view custom reports.
+
+
+
+
+ )
+}
+
+function TabsVertical() {
+ return (
+
+
+
+ Account
+ Password
+ Notifications
+
+
+
+ Manage your account preferences and profile information.
+
+
+ Update your password to keep your account secure. Use a strong
+ password with a mix of letters, numbers, and symbols.
+
+
+ Configure how you receive notifications and alerts. Choose which
+ types of notifications you want to receive and how you want to
+ receive them.
+
+
+
+
+ )
+}
+
+function TabsWithInputAndButton() {
+ return (
+
+
+
+
+ Overview
+ Analytics
+
+
+
+ Action
+
+
+
+
+ View your dashboard metrics and key performance indicators.
+
+
+ Detailed analytics and insights about your data.
+
+
+ Generate and view custom reports.
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/textarea-example.tsx b/apps/ui/src/registry/bases/base/examples/textarea-example.tsx
new file mode 100644
index 0000000..f20ba00
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/textarea-example.tsx
@@ -0,0 +1,86 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Field,
+ FieldDescription,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import { Textarea } from "@/registry/bases/base/ui/textarea"
+
+export default function TextareaExample() {
+ return (
+
+
+
+
+
+
+
+ )
+}
+
+function TextareaBasic() {
+ return (
+
+
+
+ )
+}
+
+function TextareaInvalid() {
+ return (
+
+
+
+ )
+}
+
+function TextareaWithLabel() {
+ return (
+
+
+ Message
+
+
+
+ )
+}
+
+function TextareaWithDescription() {
+ return (
+
+
+ Message
+
+
+ Type your message and press enter to send.
+
+
+
+ )
+}
+
+function TextareaDisabled() {
+ return (
+
+
+ Message
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/toggle-example.tsx b/apps/ui/src/registry/bases/base/examples/toggle-example.tsx
new file mode 100644
index 0000000..45e252b
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/toggle-example.tsx
@@ -0,0 +1,338 @@
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Toggle } from "@/registry/bases/base/ui/toggle"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ToggleExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleBasic() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleOutline() {
+ return (
+
+
+
+
+ Italic
+
+
+
+ Bold
+
+
+
+ )
+}
+
+function ToggleSizes() {
+ return (
+
+
+
+ Small
+
+
+ Default
+
+
+ Large
+
+
+
+ )
+}
+
+function ToggleWithButtonText() {
+ return (
+
+
+
+
+ Button
+
+
+ Toggle
+
+
+
+
+ Button
+
+
+ Toggle
+
+
+
+
+ Button
+
+
+ Toggle
+
+
+
+
+ )
+}
+
+function ToggleWithButtonIcon() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleWithButtonIconText() {
+ return (
+
+
+
+
+
+ Button
+
+
+
+ Toggle
+
+
+
+
+
+ Button
+
+
+
+ Toggle
+
+
+
+
+
+ Button
+
+
+
+ Toggle
+
+
+
+
+ )
+}
+
+function ToggleDisabled() {
+ return (
+
+
+
+ Disabled
+
+
+ Disabled
+
+
+
+ )
+}
+
+function ToggleWithIcon() {
+ return (
+
+
+
+
+
+
+
+ Bookmark
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/toggle-group-example.tsx b/apps/ui/src/registry/bases/base/examples/toggle-group-example.tsx
new file mode 100644
index 0000000..56446e5
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/toggle-group-example.tsx
@@ -0,0 +1,555 @@
+
+import * as React from "react"
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import {
+ Field,
+ FieldDescription,
+ FieldLabel,
+} from "@/registry/bases/base/ui/field"
+import { Input } from "@/registry/bases/base/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/bases/base/ui/select"
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/bases/base/ui/toggle-group"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function ToggleGroupExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleGroupBasic() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleGroupOutline() {
+ return (
+
+
+
+ All
+
+
+ Missed
+
+
+
+ )
+}
+
+function ToggleGroupOutlineWithIcons() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleGroupSizes() {
+ return (
+
+
+
+
+ Top
+
+
+ Bottom
+
+
+ Left
+
+
+ Right
+
+
+
+
+ Top
+
+
+ Bottom
+
+
+ Left
+
+
+ Right
+
+
+
+
+ )
+}
+
+function ToggleGroupSpacing() {
+ return (
+
+
+
+ Top
+
+
+ Bottom
+
+
+ Left
+
+
+ Right
+
+
+
+ )
+}
+
+function ToggleGroupWithIcons() {
+ return (
+
+
+
+
+ Star
+
+
+
+ Heart
+
+
+
+ Bookmark
+
+
+
+ )
+}
+
+function ToggleGroupFilter() {
+ return (
+
+
+
+ All
+
+
+ Active
+
+
+ Completed
+
+
+ Archived
+
+
+
+ )
+}
+
+function ToggleGroupDateRange() {
+ return (
+
+
+
+ Today
+
+
+ This Week
+
+
+ This Month
+
+
+ This Year
+
+
+
+ )
+}
+
+function ToggleGroupSort() {
+ return (
+
+
+
+
+ Newest
+
+
+
+ Oldest
+
+
+
+ Popular
+
+
+
+ )
+}
+
+function ToggleGroupWithInputAndSelect() {
+ const items = [
+ { label: "All", value: "all" },
+ { label: "Active", value: "active" },
+ { label: "Archived", value: "archived" },
+ ]
+ return (
+
+
+
+
+
+
+
+
+
+ {items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+
+
+
+
+ Grid
+
+
+ List
+
+
+
+
+ )
+}
+
+function ToggleGroupVertical() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleGroupVerticalOutline() {
+ return (
+
+
+
+ All
+
+
+ Active
+
+
+ Completed
+
+
+ Archived
+
+
+
+ )
+}
+
+function ToggleGroupVerticalOutlineWithIcons() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ToggleGroupFontWeightSelector() {
+ const [fontWeight, setFontWeight] = React.useState("normal")
+ return (
+
+
+ Font Weight
+ setFontWeight(value[0])}
+ variant="outline"
+ spacing={2}
+ size="lg"
+ >
+
+ Aa
+ Light
+
+
+ Aa
+ Normal
+
+
+ Aa
+ Medium
+
+
+ Aa
+ Bold
+
+
+
+ Use{" "}
+
+ font-{fontWeight}
+ {" "}
+ to set the font weight.
+
+
+
+ )
+}
+
+function ToggleGroupVerticalWithSpacing() {
+ return (
+
+
+
+ Top
+
+
+ Bottom
+
+
+ Left
+
+
+ Right
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/examples/tooltip-example.tsx b/apps/ui/src/registry/bases/base/examples/tooltip-example.tsx
new file mode 100644
index 0000000..04fff91
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/examples/tooltip-example.tsx
@@ -0,0 +1,190 @@
+
+import {
+ Example,
+ ExampleWrapper,
+} from "@/registry/bases/base/components/example"
+import { Button } from "@/registry/bases/base/ui/button"
+import { Kbd } from "@/registry/bases/base/ui/kbd"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/bases/base/ui/tooltip"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+export default function TooltipExample() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function TooltipBasic() {
+ return (
+
+
+ }>
+ Show Tooltip
+
+
+ Add to library
+
+
+
+ )
+}
+
+function TooltipSides() {
+ return (
+
+
+ {(
+ [
+ "inline-start",
+ "left",
+ "top",
+ "bottom",
+ "right",
+ "inline-end",
+ ] as const
+ ).map((side) => (
+
+ }
+ >
+ {side.replace("-", " ")}
+
+
+ Add to library
+
+
+ ))}
+
+
+ )
+}
+
+function TooltipWithIcon() {
+ return (
+
+
+ }>
+
+ Info
+
+
+ Additional information
+
+
+
+ )
+}
+
+function TooltipLongContent() {
+ return (
+
+
+ }>
+ Show Tooltip
+
+
+ To learn more about how this works, check out the docs. If you have
+ any questions, please reach out to us.
+
+
+
+ )
+}
+
+function TooltipDisabled() {
+ return (
+
+
+ }>
+
+ Disabled
+
+
+
+ This feature is currently unavailable
+
+
+
+ )
+}
+
+function TooltipWithKeyboard() {
+ return (
+
+
+ }>
+
+
+
+ Save Changes S
+
+
+
+ )
+}
+
+function TooltipOnLink() {
+ return (
+
+
+ e.preventDefault()}
+ />
+ }
+ >
+ Learn more
+
+
+ Click to read the documentation
+
+
+
+ )
+}
+
+function TooltipFormatted() {
+ return (
+
+
+ }>
+ Status
+
+
+
+
Active
+
Last updated 2 hours ago
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/hooks/_registry.ts b/apps/ui/src/registry/bases/base/hooks/_registry.ts
new file mode 100644
index 0000000..5957a81
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/hooks/_registry.ts
@@ -0,0 +1,14 @@
+import { type Registry } from "shadcn/schema"
+
+export const hooks: Registry["items"] = [
+ {
+ name: "use-mobile",
+ type: "registry:hook",
+ files: [
+ {
+ path: "hooks/use-mobile.ts",
+ type: "registry:hook",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/bases/base/hooks/use-mobile.ts b/apps/ui/src/registry/bases/base/hooks/use-mobile.ts
new file mode 100644
index 0000000..2b0fe1d
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/hooks/use-mobile.ts
@@ -0,0 +1,19 @@
+import * as React from "react"
+
+const MOBILE_BREAKPOINT = 768
+
+export function useIsMobile() {
+ const [isMobile, setIsMobile] = React.useState(undefined)
+
+ React.useEffect(() => {
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
+ const onChange = () => {
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ }
+ mql.addEventListener("change", onChange)
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ return () => mql.removeEventListener("change", onChange)
+ }, [])
+
+ return !!isMobile
+}
diff --git a/apps/ui/src/registry/bases/base/internal/_registry.ts b/apps/ui/src/registry/bases/base/internal/_registry.ts
new file mode 100644
index 0000000..d3a989b
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/internal/_registry.ts
@@ -0,0 +1,3 @@
+import { type Registry } from "shadcn/schema"
+
+export const internal: Registry["items"] = []
diff --git a/apps/ui/src/registry/bases/base/internal/sink.tsx b/apps/ui/src/registry/bases/base/internal/sink.tsx
new file mode 100644
index 0000000..6b75958
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/internal/sink.tsx
@@ -0,0 +1,115 @@
+import AccordionExample from "@/registry/bases/base/examples/accordion-example"
+import AlertDialogExample from "@/registry/bases/base/examples/alert-dialog-example"
+import AlertExample from "@/registry/bases/base/examples/alert-example"
+import AspectRatioExample from "@/registry/bases/base/examples/aspect-ratio-example"
+import AvatarExample from "@/registry/bases/base/examples/avatar-example"
+import BadgeExample from "@/registry/bases/base/examples/badge-example"
+import BreadcrumbExample from "@/registry/bases/base/examples/breadcrumb-example"
+import ButtonExample from "@/registry/bases/base/examples/button-example"
+import ButtonGroupExample from "@/registry/bases/base/examples/button-group-example"
+import CalendarExample from "@/registry/bases/base/examples/calendar-example"
+import CardExample from "@/registry/bases/base/examples/card-example"
+import CarouselExample from "@/registry/bases/base/examples/carousel-example"
+import ChartExample from "@/registry/bases/base/examples/chart-example"
+import CheckboxExample from "@/registry/bases/base/examples/checkbox-example"
+import CollapsibleExample from "@/registry/bases/base/examples/collapsible-example"
+import ComboboxExample from "@/registry/bases/base/examples/combobox-example"
+import CommandExample from "@/registry/bases/base/examples/command-example"
+import ContextMenuExample from "@/registry/bases/base/examples/context-menu-example"
+import DialogExample from "@/registry/bases/base/examples/dialog-example"
+import DrawerExample from "@/registry/bases/base/examples/drawer-example"
+import DropdownMenuExample from "@/registry/bases/base/examples/dropdown-menu-example"
+import EmptyExample from "@/registry/bases/base/examples/empty-example"
+import FieldExample from "@/registry/bases/base/examples/field-example"
+import HoverCardExample from "@/registry/bases/base/examples/hover-card-example"
+import InputExample from "@/registry/bases/base/examples/input-example"
+import InputGroupExample from "@/registry/bases/base/examples/input-group-example"
+import InputOtpExample from "@/registry/bases/base/examples/input-otp-example"
+import ItemExample from "@/registry/bases/base/examples/item-example"
+import KbdExample from "@/registry/bases/base/examples/kbd-example"
+import LabelExample from "@/registry/bases/base/examples/label-example"
+import MenubarExample from "@/registry/bases/base/examples/menubar-example"
+import NativeSelectExample from "@/registry/bases/base/examples/native-select-example"
+import NavigationMenuExample from "@/registry/bases/base/examples/navigation-menu-example"
+import PaginationExample from "@/registry/bases/base/examples/pagination-example"
+import PopoverExample from "@/registry/bases/base/examples/popover-example"
+import ProgressExample from "@/registry/bases/base/examples/progress-example"
+import RadioGroupExample from "@/registry/bases/base/examples/radio-group-example"
+import ResizableExample from "@/registry/bases/base/examples/resizable-example"
+import ScrollAreaExample from "@/registry/bases/base/examples/scroll-area-example"
+import SelectExample from "@/registry/bases/base/examples/select-example"
+import SeparatorExample from "@/registry/bases/base/examples/separator-example"
+import SheetExample from "@/registry/bases/base/examples/sheet-example"
+import SkeletonExample from "@/registry/bases/base/examples/skeleton-example"
+import SliderExample from "@/registry/bases/base/examples/slider-example"
+import SonnerExample from "@/registry/bases/base/examples/sonner-example"
+import SpinnerExample from "@/registry/bases/base/examples/spinner-example"
+import SwitchExample from "@/registry/bases/base/examples/switch-example"
+import TableExample from "@/registry/bases/base/examples/table-example"
+import TabsExample from "@/registry/bases/base/examples/tabs-example"
+import TextareaExample from "@/registry/bases/base/examples/textarea-example"
+import ToggleExample from "@/registry/bases/base/examples/toggle-example"
+import ToggleGroupExample from "@/registry/bases/base/examples/toggle-group-example"
+import TooltipExample from "@/registry/bases/base/examples/tooltip-example"
+import { Toaster } from "@/registry/bases/base/ui/sonner"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/bases/base/lib/_registry.ts b/apps/ui/src/registry/bases/base/lib/_registry.ts
new file mode 100644
index 0000000..fd94cde
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/lib/_registry.ts
@@ -0,0 +1,15 @@
+import { type Registry } from "shadcn/schema"
+
+export const lib: Registry["items"] = [
+ {
+ name: "utils",
+ type: "registry:lib",
+ dependencies: ["clsx", "tailwind-merge"],
+ files: [
+ {
+ path: "lib/utils.ts",
+ type: "registry:lib",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/bases/base/lib/utils.ts b/apps/ui/src/registry/bases/base/lib/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/apps/ui/src/registry/bases/base/registry.ts b/apps/ui/src/registry/bases/base/registry.ts
new file mode 100644
index 0000000..e2e9bc7
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/registry.ts
@@ -0,0 +1,57 @@
+import { registryItemSchema, type Registry } from "shadcn/schema"
+import { z } from "zod"
+
+import { fonts } from "@/registry/fonts"
+
+import { blocks } from "./blocks/_registry"
+import { components } from "./components/_registry"
+import { examples } from "./examples/_registry"
+import { hooks } from "./hooks/_registry"
+import { internal } from "./internal/_registry"
+import { lib } from "./lib/_registry"
+import { ui } from "./ui/_registry"
+
+// Shared between index and style.
+const BASE_STYLE = {
+ type: "registry:style",
+ dependencies: ["class-variance-authority", "lucide-react", "@base-ui/react"],
+ devDependencies: ["tw-animate-css", "shadcn"],
+ registryDependencies: ["utils"],
+ css: {
+ '@import "tw-animate-css"': {},
+ '@import "shadcn/tailwind.css"': {},
+ "@layer base": {
+ "*": {
+ "@apply border-border outline-ring/50": {},
+ },
+ body: {
+ "@apply bg-background text-foreground": {},
+ },
+ },
+ },
+ cssVars: {},
+ files: [],
+}
+
+export const registry = {
+ name: "shadcn/ui",
+ homepage: "https://ui.shadcn.com",
+ items: z.array(registryItemSchema).parse([
+ {
+ name: "index",
+ ...BASE_STYLE,
+ },
+ {
+ name: "style",
+ ...BASE_STYLE,
+ },
+ ...ui,
+ ...examples,
+ ...lib,
+ ...components,
+ ...blocks,
+ ...hooks,
+ ...internal,
+ ...fonts,
+ ]),
+} satisfies Registry
diff --git a/apps/ui/src/registry/bases/base/ui/_registry.ts b/apps/ui/src/registry/bases/base/ui/_registry.ts
new file mode 100644
index 0000000..185cfdd
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/_registry.ts
@@ -0,0 +1,1025 @@
+import { type Registry } from "shadcn/schema"
+
+export const ui: Registry["items"] = [
+ {
+ name: "accordion",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/accordion.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/accordion",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/accordion-example.tsx",
+ api: "https://base-ui.com/react/components/accordion.md",
+ },
+ },
+ },
+ {
+ name: "alert",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/alert.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/alert",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/alert-example.tsx",
+ },
+ },
+ },
+ {
+ name: "alert-dialog",
+ type: "registry:ui",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/alert-dialog.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/alert-dialog",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/alert-dialog-example.tsx",
+ api: "https://base-ui.com/react/components/alert-dialog.md",
+ },
+ },
+ },
+ {
+ name: "aspect-ratio",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/aspect-ratio.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/aspect-ratio",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/aspect-ratio-example.tsx",
+ },
+ },
+ },
+ {
+ name: "avatar",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/avatar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/avatar",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/avatar-example.tsx",
+ api: "https://base-ui.com/react/components/avatar.md",
+ },
+ },
+ },
+ {
+ name: "badge",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/badge.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/badge",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/badge-example.tsx",
+ },
+ },
+ },
+ {
+ name: "breadcrumb",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/breadcrumb.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/breadcrumb",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/breadcrumb-example.tsx",
+ },
+ },
+ },
+ {
+ name: "button",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/button.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/button",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/button-example.tsx",
+ },
+ },
+ },
+ {
+ name: "button-group",
+ type: "registry:ui",
+ registryDependencies: ["separator"],
+ files: [
+ {
+ path: "ui/button-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/button-group",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/button-group-example.tsx",
+ },
+ },
+ },
+ {
+ name: "calendar",
+ type: "registry:ui",
+ dependencies: ["react-day-picker@latest", "date-fns"],
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/calendar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/calendar",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/calendar-example.tsx",
+ api: "https://react-day-picker.js.org",
+ },
+ },
+ },
+ {
+ name: "card",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/card.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/card",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/card-example.tsx",
+ },
+ },
+ },
+ {
+ name: "carousel",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/carousel.tsx",
+ type: "registry:ui",
+ },
+ ],
+ registryDependencies: ["button"],
+ dependencies: ["embla-carousel-react"],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/carousel",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/carousel-example.tsx",
+ api: "https://www.embla-carousel.com/get-started/react",
+ },
+ },
+ },
+ {
+ name: "chart",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/chart.tsx",
+ type: "registry:ui",
+ },
+ ],
+ registryDependencies: ["card"],
+ dependencies: ["recharts@3.8.0"],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/chart",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/chart-example.tsx",
+ },
+ },
+ },
+ {
+ name: "checkbox",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/checkbox.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/checkbox",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/checkbox-example.tsx",
+ api: "https://base-ui.com/react/components/checkbox.md",
+ },
+ },
+ },
+ {
+ name: "collapsible",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/collapsible.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/collapsible",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/collapsible-example.tsx",
+ api: "https://base-ui.com/react/components/collapsible.md",
+ },
+ },
+ },
+ {
+ name: "combobox",
+ type: "registry:ui",
+ registryDependencies: ["button", "input-group"],
+ dependencies: ["@base-ui/react"],
+ files: [
+ {
+ path: "ui/combobox.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/combobox",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/combobox-example.tsx",
+ api: "https://base-ui.com/react/components/combobox.md",
+ },
+ },
+ },
+ {
+ name: "command",
+ type: "registry:ui",
+ dependencies: ["cmdk"],
+ registryDependencies: ["dialog", "input-group"],
+ files: [
+ {
+ path: "ui/command.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/command",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/command-example.tsx",
+ api: "https://github.com/dip/cmdk",
+ },
+ },
+ },
+ {
+ name: "context-menu",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/context-menu.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/context-menu",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/context-menu-example.tsx",
+ api: "https://base-ui.com/react/components/context-menu.md",
+ },
+ },
+ },
+ {
+ name: "dialog",
+ type: "registry:ui",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/dialog.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/dialog",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/dialog-example.tsx",
+ api: "https://base-ui.com/react/components/dialog.md",
+ },
+ },
+ },
+ {
+ name: "drawer",
+ type: "registry:ui",
+ dependencies: ["vaul"],
+ files: [
+ {
+ path: "ui/drawer.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/drawer",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/drawer-example.tsx",
+ api: "https://vaul.emilkowal.ski/getting-started",
+ },
+ },
+ },
+ {
+ name: "dropdown-menu",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/dropdown-menu.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/dropdown-menu",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/dropdown-menu-example.tsx",
+ api: "https://base-ui.com/react/components/menu.md",
+ },
+ },
+ },
+ {
+ name: "empty",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/empty.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/empty",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/empty-example.tsx",
+ },
+ },
+ },
+ {
+ name: "field",
+ type: "registry:ui",
+ registryDependencies: ["label", "separator"],
+ files: [
+ {
+ path: "ui/field.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/field",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/field-example.tsx",
+ },
+ },
+ },
+ {
+ name: "form",
+ type: "registry:ui",
+ },
+ {
+ name: "hover-card",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/hover-card.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/hover-card",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/hover-card-example.tsx",
+ api: "https://base-ui.com/react/components/hover-card.md",
+ },
+ },
+ },
+ {
+ name: "input",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/input.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/input",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/input-example.tsx",
+ },
+ },
+ },
+ {
+ name: "input-group",
+ type: "registry:ui",
+ registryDependencies: ["button", "input", "textarea"],
+ files: [
+ {
+ path: "ui/input-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/input-group",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/input-group-example.tsx",
+ },
+ },
+ },
+ {
+ name: "input-otp",
+ type: "registry:ui",
+ dependencies: ["input-otp"],
+ files: [
+ {
+ path: "ui/input-otp.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/input-otp",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/input-otp-example.tsx",
+ api: "https://input-otp.rodz.dev",
+ },
+ },
+ },
+ {
+ name: "item",
+ type: "registry:ui",
+ registryDependencies: ["separator"],
+ files: [
+ {
+ path: "ui/item.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/item",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/item-example.tsx",
+ },
+ },
+ },
+ {
+ name: "label",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/label.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/label",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/label-example.tsx",
+ api: "https://base-ui.com/react/components/label.md",
+ },
+ },
+ },
+ {
+ name: "menubar",
+ type: "registry:ui",
+ registryDependencies: ["dropdown-menu"],
+ files: [
+ {
+ path: "ui/menubar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/menubar",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/menubar-example.tsx",
+ api: "https://base-ui.com/react/components/menubar.md",
+ },
+ },
+ },
+ {
+ name: "navigation-menu",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/navigation-menu.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/navigation-menu",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/navigation-menu-example.tsx",
+ api: "https://base-ui.com/react/components/navigation-menu.md",
+ },
+ },
+ },
+ {
+ name: "pagination",
+ type: "registry:ui",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/pagination.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/pagination",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/pagination-example.tsx",
+ },
+ },
+ },
+ {
+ name: "popover",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/popover.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/popover",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/popover-example.tsx",
+ api: "https://base-ui.com/react/components/popover.md",
+ },
+ },
+ },
+ {
+ name: "progress",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/progress.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/progress",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/progress-example.tsx",
+ api: "https://base-ui.com/react/components/progress.md",
+ },
+ },
+ },
+ {
+ name: "radio-group",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/radio-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/radio-group",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/radio-group-example.tsx",
+ api: "https://base-ui.com/react/components/radio-group.md",
+ },
+ },
+ },
+ {
+ name: "resizable",
+ type: "registry:ui",
+ dependencies: ["react-resizable-panels"],
+ files: [
+ {
+ path: "ui/resizable.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/resizable",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/resizable-example.tsx",
+ api: "https://github.com/bvaughn/react-resizable-panels",
+ },
+ },
+ },
+ {
+ name: "scroll-area",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/scroll-area.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/scroll-area",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/scroll-area-example.tsx",
+ api: "https://base-ui.com/react/components/scroll-area.md",
+ },
+ },
+ },
+ {
+ name: "select",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/select.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/select",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/select-example.tsx",
+ api: "https://base-ui.com/react/components/select.md",
+ },
+ },
+ },
+ {
+ name: "separator",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/separator.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/separator",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/separator-example.tsx",
+ api: "https://base-ui.com/react/components/separator.md",
+ },
+ },
+ },
+ {
+ name: "sheet",
+ type: "registry:ui",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/sheet.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/sheet",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/sheet-example.tsx",
+ api: "https://base-ui.com/react/components/dialog.md",
+ },
+ },
+ },
+ {
+ name: "sidebar",
+ type: "registry:ui",
+ registryDependencies: [
+ "button",
+ "input",
+ "separator",
+ "sheet",
+ "skeleton",
+ "tooltip",
+ "use-mobile",
+ ],
+ files: [
+ {
+ path: "ui/sidebar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/sidebar",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/sidebar-example.tsx",
+ },
+ },
+ },
+ {
+ name: "skeleton",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/skeleton.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/skeleton",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/skeleton-example.tsx",
+ },
+ },
+ },
+ {
+ name: "slider",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/slider.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/slider",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/slider-example.tsx",
+ api: "https://base-ui.com/react/components/slider.md",
+ },
+ },
+ },
+ {
+ name: "sonner",
+ type: "registry:ui",
+ dependencies: ["sonner", "next-themes"],
+ files: [
+ {
+ path: "ui/sonner.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/sonner",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/sonner-example.tsx",
+ api: "https://sonner.emilkowal.ski",
+ },
+ },
+ },
+ {
+ name: "spinner",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/spinner.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/spinner",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/spinner-example.tsx",
+ },
+ },
+ },
+ {
+ name: "switch",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/switch.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/switch",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/switch-example.tsx",
+ api: "https://base-ui.com/react/components/switch.md",
+ },
+ },
+ },
+ {
+ name: "table",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/table.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/table",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/table-example.tsx",
+ },
+ },
+ },
+ {
+ name: "tabs",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/tabs.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/tabs",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/tabs-example.tsx",
+ api: "https://base-ui.com/react/components/tabs.md",
+ },
+ },
+ },
+ {
+ name: "textarea",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/textarea.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/textarea",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/textarea-example.tsx",
+ },
+ },
+ },
+ {
+ name: "toggle",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/toggle.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/toggle",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/toggle-example.tsx",
+ api: "https://base-ui.com/react/components/toggle.md",
+ },
+ },
+ },
+ {
+ name: "toggle-group",
+ type: "registry:ui",
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "ui/toggle-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/toggle-group",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/toggle-group-example.tsx",
+ api: "https://base-ui.com/react/components/toggle-group.md",
+ },
+ },
+ },
+ {
+ name: "tooltip",
+ type: "registry:ui",
+ docs: `The \`tooltip\` component has been added. Remember to wrap your app with the \`TooltipProvider\` component.
+
+\`\`\`tsx title="app/layout.tsx"
+import { TooltipProvider } from "@/components/ui/tooltip"
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+\`\`\`
+`,
+ files: [
+ {
+ path: "ui/tooltip.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/tooltip",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/tooltip-example.tsx",
+ api: "https://base-ui.com/react/components/tooltip.md",
+ },
+ },
+ },
+ {
+ name: "kbd",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/kbd.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/kbd",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/kbd-example.tsx",
+ },
+ },
+ },
+ {
+ name: "native-select",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/native-select.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/native-select",
+ examples:
+ "https://raw.githubusercontent.com/shadcn-ui/ui/refs/heads/main/apps/v4/registry/bases/base/examples/native-select-example.tsx",
+ },
+ },
+ },
+ {
+ name: "direction",
+ type: "registry:ui",
+ dependencies: ["@base-ui/react"],
+ files: [
+ {
+ path: "ui/direction.tsx",
+ type: "registry:ui",
+ },
+ ],
+ meta: {
+ links: {
+ docs: "https://ui.shadcn.com/docs/components/base/direction",
+ api: "https://base-ui.com/react/utils/direction-provider.md",
+ },
+ },
+ },
+]
diff --git a/apps/ui/src/registry/bases/base/ui/accordion.tsx b/apps/ui/src/registry/bases/base/ui/accordion.tsx
new file mode 100644
index 0000000..a33f7ca
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/accordion.tsx
@@ -0,0 +1,88 @@
+import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
+ return (
+
+ )
+}
+
+function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
+ return (
+
+ )
+}
+
+function AccordionTrigger({
+ className,
+ children,
+ ...props
+}: AccordionPrimitive.Trigger.Props) {
+ return (
+
+
+ {children}
+
+
+
+
+ )
+}
+
+function AccordionContent({
+ className,
+ children,
+ ...props
+}: AccordionPrimitive.Panel.Props) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
diff --git a/apps/ui/src/registry/bases/base/ui/alert-dialog.tsx b/apps/ui/src/registry/bases/base/ui/alert-dialog.tsx
new file mode 100644
index 0000000..b5aa61c
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/alert-dialog.tsx
@@ -0,0 +1,174 @@
+
+import * as React from "react"
+import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Button } from "@/registry/bases/base/ui/button"
+
+function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
+ return
+}
+
+function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
+ return (
+
+ )
+}
+
+function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
+ return (
+
+ )
+}
+
+function AlertDialogOverlay({
+ className,
+ ...props
+}: AlertDialogPrimitive.Backdrop.Props) {
+ return (
+
+ )
+}
+
+function AlertDialogContent({
+ className,
+ size = "default",
+ ...props
+}: AlertDialogPrimitive.Popup.Props & {
+ size?: "default" | "sm"
+}) {
+ return (
+
+
+
+
+ )
+}
+
+function AlertDialogHeader({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDialogFooter({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDialogMedia({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogAction({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogCancel({
+ className,
+ variant = "outline",
+ size = "default",
+ ...props
+}: AlertDialogPrimitive.Close.Props &
+ Pick, "variant" | "size">) {
+ return (
+ }
+ {...props}
+ />
+ )
+}
+
+export {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogMedia,
+ AlertDialogOverlay,
+ AlertDialogPortal,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+}
diff --git a/apps/ui/src/registry/bases/base/ui/alert.tsx b/apps/ui/src/registry/bases/base/ui/alert.tsx
new file mode 100644
index 0000000..f1b6630
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/alert.tsx
@@ -0,0 +1,72 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+
+const alertVariants = cva("cn-alert group/alert relative w-full", {
+ variants: {
+ variant: {
+ default: "cn-alert-variant-default",
+ destructive: "cn-alert-variant-destructive",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+})
+
+function Alert({
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ )
+}
+
+function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDescription({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export { Alert, AlertTitle, AlertDescription, AlertAction }
diff --git a/apps/ui/src/registry/bases/base/ui/aspect-ratio.tsx b/apps/ui/src/registry/bases/base/ui/aspect-ratio.tsx
new file mode 100644
index 0000000..d005931
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/aspect-ratio.tsx
@@ -0,0 +1,22 @@
+import { cn } from "@/registry/bases/base/lib/utils"
+
+function AspectRatio({
+ ratio,
+ className,
+ ...props
+}: React.ComponentProps<"div"> & { ratio: number }) {
+ return (
+
+ )
+}
+
+export { AspectRatio }
diff --git a/apps/ui/src/registry/bases/base/ui/avatar.tsx b/apps/ui/src/registry/bases/base/ui/avatar.tsx
new file mode 100644
index 0000000..b41b9aa
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/avatar.tsx
@@ -0,0 +1,109 @@
+
+import * as React from "react"
+import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+
+function Avatar({
+ className,
+ size = "default",
+ ...props
+}: AvatarPrimitive.Root.Props & {
+ size?: "default" | "sm" | "lg"
+}) {
+ return (
+
+ )
+}
+
+function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
+ return (
+
+ )
+}
+
+function AvatarFallback({
+ className,
+ ...props
+}: AvatarPrimitive.Fallback.Props) {
+ return (
+
+ )
+}
+
+function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+ svg]:hidden",
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AvatarGroupCount({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export {
+ Avatar,
+ AvatarImage,
+ AvatarFallback,
+ AvatarGroup,
+ AvatarGroupCount,
+ AvatarBadge,
+}
diff --git a/apps/ui/src/registry/bases/base/ui/badge.tsx b/apps/ui/src/registry/bases/base/ui/badge.tsx
new file mode 100644
index 0000000..c67b787
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/badge.tsx
@@ -0,0 +1,48 @@
+import { mergeProps } from "@base-ui/react/merge-props"
+import { useRender } from "@base-ui/react/use-render"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+
+const badgeVariants = cva(
+ "cn-badge group/badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none",
+ {
+ variants: {
+ variant: {
+ default: "cn-badge-variant-default",
+ secondary: "cn-badge-variant-secondary",
+ destructive: "cn-badge-variant-destructive",
+ outline: "cn-badge-variant-outline",
+ ghost: "cn-badge-variant-ghost",
+ link: "cn-badge-variant-link",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Badge({
+ className,
+ variant = "default",
+ render,
+ ...props
+}: useRender.ComponentProps<"span"> & VariantProps) {
+ return useRender({
+ defaultTagName: "span",
+ props: mergeProps<"span">(
+ {
+ className: cn(badgeVariants({ variant }), className),
+ },
+ props
+ ),
+ render,
+ state: {
+ slot: "badge",
+ variant,
+ },
+ })
+}
+
+export { Badge, badgeVariants }
diff --git a/apps/ui/src/registry/bases/base/ui/breadcrumb.tsx b/apps/ui/src/registry/bases/base/ui/breadcrumb.tsx
new file mode 100644
index 0000000..ce626ec
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/breadcrumb.tsx
@@ -0,0 +1,137 @@
+import * as React from "react"
+import { mergeProps } from "@base-ui/react/merge-props"
+import { useRender } from "@base-ui/react/use-render"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+function Breadcrumb({ className, ...props }: React.ComponentProps<"nav">) {
+ return (
+
+ )
+}
+
+function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
+ return (
+
+ )
+}
+
+function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
+ return (
+
+ )
+}
+
+function BreadcrumbLink({
+ className,
+ render,
+ ...props
+}: useRender.ComponentProps<"a">) {
+ return useRender({
+ defaultTagName: "a",
+ props: mergeProps<"a">(
+ {
+ className: cn("cn-breadcrumb-link", className),
+ },
+ props
+ ),
+ render,
+ state: {
+ slot: "breadcrumb-link",
+ },
+ })
+}
+
+function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+
+ )
+}
+
+function BreadcrumbSeparator({
+ children,
+ className,
+ ...props
+}: React.ComponentProps<"li">) {
+ return (
+
+ {children ?? (
+
+ )}
+
+ )
+}
+
+function BreadcrumbEllipsis({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+
+ More
+
+ )
+}
+
+export {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+ BreadcrumbEllipsis,
+}
diff --git a/apps/ui/src/registry/bases/base/ui/button-group.tsx b/apps/ui/src/registry/bases/base/ui/button-group.tsx
new file mode 100644
index 0000000..c1f1aea
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/button-group.tsx
@@ -0,0 +1,87 @@
+import { mergeProps } from "@base-ui/react/merge-props"
+import { useRender } from "@base-ui/react/use-render"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Separator } from "@/registry/bases/base/ui/separator"
+
+const buttonGroupVariants = cva(
+ "cn-button-group flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
+ {
+ variants: {
+ orientation: {
+ horizontal:
+ "cn-button-group-orientation-horizontal *:data-slot:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
+ vertical:
+ "cn-button-group-orientation-vertical flex-col *:data-slot:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
+ },
+ },
+ defaultVariants: {
+ orientation: "horizontal",
+ },
+ }
+)
+
+function ButtonGroup({
+ className,
+ orientation,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ )
+}
+
+function ButtonGroupText({
+ className,
+ render,
+ ...props
+}: useRender.ComponentProps<"div">) {
+ return useRender({
+ defaultTagName: "div",
+ props: mergeProps<"div">(
+ {
+ className: cn(
+ "cn-button-group-text flex items-center [&_svg]:pointer-events-none",
+ className
+ ),
+ },
+ props
+ ),
+ render,
+ state: {
+ slot: "button-group-text",
+ },
+ })
+}
+
+function ButtonGroupSeparator({
+ className,
+ orientation = "vertical",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ ButtonGroup,
+ ButtonGroupSeparator,
+ ButtonGroupText,
+ buttonGroupVariants,
+}
diff --git a/apps/ui/src/registry/bases/base/ui/button.tsx b/apps/ui/src/registry/bases/base/ui/button.tsx
new file mode 100644
index 0000000..fa34317
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/button.tsx
@@ -0,0 +1,51 @@
+import { Button as ButtonPrimitive } from "@base-ui/react/button"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+
+const buttonVariants = cva(
+ "cn-button group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
+ {
+ variants: {
+ variant: {
+ default: "cn-button-variant-default",
+ outline: "cn-button-variant-outline",
+ secondary: "cn-button-variant-secondary",
+ ghost: "cn-button-variant-ghost",
+ destructive: "cn-button-variant-destructive",
+ link: "cn-button-variant-link",
+ },
+ size: {
+ default: "cn-button-size-default",
+ xs: "cn-button-size-xs",
+ sm: "cn-button-size-sm",
+ lg: "cn-button-size-lg",
+ icon: "cn-button-size-icon",
+ "icon-xs": "cn-button-size-icon-xs",
+ "icon-sm": "cn-button-size-icon-sm",
+ "icon-lg": "cn-button-size-icon-lg",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+function Button({
+ className,
+ variant = "default",
+ size = "default",
+ ...props
+}: ButtonPrimitive.Props & VariantProps) {
+ return (
+
+ )
+}
+
+export { Button, buttonVariants }
diff --git a/apps/ui/src/registry/bases/base/ui/calendar.tsx b/apps/ui/src/registry/bases/base/ui/calendar.tsx
new file mode 100644
index 0000000..076f82b
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/calendar.tsx
@@ -0,0 +1,244 @@
+
+import * as React from "react"
+import {
+ DayPicker,
+ getDefaultClassNames,
+ type DayButton,
+ type Locale,
+} from "react-day-picker"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Button, buttonVariants } from "@/registry/bases/base/ui/button"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = "label",
+ buttonVariant = "ghost",
+ locale,
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps["variant"]
+}) {
+ const defaultClassNames = getDefaultClassNames()
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className
+ )}
+ captionLayout={captionLayout}
+ locale={locale}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString(locale?.code, { month: "short" }),
+ ...formatters,
+ }}
+ classNames={{
+ root: cn("w-fit", defaultClassNames.root),
+ months: cn(
+ "relative flex flex-col gap-4 md:flex-row",
+ defaultClassNames.months
+ ),
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
+ nav: cn(
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
+ defaultClassNames.nav
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_previous
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_next
+ ),
+ month_caption: cn(
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
+ defaultClassNames.month_caption
+ ),
+ dropdowns: cn(
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
+ defaultClassNames.dropdowns
+ ),
+ dropdown_root: cn(
+ "cn-calendar-dropdown-root relative rounded-(--cell-radius)",
+ defaultClassNames.dropdown_root
+ ),
+ dropdown: cn(
+ "absolute inset-0 bg-popover opacity-0",
+ defaultClassNames.dropdown
+ ),
+ caption_label: cn(
+ "font-medium select-none",
+ captionLayout === "label"
+ ? "cn-calendar-caption text-sm"
+ : "cn-calendar-caption-label flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
+ defaultClassNames.caption_label
+ ),
+ table: "w-full border-collapse",
+ weekdays: cn("flex", defaultClassNames.weekdays),
+ weekday: cn(
+ "flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none",
+ defaultClassNames.weekday
+ ),
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
+ week_number_header: cn(
+ "w-(--cell-size) select-none",
+ defaultClassNames.week_number_header
+ ),
+ week_number: cn(
+ "text-[0.8rem] text-muted-foreground select-none",
+ defaultClassNames.week_number
+ ),
+ day: cn(
+ "group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
+ props.showWeekNumber
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)"
+ : "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)",
+ defaultClassNames.day
+ ),
+ range_start: cn(
+ "relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted",
+ defaultClassNames.range_start
+ ),
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
+ range_end: cn(
+ "relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted",
+ defaultClassNames.range_end
+ ),
+ today: cn(
+ "rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none",
+ defaultClassNames.today
+ ),
+ outside: cn(
+ "text-muted-foreground aria-selected:text-muted-foreground",
+ defaultClassNames.outside
+ ),
+ disabled: cn(
+ "text-muted-foreground opacity-50",
+ defaultClassNames.disabled
+ ),
+ hidden: cn("invisible", defaultClassNames.hidden),
+ ...classNames,
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ )
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === "left") {
+ return (
+
+ )
+ }
+
+ if (orientation === "right") {
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+ },
+ DayButton: ({ ...props }) => (
+
+ ),
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+
+ )
+ },
+ ...components,
+ }}
+ {...props}
+ />
+ )
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ locale,
+ ...props
+}: React.ComponentProps & { locale?: Partial }) {
+ const defaultClassNames = getDefaultClassNames()
+
+ const ref = React.useRef(null)
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus()
+ }, [modifiers.focused])
+
+ return (
+ span]:text-xs [&>span]:opacity-70",
+ defaultClassNames.day,
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+export { Calendar, CalendarDayButton }
diff --git a/apps/ui/src/registry/bases/base/ui/card.tsx b/apps/ui/src/registry/bases/base/ui/card.tsx
new file mode 100644
index 0000000..bed029b
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/card.tsx
@@ -0,0 +1,94 @@
+import * as React from "react"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+
+function Card({
+ className,
+ size = "default",
+ ...props
+}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
+ return (
+
+ )
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+}
diff --git a/apps/ui/src/registry/bases/base/ui/carousel.tsx b/apps/ui/src/registry/bases/base/ui/carousel.tsx
new file mode 100644
index 0000000..5312c33
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/carousel.tsx
@@ -0,0 +1,255 @@
+
+import * as React from "react"
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+import { Button } from "@/registry/bases/base/ui/button"
+import { IconPlaceholder } from "@/routes/create/components/icon-placeholder"
+
+type CarouselApi = UseEmblaCarouselType[1]
+type UseCarouselParameters = Parameters
+type CarouselOptions = UseCarouselParameters[0]
+type CarouselPlugin = UseCarouselParameters[1]
+
+type CarouselProps = {
+ opts?: CarouselOptions
+ plugins?: CarouselPlugin
+ orientation?: "horizontal" | "vertical"
+ setApi?: (api: CarouselApi) => void
+}
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0]
+ api: ReturnType[1]
+ scrollPrev: () => void
+ scrollNext: () => void
+ canScrollPrev: boolean
+ canScrollNext: boolean
+} & CarouselProps
+
+const CarouselContext = React.createContext(null)
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext)
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ")
+ }
+
+ return context
+}
+
+function Carousel({
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ ...props
+}: React.ComponentProps<"div"> & CarouselProps) {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins
+ )
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) return
+ setCanScrollPrev(api.canScrollPrev())
+ setCanScrollNext(api.canScrollNext())
+ }, [])
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev()
+ }, [api])
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext()
+ }, [api])
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault()
+ scrollPrev()
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault()
+ scrollNext()
+ }
+ },
+ [scrollPrev, scrollNext]
+ )
+
+ React.useEffect(() => {
+ if (!api || !setApi) return
+ setApi(api)
+ }, [api, setApi])
+
+ React.useEffect(() => {
+ if (!api) return
+ onSelect(api)
+ api.on("reInit", onSelect)
+ api.on("select", onSelect)
+
+ return () => {
+ api?.off("select", onSelect)
+ }
+ }, [api, onSelect])
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
+ const { carouselRef, orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
+ const { orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+function CarouselPrevious({
+ className,
+ variant = "outline",
+ size = "icon-sm",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
+
+ return (
+
+
+ Previous slide
+
+ )
+}
+
+function CarouselNext({
+ className,
+ variant = "outline",
+ size = "icon-sm",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
+
+ return (
+
+
+ Next slide
+
+ )
+}
+
+export {
+ type CarouselApi,
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselPrevious,
+ CarouselNext,
+ useCarousel,
+}
diff --git a/apps/ui/src/registry/bases/base/ui/chart.tsx b/apps/ui/src/registry/bases/base/ui/chart.tsx
new file mode 100644
index 0000000..4ebc56c
--- /dev/null
+++ b/apps/ui/src/registry/bases/base/ui/chart.tsx
@@ -0,0 +1,369 @@
+
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+import type { TooltipValueType } from "recharts"
+
+import { cn } from "@/registry/bases/base/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+const INITIAL_DIMENSION = { width: 320, height: 200 } as const
+type TooltipNameType = number | string
+
+export type ChartConfig = Record<
+ string,
+ {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ )
+>
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a ")
+ }
+
+ return context
+}
+
+function ChartContainer({
+ id,
+ className,
+ children,
+ config,
+ initialDimension = INITIAL_DIMENSION,
+ ...props
+}: React.ComponentProps<"div"> & {
+ config: ChartConfig
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ initialDimension?: {
+ width: number
+ height: number
+ }
+}) {
+ const uniqueId = React.useId()
+ const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ )
+}
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([, config]) => config.theme ?? config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+ "
+ },
+ {
+ "name": "@diceui",
+ "homepage": "https://www.diceui.com/",
+ "url": "https://diceui.com/r/{name}.json",
+ "description": "Accessible shadcn/ui components built with React, TypeScript, and Tailwind CSS. Copy-paste ready, and customizable.",
+ "logo": " "
+ },
+ {
+ "name": "@doras-ui",
+ "homepage": "https://ui.doras.to/",
+ "url": "https://ui.doras.to/r/{name}.json",
+ "description": "A collection of beautiful, reusable component blocks built with React",
+ "logo": " "
+ },
+ {
+ "name": "@dsikeres1",
+ "homepage": "https://dsikeres1.github.io/react-date-range-picker/",
+ "url": "https://dsikeres1.github.io/react-date-range-picker/r/{name}.json",
+ "description": "A headless, composable date & date range picker for React. 5 picker types, 15 locales, dark mode, accessible. Zero dependencies.",
+ "logo": " "
+ },
+ {
+ "name": "@elements",
+ "homepage": "https://www.tryelements.dev",
+ "url": "https://www.tryelements.dev/r/{name}.json",
+ "description": "Full-stack shadcn/ui components that go beyond UI. Add auth, monetization, uploads, and AI to your app in seconds.",
+ "logo": " "
+ },
+ {
+ "name": "@elevenlabs-ui",
+ "homepage": "https://ui.elevenlabs.io",
+ "url": "https://ui.elevenlabs.io/r/{name}.json",
+ "description": "A collection of Open Source agent and audio components that you can customize and extend.",
+ "logo": "ElevenLabs "
+ },
+ {
+ "name": "@efferd",
+ "homepage": "https://efferd.com/",
+ "url": "https://efferd.com/r/{name}.json",
+ "description": "A collection of beautifully crafted Shadcn/UI blocks, designed to help developers build modern websites with ease.",
+ "logo": " "
+ },
+ {
+ "name": "@einui",
+ "homepage": "https://ui.eindev.ir",
+ "url": "https://ui.eindev.ir/r/{name}.json",
+ "description": "Beautiful, responsive Shadcn components with frosted glass morphism. Built for modern web applications with full dark mode support.",
+ "logo": " "
+ },
+ {
+ "name": "@eldoraui",
+ "homepage": "https://eldoraui.site",
+ "url": "https://eldoraui.site/r/{name}.json",
+ "description": "An open-source, modern UI component library for React, built with TypeScript, Tailwind CSS, and Framer Motion. Eldora UI offers beautifully crafted, reusable components designed for performance and elegance.",
+ "logo": " "
+ },
+ {
+ "name": "@formcn",
+ "homepage": "https://formcn.dev",
+ "url": "https://formcn.dev/r/{name}.json",
+ "description": "Build production-ready forms with a few clicks using shadcn components and modern tools.",
+ "logo": " "
+ },
+ {
+ "name": "@flightcn",
+ "homepage": "https://flightcn.yencheng.dev",
+ "url": "https://flightcn.yencheng.dev/r/{name}.json",
+ "description": "Flight routes on interactive maps with great-circle arcs, airport markers, multi-leg journeys, and optional animation, built for mapcn.",
+ "logo": " "
+ },
+ {
+ "name": "@gaia",
+ "homepage": "https://ui.heygaia.io",
+ "url": "https://ui.heygaia.io/r/{name}.json",
+ "description": "Production-ready UI components designed for building beautiful AI assistants and conversational interfaces, from the team behind GAIA.",
+ "logo": " "
+ },
+ {
+ "name": "@gc-solid",
+ "url": "https://binnodon.github.io/gc-solid-ui/r/{name}.json",
+ "description": "SolidJS port of shadcn-ui components built with Kobalte primitives. 57+ components with full TypeScript support and Vega theme.",
+ "author": "binnodon",
+ "homepage": "https://binnodon.github.io/gc-solid-ui",
+ "logo": " "
+ },
+ {
+ "name": "@gamifykit",
+ "homepage": "https://gamifykit.com",
+ "url": "https://gamifykit.com/r/{name}.json",
+ "description": "A collection of fully composable components that extend shadcn/ui with a focus on common gamification UI patterns.",
+ "logo": " "
+ },
+ {
+ "name": "@glass-ui",
+ "homepage": "https://glass-ui.crenspire.com",
+ "url": "https://glass-ui.crenspire.com/r/{name}.json",
+ "description": "A shadcn-ui compatible registry distributing 40+ glassmorphic React/TypeScript components with Apple-inspired design. Components include enhanced visual effects (glow, shimmer, ripple), theme support, and customizable glassmorphism styling.",
+ "logo": " "
+ },
+ {
+ "name": "@ha-components",
+ "homepage": "https://hacomponents.keshuac.com",
+ "url": "https://hacomponents.keshuac.com/r/{name}.json",
+ "description": "A collection of customisable components to build Home Assistant dashboards.",
+ "logo": " "
+ },
+ {
+ "name": "@hextaui",
+ "homepage": "https://hextaui.com",
+ "url": "https://hextaui.com/r/{name}.json",
+ "description": "Ready-to-use foundation components/blocks built on top of shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcnhooks",
+ "homepage": "https://shadcn-hooks.com",
+ "url": "https://shadcn-hooks.com/r/{name}.json",
+ "description": "A comprehensive React Hooks Collection built with Shadcn.",
+ "logo": " "
+ },
+ {
+ "name": "@inferencesh",
+ "homepage": "https://ui.inference.sh",
+ "url": "https://ui.inference.sh/r/{name}.json",
+ "description": "batteries-included agent components by inference.sh. chat interfaces with streaming, tool invocation rendering, syntax-highlighted code blocks, markdown renderer, and more.",
+ "logo": " "
+ },
+ {
+ "name": "@intentui",
+ "homepage": "https://intentui.com",
+ "url": "https://intentui.com/r/{name}",
+ "description": "Accessible React component library to copy, customize, and own your UI.",
+ "logo": " "
+ },
+ {
+ "name": "@jalco",
+ "homepage": "https://ui.justinlevine.me",
+ "url": "https://ui.justinlevine.me/r/{name}.json",
+ "description": "A curated collection of GitHub-integrated, documentation, and developer-facing components. Self-contained, zero-dependency, and production-ready.",
+ "logo": " "
+ },
+ {
+ "name": "@kibo-ui",
+ "homepage": "https://www.kibo-ui.com/",
+ "url": "https://www.kibo-ui.com/r/{name}.json",
+ "description": "Kibo UI is a custom registry of composable, accessible and open source components designed for use with shadcn/ui.",
+ "logo": "Kibo UI "
+ },
+ {
+ "name": "@kanpeki",
+ "homepage": "https://kanpeki.vercel.app",
+ "url": "https://kanpeki.vercel.app/r/{name}.json",
+ "description": "A set of perfect-designed components built on top of React Aria and Motion.",
+ "logo": " "
+ },
+ {
+ "name": "@kapwa",
+ "homepage": "https://kapwa-two.vercel.app",
+ "url": "https://kapwa-two.vercel.app/r/{name}.json",
+ "description": "Cleanly designed components purposely built for open-source government portals.",
+ "logo": " "
+ },
+ {
+ "name": "@kokonutui",
+ "homepage": "https://kokonutui.com",
+ "url": "https://kokonutui.com/r/{name}.json",
+ "description": "Collection of stunning components built with Tailwind CSS, shadcn/ui and Motion to use on your websites.",
+ "logo": " "
+ },
+ {
+ "name": "@launchui",
+ "homepage": "https://www.launchuicomponents.com/",
+ "url": "https://www.launchuicomponents.com/r/{name}.json",
+ "description": "Carefully crafted landing page components and templates built with React, Shadcn/ui and Tailwind.",
+ "logo": " "
+ },
+ {
+ "name": "@lens-blocks",
+ "homepage": "https://lensblocks.com",
+ "url": "https://lensblocks.com/r/{name}.json",
+ "description": "A collection of social media components for use with Lens Social Protocol.",
+ "logo": " "
+ },
+ {
+ "name": "@limeplay",
+ "homepage": "https://limeplay.winoffrg.dev",
+ "description": "Modern UI Library for building media players in React. Powered by Shaka Player.",
+ "url": "https://limeplay.winoffrg.dev/r/{name}.json",
+ "logo": " "
+ },
+ {
+ "name": "@lmscn",
+ "homepage": "https://lmscn.vercel.app",
+ "url": "https://lmscn.vercel.app/r/{name}.json",
+ "description": "LMS components for building interactive learning experiences — quiz, flashcards, matching, fill-in-the-blank, word scramble, sequencing, reading comprehension, spaced repetition and more.",
+ "logo": "lmscn "
+ },
+ {
+ "name": "@lucide-animated",
+ "homepage": "https://lucide-animated.com",
+ "url": "https://lucide-animated.com/r/{name}.json",
+ "description": "An open-source collection of smooth animated lucide icons for your projects",
+ "logo": " "
+ },
+ {
+ "name": "@lytenyte",
+ "homepage": "https://www.1771technologies.com",
+ "url": "https://www.1771technologies.com/r/{name}.json",
+ "description": "LyteNyte Grid is a high performance, light weight, headless, React data grid. Our registry provides LyteNyte Grid themed using Tailwind and the Shadcn theme variables.",
+ "logo": " "
+ },
+ {
+ "name": "@magicui",
+ "homepage": "https://magicui.design",
+ "url": "https://magicui.design/r/{name}",
+ "description": "UI Library for Design Engineers. 150+ free and open-source animated components and effects built with React, Typescript, Tailwind CSS, and Motion. Perfect companion for shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@manifest",
+ "homepage": "https://ui.manifest.build",
+ "url": "https://ui.manifest.build/r/{name}.json",
+ "description": "Agentic UI toolkit for building MCP Apps. Open-source components and blocks ready to use within your chat app.",
+ "logo": " "
+ },
+ {
+ "name": "@mapcn",
+ "homepage": "https://mapcn.dev",
+ "url": "https://mapcn.dev/r/{name}.json",
+ "description": "Beautiful maps, made simple. Ready to use, customizable map components for React. Built on MapLibre. Styled with Tailwind CSS.",
+ "logo": " "
+ },
+ {
+ "name": "@mui-treasury",
+ "homepage": "https://www.mui-treasury.com",
+ "url": "https://mui-treasury.com/r/{name}.json",
+ "description": "A collection of hand-crafted interfaces built on top of MUI components",
+ "logo": " "
+ },
+ {
+ "name": "@moleculeui",
+ "homepage": "https://www.moleculeui.design/",
+ "url": "https://www.moleculeui.design/r/{name}.json",
+ "description": "A modern React component library focused on intuitive interactions and seamless user experiences.",
+ "logo": " "
+ },
+ {
+ "name": "@motion-primitives",
+ "homepage": "https://www.motion-primitives.com",
+ "url": "https://motion-primitives.com/c/{name}.json",
+ "description": "Beautifully designed motions components. Easy copy-paste. Customizable. Open Source. Built for engineers and designers.",
+ "logo": " "
+ },
+ {
+ "name": "@ncdai",
+ "homepage": "https://chanhdai.com/components",
+ "url": "https://chanhdai.com/r/{name}.json",
+ "description": "A collection of reusable components.",
+ "logo": " "
+ },
+ {
+ "name": "@nteract",
+ "homepage": "https://nteract-elements.vercel.app/",
+ "url": "https://nteract-elements.vercel.app/r/{name}.json",
+ "description": "Components for interactive computing notebooks.",
+ "logo": " "
+ },
+ {
+ "name": "@nuqs",
+ "homepage": "https://nuqs.dev/registry",
+ "description": "Custom parsers, adapters and utilities from the community for type-safe URL state management.",
+ "url": "https://nuqs.dev/r/{name}.json",
+ "logo": " "
+ },
+ {
+ "name": "@neobrutalism",
+ "homepage": "https://www.neobrutalism.dev",
+ "url": "https://www.neobrutalism.dev/r/{name}.json",
+ "description": "A collection of neobrutalism-styled components based on shadcn/ui",
+ "logo": "N "
+ },
+ {
+ "name": "@nessra-ui",
+ "homepage": "https://nessra-ui.vercel.app",
+ "url": "https://nessra-ui.vercel.app/r/{name}.json",
+ "description": "Beautiful, accessible components built with Tailwind CSS v4 and Radix UI. Includes auth blocks, data tables, and TanStack Form integration.",
+ "logo": " "
+ },
+ {
+ "name": "@nexus-elements",
+ "homepage": "https://elements.nexus.availproject.org/docs/view-components",
+ "description": "Ready-made React components for almost any use case. Use as is or customise and go to market fast",
+ "url": "https://elements.nexus.availproject.org/r/{name}.json",
+ "logo": " "
+ },
+ {
+ "name": "@openstatus",
+ "homepage": "https://openstatus.dev/registry",
+ "url": "https://openstatus.dev/r/{name}.json",
+ "description": "Hand-crafted, accessible components for building beautiful status pages.",
+ "logo": " "
+ },
+ {
+ "name": "@optics",
+ "homepage": "https://optics.agusmayol.com.ar",
+ "url": "https://optics.agusmayol.com.ar/r/{name}.json",
+ "description": "A design system that distributes re-styled components, utilities, and hooks ready to use.",
+ "logo": " "
+ },
+ {
+ "name": "@oui",
+ "homepage": "https://oui.mw10013.workers.dev",
+ "url": "https://oui.mw10013.workers.dev/r/{name}.json",
+ "description": "React Aria Components with shadcn characteristics.Copy-and-paste react aria components that run side-by-side with shadcn components.",
+ "logo": " "
+ },
+ {
+ "name": "@pacekit",
+ "homepage": "https://ui.pacekit.dev",
+ "url": "https://ui.pacekit.dev/r/{name}.json",
+ "description": "Carefully built UI blocks for real apps and dashboards, designed to integrate smoothly from early ideas to production releases.",
+ "logo": " "
+ },
+ {
+ "name": "@pacekit-gsap",
+ "homepage": "https://gsap.pacekit.dev",
+ "url": "https://gsap.pacekit.dev/r/{name}.json",
+ "description": "Animated GSAP components crafted for smooth interaction and rich detail.",
+ "logo": " "
+ },
+ {
+ "name": "@pastecn",
+ "homepage": "https://pastecn.com",
+ "url": "https://pastecn.com/r/{name}",
+ "description": "pastebin + shadcn = pastecn. Paste your code and get a shadcn-compatible registry URL instantly.",
+ "logo": " "
+ },
+ {
+ "name": "@paykit-sdk",
+ "homepage": "https://www.usepaykit.dev",
+ "url": "https://www.usepaykit.dev/r/{name}.json",
+ "description": "Unified payments SDK for builders — handle checkout, billing, and webhooks across Stripe, PayPal, Adyen, and regional gateways with a single integration.",
+ "logo": " "
+ },
+ {
+ "name": "@plate",
+ "homepage": "https://platejs.org",
+ "url": "https://platejs.org/r/{name}.json",
+ "description": "AI-powered rich text editor for React.",
+ "logo": " "
+ },
+ {
+ "name": "@prompt-kit",
+ "homepage": "https://www.prompt-kit.com",
+ "url": "https://www.prompt-kit.com/c/{name}.json",
+ "description": "Core building blocks for AI apps. High-quality, accessible, and customizable components for AI interfaces.",
+ "logo": " "
+ },
+ {
+ "name": "@prosekit",
+ "homepage": "https://prosekit.dev",
+ "url": "https://prosekit.dev/r/{name}.json",
+ "description": "Powerful and flexible rich text editor for React, Vue, Preact, Svelte, and SolidJS.",
+ "logo": " "
+ },
+ {
+ "name": "@phucbm",
+ "homepage": "https://phucbm.com/components",
+ "url": "https://phucbm.com/r/{name}.json",
+ "description": "A collection of modern React UI components with GSAP animations.",
+ "logo": " "
+ },
+ {
+ "name": "@react-aria",
+ "homepage": "https://react-aria.adobe.com",
+ "url": "https://react-aria.adobe.com/registry/{name}.json",
+ "description": "Customizable Tailwind and Vanilla CSS components with adaptive interactions, top-tier accessibility, and internationalization.",
+ "logo": " "
+ },
+ {
+ "name": "@react-bits",
+ "homepage": "https://reactbits.dev",
+ "url": "https://reactbits.dev/r/{name}.json",
+ "description": "A large collection of animated, interactive & fully customizable React components for building memorable websites. From smooth text animations all the way to eye-catching backgrounds, you can find it here.",
+ "logo": " "
+ },
+ {
+ "name": "@react-easy-modals",
+ "homepage": "https://react-easy-modals-docs.vercel.app",
+ "url": "https://react-easy-modals-docs.vercel.app/r/styles/{style}/{name}.json",
+ "description": "Modal component for react-easy-modals. Integrates with shadcn Dialog for a simple, powerful modal system with TypeScript support and promise-based API.",
+ "logo": " "
+ },
+ {
+ "name": "@react-slot",
+ "homepage": "https://react-slot.vercel.app/",
+ "url": "https://react-slot.vercel.app/r/{name}.json",
+ "description": "Vue-style slot composition for React - Fine-grained control over component composition",
+ "logo": " "
+ },
+ {
+ "name": "@retroui",
+ "description": "A Neobrutalism styled React + TailwindCSS UI library for building bold, modern web apps. Perfect for any project using Shadcn/ui.",
+ "homepage": "https://retroui.dev",
+ "url": "https://retroui.dev/r/{name}.json",
+ "logo": " "
+ },
+ {
+ "name": "@reui",
+ "homepage": "https://reui.io",
+ "url": "https://reui.io/r/{style}/{name}.json",
+ "description": "Free & open-source library of 1,000+ components and patterns to 10x your productivity in shadcn projects.",
+ "logo": " "
+ },
+ {
+ "name": "@scrollxui",
+ "homepage": "https://www.scrollxui.dev",
+ "url": "https://www.scrollxui.dev/registry/{name}.json",
+ "description": "ScrollX UI is an open-source React and shadcn-compatible component library for animated, interactive, and customizable user interfaces. It offers motion-driven components that blend seamlessly with modern ShadCN setups.",
+ "logo": " "
+ },
+ {
+ "name": "@spell",
+ "homepage": "https://spell.sh",
+ "url": "https://spell.sh/r/{name}.json",
+ "description": "Beautiful, sophisticated UI components designed for modern React and Tailwind CSS applications.",
+ "logo": " "
+ },
+ {
+ "name": "@square-ui",
+ "homepage": "https://square.lndev.me",
+ "url": "https://square.lndev.me/registry/{name}.json",
+ "description": "Collection of beautifully crafted open-source layouts UI built with shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@systaliko-ui",
+ "homepage": "https://systaliko-ui.vercel.app",
+ "url": "https://systaliko-ui.vercel.app/r/{name}.json",
+ "description": "UI component library, Designed for flexibility, built for customization, and crafted to scale across variants and use cases.",
+ "logo": " "
+ },
+ {
+ "name": "@roiui",
+ "homepage": "https://roiui.com",
+ "url": "https://roiui.com/r/{name}.json",
+ "description": "Roi UI is a library that offers UI components and blocks built with Base UI primitives. Some blocks and components use motion (framer). Everything is open-source and will be forever.",
+ "logo": " "
+ },
+ {
+ "name": "@slide-cn",
+ "homepage": "https://slide-cn.com",
+ "url": "https://slide-cn.com/r/{name}.json",
+ "description": "A component library to build slide decks using code.",
+ "logo": " "
+ },
+ {
+ "name": "@satoriui",
+ "homepage": "https://satoriui.site",
+ "url": "https://satoriui.site/r/{name}.json",
+ "description": "A comprehensive suite of high-fidelity interaction components. It offers motion-driven components that designed with motion-react and tailwindcss, that blends seamlessly.",
+ "logo": " "
+ },
+ {
+ "name": "@solaceui",
+ "homepage": "https://www.solaceui.com",
+ "url": "https://www.solaceui.com/r/{name}.json",
+ "description": "Production-ready and tastefully crafted sections, animated components, and full-page templates for Next.js, Tailwind CSS & Motion",
+ "logo": " "
+ },
+ {
+ "name": "@shadcnblocks",
+ "homepage": "https://shadcnblocks.com",
+ "url": "https://shadcnblocks.com/r/{name}.json",
+ "description": "A registry with hundreds of extra blocks for shadcn ui.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcndesign",
+ "homepage": "https://www.shadcndesign.com",
+ "url": "https://shadcndesign-free.vercel.app/r/{name}.json",
+ "description": "A growing collection of high-quality blocks and themes for shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcnmaps",
+ "homepage": "https://shadcnmaps.com",
+ "url": "https://shadcnmaps.com/r/{name}.json",
+ "description": "Beautiful map components powered by pure SVG.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcnstore",
+ "homepage": "https://www.shadcnstore.com",
+ "url": "https://shadcnstore.com/r/{name}.json",
+ "description": "A growing collection of shadcn/ui components, blocks, and templates for building modern web apps.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcn-map",
+ "homepage": "https://shadcn-map.vercel.app",
+ "url": "http://shadcn-map.vercel.app/r/{name}.json",
+ "description": "A map component for shadcn/ui. Built with Leaflet and React Leaflet.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcn-studio",
+ "homepage": "https://shadcnstudio.com",
+ "url": "https://shadcnstudio.com/r/{name}.json",
+ "description": "An open-source set of shadcn/ui components, blocks, and templates with a powerful theme generator.",
+ "logo": " "
+ },
+ {
+ "name": "@waves-cn",
+ "homepage": "https://waves-cn.vercel.app",
+ "url": "https://waves-cn.vercel.app/r/{name}.json",
+ "description": "A collection of wave players and waveform components built with wavesurfer.js and shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcn-editor",
+ "homepage": "https://shadcn-editor.vercel.app",
+ "url": "https://raw.githubusercontent.com/htmujahid/shadcn-editor/refs/heads/main/public/r/{name}.json",
+ "description": "Accessible, Customizable, Rich Text Editor. Made with Lexical and Shadcn/UI. Open Source. Open Code.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcnui-blocks",
+ "homepage": "https://shadcnui-blocks.com",
+ "url": "https://shadcnui-blocks.com/r/{name}.json",
+ "description": "A collection of premium, production-ready shadcn/ui blocks, components and templates.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcnuikit",
+ "homepage": "https://shadcnuikit.com",
+ "url": "https://shadcnuikit.com/r/{name}.json",
+ "description": "Launch your projects faster with admin dashboards, website templates, components, blocks, and pre-built real-world examples.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcncraft",
+ "homepage": "https://shadcncraft.com",
+ "url": "https://shadcncraft.com/r/{name}.json",
+ "description": "A starter collection of polished shadcn/ui components and blocks built to production standards. Part of a larger Figma + React system designed to scale with your product.",
+ "logo": " "
+ },
+ {
+ "name": "@shark",
+ "homepage": "https://shark.vini.one",
+ "url": "https://shark.vini.one/r/{name}.json",
+ "description": "shadcn/ui-style components built on Ark UI.",
+ "logo": " "
+ },
+ {
+ "name": "@smoothui",
+ "homepage": "https://smoothui.dev",
+ "url": "https://smoothui.dev/r/{name}.json",
+ "description": "A collection of beautifully crafted motion components built with React, Framer Motion, and TailwindCSS. Designed to elevate microinteractions, each component focuses on smooth animations, subtle feedback, and delightful UX. Perfect for designers and developers who want to add refined motion to their interfaces — copy, paste, and make your UI come alive.",
+ "logo": " "
+ },
+ {
+ "name": "@spectrumui",
+ "homepage": "https://ui.spectrumhq.in",
+ "url": "https://ui.spectrumhq.in/r/{name}.json",
+ "description": "A modern component library built with shadcn/ui and Tailwind CSS. Spectrum UI offers elegant, responsive components and smooth animations designed for high-quality interfaces.",
+ "logo": " "
+ },
+ {
+ "name": "@supabase",
+ "homepage": "https://supabase.com/ui",
+ "url": "https://supabase.com/ui/r/{name}.json",
+ "description": "A collection of React components and blocks built on the shadcn/ui library that connect your front-end to your Supabase back-end via a single command.",
+ "logo": "Supabase "
+ },
+ {
+ "name": "@svgl",
+ "description": "A beautiful library with SVG logos.",
+ "homepage": "https://svgl.app",
+ "url": "https://svgl.app/r/{name}.json",
+ "logo": " "
+ },
+ {
+ "name": "@tailark",
+ "homepage": "https://tailark.com",
+ "url": "https://tailark.com/r/{name}.json",
+ "description": "Shadcn blocks designed for building modern marketing websites.",
+ "logo": " "
+ },
+ {
+ "name": "@taki",
+ "homepage": "https://taki-ui.com",
+ "url": "https://taki-ui.com/r/{name}.json",
+ "description": "Beautifully designed, accessible components that you can copy and paste into your apps. Made with React Aria Components and Shadcn tokens.",
+ "logo": " "
+ },
+ {
+ "name": "@terrae",
+ "homepage": "https://www.terrae.dev",
+ "url": "https://www.terrae.dev/{name}.json",
+ "description": "Composable, animated map components for React. Built with TypeScript, Tailwind CSS, Mapbox GL JS, and MapLibre GL. Perfect companion for shadcn/ui.",
+ "logo": "t "
+ },
+ {
+ "name": "@thegridcn",
+ "homepage": "https://thegridcn.com",
+ "url": "https://thegridcn.com/r/{name}.json",
+ "description": "A Tron-inspired shadcn/ui theme system with Greek god color schemes, glow intensity levels, and sci-fi components like DataCard, HUD, Radar, and more.",
+ "logo": " "
+ },
+ {
+ "name": "@tour",
+ "homepage": "https://onboarding-tour.vercel.app",
+ "url": "https://onboarding-tour.vercel.app/r/{name}.json",
+ "description": "A component for building onboarding tours. Designed to integrate with shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@uitripled",
+ "homepage": "https://ui.tripled.work",
+ "url": "https://ui.tripled.work/r/{name}.json",
+ "description": "An open-source, Production-ready UI components and blocks powered by shadcn/ui and Framer Motion",
+ "logo": " "
+ },
+ {
+ "name": "@utilcn",
+ "homepage": "https://utilcn.dev",
+ "url": "https://utilcn.dev/r/{name}.json",
+ "description": "Fullstack registry items to start those big features. Utilcn has ChatGPT Apps, file uploading (with progress bars) and downloading, and a way to make your env vars typesafe on the backend.",
+ "logo": " "
+ },
+ {
+ "name": "@w3-kit",
+ "homepage": "https://w3-kit.com",
+ "url": "https://w3-kit.com/registry/{name}.json",
+ "description": "Web3 UI components for blockchain dApps. Includes wallet connection, token swaps, NFT cards, staking interfaces, and 20+ more crypto components.",
+ "logo": " "
+ },
+ {
+ "name": "@wandry-ui",
+ "homepage": "http://ui.wandry.com.ua/",
+ "url": "https://ui.wandry.com.ua/r/{name}.json",
+ "description": "A set of open source fully controlled React Inertia form elements",
+ "logo": " "
+ },
+ {
+ "name": "@wds",
+ "homepage": "https://wds-shadcn-registry.netlify.app/",
+ "url": "https://wds-shadcn-registry.netlify.app/r/{name}.json",
+ "description": "A collection of accessible components built for use with Shadcn.",
+ "logo": " "
+ },
+ {
+ "name": "@wigggle-ui",
+ "description": "A beautiful collection of copy-and-paste widgets for your next project.",
+ "homepage": "https://wigggle-ui.vercel.app",
+ "url": "https://wigggle-ui.vercel.app/r/{name}.json",
+ "logo": " "
+ },
+ {
+ "name": "@zippystarter",
+ "homepage": "https://zippystarter.com",
+ "url": "https://zippystarter.com/r/{name}.json",
+ "description": "Expertly crafted blocks, components & themes for shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@uicapsule",
+ "homepage": "https://uicapsule.com",
+ "url": "https://uicapsule.com/r/{name}.json",
+ "description": "A curated collection of components that spark joy. Featuring interactive concepts, design experiments, and components in the intersection of AI/UI.",
+ "logo": " "
+ },
+ {
+ "name": "@ui-layouts",
+ "homepage": "https://ui-layouts.com/",
+ "url": "https://ui-layouts.com/r/{name}.json",
+ "description": "UI Layouts offers components, effects, design tools, and ready-made blocks that make building modern interfaces more efficient—built with React, Next.js, Tailwind CSS, and shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@pureui",
+ "homepage": "https://pure.kam-ui.com/",
+ "url": "https://pure.kam-ui.com/r/{name}.json",
+ "description": "Pure UI is a curated collection of refined, animated, and accessible components built with Base UI, Tailwind CSS, Motion, and other high-quality open source libraries.",
+ "logo": " "
+ },
+ {
+ "name": "@tailwind-builder",
+ "homepage": "https://tailwindbuilder.ai/",
+ "url": "https://tailwindbuilder.ai/r/{name}.json",
+ "description": "Tailwind Builder is a collection of free ui blocks and components and provide ai tools to generate production-ready forms, tables, and charts in seconds. Built with React, Next.js, Tailwind & ShadCN.",
+ "logo": " "
+ },
+ {
+ "name": "@tailwind-admin",
+ "homepage": "https://tailwind-admin.com/",
+ "url": "https://tailwind-admin.com/r/{name}.json",
+ "description": "Tailwind Builder provides free tailwind admin dashboard templates, components and ui-blocks built with React, Next.js, Tailwind CSS, and shadcn/ui to help you build admin panels quickly and efficiently.",
+ "logo": " "
+ },
+ {
+ "name": "@forgeui",
+ "homepage": "https://forgeui.in/",
+ "url": "https://forgeui.in/r/{name}.json",
+ "description": "Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.",
+ "logo": " "
+ },
+ {
+ "name": "@skiper-ui",
+ "homepage": "https://skiper-ui.com/",
+ "url": "https://skiper-ui.com/registry/{name}.json",
+ "description": "Brand new uncommon components for your Next.js project. Use with ease through shadcn CLI 3.0, featuring fast-growing components and collections that are easy to edit and use.",
+ "logo": " "
+ },
+ {
+ "name": "@animbits",
+ "homepage": "https://animbits.dev",
+ "url": "https://animbits.dev/r/{name}.json",
+ "description": "AnimBits is a collection animated UI components for React that use Framer Motion. The components provided include buttons, cards, text, icons, lists, loaders, and page transitions, animation hooks all of which have general-purpose effects that are not flashy and easy on the eyes, making them easy to use.",
+ "logo": " "
+ },
+ {
+ "name": "@shadcn-space",
+ "homepage": "https://shadcnspace.com",
+ "url": "https://shadcnspace.com/r/{name}.json",
+ "description": "ShadcnSpace is a collection of extra-ordinary, highly customizable shadcn/ui components, blocks, and themes to build modern UIs with speed and clarity.",
+ "logo": " "
+ },
+ {
+ "name": "@icons-animated",
+ "homepage": "https://icons.lndev.me",
+ "url": "https://icons.lndev.me/r/{name}.json",
+ "description": "An open-source library of meticulously animated icons (Tabler, Phosphor, and more) for your projects, inspired by lucide-animated.com",
+ "logo": " "
+ },
+ {
+ "name": "@heroicons-animated",
+ "homepage": "https://www.heroicons-animated.com/",
+ "url": "https://www.heroicons-animated.com/r/{name}.json",
+ "description": "An open-source collection of 316 beautifully animated heroicons for your projects.",
+ "logo": " "
+ },
+ {
+ "name": "@darx",
+ "homepage": "https://darshitdev.in/arts",
+ "url": "https://darshitdev.in/r/{name}.json",
+ "description": "Magic 3D Tabs component featuring mouse-interactive 3D rotation, floating particles background effect, and smooth spring animations.",
+ "logo": " "
+ },
+ {
+ "name": "@beste-ui",
+ "homepage": "https://ui.beste.co",
+ "url": "https://ui.beste.co/r/{name}.json",
+ "description": "Production-ready UI blocks for landing pages, dashboards, and web apps.",
+ "logo": " "
+ },
+ {
+ "name": "@tokenui",
+ "homepage": "https://www.tokenui.dev",
+ "url": "https://www.tokenui.dev/r/{name}.json",
+ "description": "Beautiful, interactive documentation components for your design tokens following industry standards.",
+ "logo": " "
+ },
+ {
+ "name": "@lumiui",
+ "homepage": "https://www.lumiui.dev",
+ "url": "https://www.lumiui.dev/r/{name}.json",
+ "description": "Composable React components powered by Base UI and Tailwind CSS — Build fast, customize everything.",
+ "logo": " "
+ },
+ {
+ "name": "@uselayouts",
+ "homepage": "https://uselayouts.com",
+ "url": "https://uselayouts.com/r/{name}.json",
+ "description": "A collection of premium animated React components and micro-interactions built with Motion for building fluid, professional interfaces.",
+ "logo": " "
+ },
+ {
+ "name": "@joyco",
+ "homepage": "https://registry.joyco.studio",
+ "url": "https://registry.joyco.studio/r/{name}.json",
+ "description": "Components including MobileMenu, ScrollArea with gradients, Chat UI, HLSVideoPlayer, and Marquee.",
+ "logo": " "
+ },
+ {
+ "name": "@gooseui",
+ "homepage": "https://gooseui.pro",
+ "url": "https://gooseui.pro/r/{name}.json",
+ "description": "Open source component library with animated components, beautiful effects, and custom toast notifications. Built with Radix UI and Tailwind CSS.",
+ "logo": " "
+ },
+ {
+ "name": "@baselayer",
+ "homepage": "https://www.baselayer.dev",
+ "url": "https://www.baselayer.dev/r/{name}.json",
+ "description": "A collection of components built on React Aria, Tailwind CSS, and tailwind-variants.",
+ "logo": " "
+ },
+ {
+ "name": "@jolyui",
+ "homepage": "https://www.jolyui.dev",
+ "url": "https://www.jolyui.dev/r/{name}.json",
+ "description": "JolyUI is a modern React component library built with TypeScript and Tailwind CSS.",
+ "logo": " "
+ },
+ {
+ "name": "@fab-ui",
+ "homepage": "https://fab-ui.com",
+ "url": "https://fab-ui.com/r/{name}.json",
+ "description": "A collection of beautifully designed UI components for building modern web applications.",
+ "logo": " "
+ },
+ {
+ "name": "@asanshay",
+ "homepage": "https://ds.asanshay.com",
+ "url": "https://ds.asanshay.com/r/{name}.json",
+ "description": "Clean, beautiful, and simple UI primitives and AI elements.",
+ "logo": " "
+ },
+ {
+ "name": "@headcodecms",
+ "homepage": "https://headcodecms.com",
+ "url": "https://headcodecms.com/r/{name}.json",
+ "description": "A Minimalistic Web CMS for Next.js, optimized for Cache Components.",
+ "logo": " "
+ },
+ {
+ "name": "@typedora-ui",
+ "homepage": "https://typedora-ui.netlify.app",
+ "url": "https://typedora-ui.netlify.app/r/{name}.json",
+ "description": "Typedora UI is a next-generation extension layer for shadcn/ui, designed to bring full type-safety to your UI components.",
+ "logo": " "
+ },
+ {
+ "name": "@sona-ui",
+ "homepage": "https://sona-ui.vercel.app",
+ "url": "https://sona-ui.vercel.app/r/{name}.json",
+ "description": "A modern UI component library built with React and TailwindCSS to help you build beautiful and accessible web applications faster.",
+ "logo": " "
+ },
+ {
+ "name": "@soundcn",
+ "homepage": "https://soundcn.xyz",
+ "url": "https://soundcn.xyz/r/{name}.json",
+ "description": "Large collection of game, interface, retro, and voice sound effects for web applications",
+ "logo": " "
+ },
+ {
+ "name": "@pixelact-ui",
+ "homepage": "https://pixelactui.com",
+ "url": "https://pixelactui.com/r/{name}.json",
+ "description": "Playful pixel art style components library built on top of shadcn. Perfect for retro style projects and games.",
+ "logo": " "
+ },
+ {
+ "name": "@emerald-ui",
+ "homepage": "https://emerald-ui.com",
+ "url": "https://emerald-ui.com/r/{name}.json",
+ "description": "Emerald UI - collection of components built with Motion, GSAP, Tailwind CSS and shadcn/ui.",
+ "logo": " "
+ },
+ {
+ "name": "@iconiq",
+ "homepage": "https://iconiqui.com",
+ "url": "https://iconiqui.com/r/{name}.json",
+ "description": "Iconiq is a collection of icons designed for web applications. It is a modern, clean, and minimalistic icon set that is perfect for web applications.",
+ "logo": " "
+ },
+ {
+ "name": "@fonttrio",
+ "homepage": "https://www.fonttrio.xyz",
+ "url": "https://www.fonttrio.xyz/r/{name}.json",
+ "description": "Curated font pairing registry for shadcn. Three fonts. One command. Install perfectly configured typography (heading + body + mono) with shadcn add. Includes editorial-grade type scales, CSS variables, and a live preview site.",
+ "logo": " "
+ },
+ {
+ "name": "@componentry",
+ "homepage": "https://componentry.fun",
+ "url": "https://componentry.fun/r/{name}.json",
+ "description": "Beautiful, interactive React + Tailwind components for modern product UIs.",
+ "logo": " "
+ },
+ {
+ "name": "@paletteui",
+ "homepage": "https://paletteui.xyz",
+ "url": "https://paletteui.xyz/r/{name}.json",
+ "description": "Curated OKLCH color themes for shadcn/ui + visual theme editor with CSS, Tailwind v4, and Figma export.",
+ "logo": " "
+ },
+ {
+ "name": "@fluid",
+ "homepage": "https://www.fluidfunctionalism.com",
+ "url": "https://www.fluidfunctionalism.com/r/{name}.json",
+ "description": "Fluid components used exclusively in service of functional clarity. Proximity hover, spring animations, font-weight transitions, and animated focus rings.",
+ "logo": "F "
+ },
+ {
+ "name": "@gammaui",
+ "homepage": "https://www.gammaui.com",
+ "url": "https://www.gammaui.com/r/{name}.json",
+ "description": "Beautifully designed landing page components built with React & Tailwind CSS & Motion.",
+ "logo": " "
+ },
+ {
+ "name": "@flx",
+ "homepage": "https://ui.flexnative.com",
+ "url": "https://ui.flexnative.com/r/{name}.json",
+ "description": "A collection of customizable UI blocks with interactive live previews",
+ "logo": " "
+ },
+ {
+ "name": "@tailgrids",
+ "homepage": "https://tailgrids.com",
+ "url": "https://tailgrids.com/docs/r/{name}.json",
+ "description": "React UI Components, Powered by Tailwind CSS",
+ "logo": " "
+ },
+ {
+ "name": "@nexus-ui",
+ "homepage": "https://nexus-ui.dev",
+ "url": "https://nexus-ui.dev/r/{name}.json",
+ "description": "Open-source component library of composable, copy-paste primitives for building AI interfaces (chat, streaming, multimodal)",
+ "logo": " "
+ },
+ {
+ "name": "@sabraman",
+ "homepage": "https://sabraman.ru/components",
+ "url": "https://sabraman.ru/r/{name}.json",
+ "description": "Legacy skeuomorphic UI components and blocks for shadcn.",
+ "logo": " "
+ },
+ {
+ "name": "@odysseyui",
+ "homepage": "https://www.odysseyui.com/docs",
+ "url": "https://www.odysseyui.com/r/{name}.json",
+ "description": "A design focused component library for Next.js, built for speed, flexibility and developer experience.",
+ "logo": " "
+ },
+ {
+ "name": "@pulkitxm",
+ "homepage": "https://pulkitxm.com",
+ "url": "https://pulkitxm.com/components/{name}.json",
+ "description": "Animated shadcn components powered by GSAP and Framer Motion. Built for expressive UIs.",
+ "logo": " "
+ },
+ {
+ "name": "@rescript-shadcn",
+ "homepage": "https://rescript-shadcn.miriad.studio",
+ "url": "https://rescript-shadcn.miriad.studio/r/{name}.json",
+ "description": "Shadcn components rewritten in Rescript, compatible with shadcn CLI.",
+ "logo": " "
+ },
+ {
+ "name": "@openpolicy",
+ "homepage": "https://www.openpolicy.sh",
+ "url": "https://www.openpolicy.sh/r/{name}.json",
+ "description": "Open-source components for building terms, privacy policies and cookie banners.",
+ "logo": " "
+ },
+ {
+ "name": "@mksingh",
+ "homepage": "https://mksingh.dev/docs",
+ "url": "https://mksingh.dev/r/{name}.json",
+ "description": "A personal registry of production-ready ShadCN components and utilities. Everything is built to drop into your existing ShadCN project with no extra setup.",
+ "logo": "MKSingh "
+ },
+ {
+ "name": "@flowkit-ui",
+ "homepage": "https://flowkit-ui.vzkiss.com",
+ "url": "https://flowkit-ui.vzkiss.com/r/{name}.json",
+ "description": "Opinionated, accessible components on Base UI and shadcn-style primitives — starting with a Creatable Combobox.",
+ "logo": " "
+ },
+ {
+ "name": "@termcn",
+ "homepage": "https://termcn.vercel.app",
+ "url": "https://termcn.vercel.app/r/{name}.json",
+ "description": "Beautiful terminal UIs, made simple. Ready to use, customizable terminal UI components for React.",
+ "logo": " "
+ },
+ {
+ "name": "@remocn",
+ "homepage": "https://www.remocn.dev/",
+ "url": "https://www.remocn.dev/r/{name}.json",
+ "description": "Production-ready components for Remotion - text animations, backgrounds, transitions, UI blocks, and full scene compositions",
+ "logo": " "
+ }
+]
diff --git a/apps/ui/src/registry/fonts.ts b/apps/ui/src/registry/fonts.ts
new file mode 100644
index 0000000..b2a998c
--- /dev/null
+++ b/apps/ui/src/registry/fonts.ts
@@ -0,0 +1,34 @@
+import { type RegistryItem } from "shadcn/schema"
+
+import { FONT_DEFINITIONS, type FontDefinition } from "@/lib/font-definitions"
+
+function createFontItem(definition: FontDefinition, role: "body" | "heading") {
+ return {
+ name:
+ role === "body"
+ ? `font-${definition.name}`
+ : `font-heading-${definition.name}`,
+ title: role === "body" ? definition.title : `${definition.title} (Heading)`,
+ type: "registry:font",
+ font: {
+ family: definition.family,
+ provider: definition.provider,
+ variable:
+ role === "body" ? definition.registryVariable : "--font-heading",
+ ...(definition.weight ? { weight: [...definition.weight] } : {}),
+ subsets: [...definition.subsets],
+ import: definition.import,
+ dependency: definition.dependency,
+ },
+ } satisfies RegistryItem
+}
+
+export const bodyFonts = FONT_DEFINITIONS.map((definition) =>
+ createFontItem(definition, "body")
+) satisfies RegistryItem[]
+
+export const headingFonts = FONT_DEFINITIONS.map((definition) =>
+ createFontItem(definition, "heading")
+) satisfies RegistryItem[]
+
+export const fonts = [...bodyFonts, ...headingFonts] satisfies RegistryItem[]
diff --git a/apps/ui/src/registry/icons/__hugeicons__.ts b/apps/ui/src/registry/icons/__hugeicons__.ts
new file mode 100644
index 0000000..160bc4a
--- /dev/null
+++ b/apps/ui/src/registry/icons/__hugeicons__.ts
@@ -0,0 +1,177 @@
+// Auto-generated by scripts/build-icons.ts
+export { ActivityIcon } from "@hugeicons/core-free-icons"
+export { Add01Icon } from "@hugeicons/core-free-icons"
+export { AddCircleIcon } from "@hugeicons/core-free-icons"
+export { Alert02Icon } from "@hugeicons/core-free-icons"
+export { AlertCircleIcon } from "@hugeicons/core-free-icons"
+export { Analytics01Icon } from "@hugeicons/core-free-icons"
+export { AnalyticsUpIcon } from "@hugeicons/core-free-icons"
+export { Archive02Icon } from "@hugeicons/core-free-icons"
+export { ArchiveIcon } from "@hugeicons/core-free-icons"
+export { ArrowDataTransferHorizontalIcon } from "@hugeicons/core-free-icons"
+export { ArrowDown01Icon } from "@hugeicons/core-free-icons"
+export { ArrowDownIcon } from "@hugeicons/core-free-icons"
+export { ArrowLeft01Icon } from "@hugeicons/core-free-icons"
+export { ArrowLeft02Icon } from "@hugeicons/core-free-icons"
+export { ArrowLeftDoubleIcon } from "@hugeicons/core-free-icons"
+export { ArrowLeftIcon } from "@hugeicons/core-free-icons"
+export { ArrowRight01Icon } from "@hugeicons/core-free-icons"
+export { ArrowRight02Icon } from "@hugeicons/core-free-icons"
+export { ArrowRightDoubleIcon } from "@hugeicons/core-free-icons"
+export { ArrowRightIcon } from "@hugeicons/core-free-icons"
+export { ArrowUp01Icon } from "@hugeicons/core-free-icons"
+export { ArrowUpIcon } from "@hugeicons/core-free-icons"
+export { ArrowUpRight01Icon } from "@hugeicons/core-free-icons"
+export { ArrowUpRightIcon } from "@hugeicons/core-free-icons"
+export { AudioWave01Icon } from "@hugeicons/core-free-icons"
+export { BankIcon } from "@hugeicons/core-free-icons"
+export { BluetoothIcon } from "@hugeicons/core-free-icons"
+export { BookOpen02Icon } from "@hugeicons/core-free-icons"
+export { BookmarkIcon } from "@hugeicons/core-free-icons"
+export { CalculatorIcon } from "@hugeicons/core-free-icons"
+export { Calendar03Icon } from "@hugeicons/core-free-icons"
+export { CalendarIcon } from "@hugeicons/core-free-icons"
+export { Camera01Icon } from "@hugeicons/core-free-icons"
+export { Cancel01Icon } from "@hugeicons/core-free-icons"
+export { Car01Icon } from "@hugeicons/core-free-icons"
+export { Chart03Icon } from "@hugeicons/core-free-icons"
+export { ChartBarLineIcon } from "@hugeicons/core-free-icons"
+export { ChartDownIcon } from "@hugeicons/core-free-icons"
+export { ChartHistogramIcon } from "@hugeicons/core-free-icons"
+export { ChartIcon } from "@hugeicons/core-free-icons"
+export { ChartRingIcon } from "@hugeicons/core-free-icons"
+export { ChartUpIcon } from "@hugeicons/core-free-icons"
+export { CheckmarkBadge02Icon } from "@hugeicons/core-free-icons"
+export { CheckmarkBadgeIcon } from "@hugeicons/core-free-icons"
+export { CheckmarkCircle01Icon } from "@hugeicons/core-free-icons"
+export { CheckmarkCircle02Icon } from "@hugeicons/core-free-icons"
+export { CircleArrowLeft02Icon } from "@hugeicons/core-free-icons"
+export { ClipboardIcon } from "@hugeicons/core-free-icons"
+export { Clock03Icon } from "@hugeicons/core-free-icons"
+export { CloudUploadIcon } from "@hugeicons/core-free-icons"
+export { CodeIcon } from "@hugeicons/core-free-icons"
+export { CoffeeIcon } from "@hugeicons/core-free-icons"
+export { CommandIcon } from "@hugeicons/core-free-icons"
+export { ComputerIcon } from "@hugeicons/core-free-icons"
+export { ComputerTerminal01Icon } from "@hugeicons/core-free-icons"
+export { ComputerTerminalIcon } from "@hugeicons/core-free-icons"
+export { Copy01Icon } from "@hugeicons/core-free-icons"
+export { CopyIcon } from "@hugeicons/core-free-icons"
+export { CreditCardIcon } from "@hugeicons/core-free-icons"
+export { CropIcon } from "@hugeicons/core-free-icons"
+export { CubeIcon } from "@hugeicons/core-free-icons"
+export { CursorInWindowIcon } from "@hugeicons/core-free-icons"
+export { DashboardSquare01Icon } from "@hugeicons/core-free-icons"
+export { DashedLineCircleIcon } from "@hugeicons/core-free-icons"
+export { Database01Icon } from "@hugeicons/core-free-icons"
+export { Delete02Icon } from "@hugeicons/core-free-icons"
+export { DeleteIcon } from "@hugeicons/core-free-icons"
+export { DownloadIcon } from "@hugeicons/core-free-icons"
+export { DragDropVerticalIcon } from "@hugeicons/core-free-icons"
+export { EditIcon } from "@hugeicons/core-free-icons"
+export { EyeIcon } from "@hugeicons/core-free-icons"
+export { FavouriteIcon } from "@hugeicons/core-free-icons"
+export { File01Icon } from "@hugeicons/core-free-icons"
+export { File02Icon } from "@hugeicons/core-free-icons"
+export { FileIcon } from "@hugeicons/core-free-icons"
+export { FlipHorizontalIcon } from "@hugeicons/core-free-icons"
+export { FlipVerticalIcon } from "@hugeicons/core-free-icons"
+export { FloppyDiskIcon } from "@hugeicons/core-free-icons"
+export { Folder01Icon } from "@hugeicons/core-free-icons"
+export { FolderAddIcon } from "@hugeicons/core-free-icons"
+export { FolderIcon } from "@hugeicons/core-free-icons"
+export { FolderOpenIcon } from "@hugeicons/core-free-icons"
+export { Globe02Icon } from "@hugeicons/core-free-icons"
+export { GridIcon } from "@hugeicons/core-free-icons"
+export { HelpCircleIcon } from "@hugeicons/core-free-icons"
+export { Home01Icon } from "@hugeicons/core-free-icons"
+export { HomeIcon } from "@hugeicons/core-free-icons"
+export { Image01Icon } from "@hugeicons/core-free-icons"
+export { ImageIcon } from "@hugeicons/core-free-icons"
+export { InboxIcon } from "@hugeicons/core-free-icons"
+export { InformationCircleIcon } from "@hugeicons/core-free-icons"
+export { KeyboardIcon } from "@hugeicons/core-free-icons"
+export { LanguageCircleIcon } from "@hugeicons/core-free-icons"
+export { LayoutBottomIcon } from "@hugeicons/core-free-icons"
+export { LayoutIcon } from "@hugeicons/core-free-icons"
+export { LayoutLeftIcon } from "@hugeicons/core-free-icons"
+export { LeftToRightListBulletIcon } from "@hugeicons/core-free-icons"
+export { LinkIcon } from "@hugeicons/core-free-icons"
+export { LinkSquare02Icon } from "@hugeicons/core-free-icons"
+export { Loading03Icon } from "@hugeicons/core-free-icons"
+export { Logout01Icon } from "@hugeicons/core-free-icons"
+export { LogoutIcon } from "@hugeicons/core-free-icons"
+export { Mail01Icon } from "@hugeicons/core-free-icons"
+export { MailIcon } from "@hugeicons/core-free-icons"
+export { MapsIcon } from "@hugeicons/core-free-icons"
+export { Menu01Icon } from "@hugeicons/core-free-icons"
+export { Menu05Icon } from "@hugeicons/core-free-icons"
+export { Menu09Icon } from "@hugeicons/core-free-icons"
+export { Message01Icon } from "@hugeicons/core-free-icons"
+export { MessageIcon } from "@hugeicons/core-free-icons"
+export { MessageQuestionIcon } from "@hugeicons/core-free-icons"
+export { MinusSignIcon } from "@hugeicons/core-free-icons"
+export { MoonIcon } from "@hugeicons/core-free-icons"
+export { MoreHorizontalCircle01Icon } from "@hugeicons/core-free-icons"
+export { MoreVerticalCircle01Icon } from "@hugeicons/core-free-icons"
+export { MultiplicationSignCircleIcon } from "@hugeicons/core-free-icons"
+export { Notification02Icon } from "@hugeicons/core-free-icons"
+export { Notification03Icon } from "@hugeicons/core-free-icons"
+export { NotificationIcon } from "@hugeicons/core-free-icons"
+export { PaintBoardIcon } from "@hugeicons/core-free-icons"
+export { PieChartIcon } from "@hugeicons/core-free-icons"
+export { PlusSignCircleIcon } from "@hugeicons/core-free-icons"
+export { PlusSignIcon } from "@hugeicons/core-free-icons"
+export { RecordIcon } from "@hugeicons/core-free-icons"
+export { RedoIcon } from "@hugeicons/core-free-icons"
+export { RefreshIcon } from "@hugeicons/core-free-icons"
+export { RepeatIcon } from "@hugeicons/core-free-icons"
+export { RoboticIcon } from "@hugeicons/core-free-icons"
+export { Rotate01Icon } from "@hugeicons/core-free-icons"
+export { ScissorIcon } from "@hugeicons/core-free-icons"
+export { Search01Icon } from "@hugeicons/core-free-icons"
+export { SearchIcon } from "@hugeicons/core-free-icons"
+export { SentIcon } from "@hugeicons/core-free-icons"
+export { ServerStackIcon } from "@hugeicons/core-free-icons"
+export { Settings01Icon } from "@hugeicons/core-free-icons"
+export { Settings05Icon } from "@hugeicons/core-free-icons"
+export { SettingsIcon } from "@hugeicons/core-free-icons"
+export { Share01Icon } from "@hugeicons/core-free-icons"
+export { Share03Icon } from "@hugeicons/core-free-icons"
+export { ShareIcon } from "@hugeicons/core-free-icons"
+export { ShieldIcon } from "@hugeicons/core-free-icons"
+export { ShoppingBag01Icon } from "@hugeicons/core-free-icons"
+export { ShoppingCart01Icon } from "@hugeicons/core-free-icons"
+export { SidebarLeftIcon } from "@hugeicons/core-free-icons"
+export { SmileIcon } from "@hugeicons/core-free-icons"
+export { SparklesIcon } from "@hugeicons/core-free-icons"
+export { SquareLock02Icon } from "@hugeicons/core-free-icons"
+export { StarIcon } from "@hugeicons/core-free-icons"
+export { StarOffIcon } from "@hugeicons/core-free-icons"
+export { Sun03Icon } from "@hugeicons/core-free-icons"
+export { SunIcon } from "@hugeicons/core-free-icons"
+export { TableIcon } from "@hugeicons/core-free-icons"
+export { Target02Icon } from "@hugeicons/core-free-icons"
+export { TextBoldIcon } from "@hugeicons/core-free-icons"
+export { TextCheckIcon } from "@hugeicons/core-free-icons"
+export { TextItalicIcon } from "@hugeicons/core-free-icons"
+export { TextUnderlineIcon } from "@hugeicons/core-free-icons"
+export { ThermometerWarmIcon } from "@hugeicons/core-free-icons"
+export { Tick02Icon } from "@hugeicons/core-free-icons"
+export { TradeUpIcon } from "@hugeicons/core-free-icons"
+export { Tv01Icon } from "@hugeicons/core-free-icons"
+export { UndoIcon } from "@hugeicons/core-free-icons"
+export { UnfoldMoreIcon } from "@hugeicons/core-free-icons"
+export { UserCircle02Icon } from "@hugeicons/core-free-icons"
+export { UserGroupIcon } from "@hugeicons/core-free-icons"
+export { UserIcon } from "@hugeicons/core-free-icons"
+export { UserRemove01Icon } from "@hugeicons/core-free-icons"
+export { ViewOffIcon } from "@hugeicons/core-free-icons"
+export { VoiceIcon } from "@hugeicons/core-free-icons"
+export { VolumeHighIcon } from "@hugeicons/core-free-icons"
+export { VolumeOffIcon } from "@hugeicons/core-free-icons"
+export { Wallet01Icon } from "@hugeicons/core-free-icons"
+export { WalletIcon } from "@hugeicons/core-free-icons"
+export { ZapIcon } from "@hugeicons/core-free-icons"
+export { ZoomInAreaIcon } from "@hugeicons/core-free-icons"
+export { ZoomOutAreaIcon } from "@hugeicons/core-free-icons"
diff --git a/apps/ui/src/registry/icons/__lucide__.ts b/apps/ui/src/registry/icons/__lucide__.ts
new file mode 100644
index 0000000..198d308
--- /dev/null
+++ b/apps/ui/src/registry/icons/__lucide__.ts
@@ -0,0 +1,173 @@
+// Auto-generated by scripts/build-icons.ts
+export { ActivityIcon } from "lucide-react"
+export { AlertCircleIcon } from "lucide-react"
+export { AlertTriangleIcon } from "lucide-react"
+export { AppWindowIcon } from "lucide-react"
+export { ArchiveIcon } from "lucide-react"
+export { ArchiveXIcon } from "lucide-react"
+export { ArrowDownIcon } from "lucide-react"
+export { ArrowLeftCircleIcon } from "lucide-react"
+export { ArrowLeftIcon } from "lucide-react"
+export { ArrowLeftRightIcon } from "lucide-react"
+export { ArrowRightIcon } from "lucide-react"
+export { ArrowUpIcon } from "lucide-react"
+export { ArrowUpRightIcon } from "lucide-react"
+export { AudioLinesIcon } from "lucide-react"
+export { BadgeCheck } from "lucide-react"
+export { BadgeCheckIcon } from "lucide-react"
+export { BellIcon } from "lucide-react"
+export { BlocksIcon } from "lucide-react"
+export { BluetoothIcon } from "lucide-react"
+export { BoldIcon } from "lucide-react"
+export { BookOpen } from "lucide-react"
+export { BookOpenIcon } from "lucide-react"
+export { BookmarkIcon } from "lucide-react"
+export { BotIcon } from "lucide-react"
+export { Building2Icon } from "lucide-react"
+export { CalculatorIcon } from "lucide-react"
+export { CalendarIcon } from "lucide-react"
+export { CameraIcon } from "lucide-react"
+export { CaptionsIcon } from "lucide-react"
+export { CarIcon } from "lucide-react"
+export { ChartBarIcon } from "lucide-react"
+export { ChartLineIcon } from "lucide-react"
+export { ChartPieIcon } from "lucide-react"
+export { CheckCircle2Icon } from "lucide-react"
+export { CheckIcon } from "lucide-react"
+export { ChevronDownIcon } from "lucide-react"
+export { ChevronLeftIcon } from "lucide-react"
+export { ChevronRightIcon } from "lucide-react"
+export { ChevronUpIcon } from "lucide-react"
+export { ChevronsLeftIcon } from "lucide-react"
+export { ChevronsRightIcon } from "lucide-react"
+export { ChevronsUpDownIcon } from "lucide-react"
+export { CircleAlertIcon } from "lucide-react"
+export { CircleCheckIcon } from "lucide-react"
+export { CircleDashedIcon } from "lucide-react"
+export { CircleHelpIcon } from "lucide-react"
+export { CirclePlusIcon } from "lucide-react"
+export { CircleUserRoundIcon } from "lucide-react"
+export { ClipboardPasteIcon } from "lucide-react"
+export { Clock2Icon } from "lucide-react"
+export { CloudIcon } from "lucide-react"
+export { CodeIcon } from "lucide-react"
+export { CoffeeIcon } from "lucide-react"
+export { Columns3Icon } from "lucide-react"
+export { CommandIcon } from "lucide-react"
+export { ContainerIcon } from "lucide-react"
+export { CopyIcon } from "lucide-react"
+export { CornerUpLeftIcon } from "lucide-react"
+export { CornerUpRightIcon } from "lucide-react"
+export { CreditCardIcon } from "lucide-react"
+export { DatabaseIcon } from "lucide-react"
+export { DownloadIcon } from "lucide-react"
+export { EllipsisVerticalIcon } from "lucide-react"
+export { ExternalLinkIcon } from "lucide-react"
+export { EyeIcon } from "lucide-react"
+export { EyeOffIcon } from "lucide-react"
+export { FileBarChartIcon } from "lucide-react"
+export { FileChartColumnIcon } from "lucide-react"
+export { FileCodeIcon } from "lucide-react"
+export { FileIcon } from "lucide-react"
+export { FileTextIcon } from "lucide-react"
+export { FlipHorizontalIcon } from "lucide-react"
+export { FlipVerticalIcon } from "lucide-react"
+export { FolderIcon } from "lucide-react"
+export { FolderOpenIcon } from "lucide-react"
+export { FolderPlusIcon } from "lucide-react"
+export { FolderSearchIcon } from "lucide-react"
+export { FrameIcon } from "lucide-react"
+export { GalleryVerticalEndIcon } from "lucide-react"
+export { GaugeIcon } from "lucide-react"
+export { GlobeIcon } from "lucide-react"
+export { GripVerticalIcon } from "lucide-react"
+export { HeartIcon } from "lucide-react"
+export { HelpCircleIcon } from "lucide-react"
+export { HomeIcon } from "lucide-react"
+export { ImageIcon } from "lucide-react"
+export { InboxIcon } from "lucide-react"
+export { InfoIcon } from "lucide-react"
+export { ItalicIcon } from "lucide-react"
+export { KeyboardIcon } from "lucide-react"
+export { LanguagesIcon } from "lucide-react"
+export { LayoutDashboardIcon } from "lucide-react"
+export { LayoutGridIcon } from "lucide-react"
+export { LayoutIcon } from "lucide-react"
+export { LifeBuoy } from "lucide-react"
+export { LifeBuoyIcon } from "lucide-react"
+export { LinkIcon } from "lucide-react"
+export { ListIcon } from "lucide-react"
+export { Loader2Icon } from "lucide-react"
+export { LoaderIcon } from "lucide-react"
+export { LockIcon } from "lucide-react"
+export { LockKeyholeIcon } from "lucide-react"
+export { LogOutIcon } from "lucide-react"
+export { MailIcon } from "lucide-react"
+export { MapIcon } from "lucide-react"
+export { MaximizeIcon } from "lucide-react"
+export { MenuIcon } from "lucide-react"
+export { MessageCircleIcon } from "lucide-react"
+export { MessageCircleQuestionIcon } from "lucide-react"
+export { MessageSquareIcon } from "lucide-react"
+export { MicIcon } from "lucide-react"
+export { MinimizeIcon } from "lucide-react"
+export { MinusIcon } from "lucide-react"
+export { MonitorIcon } from "lucide-react"
+export { MoonIcon } from "lucide-react"
+export { MoreHorizontalIcon } from "lucide-react"
+export { MoreVerticalIcon } from "lucide-react"
+export { OctagonXIcon } from "lucide-react"
+export { PaintbrushIcon } from "lucide-react"
+export { PaletteIcon } from "lucide-react"
+export { PanelLeftIcon } from "lucide-react"
+export { PencilIcon } from "lucide-react"
+export { PieChartIcon } from "lucide-react"
+export { PlusCircleIcon } from "lucide-react"
+export { PlusIcon } from "lucide-react"
+export { RadioIcon } from "lucide-react"
+export { RefreshCwIcon } from "lucide-react"
+export { RepeatIcon } from "lucide-react"
+export { RotateCwIcon } from "lucide-react"
+export { SaveIcon } from "lucide-react"
+export { ScissorsIcon } from "lucide-react"
+export { SearchIcon } from "lucide-react"
+export { Send } from "lucide-react"
+export { SendIcon } from "lucide-react"
+export { ServerIcon } from "lucide-react"
+export { Settings2Icon } from "lucide-react"
+export { SettingsIcon } from "lucide-react"
+export { ShareIcon } from "lucide-react"
+export { ShieldIcon } from "lucide-react"
+export { ShoppingBagIcon } from "lucide-react"
+export { ShoppingCartIcon } from "lucide-react"
+export { SmileIcon } from "lucide-react"
+export { SparklesIcon } from "lucide-react"
+export { StarIcon } from "lucide-react"
+export { StarOffIcon } from "lucide-react"
+export { SunIcon } from "lucide-react"
+export { TableIcon } from "lucide-react"
+export { TargetIcon } from "lucide-react"
+export { TerminalIcon } from "lucide-react"
+export { TerminalSquareIcon } from "lucide-react"
+export { ThermometerIcon } from "lucide-react"
+export { TimerIcon } from "lucide-react"
+export { Trash2Icon } from "lucide-react"
+export { TrashIcon } from "lucide-react"
+export { TrendingDownIcon } from "lucide-react"
+export { TrendingUpIcon } from "lucide-react"
+export { TriangleAlertIcon } from "lucide-react"
+export { TvIcon } from "lucide-react"
+export { UnderlineIcon } from "lucide-react"
+export { UploadCloudIcon } from "lucide-react"
+export { UserIcon } from "lucide-react"
+export { UserRoundXIcon } from "lucide-react"
+export { UsersIcon } from "lucide-react"
+export { VideoIcon } from "lucide-react"
+export { Volume2Icon } from "lucide-react"
+export { VolumeOffIcon } from "lucide-react"
+export { VolumeX } from "lucide-react"
+export { WalletIcon } from "lucide-react"
+export { XIcon } from "lucide-react"
+export { ZapIcon } from "lucide-react"
+export { ZoomInIcon } from "lucide-react"
+export { ZoomOutIcon } from "lucide-react"
diff --git a/apps/ui/src/registry/icons/__phosphor__.ts b/apps/ui/src/registry/icons/__phosphor__.ts
new file mode 100644
index 0000000..e92f0eb
--- /dev/null
+++ b/apps/ui/src/registry/icons/__phosphor__.ts
@@ -0,0 +1,147 @@
+// Auto-generated by scripts/build-icons.ts
+export { ActivityIcon } from "@phosphor-icons/react"
+export { AppWindowIcon } from "@phosphor-icons/react"
+export { ArchiveIcon } from "@phosphor-icons/react"
+export { ArrowBendUpLeftIcon } from "@phosphor-icons/react"
+export { ArrowBendUpRightIcon } from "@phosphor-icons/react"
+export { ArrowCircleLeftIcon } from "@phosphor-icons/react"
+export { ArrowClockwiseIcon } from "@phosphor-icons/react"
+export { ArrowDownIcon } from "@phosphor-icons/react"
+export { ArrowLeftIcon } from "@phosphor-icons/react"
+export { ArrowRightIcon } from "@phosphor-icons/react"
+export { ArrowSquareOutIcon } from "@phosphor-icons/react"
+export { ArrowUpIcon } from "@phosphor-icons/react"
+export { ArrowUpRightIcon } from "@phosphor-icons/react"
+export { ArrowsClockwiseIcon } from "@phosphor-icons/react"
+export { ArrowsHorizontalIcon } from "@phosphor-icons/react"
+export { ArrowsLeftRightIcon } from "@phosphor-icons/react"
+export { ArrowsVerticalIcon } from "@phosphor-icons/react"
+export { BagIcon } from "@phosphor-icons/react"
+export { BankIcon } from "@phosphor-icons/react"
+export { BellIcon } from "@phosphor-icons/react"
+export { BluetoothIcon } from "@phosphor-icons/react"
+export { BookOpenIcon } from "@phosphor-icons/react"
+export { BookmarkIcon } from "@phosphor-icons/react"
+export { CalculatorIcon } from "@phosphor-icons/react"
+export { CalendarBlankIcon } from "@phosphor-icons/react"
+export { CalendarIcon } from "@phosphor-icons/react"
+export { CameraIcon } from "@phosphor-icons/react"
+export { CarIcon } from "@phosphor-icons/react"
+export { CaretDoubleLeftIcon } from "@phosphor-icons/react"
+export { CaretDoubleRightIcon } from "@phosphor-icons/react"
+export { CaretDownIcon } from "@phosphor-icons/react"
+export { CaretLeftIcon } from "@phosphor-icons/react"
+export { CaretRightIcon } from "@phosphor-icons/react"
+export { CaretUpDownIcon } from "@phosphor-icons/react"
+export { CaretUpIcon } from "@phosphor-icons/react"
+export { ChartBarIcon } from "@phosphor-icons/react"
+export { ChartLineIcon } from "@phosphor-icons/react"
+export { ChartPieIcon } from "@phosphor-icons/react"
+export { ChatCircleIcon } from "@phosphor-icons/react"
+export { ChatIcon } from "@phosphor-icons/react"
+export { CheckCircleIcon } from "@phosphor-icons/react"
+export { CheckIcon } from "@phosphor-icons/react"
+export { CircleDashedIcon } from "@phosphor-icons/react"
+export { ClipboardIcon } from "@phosphor-icons/react"
+export { ClockIcon } from "@phosphor-icons/react"
+export { CloudArrowUpIcon } from "@phosphor-icons/react"
+export { CloudIcon } from "@phosphor-icons/react"
+export { CodeIcon } from "@phosphor-icons/react"
+export { CoffeeIcon } from "@phosphor-icons/react"
+export { ColumnsIcon } from "@phosphor-icons/react"
+export { CommandIcon } from "@phosphor-icons/react"
+export { CopyIcon } from "@phosphor-icons/react"
+export { CreditCardIcon } from "@phosphor-icons/react"
+export { CropIcon } from "@phosphor-icons/react"
+export { CubeIcon } from "@phosphor-icons/react"
+export { DatabaseIcon } from "@phosphor-icons/react"
+export { DotsSixVerticalIcon } from "@phosphor-icons/react"
+export { DotsThreeIcon } from "@phosphor-icons/react"
+export { DotsThreeOutlineIcon } from "@phosphor-icons/react"
+export { DotsThreeVerticalIcon } from "@phosphor-icons/react"
+export { DownloadIcon } from "@phosphor-icons/react"
+export { EnvelopeIcon } from "@phosphor-icons/react"
+export { EyeIcon } from "@phosphor-icons/react"
+export { EyeSlashIcon } from "@phosphor-icons/react"
+export { FileIcon } from "@phosphor-icons/react"
+export { FileTextIcon } from "@phosphor-icons/react"
+export { FloppyDiskIcon } from "@phosphor-icons/react"
+export { FolderIcon } from "@phosphor-icons/react"
+export { FolderOpenIcon } from "@phosphor-icons/react"
+export { FolderPlusIcon } from "@phosphor-icons/react"
+export { GaugeIcon } from "@phosphor-icons/react"
+export { GearIcon } from "@phosphor-icons/react"
+export { GlobeIcon } from "@phosphor-icons/react"
+export { GridFourIcon } from "@phosphor-icons/react"
+export { HardDrivesIcon } from "@phosphor-icons/react"
+export { HeartIcon } from "@phosphor-icons/react"
+export { HouseIcon } from "@phosphor-icons/react"
+export { ImageIcon } from "@phosphor-icons/react"
+export { InfoIcon } from "@phosphor-icons/react"
+export { KeyboardIcon } from "@phosphor-icons/react"
+export { LayoutIcon } from "@phosphor-icons/react"
+export { LifebuoyIcon } from "@phosphor-icons/react"
+export { LightningIcon } from "@phosphor-icons/react"
+export { LinkIcon } from "@phosphor-icons/react"
+export { ListIcon } from "@phosphor-icons/react"
+export { LockIcon } from "@phosphor-icons/react"
+export { LockKeyIcon } from "@phosphor-icons/react"
+export { MagnifyingGlassIcon } from "@phosphor-icons/react"
+export { MagnifyingGlassMinusIcon } from "@phosphor-icons/react"
+export { MagnifyingGlassPlusIcon } from "@phosphor-icons/react"
+export { MapTrifoldIcon } from "@phosphor-icons/react"
+export { MicrophoneIcon } from "@phosphor-icons/react"
+export { MinusIcon } from "@phosphor-icons/react"
+export { MonitorIcon } from "@phosphor-icons/react"
+export { MoonIcon } from "@phosphor-icons/react"
+export { PaletteIcon } from "@phosphor-icons/react"
+export { PaperPlaneTiltIcon } from "@phosphor-icons/react"
+export { PencilIcon } from "@phosphor-icons/react"
+export { PlusCircleIcon } from "@phosphor-icons/react"
+export { PlusIcon } from "@phosphor-icons/react"
+export { QuestionIcon } from "@phosphor-icons/react"
+export { RecordIcon } from "@phosphor-icons/react"
+export { RepeatIcon } from "@phosphor-icons/react"
+export { RobotIcon } from "@phosphor-icons/react"
+export { RowsIcon } from "@phosphor-icons/react"
+export { ScissorsIcon } from "@phosphor-icons/react"
+export { ShareFatIcon } from "@phosphor-icons/react"
+export { ShareIcon } from "@phosphor-icons/react"
+export { ShieldIcon } from "@phosphor-icons/react"
+export { ShoppingCartIcon } from "@phosphor-icons/react"
+export { SidebarIcon } from "@phosphor-icons/react"
+export { SignOutIcon } from "@phosphor-icons/react"
+export { SmileyIcon } from "@phosphor-icons/react"
+export { SparkleIcon } from "@phosphor-icons/react"
+export { SpeakerHighIcon } from "@phosphor-icons/react"
+export { SpeakerSlashIcon } from "@phosphor-icons/react"
+export { SpinnerIcon } from "@phosphor-icons/react"
+export { SquaresFourIcon } from "@phosphor-icons/react"
+export { StarIcon } from "@phosphor-icons/react"
+export { SunIcon } from "@phosphor-icons/react"
+export { TableIcon } from "@phosphor-icons/react"
+export { TargetIcon } from "@phosphor-icons/react"
+export { TelevisionIcon } from "@phosphor-icons/react"
+export { TerminalIcon } from "@phosphor-icons/react"
+export { TextBIcon } from "@phosphor-icons/react"
+export { TextItalicIcon } from "@phosphor-icons/react"
+export { TextTIcon } from "@phosphor-icons/react"
+export { TextUnderlineIcon } from "@phosphor-icons/react"
+export { ThermometerIcon } from "@phosphor-icons/react"
+export { TimerIcon } from "@phosphor-icons/react"
+export { TranslateIcon } from "@phosphor-icons/react"
+export { TrashIcon } from "@phosphor-icons/react"
+export { TrayIcon } from "@phosphor-icons/react"
+export { TrendDownIcon } from "@phosphor-icons/react"
+export { TrendUpIcon } from "@phosphor-icons/react"
+export { UserCircleIcon } from "@phosphor-icons/react"
+export { UserIcon } from "@phosphor-icons/react"
+export { UserMinusIcon } from "@phosphor-icons/react"
+export { UsersIcon } from "@phosphor-icons/react"
+export { VideoIcon } from "@phosphor-icons/react"
+export { WalletIcon } from "@phosphor-icons/react"
+export { WarningCircleIcon } from "@phosphor-icons/react"
+export { WarningIcon } from "@phosphor-icons/react"
+export { WaveformIcon } from "@phosphor-icons/react"
+export { XCircleIcon } from "@phosphor-icons/react"
+export { XIcon } from "@phosphor-icons/react"
diff --git a/apps/ui/src/registry/icons/__remixicon__.ts b/apps/ui/src/registry/icons/__remixicon__.ts
new file mode 100644
index 0000000..dedb1ea
--- /dev/null
+++ b/apps/ui/src/registry/icons/__remixicon__.ts
@@ -0,0 +1,145 @@
+// Auto-generated by scripts/build-icons.ts
+export { RiAddCircleFill } from "@remixicon/react"
+export { RiAddCircleLine } from "@remixicon/react"
+export { RiAddLine } from "@remixicon/react"
+export { RiArchiveLine } from "@remixicon/react"
+export { RiArrowDownLine } from "@remixicon/react"
+export { RiArrowDownSLine } from "@remixicon/react"
+export { RiArrowLeftCircleLine } from "@remixicon/react"
+export { RiArrowLeftLine } from "@remixicon/react"
+export { RiArrowLeftRightLine } from "@remixicon/react"
+export { RiArrowLeftSLine } from "@remixicon/react"
+export { RiArrowRightLine } from "@remixicon/react"
+export { RiArrowRightSLine } from "@remixicon/react"
+export { RiArrowRightUpLine } from "@remixicon/react"
+export { RiArrowUpDownLine } from "@remixicon/react"
+export { RiArrowUpLine } from "@remixicon/react"
+export { RiArrowUpSLine } from "@remixicon/react"
+export { RiBankCardLine } from "@remixicon/react"
+export { RiBankLine } from "@remixicon/react"
+export { RiBarChartLine } from "@remixicon/react"
+export { RiBellLine } from "@remixicon/react"
+export { RiBluetoothLine } from "@remixicon/react"
+export { RiBold } from "@remixicon/react"
+export { RiBookOpenLine } from "@remixicon/react"
+export { RiBookmarkLine } from "@remixicon/react"
+export { RiBox1Line } from "@remixicon/react"
+export { RiBox3Line } from "@remixicon/react"
+export { RiCalculatorLine } from "@remixicon/react"
+export { RiCalendarLine } from "@remixicon/react"
+export { RiCameraLine } from "@remixicon/react"
+export { RiCarLine } from "@remixicon/react"
+export { RiChat1Line } from "@remixicon/react"
+export { RiCheckLine } from "@remixicon/react"
+export { RiCheckboxCircleFill } from "@remixicon/react"
+export { RiCheckboxCircleLine } from "@remixicon/react"
+export { RiClipboardLine } from "@remixicon/react"
+export { RiCloseCircleLine } from "@remixicon/react"
+export { RiCloseLine } from "@remixicon/react"
+export { RiCloudLine } from "@remixicon/react"
+export { RiCodeLine } from "@remixicon/react"
+export { RiCommandLine } from "@remixicon/react"
+export { RiComputerLine } from "@remixicon/react"
+export { RiCornerUpLeftLine } from "@remixicon/react"
+export { RiCornerUpRightLine } from "@remixicon/react"
+export { RiCropLine } from "@remixicon/react"
+export { RiCupLine } from "@remixicon/react"
+export { RiDashboardLine } from "@remixicon/react"
+export { RiDatabase2Line } from "@remixicon/react"
+export { RiDeleteBinLine } from "@remixicon/react"
+export { RiDownloadLine } from "@remixicon/react"
+export { RiDraggable } from "@remixicon/react"
+export { RiEmotionLine } from "@remixicon/react"
+export { RiErrorWarningLine } from "@remixicon/react"
+export { RiExternalLinkLine } from "@remixicon/react"
+export { RiEyeLine } from "@remixicon/react"
+export { RiEyeOffLine } from "@remixicon/react"
+export { RiFileChartLine } from "@remixicon/react"
+export { RiFileCopyLine } from "@remixicon/react"
+export { RiFileLine } from "@remixicon/react"
+export { RiFileTextLine } from "@remixicon/react"
+export { RiFlashlightLine } from "@remixicon/react"
+export { RiFocus3Line } from "@remixicon/react"
+export { RiFolderAddLine } from "@remixicon/react"
+export { RiFolderLine } from "@remixicon/react"
+export { RiFolderOpenLine } from "@remixicon/react"
+export { RiGalleryLine } from "@remixicon/react"
+export { RiGlobalLine } from "@remixicon/react"
+export { RiGlobeLine } from "@remixicon/react"
+export { RiGridLine } from "@remixicon/react"
+export { RiGroupLine } from "@remixicon/react"
+export { RiHardDriveLine } from "@remixicon/react"
+export { RiHeartLine } from "@remixicon/react"
+export { RiHomeLine } from "@remixicon/react"
+export { RiImageLine } from "@remixicon/react"
+export { RiInboxLine } from "@remixicon/react"
+export { RiInformationLine } from "@remixicon/react"
+export { RiItalic } from "@remixicon/react"
+export { RiKeyboardLine } from "@remixicon/react"
+export { RiLayoutColumnLine } from "@remixicon/react"
+export { RiLayoutLeftLine } from "@remixicon/react"
+export { RiLayoutLine } from "@remixicon/react"
+export { RiLifebuoyLine } from "@remixicon/react"
+export { RiLineChartLine } from "@remixicon/react"
+export { RiLinksLine } from "@remixicon/react"
+export { RiListUnordered } from "@remixicon/react"
+export { RiLoader4Line } from "@remixicon/react"
+export { RiLoaderLine } from "@remixicon/react"
+export { RiLockLine } from "@remixicon/react"
+export { RiLogoutBoxLine } from "@remixicon/react"
+export { RiMailLine } from "@remixicon/react"
+export { RiMapLine } from "@remixicon/react"
+export { RiMenuLine } from "@remixicon/react"
+export { RiMicLine } from "@remixicon/react"
+export { RiMoonLine } from "@remixicon/react"
+export { RiMore2Line } from "@remixicon/react"
+export { RiMoreLine } from "@remixicon/react"
+export { RiNotification3Line } from "@remixicon/react"
+export { RiNotificationLine } from "@remixicon/react"
+export { RiPaletteLine } from "@remixicon/react"
+export { RiPencilLine } from "@remixicon/react"
+export { RiPieChartLine } from "@remixicon/react"
+export { RiPulseLine } from "@remixicon/react"
+export { RiQuestionLine } from "@remixicon/react"
+export { RiRecordCircleLine } from "@remixicon/react"
+export { RiRefreshLine } from "@remixicon/react"
+export { RiRepeatLine } from "@remixicon/react"
+export { RiRobotLine } from "@remixicon/react"
+export { RiSaveLine } from "@remixicon/react"
+export { RiScissorsLine } from "@remixicon/react"
+export { RiSearchEyeLine } from "@remixicon/react"
+export { RiSearchLine } from "@remixicon/react"
+export { RiSendPlaneLine } from "@remixicon/react"
+export { RiSettingsLine } from "@remixicon/react"
+export { RiShareForwardLine } from "@remixicon/react"
+export { RiShareLine } from "@remixicon/react"
+export { RiShieldLine } from "@remixicon/react"
+export { RiShoppingBagLine } from "@remixicon/react"
+export { RiShoppingCartLine } from "@remixicon/react"
+export { RiSideBarLine } from "@remixicon/react"
+export { RiSkipLeftLine } from "@remixicon/react"
+export { RiSkipRightLine } from "@remixicon/react"
+export { RiSparklingLine } from "@remixicon/react"
+export { RiStarLine } from "@remixicon/react"
+export { RiStarOffLine } from "@remixicon/react"
+export { RiSubtractLine } from "@remixicon/react"
+export { RiSunLine } from "@remixicon/react"
+export { RiTableLine } from "@remixicon/react"
+export { RiTeamLine } from "@remixicon/react"
+export { RiTerminalBoxLine } from "@remixicon/react"
+export { RiTextWrap } from "@remixicon/react"
+export { RiThermometerLine } from "@remixicon/react"
+export { RiTimeLine } from "@remixicon/react"
+export { RiTimerLine } from "@remixicon/react"
+export { RiTranslate } from "@remixicon/react"
+export { RiTvLine } from "@remixicon/react"
+export { RiUnderline } from "@remixicon/react"
+export { RiUploadCloudLine } from "@remixicon/react"
+export { RiUserLine } from "@remixicon/react"
+export { RiUserUnfollowLine } from "@remixicon/react"
+export { RiVideoLine } from "@remixicon/react"
+export { RiVolumeMuteLine } from "@remixicon/react"
+export { RiVolumeUpLine } from "@remixicon/react"
+export { RiWalletLine } from "@remixicon/react"
+export { RiWindowLine } from "@remixicon/react"
+export { RiZoomInLine } from "@remixicon/react"
diff --git a/apps/ui/src/registry/icons/__tabler__.ts b/apps/ui/src/registry/icons/__tabler__.ts
new file mode 100644
index 0000000..b81a761
--- /dev/null
+++ b/apps/ui/src/registry/icons/__tabler__.ts
@@ -0,0 +1,172 @@
+// Auto-generated by scripts/build-icons.ts
+export { IconActivity } from "@tabler/icons-react"
+export { IconAlertCircle } from "@tabler/icons-react"
+export { IconAlertOctagon } from "@tabler/icons-react"
+export { IconAlertTriangle } from "@tabler/icons-react"
+export { IconAppWindow } from "@tabler/icons-react"
+export { IconArchive } from "@tabler/icons-react"
+export { IconArchiveOff } from "@tabler/icons-react"
+export { IconArrowDown } from "@tabler/icons-react"
+export { IconArrowForward } from "@tabler/icons-react"
+export { IconArrowLeft } from "@tabler/icons-react"
+export { IconArrowRight } from "@tabler/icons-react"
+export { IconArrowUp } from "@tabler/icons-react"
+export { IconArrowUpRight } from "@tabler/icons-react"
+export { IconArrowsLeftRight } from "@tabler/icons-react"
+export { IconBell } from "@tabler/icons-react"
+export { IconBluetooth } from "@tabler/icons-react"
+export { IconBold } from "@tabler/icons-react"
+export { IconBolt } from "@tabler/icons-react"
+export { IconBook } from "@tabler/icons-react"
+export { IconBookmark } from "@tabler/icons-react"
+export { IconBox } from "@tabler/icons-react"
+export { IconBrandJavascript } from "@tabler/icons-react"
+export { IconBuildingBank } from "@tabler/icons-react"
+export { IconCalculator } from "@tabler/icons-react"
+export { IconCalendar } from "@tabler/icons-react"
+export { IconCamera } from "@tabler/icons-react"
+export { IconCar } from "@tabler/icons-react"
+export { IconChartBar } from "@tabler/icons-react"
+export { IconChartLine } from "@tabler/icons-react"
+export { IconChartPie } from "@tabler/icons-react"
+export { IconCheck } from "@tabler/icons-react"
+export { IconChevronDown } from "@tabler/icons-react"
+export { IconChevronLeft } from "@tabler/icons-react"
+export { IconChevronRight } from "@tabler/icons-react"
+export { IconChevronUp } from "@tabler/icons-react"
+export { IconChevronsLeft } from "@tabler/icons-react"
+export { IconChevronsRight } from "@tabler/icons-react"
+export { IconCircleArrowLeft } from "@tabler/icons-react"
+export { IconCircleCheck } from "@tabler/icons-react"
+export { IconCircleCheckFilled } from "@tabler/icons-react"
+export { IconCircleDashed } from "@tabler/icons-react"
+export { IconCirclePlus } from "@tabler/icons-react"
+export { IconCirclePlusFilled } from "@tabler/icons-react"
+export { IconClipboard } from "@tabler/icons-react"
+export { IconClock } from "@tabler/icons-react"
+export { IconClockHour2 } from "@tabler/icons-react"
+export { IconCloud } from "@tabler/icons-react"
+export { IconCloudUpload } from "@tabler/icons-react"
+export { IconCode } from "@tabler/icons-react"
+export { IconCoffee } from "@tabler/icons-react"
+export { IconCommand } from "@tabler/icons-react"
+export { IconCopy } from "@tabler/icons-react"
+export { IconCornerUpLeft } from "@tabler/icons-react"
+export { IconCornerUpRight } from "@tabler/icons-react"
+export { IconCreditCard } from "@tabler/icons-react"
+export { IconCube } from "@tabler/icons-react"
+export { IconCut } from "@tabler/icons-react"
+export { IconDashboard } from "@tabler/icons-react"
+export { IconDatabase } from "@tabler/icons-react"
+export { IconDeviceDesktop } from "@tabler/icons-react"
+export { IconDeviceFloppy } from "@tabler/icons-react"
+export { IconDeviceTv } from "@tabler/icons-react"
+export { IconDots } from "@tabler/icons-react"
+export { IconDotsVertical } from "@tabler/icons-react"
+export { IconDownload } from "@tabler/icons-react"
+export { IconExclamationCircle } from "@tabler/icons-react"
+export { IconExternalLink } from "@tabler/icons-react"
+export { IconEye } from "@tabler/icons-react"
+export { IconEyeClosed } from "@tabler/icons-react"
+export { IconFile } from "@tabler/icons-react"
+export { IconFileAi } from "@tabler/icons-react"
+export { IconFileCode } from "@tabler/icons-react"
+export { IconFileDescription } from "@tabler/icons-react"
+export { IconFileText } from "@tabler/icons-react"
+export { IconFileWord } from "@tabler/icons-react"
+export { IconFlipHorizontal } from "@tabler/icons-react"
+export { IconFlipVertical } from "@tabler/icons-react"
+export { IconFolder } from "@tabler/icons-react"
+export { IconFolderOpen } from "@tabler/icons-react"
+export { IconFolderPlus } from "@tabler/icons-react"
+export { IconFolderSearch } from "@tabler/icons-react"
+export { IconFrame } from "@tabler/icons-react"
+export { IconGauge } from "@tabler/icons-react"
+export { IconGlobe } from "@tabler/icons-react"
+export { IconGripVertical } from "@tabler/icons-react"
+export { IconHeadphones } from "@tabler/icons-react"
+export { IconHeart } from "@tabler/icons-react"
+export { IconHelp } from "@tabler/icons-react"
+export { IconHelpCircle } from "@tabler/icons-react"
+export { IconHome } from "@tabler/icons-react"
+export { IconInbox } from "@tabler/icons-react"
+export { IconInfoCircle } from "@tabler/icons-react"
+export { IconInnerShadowTop } from "@tabler/icons-react"
+export { IconItalic } from "@tabler/icons-react"
+export { IconKeyboard } from "@tabler/icons-react"
+export { IconLanguage } from "@tabler/icons-react"
+export { IconLayout } from "@tabler/icons-react"
+export { IconLayoutColumns } from "@tabler/icons-react"
+export { IconLayoutDashboard } from "@tabler/icons-react"
+export { IconLayoutGrid } from "@tabler/icons-react"
+export { IconLayoutRows } from "@tabler/icons-react"
+export { IconLayoutSidebar } from "@tabler/icons-react"
+export { IconLifebuoy } from "@tabler/icons-react"
+export { IconLink } from "@tabler/icons-react"
+export { IconList } from "@tabler/icons-react"
+export { IconListDetails } from "@tabler/icons-react"
+export { IconLoader } from "@tabler/icons-react"
+export { IconLock } from "@tabler/icons-react"
+export { IconLogout } from "@tabler/icons-react"
+export { IconMail } from "@tabler/icons-react"
+export { IconMap } from "@tabler/icons-react"
+export { IconMaximize } from "@tabler/icons-react"
+export { IconMenu } from "@tabler/icons-react"
+export { IconMessage } from "@tabler/icons-react"
+export { IconMessageQuestion } from "@tabler/icons-react"
+export { IconMicrophone } from "@tabler/icons-react"
+export { IconMinimize } from "@tabler/icons-react"
+export { IconMinus } from "@tabler/icons-react"
+export { IconMoodSmile } from "@tabler/icons-react"
+export { IconMoon } from "@tabler/icons-react"
+export { IconNotification } from "@tabler/icons-react"
+export { IconPalette } from "@tabler/icons-react"
+export { IconPencil } from "@tabler/icons-react"
+export { IconPhoto } from "@tabler/icons-react"
+export { IconPlayerRecordFilled } from "@tabler/icons-react"
+export { IconPlus } from "@tabler/icons-react"
+export { IconRefresh } from "@tabler/icons-react"
+export { IconRepeat } from "@tabler/icons-react"
+export { IconReport } from "@tabler/icons-react"
+export { IconReportAnalytics } from "@tabler/icons-react"
+export { IconRobot } from "@tabler/icons-react"
+export { IconRosetteDiscountCheck } from "@tabler/icons-react"
+export { IconRotateClockwise2 } from "@tabler/icons-react"
+export { IconSearch } from "@tabler/icons-react"
+export { IconSelector } from "@tabler/icons-react"
+export { IconSend } from "@tabler/icons-react"
+export { IconServer } from "@tabler/icons-react"
+export { IconServerSpark } from "@tabler/icons-react"
+export { IconSettings } from "@tabler/icons-react"
+export { IconShare } from "@tabler/icons-react"
+export { IconShare2 } from "@tabler/icons-react"
+export { IconShare3 } from "@tabler/icons-react"
+export { IconShield } from "@tabler/icons-react"
+export { IconShoppingBag } from "@tabler/icons-react"
+export { IconShoppingCart } from "@tabler/icons-react"
+export { IconSparkles } from "@tabler/icons-react"
+export { IconStar } from "@tabler/icons-react"
+export { IconStarOff } from "@tabler/icons-react"
+export { IconSun } from "@tabler/icons-react"
+export { IconTable } from "@tabler/icons-react"
+export { IconTarget } from "@tabler/icons-react"
+export { IconTerminal } from "@tabler/icons-react"
+export { IconTerminal2 } from "@tabler/icons-react"
+export { IconTextCaption } from "@tabler/icons-react"
+export { IconThermometer } from "@tabler/icons-react"
+export { IconTrash } from "@tabler/icons-react"
+export { IconTrendingDown } from "@tabler/icons-react"
+export { IconTrendingUp } from "@tabler/icons-react"
+export { IconUnderline } from "@tabler/icons-react"
+export { IconUser } from "@tabler/icons-react"
+export { IconUserCircle } from "@tabler/icons-react"
+export { IconUserX } from "@tabler/icons-react"
+export { IconUsers } from "@tabler/icons-react"
+export { IconVideoPlus } from "@tabler/icons-react"
+export { IconVolume } from "@tabler/icons-react"
+export { IconWallet } from "@tabler/icons-react"
+export { IconWaveSine } from "@tabler/icons-react"
+export { IconWorld } from "@tabler/icons-react"
+export { IconX } from "@tabler/icons-react"
+export { IconZoomIn } from "@tabler/icons-react"
+export { IconZoomOut } from "@tabler/icons-react"
diff --git a/apps/ui/src/registry/icons/create-icon-loader.tsx b/apps/ui/src/registry/icons/create-icon-loader.tsx
new file mode 100644
index 0000000..18c4e7a
--- /dev/null
+++ b/apps/ui/src/registry/icons/create-icon-loader.tsx
@@ -0,0 +1,50 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import { use } from "react"
+import { HugeiconsIcon, type IconSvgElement } from "@hugeicons/react"
+
+const iconPromiseCaches = new Map>>()
+
+function getCache(libraryName: string) {
+ if (!iconPromiseCaches.has(libraryName)) {
+ iconPromiseCaches.set(libraryName, new Map())
+ }
+ return iconPromiseCaches.get(libraryName)!
+}
+
+function isIconData(data: any): data is IconSvgElement {
+ return Array.isArray(data)
+}
+
+export function createIconLoader(libraryName: string) {
+ const cache = getCache(libraryName)
+
+ return function IconLoader({
+ name,
+ strokeWidth = 2,
+ ...props
+ }: {
+ name: string
+ } & React.ComponentProps<"svg">) {
+ if (!cache.has(name)) {
+ const promise = import(/* @vite-ignore */ `./__${libraryName}__.ts`).then((mod) => {
+ const icon = mod[name as keyof typeof mod]
+ return icon || null
+ })
+ cache.set(name, promise)
+ }
+
+ const iconData = use(cache.get(name)!)
+
+ if (!iconData) {
+ return null
+ }
+
+ if (isIconData(iconData)) {
+ return
+ }
+
+ const IconComponent = iconData
+ return
+ }
+}
diff --git a/apps/ui/src/registry/icons/icon-hugeicons.tsx b/apps/ui/src/registry/icons/icon-hugeicons.tsx
new file mode 100644
index 0000000..3684eaa
--- /dev/null
+++ b/apps/ui/src/registry/icons/icon-hugeicons.tsx
@@ -0,0 +1,3 @@
+import { createIconLoader } from "./create-icon-loader"
+
+export const IconHugeicons = createIconLoader("hugeicons")
diff --git a/apps/ui/src/registry/icons/icon-lucide.tsx b/apps/ui/src/registry/icons/icon-lucide.tsx
new file mode 100644
index 0000000..18552f4
--- /dev/null
+++ b/apps/ui/src/registry/icons/icon-lucide.tsx
@@ -0,0 +1,3 @@
+import { createIconLoader } from "./create-icon-loader"
+
+export const IconLucide = createIconLoader("lucide")
diff --git a/apps/ui/src/registry/icons/icon-phosphor.tsx b/apps/ui/src/registry/icons/icon-phosphor.tsx
new file mode 100644
index 0000000..8aba47e
--- /dev/null
+++ b/apps/ui/src/registry/icons/icon-phosphor.tsx
@@ -0,0 +1,3 @@
+import { createIconLoader } from "./create-icon-loader"
+
+export const IconPhosphor = createIconLoader("phosphor")
diff --git a/apps/ui/src/registry/icons/icon-remixicon.tsx b/apps/ui/src/registry/icons/icon-remixicon.tsx
new file mode 100644
index 0000000..a5d5243
--- /dev/null
+++ b/apps/ui/src/registry/icons/icon-remixicon.tsx
@@ -0,0 +1,3 @@
+import { createIconLoader } from "./create-icon-loader"
+
+export const IconRemixicon = createIconLoader("remixicon")
diff --git a/apps/ui/src/registry/icons/icon-tabler.tsx b/apps/ui/src/registry/icons/icon-tabler.tsx
new file mode 100644
index 0000000..dbebafb
--- /dev/null
+++ b/apps/ui/src/registry/icons/icon-tabler.tsx
@@ -0,0 +1,3 @@
+import { createIconLoader } from "./create-icon-loader"
+
+export const IconTabler = createIconLoader("tabler")
diff --git a/apps/ui/src/registry/new-york-v4/blocks/_registry.ts b/apps/ui/src/registry/new-york-v4/blocks/_registry.ts
new file mode 100644
index 0000000..ae9dc96
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/_registry.ts
@@ -0,0 +1,799 @@
+import { type Registry } from "shadcn/schema"
+
+export const blocks: Registry["items"] = [
+ {
+ name: "dashboard-01",
+ type: "registry:block",
+ description: "A dashboard with sidebar, charts and data table.",
+ dependencies: [
+ "@dnd-kit/core",
+ "@dnd-kit/modifiers",
+ "@dnd-kit/sortable",
+ "@dnd-kit/utilities",
+ "@tabler/icons-react",
+ "@tanstack/react-table",
+ "zod",
+ ],
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "label",
+ "chart",
+ "card",
+ "select",
+ "tabs",
+ "table",
+ "toggle-group",
+ "badge",
+ "button",
+ "checkbox",
+ "dropdown-menu",
+ "drawer",
+ "input",
+ "avatar",
+ "sheet",
+ "sonner",
+ ],
+ files: [
+ {
+ path: "blocks/dashboard-01/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/dashboard-01/data.json",
+ type: "registry:file",
+ target: "app/dashboard/data.json",
+ },
+ {
+ path: "blocks/dashboard-01/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/chart-area-interactive.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/data-table.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/nav-documents.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/nav-main.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/nav-secondary.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/nav-user.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/section-cards.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/dashboard-01/components/site-header.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["dashboard"],
+ meta: {
+ iframeHeight: "1000px",
+ },
+ },
+ {
+ name: "sidebar-01",
+ type: "registry:block",
+ description: "A simple sidebar with navigation grouped by section.",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "label",
+ "dropdown-menu",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-01/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-01/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-01/components/search-form.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-01/components/version-switcher.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-02",
+ description: "A sidebar with collapsible sections.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "label",
+ "dropdown-menu",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-02/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-02/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-02/components/search-form.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-02/components/version-switcher.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-03",
+ description: "A sidebar with submenus.",
+ type: "registry:block",
+ registryDependencies: ["sidebar", "breadcrumb"],
+ files: [
+ {
+ path: "blocks/sidebar-03/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-03/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-04",
+ description: "A floating sidebar with submenus.",
+ type: "registry:block",
+ registryDependencies: ["sidebar", "breadcrumb", "separator"],
+ files: [
+ {
+ path: "blocks/sidebar-04/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-04/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-05",
+ description: "A sidebar with collapsible submenus.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "label",
+ "collapsible",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-05/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-05/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-05/components/search-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-06",
+ description: "A sidebar with submenus as dropdowns.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "card",
+ "dropdown-menu",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-06/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-06/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-06/components/nav-main.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-06/components/sidebar-opt-in-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-07",
+ type: "registry:block",
+ description: "A sidebar that collapses to icons.",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "collapsible",
+ "dropdown-menu",
+ "avatar",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-07/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-07/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-07/components/nav-main.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-07/components/nav-projects.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-07/components/nav-user.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-07/components/team-switcher.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-08",
+ description: "An inset sidebar with secondary navigation.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "collapsible",
+ "dropdown-menu",
+ "avatar",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-08/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-08/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-08/components/nav-main.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-08/components/nav-projects.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-08/components/nav-secondary.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-08/components/nav-user.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-09",
+ description: "Collapsible nested sidebars.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "collapsible",
+ "dropdown-menu",
+ "avatar",
+ "switch",
+ "label",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-09/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-09/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-09/components/nav-user.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-10",
+ description: "A sidebar in a popover.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "popover",
+ "collapsible",
+ "dropdown-menu",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-10/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-10/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-10/components/nav-actions.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-10/components/nav-favorites.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-10/components/nav-main.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-10/components/nav-secondary.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-10/components/nav-workspaces.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-10/components/team-switcher.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-11",
+ description: "A sidebar with a collapsible file tree.",
+ type: "registry:block",
+ registryDependencies: ["sidebar", "breadcrumb", "separator", "collapsible"],
+ files: [
+ {
+ path: "blocks/sidebar-11/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-11/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-12",
+ description: "A sidebar with a calendar.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "collapsible",
+ "calendar",
+ "dropdown-menu",
+ "avatar",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-12/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-12/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-12/components/calendars.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-12/components/date-picker.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-12/components/nav-user.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-13",
+ description: "A sidebar in a dialog.",
+ type: "registry:block",
+ registryDependencies: ["sidebar", "breadcrumb", "button", "dialog"],
+ files: [
+ {
+ path: "blocks/sidebar-13/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-13/components/settings-dialog.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-14",
+ description: "A sidebar on the right.",
+ type: "registry:block",
+ registryDependencies: ["sidebar", "breadcrumb"],
+ files: [
+ {
+ path: "blocks/sidebar-14/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-14/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-15",
+ description: "A left and right sidebar.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "popover",
+ "collapsible",
+ "dropdown-menu",
+ "calendar",
+ "avatar",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-15/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-15/components/calendars.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/date-picker.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/nav-favorites.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/nav-main.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/nav-secondary.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/nav-user.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/nav-workspaces.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/sidebar-left.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/sidebar-right.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-15/components/team-switcher.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "sidebar-16",
+ description: "A sidebar with a sticky site header.",
+ type: "registry:block",
+ registryDependencies: [
+ "sidebar",
+ "breadcrumb",
+ "separator",
+ "collapsible",
+ "dropdown-menu",
+ "avatar",
+ "button",
+ "label",
+ ],
+ files: [
+ {
+ path: "blocks/sidebar-16/page.tsx",
+ type: "registry:page",
+ target: "app/dashboard/page.tsx",
+ },
+ {
+ path: "blocks/sidebar-16/components/app-sidebar.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-16/components/nav-main.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-16/components/nav-projects.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-16/components/nav-secondary.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-16/components/nav-user.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-16/components/search-form.tsx",
+ type: "registry:component",
+ },
+ {
+ path: "blocks/sidebar-16/components/site-header.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["sidebar", "dashboard"],
+ },
+ {
+ name: "login-01",
+ description: "A simple login form.",
+ type: "registry:block",
+ registryDependencies: ["button", "card", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/login-01/page.tsx",
+ target: "app/login/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/login-01/components/login-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "login"],
+ },
+ {
+ name: "login-02",
+ description: "A two column login page with a cover image.",
+ type: "registry:block",
+ registryDependencies: ["button", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/login-02/page.tsx",
+ target: "app/login/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/login-02/components/login-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "login"],
+ },
+ {
+ name: "login-03",
+ description: "A login page with a muted background color.",
+ type: "registry:block",
+ registryDependencies: ["button", "card", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/login-03/page.tsx",
+ target: "app/login/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/login-03/components/login-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "login"],
+ },
+ {
+ name: "login-04",
+ description: "A login page with form and image.",
+ type: "registry:block",
+ registryDependencies: ["button", "card", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/login-04/page.tsx",
+ target: "app/login/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/login-04/components/login-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "login"],
+ },
+ {
+ name: "login-05",
+ description: "A simple email-only login page.",
+ type: "registry:block",
+ registryDependencies: ["button", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/login-05/page.tsx",
+ target: "app/login/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/login-05/components/login-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "login"],
+ },
+ {
+ name: "signup-01",
+ description: "A simple signup form.",
+ type: "registry:block",
+ registryDependencies: ["button", "card", "input", "label"],
+ files: [
+ {
+ path: "blocks/signup-01/page.tsx",
+ target: "app/signup/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/signup-01/components/signup-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "signup"],
+ },
+ {
+ name: "signup-02",
+ description: "A two column signup page with a cover image.",
+ type: "registry:block",
+ registryDependencies: ["button", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/signup-02/page.tsx",
+ target: "app/signup/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/signup-02/components/signup-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "signup"],
+ },
+ {
+ name: "signup-03",
+ description: "A signup page with a muted background color.",
+ type: "registry:block",
+ registryDependencies: ["button", "card", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/signup-03/page.tsx",
+ target: "app/signup/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/signup-03/components/signup-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "signup"],
+ },
+ {
+ name: "signup-04",
+ description: "A signup page with form and image.",
+ type: "registry:block",
+ registryDependencies: ["button", "card", "input", "label", "field"],
+ files: [
+ {
+ path: "blocks/signup-04/page.tsx",
+ target: "app/signup/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/signup-04/components/signup-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "signup"],
+ },
+ {
+ name: "signup-05",
+ description: "A simple signup form with social providers.",
+ type: "registry:block",
+ registryDependencies: ["button", "input", "label"],
+ files: [
+ {
+ path: "blocks/signup-05/page.tsx",
+ target: "app/signup/page.tsx",
+ type: "registry:page",
+ },
+ {
+ path: "blocks/signup-05/components/signup-form.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["authentication", "signup"],
+ },
+]
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/app-sidebar.tsx
new file mode 100644
index 0000000..138d345
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/app-sidebar.tsx
@@ -0,0 +1,180 @@
+
+import * as React from "react"
+import {
+ IconCamera,
+ IconChartBar,
+ IconDashboard,
+ IconDatabase,
+ IconFileAi,
+ IconFileDescription,
+ IconFileWord,
+ IconFolder,
+ IconHelp,
+ IconInnerShadowTop,
+ IconListDetails,
+ IconReport,
+ IconSearch,
+ IconSettings,
+ IconUsers,
+} from "@tabler/icons-react"
+
+import { NavDocuments } from "@/registry/new-york-v4/blocks/dashboard-01/components/nav-documents"
+import { NavMain } from "@/registry/new-york-v4/blocks/dashboard-01/components/nav-main"
+import { NavSecondary } from "@/registry/new-york-v4/blocks/dashboard-01/components/nav-secondary"
+import { NavUser } from "@/registry/new-york-v4/blocks/dashboard-01/components/nav-user"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ navMain: [
+ {
+ title: "Dashboard",
+ url: "#",
+ icon: IconDashboard,
+ },
+ {
+ title: "Lifecycle",
+ url: "#",
+ icon: IconListDetails,
+ },
+ {
+ title: "Analytics",
+ url: "#",
+ icon: IconChartBar,
+ },
+ {
+ title: "Projects",
+ url: "#",
+ icon: IconFolder,
+ },
+ {
+ title: "Team",
+ url: "#",
+ icon: IconUsers,
+ },
+ ],
+ navClouds: [
+ {
+ title: "Capture",
+ icon: IconCamera,
+ isActive: true,
+ url: "#",
+ items: [
+ {
+ title: "Active Proposals",
+ url: "#",
+ },
+ {
+ title: "Archived",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Proposal",
+ icon: IconFileDescription,
+ url: "#",
+ items: [
+ {
+ title: "Active Proposals",
+ url: "#",
+ },
+ {
+ title: "Archived",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Prompts",
+ icon: IconFileAi,
+ url: "#",
+ items: [
+ {
+ title: "Active Proposals",
+ url: "#",
+ },
+ {
+ title: "Archived",
+ url: "#",
+ },
+ ],
+ },
+ ],
+ navSecondary: [
+ {
+ title: "Settings",
+ url: "#",
+ icon: IconSettings,
+ },
+ {
+ title: "Get Help",
+ url: "#",
+ icon: IconHelp,
+ },
+ {
+ title: "Search",
+ url: "#",
+ icon: IconSearch,
+ },
+ ],
+ documents: [
+ {
+ name: "Data Library",
+ url: "#",
+ icon: IconDatabase,
+ },
+ {
+ name: "Reports",
+ url: "#",
+ icon: IconReport,
+ },
+ {
+ name: "Word Assistant",
+ url: "#",
+ icon: IconFileWord,
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+ Acme Inc.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/chart-area-interactive.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/chart-area-interactive.tsx
new file mode 100644
index 0000000..00900e5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/chart-area-interactive.tsx
@@ -0,0 +1,290 @@
+
+import * as React from "react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import { useIsMobile } from "@/registry/new-york-v4/hooks/use-mobile"
+import {
+ Card,
+ CardAction,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export const description = "An interactive area chart"
+
+const chartData = [
+ { date: "2024-04-01", desktop: 222, mobile: 150 },
+ { date: "2024-04-02", desktop: 97, mobile: 180 },
+ { date: "2024-04-03", desktop: 167, mobile: 120 },
+ { date: "2024-04-04", desktop: 242, mobile: 260 },
+ { date: "2024-04-05", desktop: 373, mobile: 290 },
+ { date: "2024-04-06", desktop: 301, mobile: 340 },
+ { date: "2024-04-07", desktop: 245, mobile: 180 },
+ { date: "2024-04-08", desktop: 409, mobile: 320 },
+ { date: "2024-04-09", desktop: 59, mobile: 110 },
+ { date: "2024-04-10", desktop: 261, mobile: 190 },
+ { date: "2024-04-11", desktop: 327, mobile: 350 },
+ { date: "2024-04-12", desktop: 292, mobile: 210 },
+ { date: "2024-04-13", desktop: 342, mobile: 380 },
+ { date: "2024-04-14", desktop: 137, mobile: 220 },
+ { date: "2024-04-15", desktop: 120, mobile: 170 },
+ { date: "2024-04-16", desktop: 138, mobile: 190 },
+ { date: "2024-04-17", desktop: 446, mobile: 360 },
+ { date: "2024-04-18", desktop: 364, mobile: 410 },
+ { date: "2024-04-19", desktop: 243, mobile: 180 },
+ { date: "2024-04-20", desktop: 89, mobile: 150 },
+ { date: "2024-04-21", desktop: 137, mobile: 200 },
+ { date: "2024-04-22", desktop: 224, mobile: 170 },
+ { date: "2024-04-23", desktop: 138, mobile: 230 },
+ { date: "2024-04-24", desktop: 387, mobile: 290 },
+ { date: "2024-04-25", desktop: 215, mobile: 250 },
+ { date: "2024-04-26", desktop: 75, mobile: 130 },
+ { date: "2024-04-27", desktop: 383, mobile: 420 },
+ { date: "2024-04-28", desktop: 122, mobile: 180 },
+ { date: "2024-04-29", desktop: 315, mobile: 240 },
+ { date: "2024-04-30", desktop: 454, mobile: 380 },
+ { date: "2024-05-01", desktop: 165, mobile: 220 },
+ { date: "2024-05-02", desktop: 293, mobile: 310 },
+ { date: "2024-05-03", desktop: 247, mobile: 190 },
+ { date: "2024-05-04", desktop: 385, mobile: 420 },
+ { date: "2024-05-05", desktop: 481, mobile: 390 },
+ { date: "2024-05-06", desktop: 498, mobile: 520 },
+ { date: "2024-05-07", desktop: 388, mobile: 300 },
+ { date: "2024-05-08", desktop: 149, mobile: 210 },
+ { date: "2024-05-09", desktop: 227, mobile: 180 },
+ { date: "2024-05-10", desktop: 293, mobile: 330 },
+ { date: "2024-05-11", desktop: 335, mobile: 270 },
+ { date: "2024-05-12", desktop: 197, mobile: 240 },
+ { date: "2024-05-13", desktop: 197, mobile: 160 },
+ { date: "2024-05-14", desktop: 448, mobile: 490 },
+ { date: "2024-05-15", desktop: 473, mobile: 380 },
+ { date: "2024-05-16", desktop: 338, mobile: 400 },
+ { date: "2024-05-17", desktop: 499, mobile: 420 },
+ { date: "2024-05-18", desktop: 315, mobile: 350 },
+ { date: "2024-05-19", desktop: 235, mobile: 180 },
+ { date: "2024-05-20", desktop: 177, mobile: 230 },
+ { date: "2024-05-21", desktop: 82, mobile: 140 },
+ { date: "2024-05-22", desktop: 81, mobile: 120 },
+ { date: "2024-05-23", desktop: 252, mobile: 290 },
+ { date: "2024-05-24", desktop: 294, mobile: 220 },
+ { date: "2024-05-25", desktop: 201, mobile: 250 },
+ { date: "2024-05-26", desktop: 213, mobile: 170 },
+ { date: "2024-05-27", desktop: 420, mobile: 460 },
+ { date: "2024-05-28", desktop: 233, mobile: 190 },
+ { date: "2024-05-29", desktop: 78, mobile: 130 },
+ { date: "2024-05-30", desktop: 340, mobile: 280 },
+ { date: "2024-05-31", desktop: 178, mobile: 230 },
+ { date: "2024-06-01", desktop: 178, mobile: 200 },
+ { date: "2024-06-02", desktop: 470, mobile: 410 },
+ { date: "2024-06-03", desktop: 103, mobile: 160 },
+ { date: "2024-06-04", desktop: 439, mobile: 380 },
+ { date: "2024-06-05", desktop: 88, mobile: 140 },
+ { date: "2024-06-06", desktop: 294, mobile: 250 },
+ { date: "2024-06-07", desktop: 323, mobile: 370 },
+ { date: "2024-06-08", desktop: 385, mobile: 320 },
+ { date: "2024-06-09", desktop: 438, mobile: 480 },
+ { date: "2024-06-10", desktop: 155, mobile: 200 },
+ { date: "2024-06-11", desktop: 92, mobile: 150 },
+ { date: "2024-06-12", desktop: 492, mobile: 420 },
+ { date: "2024-06-13", desktop: 81, mobile: 130 },
+ { date: "2024-06-14", desktop: 426, mobile: 380 },
+ { date: "2024-06-15", desktop: 307, mobile: 350 },
+ { date: "2024-06-16", desktop: 371, mobile: 310 },
+ { date: "2024-06-17", desktop: 475, mobile: 520 },
+ { date: "2024-06-18", desktop: 107, mobile: 170 },
+ { date: "2024-06-19", desktop: 341, mobile: 290 },
+ { date: "2024-06-20", desktop: 408, mobile: 450 },
+ { date: "2024-06-21", desktop: 169, mobile: 210 },
+ { date: "2024-06-22", desktop: 317, mobile: 270 },
+ { date: "2024-06-23", desktop: 480, mobile: 530 },
+ { date: "2024-06-24", desktop: 132, mobile: 180 },
+ { date: "2024-06-25", desktop: 141, mobile: 190 },
+ { date: "2024-06-26", desktop: 434, mobile: 380 },
+ { date: "2024-06-27", desktop: 448, mobile: 490 },
+ { date: "2024-06-28", desktop: 149, mobile: 200 },
+ { date: "2024-06-29", desktop: 103, mobile: 160 },
+ { date: "2024-06-30", desktop: 446, mobile: 400 },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ desktop: {
+ label: "Desktop",
+ color: "var(--primary)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--primary)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaInteractive() {
+ const isMobile = useIsMobile()
+ const [timeRange, setTimeRange] = React.useState("90d")
+
+ React.useEffect(() => {
+ if (isMobile) {
+ setTimeRange("7d")
+ }
+ }, [isMobile])
+
+ const filteredData = chartData.filter((item) => {
+ const date = new Date(item.date)
+ const referenceDate = new Date("2024-06-30")
+ let daysToSubtract = 90
+ if (timeRange === "30d") {
+ daysToSubtract = 30
+ } else if (timeRange === "7d") {
+ daysToSubtract = 7
+ }
+ const startDate = new Date(referenceDate)
+ startDate.setDate(startDate.getDate() - daysToSubtract)
+ return date >= startDate
+ })
+
+ return (
+
+
+ Total Visitors
+
+
+ Total for the last 3 months
+
+ Last 3 months
+
+
+
+ Last 3 months
+ Last 30 days
+ Last 7 days
+
+
+
+
+
+
+
+ Last 3 months
+
+
+ Last 30 days
+
+
+ Last 7 days
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ const date = new Date(value)
+ return date.toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ />
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ indicator="dot"
+ />
+ }
+ />
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/data-table.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/data-table.tsx
new file mode 100644
index 0000000..9a21331
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/data-table.tsx
@@ -0,0 +1,806 @@
+
+import * as React from "react"
+import {
+ closestCenter,
+ DndContext,
+ KeyboardSensor,
+ MouseSensor,
+ TouchSensor,
+ useSensor,
+ useSensors,
+ type DragEndEvent,
+ type UniqueIdentifier,
+} from "@dnd-kit/core"
+import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
+import {
+ arrayMove,
+ SortableContext,
+ useSortable,
+ verticalListSortingStrategy,
+} from "@dnd-kit/sortable"
+import { CSS } from "@dnd-kit/utilities"
+import {
+ IconChevronDown,
+ IconChevronLeft,
+ IconChevronRight,
+ IconChevronsLeft,
+ IconChevronsRight,
+ IconCircleCheckFilled,
+ IconDotsVertical,
+ IconGripVertical,
+ IconLayoutColumns,
+ IconLoader,
+ IconPlus,
+ IconTrendingUp,
+} from "@tabler/icons-react"
+import {
+ flexRender,
+ getCoreRowModel,
+ getFacetedRowModel,
+ getFacetedUniqueValues,
+ getFilteredRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable,
+ type ColumnDef,
+ type ColumnFiltersState,
+ type Row,
+ type SortingState,
+ type VisibilityState,
+} from "@tanstack/react-table"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+import { toast } from "sonner"
+import { z } from "zod"
+
+import { useIsMobile } from "@/registry/new-york-v4/hooks/use-mobile"
+import { Badge } from "@/registry/new-york-v4/ui/badge"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/registry/new-york-v4/ui/drawer"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/registry/new-york-v4/ui/table"
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from "@/registry/new-york-v4/ui/tabs"
+
+export const schema = z.object({
+ id: z.number(),
+ header: z.string(),
+ type: z.string(),
+ status: z.string(),
+ target: z.string(),
+ limit: z.string(),
+ reviewer: z.string(),
+})
+
+// Create a separate component for the drag handle
+function DragHandle({ id }: { id: number }) {
+ const { attributes, listeners } = useSortable({
+ id,
+ })
+
+ return (
+
+
+ Drag to reorder
+
+ )
+}
+
+const columns: ColumnDef>[] = [
+ {
+ id: "drag",
+ header: () => null,
+ cell: ({ row }) => ,
+ },
+ {
+ id: "select",
+ header: ({ table }) => (
+
+ table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Select all"
+ />
+
+ ),
+ cell: ({ row }) => (
+
+ row.toggleSelected(!!value)}
+ aria-label="Select row"
+ />
+
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+ {
+ accessorKey: "header",
+ header: "Header",
+ cell: ({ row }) => {
+ return
+ },
+ enableHiding: false,
+ },
+ {
+ accessorKey: "type",
+ header: "Section Type",
+ cell: ({ row }) => (
+
+
+ {row.original.type}
+
+
+ ),
+ },
+ {
+ accessorKey: "status",
+ header: "Status",
+ cell: ({ row }) => (
+
+ {row.original.status === "Done" ? (
+
+ ) : (
+
+ )}
+ {row.original.status}
+
+ ),
+ },
+ {
+ accessorKey: "target",
+ header: () => Target
,
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ accessorKey: "limit",
+ header: () => Limit
,
+ cell: ({ row }) => (
+
+ ),
+ },
+ {
+ accessorKey: "reviewer",
+ header: "Reviewer",
+ cell: ({ row }) => {
+ const isAssigned = row.original.reviewer !== "Assign reviewer"
+
+ if (isAssigned) {
+ return row.original.reviewer
+ }
+
+ return (
+ <>
+
+ Reviewer
+
+
+
+
+
+
+ Eddie Lake
+
+ Jamik Tashpulatov
+
+
+
+ >
+ )
+ },
+ },
+ {
+ id: "actions",
+ cell: () => (
+
+
+
+
+ Open menu
+
+
+
+ Edit
+ Make a copy
+ Favorite
+
+ Delete
+
+
+ ),
+ },
+]
+
+function DraggableRow({ row }: { row: Row> }) {
+ const { transform, transition, setNodeRef, isDragging } = useSortable({
+ id: row.original.id,
+ })
+
+ return (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ))}
+
+ )
+}
+
+export function DataTable({
+ data: initialData,
+}: {
+ data: z.infer[]
+}) {
+ const [data, setData] = React.useState(() => initialData)
+ const [rowSelection, setRowSelection] = React.useState({})
+ const [columnVisibility, setColumnVisibility] =
+ React.useState({})
+ const [columnFilters, setColumnFilters] = React.useState(
+ []
+ )
+ const [sorting, setSorting] = React.useState([])
+ const [pagination, setPagination] = React.useState({
+ pageIndex: 0,
+ pageSize: 10,
+ })
+ const sortableId = React.useId()
+ const sensors = useSensors(
+ useSensor(MouseSensor, {}),
+ useSensor(TouchSensor, {}),
+ useSensor(KeyboardSensor, {})
+ )
+
+ const dataIds = React.useMemo(
+ () => data?.map(({ id }) => id) || [],
+ [data]
+ )
+
+ const table = useReactTable({
+ data,
+ columns,
+ state: {
+ sorting,
+ columnVisibility,
+ rowSelection,
+ columnFilters,
+ pagination,
+ },
+ getRowId: (row) => row.id.toString(),
+ enableRowSelection: true,
+ onRowSelectionChange: setRowSelection,
+ onSortingChange: setSorting,
+ onColumnFiltersChange: setColumnFilters,
+ onColumnVisibilityChange: setColumnVisibility,
+ onPaginationChange: setPagination,
+ getCoreRowModel: getCoreRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFacetedRowModel: getFacetedRowModel(),
+ getFacetedUniqueValues: getFacetedUniqueValues(),
+ })
+
+ function handleDragEnd(event: DragEndEvent) {
+ const { active, over } = event
+ if (active && over && active.id !== over.id) {
+ setData((data) => {
+ const oldIndex = dataIds.indexOf(active.id)
+ const newIndex = dataIds.indexOf(over.id)
+ return arrayMove(data, oldIndex, newIndex)
+ })
+ }
+ }
+
+ return (
+
+
+
+ View
+
+
+
+
+
+
+ Outline
+ Past Performance
+ Key Personnel
+ Focus Documents
+
+
+
+ Outline
+
+ Past Performance 3
+
+
+ Key Personnel 2
+
+ Focus Documents
+
+
+
+
+
+
+ Customize Columns
+ Columns
+
+
+
+
+ {table
+ .getAllColumns()
+ .filter(
+ (column) =>
+ typeof column.accessorFn !== "undefined" &&
+ column.getCanHide()
+ )
+ .map((column) => {
+ return (
+
+ column.toggleVisibility(!!value)
+ }
+ >
+ {column.id}
+
+ )
+ })}
+
+
+
+
+ Add Section
+
+
+
+
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => {
+ return (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ )
+ })}
+
+ ))}
+
+
+ {table.getRowModel().rows?.length ? (
+
+ {table.getRowModel().rows.map((row) => (
+
+ ))}
+
+ ) : (
+
+
+ No results.
+
+
+ )}
+
+
+
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "}
+ {table.getFilteredRowModel().rows.length} row(s) selected.
+
+
+
+
+ Rows per page
+
+ {
+ table.setPageSize(Number(value))
+ }}
+ >
+
+
+
+
+ {[10, 20, 30, 40, 50].map((pageSize) => (
+
+ {pageSize}
+
+ ))}
+
+
+
+
+ Page {table.getState().pagination.pageIndex + 1} of{" "}
+ {table.getPageCount()}
+
+
+ table.setPageIndex(0)}
+ disabled={!table.getCanPreviousPage()}
+ >
+ Go to first page
+
+
+ table.previousPage()}
+ disabled={!table.getCanPreviousPage()}
+ >
+ Go to previous page
+
+
+ table.nextPage()}
+ disabled={!table.getCanNextPage()}
+ >
+ Go to next page
+
+
+ table.setPageIndex(table.getPageCount() - 1)}
+ disabled={!table.getCanNextPage()}
+ >
+ Go to last page
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--primary)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--primary)",
+ },
+} satisfies ChartConfig
+
+function TableCellViewer({ item }: { item: z.infer }) {
+ const isMobile = useIsMobile()
+
+ return (
+
+
+
+ {item.header}
+
+
+
+
+ {item.header}
+
+ Showing total visitors for the last 6 months
+
+
+
+ {!isMobile && (
+ <>
+
+
+
+ value.slice(0, 3)}
+ hide
+ />
+ }
+ />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month{" "}
+
+
+
+ Showing total visitors for the last 6 months. This is just
+ some random text to test the layout. It spans multiple lines
+ and should wrap around.
+
+
+
+ >
+ )}
+
+
+
+ Submit
+
+ Done
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-documents.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-documents.tsx
new file mode 100644
index 0000000..9b66672
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-documents.tsx
@@ -0,0 +1,91 @@
+
+import {
+ IconDots,
+ IconFolder,
+ IconShare3,
+ IconTrash,
+ type Icon,
+} from "@tabler/icons-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavDocuments({
+ items,
+}: {
+ items: {
+ name: string
+ url: string
+ icon: Icon
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+ Documents
+
+ {items.map((item) => (
+
+
+
+
+ {item.name}
+
+
+
+
+
+
+ More
+
+
+
+
+
+ Open
+
+
+
+ Share
+
+
+
+
+ Delete
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-main.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-main.tsx
new file mode 100644
index 0000000..7a586a0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-main.tsx
@@ -0,0 +1,57 @@
+
+import { IconCirclePlusFilled, IconMail, type Icon } from "@tabler/icons-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavMain({
+ items,
+}: {
+ items: {
+ title: string
+ url: string
+ icon?: Icon
+ }[]
+}) {
+ return (
+
+
+
+
+
+
+ Quick Create
+
+
+
+ Inbox
+
+
+
+
+ {items.map((item) => (
+
+
+ {item.icon && }
+ {item.title}
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-secondary.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-secondary.tsx
new file mode 100644
index 0000000..e18da8c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-secondary.tsx
@@ -0,0 +1,41 @@
+
+import * as React from "react"
+import { type Icon } from "@tabler/icons-react"
+
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavSecondary({
+ items,
+ ...props
+}: {
+ items: {
+ title: string
+ url: string
+ icon: Icon
+ }[]
+} & React.ComponentPropsWithoutRef) {
+ return (
+
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-user.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-user.tsx
new file mode 100644
index 0000000..ab31378
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/nav-user.tsx
@@ -0,0 +1,109 @@
+
+import {
+ IconCreditCard,
+ IconDotsVertical,
+ IconLogout,
+ IconNotification,
+ IconUserCircle,
+} from "@tabler/icons-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name: string
+ email: string
+ avatar: string
+ }
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+
+ {user.email}
+
+
+
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+
+ {user.email}
+
+
+
+
+
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Log out
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/section-cards.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/section-cards.tsx
new file mode 100644
index 0000000..8419b5e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/section-cards.tsx
@@ -0,0 +1,102 @@
+import { IconTrendingDown, IconTrendingUp } from "@tabler/icons-react"
+
+import { Badge } from "@/registry/new-york-v4/ui/badge"
+import {
+ Card,
+ CardAction,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+
+export function SectionCards() {
+ return (
+
+
+
+ Total Revenue
+
+ $1,250.00
+
+
+
+
+ +12.5%
+
+
+
+
+
+ Trending up this month
+
+
+ Visitors for the last 6 months
+
+
+
+
+
+ New Customers
+
+ 1,234
+
+
+
+
+ -20%
+
+
+
+
+
+ Down 20% this period
+
+
+ Acquisition needs attention
+
+
+
+
+
+ Active Accounts
+
+ 45,678
+
+
+
+
+ +12.5%
+
+
+
+
+
+ Strong user retention
+
+ Engagement exceed targets
+
+
+
+
+ Growth Rate
+
+ 4.5%
+
+
+
+
+ +4.5%
+
+
+
+
+
+ Steady performance increase
+
+ Meets growth projections
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/site-header.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/site-header.tsx
new file mode 100644
index 0000000..dd4210d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/components/site-header.tsx
@@ -0,0 +1,30 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import { SidebarTrigger } from "@/registry/new-york-v4/ui/sidebar"
+
+export function SiteHeader() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/data.json b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/data.json
new file mode 100644
index 0000000..ec08736
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/data.json
@@ -0,0 +1,614 @@
+[
+ {
+ "id": 1,
+ "header": "Cover page",
+ "type": "Cover page",
+ "status": "In Process",
+ "target": "18",
+ "limit": "5",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 2,
+ "header": "Table of contents",
+ "type": "Table of contents",
+ "status": "Done",
+ "target": "29",
+ "limit": "24",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 3,
+ "header": "Executive summary",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "10",
+ "limit": "13",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 4,
+ "header": "Technical approach",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "27",
+ "limit": "23",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 5,
+ "header": "Design",
+ "type": "Narrative",
+ "status": "In Process",
+ "target": "2",
+ "limit": "16",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 6,
+ "header": "Capabilities",
+ "type": "Narrative",
+ "status": "In Process",
+ "target": "20",
+ "limit": "8",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 7,
+ "header": "Integration with existing systems",
+ "type": "Narrative",
+ "status": "In Process",
+ "target": "19",
+ "limit": "21",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 8,
+ "header": "Innovation and Advantages",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "25",
+ "limit": "26",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 9,
+ "header": "Overview of EMR's Innovative Solutions",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "7",
+ "limit": "23",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 10,
+ "header": "Advanced Algorithms and Machine Learning",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "30",
+ "limit": "28",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 11,
+ "header": "Adaptive Communication Protocols",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "9",
+ "limit": "31",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 12,
+ "header": "Advantages Over Current Technologies",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "12",
+ "limit": "0",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 13,
+ "header": "Past Performance",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "22",
+ "limit": "33",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 14,
+ "header": "Customer Feedback and Satisfaction Levels",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "15",
+ "limit": "34",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 15,
+ "header": "Implementation Challenges and Solutions",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "3",
+ "limit": "35",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 16,
+ "header": "Security Measures and Data Protection Policies",
+ "type": "Narrative",
+ "status": "In Process",
+ "target": "6",
+ "limit": "36",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 17,
+ "header": "Scalability and Future Proofing",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "4",
+ "limit": "37",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 18,
+ "header": "Cost-Benefit Analysis",
+ "type": "Plain language",
+ "status": "Done",
+ "target": "14",
+ "limit": "38",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 19,
+ "header": "User Training and Onboarding Experience",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "17",
+ "limit": "39",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 20,
+ "header": "Future Development Roadmap",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "11",
+ "limit": "40",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 21,
+ "header": "System Architecture Overview",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "24",
+ "limit": "18",
+ "reviewer": "Maya Johnson"
+ },
+ {
+ "id": 22,
+ "header": "Risk Management Plan",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "15",
+ "limit": "22",
+ "reviewer": "Carlos Rodriguez"
+ },
+ {
+ "id": 23,
+ "header": "Compliance Documentation",
+ "type": "Legal",
+ "status": "In Process",
+ "target": "31",
+ "limit": "27",
+ "reviewer": "Sarah Chen"
+ },
+ {
+ "id": 24,
+ "header": "API Documentation",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "8",
+ "limit": "12",
+ "reviewer": "Raj Patel"
+ },
+ {
+ "id": 25,
+ "header": "User Interface Mockups",
+ "type": "Visual",
+ "status": "In Process",
+ "target": "19",
+ "limit": "25",
+ "reviewer": "Leila Ahmadi"
+ },
+ {
+ "id": 26,
+ "header": "Database Schema",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "22",
+ "limit": "20",
+ "reviewer": "Thomas Wilson"
+ },
+ {
+ "id": 27,
+ "header": "Testing Methodology",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "17",
+ "limit": "14",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 28,
+ "header": "Deployment Strategy",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "26",
+ "limit": "30",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 29,
+ "header": "Budget Breakdown",
+ "type": "Financial",
+ "status": "In Process",
+ "target": "13",
+ "limit": "16",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 30,
+ "header": "Market Analysis",
+ "type": "Research",
+ "status": "Done",
+ "target": "29",
+ "limit": "32",
+ "reviewer": "Sophia Martinez"
+ },
+ {
+ "id": 31,
+ "header": "Competitor Comparison",
+ "type": "Research",
+ "status": "In Process",
+ "target": "21",
+ "limit": "19",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 32,
+ "header": "Maintenance Plan",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "16",
+ "limit": "23",
+ "reviewer": "Alex Thompson"
+ },
+ {
+ "id": 33,
+ "header": "User Personas",
+ "type": "Research",
+ "status": "In Process",
+ "target": "27",
+ "limit": "24",
+ "reviewer": "Nina Patel"
+ },
+ {
+ "id": 34,
+ "header": "Accessibility Compliance",
+ "type": "Legal",
+ "status": "Done",
+ "target": "18",
+ "limit": "21",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 35,
+ "header": "Performance Metrics",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "23",
+ "limit": "26",
+ "reviewer": "David Kim"
+ },
+ {
+ "id": 36,
+ "header": "Disaster Recovery Plan",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "14",
+ "limit": "17",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 37,
+ "header": "Third-party Integrations",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "25",
+ "limit": "28",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 38,
+ "header": "User Feedback Summary",
+ "type": "Research",
+ "status": "Done",
+ "target": "20",
+ "limit": "15",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 39,
+ "header": "Localization Strategy",
+ "type": "Narrative",
+ "status": "In Process",
+ "target": "12",
+ "limit": "19",
+ "reviewer": "Maria Garcia"
+ },
+ {
+ "id": 40,
+ "header": "Mobile Compatibility",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "28",
+ "limit": "31",
+ "reviewer": "James Wilson"
+ },
+ {
+ "id": 41,
+ "header": "Data Migration Plan",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "19",
+ "limit": "22",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 42,
+ "header": "Quality Assurance Protocols",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "30",
+ "limit": "33",
+ "reviewer": "Priya Singh"
+ },
+ {
+ "id": 43,
+ "header": "Stakeholder Analysis",
+ "type": "Research",
+ "status": "In Process",
+ "target": "11",
+ "limit": "14",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 44,
+ "header": "Environmental Impact Assessment",
+ "type": "Research",
+ "status": "Done",
+ "target": "24",
+ "limit": "27",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 45,
+ "header": "Intellectual Property Rights",
+ "type": "Legal",
+ "status": "In Process",
+ "target": "17",
+ "limit": "20",
+ "reviewer": "Sarah Johnson"
+ },
+ {
+ "id": 46,
+ "header": "Customer Support Framework",
+ "type": "Narrative",
+ "status": "Done",
+ "target": "22",
+ "limit": "25",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 47,
+ "header": "Version Control Strategy",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "15",
+ "limit": "18",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 48,
+ "header": "Continuous Integration Pipeline",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "26",
+ "limit": "29",
+ "reviewer": "Michael Chen"
+ },
+ {
+ "id": 49,
+ "header": "Regulatory Compliance",
+ "type": "Legal",
+ "status": "In Process",
+ "target": "13",
+ "limit": "16",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 50,
+ "header": "User Authentication System",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "28",
+ "limit": "31",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 51,
+ "header": "Data Analytics Framework",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "21",
+ "limit": "24",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 52,
+ "header": "Cloud Infrastructure",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "16",
+ "limit": "19",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 53,
+ "header": "Network Security Measures",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "29",
+ "limit": "32",
+ "reviewer": "Lisa Wong"
+ },
+ {
+ "id": 54,
+ "header": "Project Timeline",
+ "type": "Planning",
+ "status": "Done",
+ "target": "14",
+ "limit": "17",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 55,
+ "header": "Resource Allocation",
+ "type": "Planning",
+ "status": "In Process",
+ "target": "27",
+ "limit": "30",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 56,
+ "header": "Team Structure and Roles",
+ "type": "Planning",
+ "status": "Done",
+ "target": "20",
+ "limit": "23",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 57,
+ "header": "Communication Protocols",
+ "type": "Planning",
+ "status": "In Process",
+ "target": "15",
+ "limit": "18",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 58,
+ "header": "Success Metrics",
+ "type": "Planning",
+ "status": "Done",
+ "target": "30",
+ "limit": "33",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 59,
+ "header": "Internationalization Support",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "23",
+ "limit": "26",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 60,
+ "header": "Backup and Recovery Procedures",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "18",
+ "limit": "21",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 61,
+ "header": "Monitoring and Alerting System",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "25",
+ "limit": "28",
+ "reviewer": "Daniel Park"
+ },
+ {
+ "id": 62,
+ "header": "Code Review Guidelines",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "12",
+ "limit": "15",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 63,
+ "header": "Documentation Standards",
+ "type": "Technical content",
+ "status": "In Process",
+ "target": "27",
+ "limit": "30",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 64,
+ "header": "Release Management Process",
+ "type": "Planning",
+ "status": "Done",
+ "target": "22",
+ "limit": "25",
+ "reviewer": "Assign reviewer"
+ },
+ {
+ "id": 65,
+ "header": "Feature Prioritization Matrix",
+ "type": "Planning",
+ "status": "In Process",
+ "target": "19",
+ "limit": "22",
+ "reviewer": "Emma Davis"
+ },
+ {
+ "id": 66,
+ "header": "Technical Debt Assessment",
+ "type": "Technical content",
+ "status": "Done",
+ "target": "24",
+ "limit": "27",
+ "reviewer": "Eddie Lake"
+ },
+ {
+ "id": 67,
+ "header": "Capacity Planning",
+ "type": "Planning",
+ "status": "In Process",
+ "target": "21",
+ "limit": "24",
+ "reviewer": "Jamik Tashpulatov"
+ },
+ {
+ "id": 68,
+ "header": "Service Level Agreements",
+ "type": "Legal",
+ "status": "Done",
+ "target": "26",
+ "limit": "29",
+ "reviewer": "Assign reviewer"
+ }
+]
diff --git a/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/page.tsx
new file mode 100644
index 0000000..c936e0c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/dashboard-01/page.tsx
@@ -0,0 +1,40 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/dashboard-01/components/app-sidebar"
+import { ChartAreaInteractive } from "@/registry/new-york-v4/blocks/dashboard-01/components/chart-area-interactive"
+import { DataTable } from "@/registry/new-york-v4/blocks/dashboard-01/components/data-table"
+import { SectionCards } from "@/registry/new-york-v4/blocks/dashboard-01/components/section-cards"
+import { SiteHeader } from "@/registry/new-york-v4/blocks/dashboard-01/components/site-header"
+import {
+ SidebarInset,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+import data from "./data.json"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-01/components/login-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-01/components/login-form.tsx
new file mode 100644
index 0000000..dcba293
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-01/components/login-form.tsx
@@ -0,0 +1,70 @@
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function LoginForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+
+
+ Login to your account
+
+ Enter your email below to login to your account
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-01/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-01/page.tsx
new file mode 100644
index 0000000..88eb8bd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-01/page.tsx
@@ -0,0 +1,11 @@
+import { LoginForm } from "@/registry/new-york-v4/blocks/login-01/components/login-form"
+
+export default function Page() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-02/components/login-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-02/components/login-form.tsx
new file mode 100644
index 0000000..e108bb8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-02/components/login-form.tsx
@@ -0,0 +1,65 @@
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function LoginForm({
+ className,
+ ...props
+}: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-02/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-02/page.tsx
new file mode 100644
index 0000000..89e92fb
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-02/page.tsx
@@ -0,0 +1,32 @@
+import { GalleryVerticalEnd } from "lucide-react"
+
+import { LoginForm } from "@/registry/new-york-v4/blocks/login-02/components/login-form"
+
+export default function LoginPage() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-03/components/login-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-03/components/login-form.tsx
new file mode 100644
index 0000000..22ee524
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-03/components/login-form.tsx
@@ -0,0 +1,95 @@
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function LoginForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+
+
+ Welcome back
+
+ Login with your Apple or Google account
+
+
+
+
+
+
+
+ By clicking continue, you agree to our Terms of Service {" "}
+ and Privacy Policy .
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-03/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-03/page.tsx
new file mode 100644
index 0000000..69f53e3
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-03/page.tsx
@@ -0,0 +1,19 @@
+import { GalleryVerticalEnd } from "lucide-react"
+
+import { LoginForm } from "@/registry/new-york-v4/blocks/login-03/components/login-form"
+
+export default function LoginPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-04/components/login-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-04/components/login-form.tsx
new file mode 100644
index 0000000..3091c52
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-04/components/login-form.tsx
@@ -0,0 +1,105 @@
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function LoginForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-04/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-04/page.tsx
new file mode 100644
index 0000000..725828f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-04/page.tsx
@@ -0,0 +1,11 @@
+import { LoginForm } from "@/registry/new-york-v4/blocks/login-04/components/login-form"
+
+export default function LoginPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-05/components/login-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-05/components/login-form.tsx
new file mode 100644
index 0000000..6606252
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-05/components/login-form.tsx
@@ -0,0 +1,78 @@
+import { GalleryVerticalEnd } from "lucide-react"
+
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function LoginForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/login-05/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/login-05/page.tsx
new file mode 100644
index 0000000..e40e062
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/login-05/page.tsx
@@ -0,0 +1,11 @@
+import { LoginForm } from "@/registry/new-york-v4/blocks/login-05/components/login-form"
+
+export default function LoginPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/app-sidebar.tsx
new file mode 100644
index 0000000..e3646a1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/app-sidebar.tsx
@@ -0,0 +1,182 @@
+import * as React from "react"
+
+import { SearchForm } from "@/registry/new-york-v4/blocks/sidebar-01/components/search-form"
+import { VersionSwitcher } from "@/registry/new-york-v4/blocks/sidebar-01/components/version-switcher"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ versions: ["1.0.1", "1.1.0-alpha", "2.0.0-beta1"],
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {/* We create a SidebarGroup for each parent. */}
+ {data.navMain.map((item) => (
+
+ {item.title}
+
+
+ {item.items.map((item) => (
+
+
+ {item.title}
+
+
+ ))}
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/search-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/search-form.tsx
new file mode 100644
index 0000000..ec4ac46
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/search-form.tsx
@@ -0,0 +1,28 @@
+import { Search } from "lucide-react"
+
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarInput,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function SearchForm({ ...props }: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/version-switcher.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/version-switcher.tsx
new file mode 100644
index 0000000..a89b737
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/components/version-switcher.tsx
@@ -0,0 +1,63 @@
+
+import * as React from "react"
+import { Check, ChevronsUpDown, GalleryVerticalEnd } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function VersionSwitcher({
+ versions,
+ defaultVersion,
+}: {
+ versions: string[]
+ defaultVersion: string
+}) {
+ const [selectedVersion, setSelectedVersion] = React.useState(defaultVersion)
+
+ return (
+
+
+
+
+
+
+
+
+
+ Documentation
+ v{selectedVersion}
+
+
+
+
+
+ {versions.map((version) => (
+ setSelectedVersion(version)}
+ >
+ v{version}{" "}
+ {version === selectedVersion && }
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/page.tsx
new file mode 100644
index 0000000..7ed7a32
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-01/page.tsx
@@ -0,0 +1,51 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-01/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ Build Your Application
+
+
+
+ Data Fetching
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/app-sidebar.tsx
new file mode 100644
index 0000000..650c7c9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/app-sidebar.tsx
@@ -0,0 +1,215 @@
+import * as React from "react"
+import { ChevronRight } from "lucide-react"
+
+import { SearchForm } from "@/registry/new-york-v4/blocks/sidebar-02/components/search-form"
+import { VersionSwitcher } from "@/registry/new-york-v4/blocks/sidebar-02/components/version-switcher"
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ versions: ["1.0.1", "1.1.0-alpha", "2.0.0-beta1"],
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Community",
+ url: "#",
+ items: [
+ {
+ title: "Contribution Guide",
+ url: "#",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {/* We create a collapsible SidebarGroup for each parent. */}
+ {data.navMain.map((item) => (
+
+
+
+
+ {item.title}{" "}
+
+
+
+
+
+
+ {item.items.map((item) => (
+
+
+ {item.title}
+
+
+ ))}
+
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/search-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/search-form.tsx
new file mode 100644
index 0000000..ec4ac46
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/search-form.tsx
@@ -0,0 +1,28 @@
+import { Search } from "lucide-react"
+
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarInput,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function SearchForm({ ...props }: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/version-switcher.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/version-switcher.tsx
new file mode 100644
index 0000000..a89b737
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/components/version-switcher.tsx
@@ -0,0 +1,63 @@
+
+import * as React from "react"
+import { Check, ChevronsUpDown, GalleryVerticalEnd } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function VersionSwitcher({
+ versions,
+ defaultVersion,
+}: {
+ versions: string[]
+ defaultVersion: string
+}) {
+ const [selectedVersion, setSelectedVersion] = React.useState(defaultVersion)
+
+ return (
+
+
+
+
+
+
+
+
+
+ Documentation
+ v{selectedVersion}
+
+
+
+
+
+ {versions.map((version) => (
+ setSelectedVersion(version)}
+ >
+ v{version}{" "}
+ {version === selectedVersion && }
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/page.tsx
new file mode 100644
index 0000000..ebd77c2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-02/page.tsx
@@ -0,0 +1,48 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-02/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ Build Your Application
+
+
+
+ Data Fetching
+
+
+
+
+
+ {Array.from({ length: 24 }).map((_, index) => (
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-03/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-03/components/app-sidebar.tsx
new file mode 100644
index 0000000..fe72986
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-03/components/app-sidebar.tsx
@@ -0,0 +1,208 @@
+import * as React from "react"
+import { GalleryVerticalEnd } from "lucide-react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Community",
+ url: "#",
+ items: [
+ {
+ title: "Contribution Guide",
+ url: "#",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Documentation
+ v1.0.0
+
+
+
+
+
+
+
+
+
+ {data.navMain.map((item) => (
+
+
+
+ {item.title}
+
+
+ {item.items?.length ? (
+
+ {item.items.map((item) => (
+
+
+ {item.title}
+
+
+ ))}
+
+ ) : null}
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-03/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-03/page.tsx
new file mode 100644
index 0000000..46c57c1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-03/page.tsx
@@ -0,0 +1,52 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-03/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-04/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-04/components/app-sidebar.tsx
new file mode 100644
index 0000000..10f7bf7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-04/components/app-sidebar.tsx
@@ -0,0 +1,206 @@
+import * as React from "react"
+import { GalleryVerticalEnd } from "lucide-react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Community",
+ url: "#",
+ items: [
+ {
+ title: "Contribution Guide",
+ url: "#",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Documentation
+ v1.0.0
+
+
+
+
+
+
+
+
+
+ {data.navMain.map((item) => (
+
+
+
+ {item.title}
+
+
+ {item.items?.length ? (
+
+ {item.items.map((item) => (
+
+
+ {item.title}
+
+
+ ))}
+
+ ) : null}
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-04/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-04/page.tsx
new file mode 100644
index 0000000..624f18e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-04/page.tsx
@@ -0,0 +1,57 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-04/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ Build Your Application
+
+
+
+ Data Fetching
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/components/app-sidebar.tsx
new file mode 100644
index 0000000..b4bda37
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/components/app-sidebar.tsx
@@ -0,0 +1,228 @@
+import * as React from "react"
+import { GalleryVerticalEnd, Minus, Plus } from "lucide-react"
+
+import { SearchForm } from "@/registry/new-york-v4/blocks/sidebar-05/components/search-form"
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Community",
+ url: "#",
+ items: [
+ {
+ title: "Contribution Guide",
+ url: "#",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Documentation
+ v1.0.0
+
+
+
+
+
+
+
+
+
+
+ {data.navMain.map((item, index) => (
+
+
+
+
+ {item.title}{" "}
+
+
+
+
+ {item.items?.length ? (
+
+
+ {item.items.map((item) => (
+
+
+ {item.title}
+
+
+ ))}
+
+
+ ) : null}
+
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/components/search-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/components/search-form.tsx
new file mode 100644
index 0000000..ec4ac46
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/components/search-form.tsx
@@ -0,0 +1,28 @@
+import { Search } from "lucide-react"
+
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarInput,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function SearchForm({ ...props }: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/page.tsx
new file mode 100644
index 0000000..a18168e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-05/page.tsx
@@ -0,0 +1,51 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-05/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ Build Your Application
+
+
+
+ Data Fetching
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/app-sidebar.tsx
new file mode 100644
index 0000000..78931f8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/app-sidebar.tsx
@@ -0,0 +1,179 @@
+import * as React from "react"
+import { GalleryVerticalEnd } from "lucide-react"
+
+import { NavMain } from "@/registry/new-york-v4/blocks/sidebar-06/components/nav-main"
+import { SidebarOptInForm } from "@/registry/new-york-v4/blocks/sidebar-06/components/sidebar-opt-in-form"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Documentation
+ v1.0.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/nav-main.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/nav-main.tsx
new file mode 100644
index 0000000..058f4c5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/nav-main.tsx
@@ -0,0 +1,64 @@
+
+import { MoreHorizontal, type LucideIcon } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarGroup,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavMain({
+ items,
+}: {
+ items: {
+ title: string
+ url: string
+ icon?: LucideIcon
+ isActive?: boolean
+ items?: {
+ title: string
+ url: string
+ }[]
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+ {item.items?.length ? (
+
+ {item.items.map((item) => (
+
+ {item.title}
+
+ ))}
+
+ ) : null}
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/sidebar-opt-in-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/sidebar-opt-in-form.tsx
new file mode 100644
index 0000000..ed567e1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/components/sidebar-opt-in-form.tsx
@@ -0,0 +1,35 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import { SidebarInput } from "@/registry/new-york-v4/ui/sidebar"
+
+export function SidebarOptInForm() {
+ return (
+
+
+ Subscribe to our newsletter
+
+ Opt-in to receive updates and news about the sidebar.
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/page.tsx
new file mode 100644
index 0000000..5b990ea
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-06/page.tsx
@@ -0,0 +1,51 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-06/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ Build Your Application
+
+
+
+ Data Fetching
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/app-sidebar.tsx
new file mode 100644
index 0000000..8e07836
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/app-sidebar.tsx
@@ -0,0 +1,174 @@
+
+import * as React from "react"
+import {
+ AudioWaveform,
+ BookOpen,
+ Bot,
+ Command,
+ Frame,
+ GalleryVerticalEnd,
+ Map,
+ PieChart,
+ Settings2,
+ SquareTerminal,
+} from "lucide-react"
+
+import { NavMain } from "@/registry/new-york-v4/blocks/sidebar-07/components/nav-main"
+import { NavProjects } from "@/registry/new-york-v4/blocks/sidebar-07/components/nav-projects"
+import { NavUser } from "@/registry/new-york-v4/blocks/sidebar-07/components/nav-user"
+import { TeamSwitcher } from "@/registry/new-york-v4/blocks/sidebar-07/components/team-switcher"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ teams: [
+ {
+ name: "Acme Inc",
+ logo: GalleryVerticalEnd,
+ plan: "Enterprise",
+ },
+ {
+ name: "Acme Corp.",
+ logo: AudioWaveform,
+ plan: "Startup",
+ },
+ {
+ name: "Evil Corp.",
+ logo: Command,
+ plan: "Free",
+ },
+ ],
+ navMain: [
+ {
+ title: "Playground",
+ url: "#",
+ icon: SquareTerminal,
+ isActive: true,
+ items: [
+ {
+ title: "History",
+ url: "#",
+ },
+ {
+ title: "Starred",
+ url: "#",
+ },
+ {
+ title: "Settings",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Models",
+ url: "#",
+ icon: Bot,
+ items: [
+ {
+ title: "Genesis",
+ url: "#",
+ },
+ {
+ title: "Explorer",
+ url: "#",
+ },
+ {
+ title: "Quantum",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Documentation",
+ url: "#",
+ icon: BookOpen,
+ items: [
+ {
+ title: "Introduction",
+ url: "#",
+ },
+ {
+ title: "Get Started",
+ url: "#",
+ },
+ {
+ title: "Tutorials",
+ url: "#",
+ },
+ {
+ title: "Changelog",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: Settings2,
+ items: [
+ {
+ title: "General",
+ url: "#",
+ },
+ {
+ title: "Team",
+ url: "#",
+ },
+ {
+ title: "Billing",
+ url: "#",
+ },
+ {
+ title: "Limits",
+ url: "#",
+ },
+ ],
+ },
+ ],
+ projects: [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: Frame,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChart,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: Map,
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-main.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-main.tsx
new file mode 100644
index 0000000..960518c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-main.tsx
@@ -0,0 +1,72 @@
+
+import { ChevronRight, type LucideIcon } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavMain({
+ items,
+}: {
+ items: {
+ title: string
+ url: string
+ icon?: LucideIcon
+ isActive?: boolean
+ items?: {
+ title: string
+ url: string
+ }[]
+ }[]
+}) {
+ return (
+
+ Platform
+
+ {items.map((item) => (
+
+
+
+
+ {item.icon && }
+ {item.title}
+
+
+
+
+
+ {item.items?.map((subItem) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-projects.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-projects.tsx
new file mode 100644
index 0000000..11a33b1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-projects.tsx
@@ -0,0 +1,88 @@
+
+import {
+ Folder,
+ Forward,
+ MoreHorizontal,
+ Trash2,
+ type LucideIcon,
+} from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavProjects({
+ projects,
+}: {
+ projects: {
+ name: string
+ url: string
+ icon: LucideIcon
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+ Projects
+
+ {projects.map((item) => (
+
+
+
+
+ {item.name}
+
+
+
+
+
+
+ More
+
+
+
+
+
+ View Project
+
+
+
+ Share Project
+
+
+
+
+ Delete Project
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-user.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-user.tsx
new file mode 100644
index 0000000..f57f90c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/nav-user.tsx
@@ -0,0 +1,113 @@
+
+import {
+ BadgeCheck,
+ Bell,
+ ChevronsUpDown,
+ CreditCard,
+ LogOut,
+ Sparkles,
+} from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name: string
+ email: string
+ avatar: string
+ }
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+ Upgrade to Pro
+
+
+
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Log out
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/team-switcher.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/team-switcher.tsx
new file mode 100644
index 0000000..711a082
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/components/team-switcher.tsx
@@ -0,0 +1,90 @@
+
+import * as React from "react"
+import { ChevronsUpDown, Plus } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function TeamSwitcher({
+ teams,
+}: {
+ teams: {
+ name: string
+ logo: React.ElementType
+ plan: string
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+ const [activeTeam, setActiveTeam] = React.useState(teams[0])
+
+ if (!activeTeam) {
+ return null
+ }
+
+ return (
+
+
+
+
+
+
+
+ {activeTeam.name}
+ {activeTeam.plan}
+
+
+
+
+
+
+ Teams
+
+ {teams.map((team, index) => (
+ setActiveTeam(team)}
+ className="gap-2 p-2"
+ >
+
+
+
+ {team.name}
+ ⌘{index + 1}
+
+ ))}
+
+
+
+ Add team
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/page.tsx
new file mode 100644
index 0000000..c388c63
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-07/page.tsx
@@ -0,0 +1,55 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-07/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/app-sidebar.tsx
new file mode 100644
index 0000000..f851127
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/app-sidebar.tsx
@@ -0,0 +1,184 @@
+
+import * as React from "react"
+import {
+ BookOpen,
+ Bot,
+ Command,
+ Frame,
+ LifeBuoy,
+ Map,
+ PieChart,
+ Send,
+ Settings2,
+ SquareTerminal,
+} from "lucide-react"
+
+import { NavMain } from "@/registry/new-york-v4/blocks/sidebar-08/components/nav-main"
+import { NavProjects } from "@/registry/new-york-v4/blocks/sidebar-08/components/nav-projects"
+import { NavSecondary } from "@/registry/new-york-v4/blocks/sidebar-08/components/nav-secondary"
+import { NavUser } from "@/registry/new-york-v4/blocks/sidebar-08/components/nav-user"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ navMain: [
+ {
+ title: "Playground",
+ url: "#",
+ icon: SquareTerminal,
+ isActive: true,
+ items: [
+ {
+ title: "History",
+ url: "#",
+ },
+ {
+ title: "Starred",
+ url: "#",
+ },
+ {
+ title: "Settings",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Models",
+ url: "#",
+ icon: Bot,
+ items: [
+ {
+ title: "Genesis",
+ url: "#",
+ },
+ {
+ title: "Explorer",
+ url: "#",
+ },
+ {
+ title: "Quantum",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Documentation",
+ url: "#",
+ icon: BookOpen,
+ items: [
+ {
+ title: "Introduction",
+ url: "#",
+ },
+ {
+ title: "Get Started",
+ url: "#",
+ },
+ {
+ title: "Tutorials",
+ url: "#",
+ },
+ {
+ title: "Changelog",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: Settings2,
+ items: [
+ {
+ title: "General",
+ url: "#",
+ },
+ {
+ title: "Team",
+ url: "#",
+ },
+ {
+ title: "Billing",
+ url: "#",
+ },
+ {
+ title: "Limits",
+ url: "#",
+ },
+ ],
+ },
+ ],
+ navSecondary: [
+ {
+ title: "Support",
+ url: "#",
+ icon: LifeBuoy,
+ },
+ {
+ title: "Feedback",
+ url: "#",
+ icon: Send,
+ },
+ ],
+ projects: [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: Frame,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChart,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: Map,
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Acme Inc
+ Enterprise
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-main.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-main.tsx
new file mode 100644
index 0000000..fc31849
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-main.tsx
@@ -0,0 +1,77 @@
+
+import { ChevronRight, type LucideIcon } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavMain({
+ items,
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ isActive?: boolean
+ items?: {
+ title: string
+ url: string
+ }[]
+ }[]
+}) {
+ return (
+
+ Platform
+
+ {items.map((item) => (
+
+
+
+
+
+ {item.title}
+
+
+ {item.items?.length ? (
+ <>
+
+
+
+ Toggle
+
+
+
+
+ {item.items?.map((subItem) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+ >
+ ) : null}
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-projects.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-projects.tsx
new file mode 100644
index 0000000..017a0f0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-projects.tsx
@@ -0,0 +1,88 @@
+
+import {
+ Folder,
+ MoreHorizontal,
+ Share,
+ Trash2,
+ type LucideIcon,
+} from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavProjects({
+ projects,
+}: {
+ projects: {
+ name: string
+ url: string
+ icon: LucideIcon
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+ Projects
+
+ {projects.map((item) => (
+
+
+
+
+ {item.name}
+
+
+
+
+
+
+ More
+
+
+
+
+
+ View Project
+
+
+
+ Share Project
+
+
+
+
+ Delete Project
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-secondary.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-secondary.tsx
new file mode 100644
index 0000000..66924a1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-secondary.tsx
@@ -0,0 +1,40 @@
+import * as React from "react"
+import { type LucideIcon } from "lucide-react"
+
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavSecondary({
+ items,
+ ...props
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ }[]
+} & React.ComponentPropsWithoutRef) {
+ return (
+
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-user.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-user.tsx
new file mode 100644
index 0000000..f57f90c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/components/nav-user.tsx
@@ -0,0 +1,113 @@
+
+import {
+ BadgeCheck,
+ Bell,
+ ChevronsUpDown,
+ CreditCard,
+ LogOut,
+ Sparkles,
+} from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name: string
+ email: string
+ avatar: string
+ }
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+ Upgrade to Pro
+
+
+
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Log out
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/page.tsx
new file mode 100644
index 0000000..d8ca40c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-08/page.tsx
@@ -0,0 +1,55 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-08/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/components/app-sidebar.tsx
new file mode 100644
index 0000000..add3bbd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/components/app-sidebar.tsx
@@ -0,0 +1,261 @@
+
+import * as React from "react"
+import { ArchiveX, Command, File, Inbox, Send, Trash2 } from "lucide-react"
+
+import { NavUser } from "@/registry/new-york-v4/blocks/sidebar-09/components/nav-user"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarHeader,
+ SidebarInput,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+import { Switch } from "@/registry/new-york-v4/ui/switch"
+
+// This is sample data
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ navMain: [
+ {
+ title: "Inbox",
+ url: "#",
+ icon: Inbox,
+ isActive: true,
+ },
+ {
+ title: "Drafts",
+ url: "#",
+ icon: File,
+ isActive: false,
+ },
+ {
+ title: "Sent",
+ url: "#",
+ icon: Send,
+ isActive: false,
+ },
+ {
+ title: "Junk",
+ url: "#",
+ icon: ArchiveX,
+ isActive: false,
+ },
+ {
+ title: "Trash",
+ url: "#",
+ icon: Trash2,
+ isActive: false,
+ },
+ ],
+ mails: [
+ {
+ name: "William Smith",
+ email: "williamsmith@example.com",
+ subject: "Meeting Tomorrow",
+ date: "09:34 AM",
+ teaser:
+ "Hi team, just a reminder about our meeting tomorrow at 10 AM.\nPlease come prepared with your project updates.",
+ },
+ {
+ name: "Alice Smith",
+ email: "alicesmith@example.com",
+ subject: "Re: Project Update",
+ date: "Yesterday",
+ teaser:
+ "Thanks for the update. The progress looks great so far.\nLet's schedule a call to discuss the next steps.",
+ },
+ {
+ name: "Bob Johnson",
+ email: "bobjohnson@example.com",
+ subject: "Weekend Plans",
+ date: "2 days ago",
+ teaser:
+ "Hey everyone! I'm thinking of organizing a team outing this weekend.\nWould you be interested in a hiking trip or a beach day?",
+ },
+ {
+ name: "Emily Davis",
+ email: "emilydavis@example.com",
+ subject: "Re: Question about Budget",
+ date: "2 days ago",
+ teaser:
+ "I've reviewed the budget numbers you sent over.\nCan we set up a quick call to discuss some potential adjustments?",
+ },
+ {
+ name: "Michael Wilson",
+ email: "michaelwilson@example.com",
+ subject: "Important Announcement",
+ date: "1 week ago",
+ teaser:
+ "Please join us for an all-hands meeting this Friday at 3 PM.\nWe have some exciting news to share about the company's future.",
+ },
+ {
+ name: "Sarah Brown",
+ email: "sarahbrown@example.com",
+ subject: "Re: Feedback on Proposal",
+ date: "1 week ago",
+ teaser:
+ "Thank you for sending over the proposal. I've reviewed it and have some thoughts.\nCould we schedule a meeting to discuss my feedback in detail?",
+ },
+ {
+ name: "David Lee",
+ email: "davidlee@example.com",
+ subject: "New Project Idea",
+ date: "1 week ago",
+ teaser:
+ "I've been brainstorming and came up with an interesting project concept.\nDo you have time this week to discuss its potential impact and feasibility?",
+ },
+ {
+ name: "Olivia Wilson",
+ email: "oliviawilson@example.com",
+ subject: "Vacation Plans",
+ date: "1 week ago",
+ teaser:
+ "Just a heads up that I'll be taking a two-week vacation next month.\nI'll make sure all my projects are up to date before I leave.",
+ },
+ {
+ name: "James Martin",
+ email: "jamesmartin@example.com",
+ subject: "Re: Conference Registration",
+ date: "1 week ago",
+ teaser:
+ "I've completed the registration for the upcoming tech conference.\nLet me know if you need any additional information from my end.",
+ },
+ {
+ name: "Sophia White",
+ email: "sophiawhite@example.com",
+ subject: "Team Dinner",
+ date: "1 week ago",
+ teaser:
+ "To celebrate our recent project success, I'd like to organize a team dinner.\nAre you available next Friday evening? Please let me know your preferences.",
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ // Note: I'm using state to show active item.
+ // IRL you should use the url/router.
+ const [activeItem, setActiveItem] = React.useState(data.navMain[0])
+ const [mails, setMails] = React.useState(data.mails)
+ const { setOpen } = useSidebar()
+
+ return (
+
+ {/* This is the first sidebar */}
+ {/* We disable collapsible and adjust width to icon. */}
+ {/* This will make the sidebar appear as icons. */}
+
+
+
+
+
+
+
+
+
+
+ Acme Inc
+ Enterprise
+
+
+
+
+
+
+
+
+
+
+ {data.navMain.map((item) => (
+
+ {
+ setActiveItem(item)
+ const mail = data.mails.sort(() => Math.random() - 0.5)
+ setMails(
+ mail.slice(
+ 0,
+ Math.max(5, Math.floor(Math.random() * 10) + 1)
+ )
+ )
+ setOpen(true)
+ }}
+ isActive={activeItem?.title === item.title}
+ className="px-2.5 md:px-2"
+ >
+
+ {item.title}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {/* This is the second sidebar */}
+ {/* We disable collapsible and let it fill remaining space */}
+
+
+
+
+ {activeItem?.title}
+
+
+ Unreads
+
+
+
+
+
+
+
+
+ {mails.map((mail) => (
+
+
+ {mail.name} {" "}
+ {mail.date}
+
+ {mail.subject}
+
+ {mail.teaser}
+
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/components/nav-user.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/components/nav-user.tsx
new file mode 100644
index 0000000..4e36fd8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/components/nav-user.tsx
@@ -0,0 +1,113 @@
+
+import {
+ BadgeCheck,
+ Bell,
+ ChevronsUpDown,
+ CreditCard,
+ LogOut,
+ Sparkles,
+} from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name: string
+ email: string
+ avatar: string
+ }
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+ Upgrade to Pro
+
+
+
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Log out
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/page.tsx
new file mode 100644
index 0000000..b44b4dd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-09/page.tsx
@@ -0,0 +1,57 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-09/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ All Inboxes
+
+
+
+ Inbox
+
+
+
+
+
+ {Array.from({ length: 24 }).map((_, index) => (
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/app-sidebar.tsx
new file mode 100644
index 0000000..9ff0709
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/app-sidebar.tsx
@@ -0,0 +1,275 @@
+
+import * as React from "react"
+import {
+ AudioWaveform,
+ Blocks,
+ Calendar,
+ Command,
+ Home,
+ Inbox,
+ MessageCircleQuestion,
+ Search,
+ Settings2,
+ Sparkles,
+ Trash2,
+} from "lucide-react"
+
+import { NavFavorites } from "@/registry/new-york-v4/blocks/sidebar-10/components/nav-favorites"
+import { NavMain } from "@/registry/new-york-v4/blocks/sidebar-10/components/nav-main"
+import { NavSecondary } from "@/registry/new-york-v4/blocks/sidebar-10/components/nav-secondary"
+import { NavWorkspaces } from "@/registry/new-york-v4/blocks/sidebar-10/components/nav-workspaces"
+import { TeamSwitcher } from "@/registry/new-york-v4/blocks/sidebar-10/components/team-switcher"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarHeader,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ teams: [
+ {
+ name: "Acme Inc",
+ logo: Command,
+ plan: "Enterprise",
+ },
+ {
+ name: "Acme Corp.",
+ logo: AudioWaveform,
+ plan: "Startup",
+ },
+ {
+ name: "Evil Corp.",
+ logo: Command,
+ plan: "Free",
+ },
+ ],
+ navMain: [
+ {
+ title: "Search",
+ url: "#",
+ icon: Search,
+ },
+ {
+ title: "Ask AI",
+ url: "#",
+ icon: Sparkles,
+ },
+ {
+ title: "Home",
+ url: "#",
+ icon: Home,
+ isActive: true,
+ },
+ {
+ title: "Inbox",
+ url: "#",
+ icon: Inbox,
+ badge: "10",
+ },
+ ],
+ navSecondary: [
+ {
+ title: "Calendar",
+ url: "#",
+ icon: Calendar,
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: Settings2,
+ },
+ {
+ title: "Templates",
+ url: "#",
+ icon: Blocks,
+ },
+ {
+ title: "Trash",
+ url: "#",
+ icon: Trash2,
+ },
+ {
+ title: "Help",
+ url: "#",
+ icon: MessageCircleQuestion,
+ },
+ ],
+ favorites: [
+ {
+ name: "Project Management & Task Tracking",
+ url: "#",
+ emoji: "📊",
+ },
+ {
+ name: "Family Recipe Collection & Meal Planning",
+ url: "#",
+ emoji: "🍳",
+ },
+ {
+ name: "Fitness Tracker & Workout Routines",
+ url: "#",
+ emoji: "💪",
+ },
+ {
+ name: "Book Notes & Reading List",
+ url: "#",
+ emoji: "📚",
+ },
+ {
+ name: "Sustainable Gardening Tips & Plant Care",
+ url: "#",
+ emoji: "🌱",
+ },
+ {
+ name: "Language Learning Progress & Resources",
+ url: "#",
+ emoji: "🗣️",
+ },
+ {
+ name: "Home Renovation Ideas & Budget Tracker",
+ url: "#",
+ emoji: "🏠",
+ },
+ {
+ name: "Personal Finance & Investment Portfolio",
+ url: "#",
+ emoji: "💰",
+ },
+ {
+ name: "Movie & TV Show Watchlist with Reviews",
+ url: "#",
+ emoji: "🎬",
+ },
+ {
+ name: "Daily Habit Tracker & Goal Setting",
+ url: "#",
+ emoji: "✅",
+ },
+ ],
+ workspaces: [
+ {
+ name: "Personal Life Management",
+ emoji: "🏠",
+ pages: [
+ {
+ name: "Daily Journal & Reflection",
+ url: "#",
+ emoji: "📔",
+ },
+ {
+ name: "Health & Wellness Tracker",
+ url: "#",
+ emoji: "🍏",
+ },
+ {
+ name: "Personal Growth & Learning Goals",
+ url: "#",
+ emoji: "🌟",
+ },
+ ],
+ },
+ {
+ name: "Professional Development",
+ emoji: "💼",
+ pages: [
+ {
+ name: "Career Objectives & Milestones",
+ url: "#",
+ emoji: "🎯",
+ },
+ {
+ name: "Skill Acquisition & Training Log",
+ url: "#",
+ emoji: "🧠",
+ },
+ {
+ name: "Networking Contacts & Events",
+ url: "#",
+ emoji: "🤝",
+ },
+ ],
+ },
+ {
+ name: "Creative Projects",
+ emoji: "🎨",
+ pages: [
+ {
+ name: "Writing Ideas & Story Outlines",
+ url: "#",
+ emoji: "✍️",
+ },
+ {
+ name: "Art & Design Portfolio",
+ url: "#",
+ emoji: "🖼️",
+ },
+ {
+ name: "Music Composition & Practice Log",
+ url: "#",
+ emoji: "🎵",
+ },
+ ],
+ },
+ {
+ name: "Home Management",
+ emoji: "🏡",
+ pages: [
+ {
+ name: "Household Budget & Expense Tracking",
+ url: "#",
+ emoji: "💰",
+ },
+ {
+ name: "Home Maintenance Schedule & Tasks",
+ url: "#",
+ emoji: "🔧",
+ },
+ {
+ name: "Family Calendar & Event Planning",
+ url: "#",
+ emoji: "📅",
+ },
+ ],
+ },
+ {
+ name: "Travel & Adventure",
+ emoji: "🧳",
+ pages: [
+ {
+ name: "Trip Planning & Itineraries",
+ url: "#",
+ emoji: "🗺️",
+ },
+ {
+ name: "Travel Bucket List & Inspiration",
+ url: "#",
+ emoji: "🌎",
+ },
+ {
+ name: "Travel Journal & Photo Gallery",
+ url: "#",
+ emoji: "📸",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-actions.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-actions.tsx
new file mode 100644
index 0000000..e113874
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-actions.tsx
@@ -0,0 +1,152 @@
+
+import * as React from "react"
+import {
+ ArrowDown,
+ ArrowUp,
+ Bell,
+ Copy,
+ CornerUpLeft,
+ CornerUpRight,
+ FileText,
+ GalleryVerticalEnd,
+ LineChart,
+ Link,
+ MoreHorizontal,
+ Settings2,
+ Star,
+ Trash,
+ Trash2,
+} from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const data = [
+ [
+ {
+ label: "Customize Page",
+ icon: Settings2,
+ },
+ {
+ label: "Turn into wiki",
+ icon: FileText,
+ },
+ ],
+ [
+ {
+ label: "Copy Link",
+ icon: Link,
+ },
+ {
+ label: "Duplicate",
+ icon: Copy,
+ },
+ {
+ label: "Move to",
+ icon: CornerUpRight,
+ },
+ {
+ label: "Move to Trash",
+ icon: Trash2,
+ },
+ ],
+ [
+ {
+ label: "Undo",
+ icon: CornerUpLeft,
+ },
+ {
+ label: "View analytics",
+ icon: LineChart,
+ },
+ {
+ label: "Version History",
+ icon: GalleryVerticalEnd,
+ },
+ {
+ label: "Show delete pages",
+ icon: Trash,
+ },
+ {
+ label: "Notifications",
+ icon: Bell,
+ },
+ ],
+ [
+ {
+ label: "Import",
+ icon: ArrowUp,
+ },
+ {
+ label: "Export",
+ icon: ArrowDown,
+ },
+ ],
+]
+
+export function NavActions() {
+ const [isOpen, setIsOpen] = React.useState(false)
+
+ React.useEffect(() => {
+ setIsOpen(true)
+ }, [])
+
+ return (
+
+
+ Edit Oct 08
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {data.map((group, index) => (
+
+
+
+ {group.map((item, index) => (
+
+
+ {item.label}
+
+
+ ))}
+
+
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-favorites.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-favorites.tsx
new file mode 100644
index 0000000..de877f1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-favorites.tsx
@@ -0,0 +1,93 @@
+
+import {
+ ArrowUpRight,
+ Link,
+ MoreHorizontal,
+ StarOff,
+ Trash2,
+} from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavFavorites({
+ favorites,
+}: {
+ favorites: {
+ name: string
+ url: string
+ emoji: string
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+ Favorites
+
+ {favorites.map((item) => (
+
+
+
+ {item.emoji}
+ {item.name}
+
+
+
+
+
+
+ More
+
+
+
+
+
+ Remove from Favorites
+
+
+
+
+ Copy Link
+
+
+
+ Open in New Tab
+
+
+
+
+ Delete
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-main.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-main.tsx
new file mode 100644
index 0000000..e8e9783
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-main.tsx
@@ -0,0 +1,34 @@
+
+import { type LucideIcon } from "lucide-react"
+
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavMain({
+ items,
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ isActive?: boolean
+ }[]
+}) {
+ return (
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-secondary.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-secondary.tsx
new file mode 100644
index 0000000..901c63e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-secondary.tsx
@@ -0,0 +1,43 @@
+import React from "react"
+import { type LucideIcon } from "lucide-react"
+
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuBadge,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavSecondary({
+ items,
+ ...props
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ badge?: React.ReactNode
+ }[]
+} & React.ComponentPropsWithoutRef) {
+ return (
+
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+ {item.badge && {item.badge} }
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-workspaces.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-workspaces.tsx
new file mode 100644
index 0000000..349b807
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/nav-workspaces.tsx
@@ -0,0 +1,85 @@
+import { ChevronRight, MoreHorizontal, Plus } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavWorkspaces({
+ workspaces,
+}: {
+ workspaces: {
+ name: string
+ emoji: React.ReactNode
+ pages: {
+ name: string
+ emoji: React.ReactNode
+ }[]
+ }[]
+}) {
+ return (
+
+ Workspaces
+
+
+ {workspaces.map((workspace) => (
+
+
+
+
+ {workspace.emoji}
+ {workspace.name}
+
+
+
+
+
+
+
+
+
+
+
+
+ {workspace.pages.map((page) => (
+
+
+
+ {page.emoji}
+ {page.name}
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/team-switcher.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/team-switcher.tsx
new file mode 100644
index 0000000..918bc71
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/components/team-switcher.tsx
@@ -0,0 +1,82 @@
+
+import * as React from "react"
+import { ChevronDown, Plus } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function TeamSwitcher({
+ teams,
+}: {
+ teams: {
+ name: string
+ logo: React.ElementType
+ plan: string
+ }[]
+}) {
+ const [activeTeam, setActiveTeam] = React.useState(teams[0])
+
+ if (!activeTeam) {
+ return null
+ }
+
+ return (
+
+
+
+
+
+
+ {activeTeam.name}
+
+
+
+
+
+ Teams
+
+ {teams.map((team, index) => (
+ setActiveTeam(team)}
+ className="gap-2 p-2"
+ >
+
+
+
+ {team.name}
+ ⌘{index + 1}
+
+ ))}
+
+
+
+ Add team
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/page.tsx
new file mode 100644
index 0000000..6eb9fb6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-10/page.tsx
@@ -0,0 +1,49 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-10/components/app-sidebar"
+import { NavActions } from "@/registry/new-york-v4/blocks/sidebar-10/components/nav-actions"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbList,
+ BreadcrumbPage,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-11/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-11/components/app-sidebar.tsx
new file mode 100644
index 0000000..fd8a59e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-11/components/app-sidebar.tsx
@@ -0,0 +1,143 @@
+import * as React from "react"
+import { ChevronRight, File, Folder } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuBadge,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ changes: [
+ {
+ file: "README.md",
+ state: "M",
+ },
+ {
+ file: "api/hello/route.ts",
+ state: "U",
+ },
+ {
+ file: "app/layout.tsx",
+ state: "M",
+ },
+ ],
+ tree: [
+ [
+ "app",
+ [
+ "api",
+ ["hello", ["route.ts"]],
+ "page.tsx",
+ "layout.tsx",
+ ["blog", ["page.tsx"]],
+ ],
+ ],
+ [
+ "components",
+ ["ui", "button.tsx", "card.tsx"],
+ "header.tsx",
+ "footer.tsx",
+ ],
+ ["lib", ["util.ts"]],
+ ["public", "favicon.ico", "vercel.svg"],
+ ".eslintrc.json",
+ ".gitignore",
+ "next.config.js",
+ "tailwind.config.js",
+ "package.json",
+ "README.md",
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+ Changes
+
+
+ {data.changes.map((item, index) => (
+
+
+
+ {item.file}
+
+ {item.state}
+
+ ))}
+
+
+
+
+ Files
+
+
+ {data.tree.map((item, index) => (
+
+ ))}
+
+
+
+
+
+
+ )
+}
+
+type TreeItem = string | TreeItem[]
+
+function Tree({ item }: { item: TreeItem }) {
+ const [name, ...items] = Array.isArray(item) ? item : [item]
+
+ if (!items.length) {
+ return (
+
+
+ {name}
+
+ )
+ }
+
+ return (
+
+
+
+
+
+
+ {name}
+
+
+
+
+ {items.map((subItem, index) => (
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-11/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-11/page.tsx
new file mode 100644
index 0000000..17d0894
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-11/page.tsx
@@ -0,0 +1,55 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-11/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ components
+
+
+
+ ui
+
+
+
+ button.tsx
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/app-sidebar.tsx
new file mode 100644
index 0000000..74f7a9b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/app-sidebar.tsx
@@ -0,0 +1,66 @@
+import * as React from "react"
+import { Plus } from "lucide-react"
+
+import { Calendars } from "@/registry/new-york-v4/blocks/sidebar-12/components/calendars"
+import { DatePicker } from "@/registry/new-york-v4/blocks/sidebar-12/components/date-picker"
+import { NavUser } from "@/registry/new-york-v4/blocks/sidebar-12/components/nav-user"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarRail,
+ SidebarSeparator,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ calendars: [
+ {
+ name: "My Calendars",
+ items: ["Personal", "Work", "Family"],
+ },
+ {
+ name: "Favorites",
+ items: ["Holidays", "Birthdays"],
+ },
+ {
+ name: "Other",
+ items: ["Travel", "Reminders", "Deadlines"],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ New Calendar
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/calendars.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/calendars.tsx
new file mode 100644
index 0000000..403df05
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/calendars.tsx
@@ -0,0 +1,71 @@
+import * as React from "react"
+import { Check, ChevronRight } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarSeparator,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function Calendars({
+ calendars,
+}: {
+ calendars: {
+ name: string
+ items: string[]
+ }[]
+}) {
+ return (
+ <>
+ {calendars.map((calendar, index) => (
+
+
+
+
+
+ {calendar.name}{" "}
+
+
+
+
+
+
+ {calendar.items.map((item, index) => (
+
+
+
+
+
+ {item}
+
+
+ ))}
+
+
+
+
+
+
+
+ ))}
+ >
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/date-picker.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/date-picker.tsx
new file mode 100644
index 0000000..d67e9cc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/date-picker.tsx
@@ -0,0 +1,15 @@
+import { Calendar } from "@/registry/new-york-v4/ui/calendar"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function DatePicker() {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/nav-user.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/nav-user.tsx
new file mode 100644
index 0000000..5f760f5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/components/nav-user.tsx
@@ -0,0 +1,113 @@
+
+import {
+ BadgeCheck,
+ Bell,
+ ChevronsUpDown,
+ CreditCard,
+ LogOut,
+ Sparkles,
+} from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name: string
+ email: string
+ avatar: string
+ }
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+ Upgrade to Pro
+
+
+
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Log out
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/page.tsx
new file mode 100644
index 0000000..7f1fbca
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-12/page.tsx
@@ -0,0 +1,44 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-12/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbList,
+ BreadcrumbPage,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+
+ October 2024
+
+
+
+
+
+
+ {Array.from({ length: 20 }).map((_, i) => (
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-13/components/settings-dialog.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-13/components/settings-dialog.tsx
new file mode 100644
index 0000000..6879240
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-13/components/settings-dialog.tsx
@@ -0,0 +1,128 @@
+
+import * as React from "react"
+import {
+ Bell,
+ Check,
+ Globe,
+ Home,
+ Keyboard,
+ Link,
+ Lock,
+ Menu,
+ MessageCircle,
+ Paintbrush,
+ Settings,
+ Video,
+} from "lucide-react"
+
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/new-york-v4/ui/dialog"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const data = {
+ nav: [
+ { name: "Notifications", icon: Bell },
+ { name: "Navigation", icon: Menu },
+ { name: "Home", icon: Home },
+ { name: "Appearance", icon: Paintbrush },
+ { name: "Messages & media", icon: MessageCircle },
+ { name: "Language & region", icon: Globe },
+ { name: "Accessibility", icon: Keyboard },
+ { name: "Mark as read", icon: Check },
+ { name: "Audio & video", icon: Video },
+ { name: "Connected accounts", icon: Link },
+ { name: "Privacy & visibility", icon: Lock },
+ { name: "Advanced", icon: Settings },
+ ],
+}
+
+export function SettingsDialog() {
+ const [open, setOpen] = React.useState(true)
+
+ return (
+
+
+ Open Dialog
+
+
+ Settings
+
+ Customize your settings here.
+
+
+
+
+
+
+
+ {data.nav.map((item) => (
+
+
+
+
+ {item.name}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+ {Array.from({ length: 10 }).map((_, i) => (
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-13/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-13/page.tsx
new file mode 100644
index 0000000..fc2bd2d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-13/page.tsx
@@ -0,0 +1,9 @@
+import { SettingsDialog } from "@/registry/new-york-v4/blocks/sidebar-13/components/settings-dialog"
+
+export default function Page() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-14/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-14/components/app-sidebar.tsx
new file mode 100644
index 0000000..ede14d9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-14/components/app-sidebar.tsx
@@ -0,0 +1,197 @@
+import * as React from "react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ navMain: [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Community",
+ url: "#",
+ items: [
+ {
+ title: "Contribution Guide",
+ url: "#",
+ },
+ ],
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+ Table of Contents
+
+
+ {data.navMain.map((item) => (
+
+
+
+ {item.title}
+
+
+ {item.items?.length ? (
+
+ {item.items.map((item) => (
+
+
+ {item.title}
+
+
+ ))}
+
+ ) : null}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-14/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-14/page.tsx
new file mode 100644
index 0000000..2b66a32
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-14/page.tsx
@@ -0,0 +1,46 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-14/components/app-sidebar"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+ Build Your Application
+
+
+
+ Data Fetching
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/calendars.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/calendars.tsx
new file mode 100644
index 0000000..cf20870
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/calendars.tsx
@@ -0,0 +1,71 @@
+import * as React from "react"
+import { Check, ChevronRight } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarSeparator,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function Calendars({
+ calendars,
+}: {
+ calendars: {
+ name: string
+ items: string[]
+ }[]
+}) {
+ return (
+ <>
+ {calendars.map((calendar, index) => (
+
+
+
+
+
+ {calendar.name}{" "}
+
+
+
+
+
+
+ {calendar.items.map((item, index) => (
+
+
+
+
+
+ {item}
+
+
+ ))}
+
+
+
+
+
+
+
+ ))}
+ >
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/date-picker.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/date-picker.tsx
new file mode 100644
index 0000000..d67e9cc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/date-picker.tsx
@@ -0,0 +1,15 @@
+import { Calendar } from "@/registry/new-york-v4/ui/calendar"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function DatePicker() {
+ return (
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-favorites.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-favorites.tsx
new file mode 100644
index 0000000..de877f1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-favorites.tsx
@@ -0,0 +1,93 @@
+
+import {
+ ArrowUpRight,
+ Link,
+ MoreHorizontal,
+ StarOff,
+ Trash2,
+} from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavFavorites({
+ favorites,
+}: {
+ favorites: {
+ name: string
+ url: string
+ emoji: string
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+ Favorites
+
+ {favorites.map((item) => (
+
+
+
+ {item.emoji}
+ {item.name}
+
+
+
+
+
+
+ More
+
+
+
+
+
+ Remove from Favorites
+
+
+
+
+ Copy Link
+
+
+
+ Open in New Tab
+
+
+
+
+ Delete
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-main.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-main.tsx
new file mode 100644
index 0000000..e8e9783
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-main.tsx
@@ -0,0 +1,34 @@
+
+import { type LucideIcon } from "lucide-react"
+
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavMain({
+ items,
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ isActive?: boolean
+ }[]
+}) {
+ return (
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-secondary.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-secondary.tsx
new file mode 100644
index 0000000..901c63e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-secondary.tsx
@@ -0,0 +1,43 @@
+import React from "react"
+import { type LucideIcon } from "lucide-react"
+
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuBadge,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavSecondary({
+ items,
+ ...props
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ badge?: React.ReactNode
+ }[]
+} & React.ComponentPropsWithoutRef) {
+ return (
+
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+ {item.badge && {item.badge} }
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-user.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-user.tsx
new file mode 100644
index 0000000..5f760f5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-user.tsx
@@ -0,0 +1,113 @@
+
+import {
+ BadgeCheck,
+ Bell,
+ ChevronsUpDown,
+ CreditCard,
+ LogOut,
+ Sparkles,
+} from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name: string
+ email: string
+ avatar: string
+ }
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+ Upgrade to Pro
+
+
+
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Log out
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-workspaces.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-workspaces.tsx
new file mode 100644
index 0000000..349b807
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/nav-workspaces.tsx
@@ -0,0 +1,85 @@
+import { ChevronRight, MoreHorizontal, Plus } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavWorkspaces({
+ workspaces,
+}: {
+ workspaces: {
+ name: string
+ emoji: React.ReactNode
+ pages: {
+ name: string
+ emoji: React.ReactNode
+ }[]
+ }[]
+}) {
+ return (
+
+ Workspaces
+
+
+ {workspaces.map((workspace) => (
+
+
+
+
+ {workspace.emoji}
+ {workspace.name}
+
+
+
+
+
+
+
+
+
+
+
+
+ {workspace.pages.map((page) => (
+
+
+
+ {page.emoji}
+ {page.name}
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/sidebar-left.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/sidebar-left.tsx
new file mode 100644
index 0000000..7f20472
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/sidebar-left.tsx
@@ -0,0 +1,277 @@
+
+import * as React from "react"
+import {
+ AudioWaveform,
+ Blocks,
+ Calendar,
+ Command,
+ Home,
+ Inbox,
+ MessageCircleQuestion,
+ Search,
+ Settings2,
+ Sparkles,
+ Trash2,
+} from "lucide-react"
+
+import { NavFavorites } from "@/registry/new-york-v4/blocks/sidebar-15/components/nav-favorites"
+import { NavMain } from "@/registry/new-york-v4/blocks/sidebar-15/components/nav-main"
+import { NavSecondary } from "@/registry/new-york-v4/blocks/sidebar-15/components/nav-secondary"
+import { NavWorkspaces } from "@/registry/new-york-v4/blocks/sidebar-15/components/nav-workspaces"
+import { TeamSwitcher } from "@/registry/new-york-v4/blocks/sidebar-15/components/team-switcher"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarHeader,
+ SidebarRail,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ teams: [
+ {
+ name: "Acme Inc",
+ logo: Command,
+ plan: "Enterprise",
+ },
+ {
+ name: "Acme Corp.",
+ logo: AudioWaveform,
+ plan: "Startup",
+ },
+ {
+ name: "Evil Corp.",
+ logo: Command,
+ plan: "Free",
+ },
+ ],
+ navMain: [
+ {
+ title: "Search",
+ url: "#",
+ icon: Search,
+ },
+ {
+ title: "Ask AI",
+ url: "#",
+ icon: Sparkles,
+ },
+ {
+ title: "Home",
+ url: "#",
+ icon: Home,
+ isActive: true,
+ },
+ {
+ title: "Inbox",
+ url: "#",
+ icon: Inbox,
+ badge: "10",
+ },
+ ],
+ navSecondary: [
+ {
+ title: "Calendar",
+ url: "#",
+ icon: Calendar,
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: Settings2,
+ },
+ {
+ title: "Templates",
+ url: "#",
+ icon: Blocks,
+ },
+ {
+ title: "Trash",
+ url: "#",
+ icon: Trash2,
+ },
+ {
+ title: "Help",
+ url: "#",
+ icon: MessageCircleQuestion,
+ },
+ ],
+ favorites: [
+ {
+ name: "Project Management & Task Tracking",
+ url: "#",
+ emoji: "📊",
+ },
+ {
+ name: "Family Recipe Collection & Meal Planning",
+ url: "#",
+ emoji: "🍳",
+ },
+ {
+ name: "Fitness Tracker & Workout Routines",
+ url: "#",
+ emoji: "💪",
+ },
+ {
+ name: "Book Notes & Reading List",
+ url: "#",
+ emoji: "📚",
+ },
+ {
+ name: "Sustainable Gardening Tips & Plant Care",
+ url: "#",
+ emoji: "🌱",
+ },
+ {
+ name: "Language Learning Progress & Resources",
+ url: "#",
+ emoji: "🗣️",
+ },
+ {
+ name: "Home Renovation Ideas & Budget Tracker",
+ url: "#",
+ emoji: "🏠",
+ },
+ {
+ name: "Personal Finance & Investment Portfolio",
+ url: "#",
+ emoji: "💰",
+ },
+ {
+ name: "Movie & TV Show Watchlist with Reviews",
+ url: "#",
+ emoji: "🎬",
+ },
+ {
+ name: "Daily Habit Tracker & Goal Setting",
+ url: "#",
+ emoji: "✅",
+ },
+ ],
+ workspaces: [
+ {
+ name: "Personal Life Management",
+ emoji: "🏠",
+ pages: [
+ {
+ name: "Daily Journal & Reflection",
+ url: "#",
+ emoji: "📔",
+ },
+ {
+ name: "Health & Wellness Tracker",
+ url: "#",
+ emoji: "🍏",
+ },
+ {
+ name: "Personal Growth & Learning Goals",
+ url: "#",
+ emoji: "🌟",
+ },
+ ],
+ },
+ {
+ name: "Professional Development",
+ emoji: "💼",
+ pages: [
+ {
+ name: "Career Objectives & Milestones",
+ url: "#",
+ emoji: "🎯",
+ },
+ {
+ name: "Skill Acquisition & Training Log",
+ url: "#",
+ emoji: "🧠",
+ },
+ {
+ name: "Networking Contacts & Events",
+ url: "#",
+ emoji: "🤝",
+ },
+ ],
+ },
+ {
+ name: "Creative Projects",
+ emoji: "🎨",
+ pages: [
+ {
+ name: "Writing Ideas & Story Outlines",
+ url: "#",
+ emoji: "✍️",
+ },
+ {
+ name: "Art & Design Portfolio",
+ url: "#",
+ emoji: "🖼️",
+ },
+ {
+ name: "Music Composition & Practice Log",
+ url: "#",
+ emoji: "🎵",
+ },
+ ],
+ },
+ {
+ name: "Home Management",
+ emoji: "🏡",
+ pages: [
+ {
+ name: "Household Budget & Expense Tracking",
+ url: "#",
+ emoji: "💰",
+ },
+ {
+ name: "Home Maintenance Schedule & Tasks",
+ url: "#",
+ emoji: "🔧",
+ },
+ {
+ name: "Family Calendar & Event Planning",
+ url: "#",
+ emoji: "📅",
+ },
+ ],
+ },
+ {
+ name: "Travel & Adventure",
+ emoji: "🧳",
+ pages: [
+ {
+ name: "Trip Planning & Itineraries",
+ url: "#",
+ emoji: "🗺️",
+ },
+ {
+ name: "Travel Bucket List & Inspiration",
+ url: "#",
+ emoji: "🌎",
+ },
+ {
+ name: "Travel Journal & Photo Gallery",
+ url: "#",
+ emoji: "📸",
+ },
+ ],
+ },
+ ],
+}
+
+export function SidebarLeft({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/sidebar-right.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/sidebar-right.tsx
new file mode 100644
index 0000000..70d2089
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/sidebar-right.tsx
@@ -0,0 +1,71 @@
+import * as React from "react"
+import { Plus } from "lucide-react"
+
+import { Calendars } from "@/registry/new-york-v4/blocks/sidebar-15/components/calendars"
+import { DatePicker } from "@/registry/new-york-v4/blocks/sidebar-15/components/date-picker"
+import { NavUser } from "@/registry/new-york-v4/blocks/sidebar-15/components/nav-user"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarRail,
+ SidebarSeparator,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// This is sample data.
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ calendars: [
+ {
+ name: "My Calendars",
+ items: ["Personal", "Work", "Family"],
+ },
+ {
+ name: "Favorites",
+ items: ["Holidays", "Birthdays"],
+ },
+ {
+ name: "Other",
+ items: ["Travel", "Reminders", "Deadlines"],
+ },
+ ],
+}
+
+export function SidebarRight({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ New Calendar
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/team-switcher.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/team-switcher.tsx
new file mode 100644
index 0000000..918bc71
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/components/team-switcher.tsx
@@ -0,0 +1,82 @@
+
+import * as React from "react"
+import { ChevronDown, Plus } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function TeamSwitcher({
+ teams,
+}: {
+ teams: {
+ name: string
+ logo: React.ElementType
+ plan: string
+ }[]
+}) {
+ const [activeTeam, setActiveTeam] = React.useState(teams[0])
+
+ if (!activeTeam) {
+ return null
+ }
+
+ return (
+
+
+
+
+
+
+ {activeTeam.name}
+
+
+
+
+
+ Teams
+
+ {teams.map((team, index) => (
+ setActiveTeam(team)}
+ className="gap-2 p-2"
+ >
+
+
+
+ {team.name}
+ ⌘{index + 1}
+
+ ))}
+
+
+
+ Add team
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/page.tsx
new file mode 100644
index 0000000..ad164cc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-15/page.tsx
@@ -0,0 +1,47 @@
+import { SidebarLeft } from "@/registry/new-york-v4/blocks/sidebar-15/components/sidebar-left"
+import { SidebarRight } from "@/registry/new-york-v4/blocks/sidebar-15/components/sidebar-right"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbList,
+ BreadcrumbPage,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ SidebarInset,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function Page() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/app-sidebar.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/app-sidebar.tsx
new file mode 100644
index 0000000..e1a2343
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/app-sidebar.tsx
@@ -0,0 +1,187 @@
+
+import * as React from "react"
+import {
+ BookOpen,
+ Bot,
+ Command,
+ Frame,
+ LifeBuoy,
+ Map,
+ PieChart,
+ Send,
+ Settings2,
+ SquareTerminal,
+} from "lucide-react"
+
+import { NavMain } from "@/registry/new-york-v4/blocks/sidebar-16/components/nav-main"
+import { NavProjects } from "@/registry/new-york-v4/blocks/sidebar-16/components/nav-projects"
+import { NavSecondary } from "@/registry/new-york-v4/blocks/sidebar-16/components/nav-secondary"
+import { NavUser } from "@/registry/new-york-v4/blocks/sidebar-16/components/nav-user"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const data = {
+ user: {
+ name: "shadcn",
+ email: "m@example.com",
+ avatar: "/avatars/shadcn.jpg",
+ },
+ navMain: [
+ {
+ title: "Playground",
+ url: "#",
+ icon: SquareTerminal,
+ isActive: true,
+ items: [
+ {
+ title: "History",
+ url: "#",
+ },
+ {
+ title: "Starred",
+ url: "#",
+ },
+ {
+ title: "Settings",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Models",
+ url: "#",
+ icon: Bot,
+ items: [
+ {
+ title: "Genesis",
+ url: "#",
+ },
+ {
+ title: "Explorer",
+ url: "#",
+ },
+ {
+ title: "Quantum",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Documentation",
+ url: "#",
+ icon: BookOpen,
+ items: [
+ {
+ title: "Introduction",
+ url: "#",
+ },
+ {
+ title: "Get Started",
+ url: "#",
+ },
+ {
+ title: "Tutorials",
+ url: "#",
+ },
+ {
+ title: "Changelog",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: Settings2,
+ items: [
+ {
+ title: "General",
+ url: "#",
+ },
+ {
+ title: "Team",
+ url: "#",
+ },
+ {
+ title: "Billing",
+ url: "#",
+ },
+ {
+ title: "Limits",
+ url: "#",
+ },
+ ],
+ },
+ ],
+ navSecondary: [
+ {
+ title: "Support",
+ url: "#",
+ icon: LifeBuoy,
+ },
+ {
+ title: "Feedback",
+ url: "#",
+ icon: Send,
+ },
+ ],
+ projects: [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: Frame,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChart,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: Map,
+ },
+ ],
+}
+
+export function AppSidebar({ ...props }: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Acme Inc
+ Enterprise
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-main.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-main.tsx
new file mode 100644
index 0000000..fc31849
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-main.tsx
@@ -0,0 +1,77 @@
+
+import { ChevronRight, type LucideIcon } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavMain({
+ items,
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ isActive?: boolean
+ items?: {
+ title: string
+ url: string
+ }[]
+ }[]
+}) {
+ return (
+
+ Platform
+
+ {items.map((item) => (
+
+
+
+
+
+ {item.title}
+
+
+ {item.items?.length ? (
+ <>
+
+
+
+ Toggle
+
+
+
+
+ {item.items?.map((subItem) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+ >
+ ) : null}
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-projects.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-projects.tsx
new file mode 100644
index 0000000..017a0f0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-projects.tsx
@@ -0,0 +1,88 @@
+
+import {
+ Folder,
+ MoreHorizontal,
+ Share,
+ Trash2,
+ type LucideIcon,
+} from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarGroup,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavProjects({
+ projects,
+}: {
+ projects: {
+ name: string
+ url: string
+ icon: LucideIcon
+ }[]
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+ Projects
+
+ {projects.map((item) => (
+
+
+
+
+ {item.name}
+
+
+
+
+
+
+ More
+
+
+
+
+
+ View Project
+
+
+
+ Share Project
+
+
+
+
+ Delete Project
+
+
+
+
+ ))}
+
+
+
+ More
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-secondary.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-secondary.tsx
new file mode 100644
index 0000000..66924a1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-secondary.tsx
@@ -0,0 +1,40 @@
+import * as React from "react"
+import { type LucideIcon } from "lucide-react"
+
+import {
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavSecondary({
+ items,
+ ...props
+}: {
+ items: {
+ title: string
+ url: string
+ icon: LucideIcon
+ }[]
+} & React.ComponentPropsWithoutRef) {
+ return (
+
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-user.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-user.tsx
new file mode 100644
index 0000000..f57f90c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/nav-user.tsx
@@ -0,0 +1,113 @@
+
+import {
+ BadgeCheck,
+ Bell,
+ ChevronsUpDown,
+ CreditCard,
+ LogOut,
+ Sparkles,
+} from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ useSidebar,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export function NavUser({
+ user,
+}: {
+ user: {
+ name: string
+ email: string
+ avatar: string
+ }
+}) {
+ const { isMobile } = useSidebar()
+
+ return (
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+
+
+ CN
+
+
+ {user.name}
+ {user.email}
+
+
+
+
+
+
+
+ Upgrade to Pro
+
+
+
+
+
+
+ Account
+
+
+
+ Billing
+
+
+
+ Notifications
+
+
+
+
+
+ Log out
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/search-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/search-form.tsx
new file mode 100644
index 0000000..0491880
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/search-form.tsx
@@ -0,0 +1,22 @@
+import { Search } from "lucide-react"
+
+import { Label } from "@/registry/new-york-v4/ui/label"
+import { SidebarInput } from "@/registry/new-york-v4/ui/sidebar"
+
+export function SearchForm({ ...props }: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/site-header.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/site-header.tsx
new file mode 100644
index 0000000..f371899
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/components/site-header.tsx
@@ -0,0 +1,47 @@
+
+import { SidebarIcon } from "lucide-react"
+
+import { SearchForm } from "@/registry/new-york-v4/blocks/sidebar-16/components/search-form"
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import { useSidebar } from "@/registry/new-york-v4/ui/sidebar"
+
+export function SiteHeader() {
+ const { toggleSidebar } = useSidebar()
+
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/page.tsx
new file mode 100644
index 0000000..7cc2ac2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/sidebar-16/page.tsx
@@ -0,0 +1,33 @@
+import { AppSidebar } from "@/registry/new-york-v4/blocks/sidebar-16/components/app-sidebar"
+import { SiteHeader } from "@/registry/new-york-v4/blocks/sidebar-16/components/site-header"
+import {
+ SidebarInset,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export const iframeHeight = "800px"
+
+export const description = "A sidebar with a header and a search form."
+
+export default function Page() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-01/components/signup-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-01/components/signup-form.tsx
new file mode 100644
index 0000000..f90dea5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-01/components/signup-form.tsx
@@ -0,0 +1,76 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function SignupForm({ ...props }: React.ComponentProps) {
+ return (
+
+
+ Create an account
+
+ Enter your information below to create your account
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-01/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-01/page.tsx
new file mode 100644
index 0000000..64ab607
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-01/page.tsx
@@ -0,0 +1,11 @@
+import { SignupForm } from "@/registry/new-york-v4/blocks/signup-01/components/signup-form"
+
+export default function Page() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-02/components/signup-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-02/components/signup-form.tsx
new file mode 100644
index 0000000..d04cd1e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-02/components/signup-form.tsx
@@ -0,0 +1,70 @@
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-02/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-02/page.tsx
new file mode 100644
index 0000000..0ae94e8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-02/page.tsx
@@ -0,0 +1,32 @@
+import { GalleryVerticalEnd } from "lucide-react"
+
+import { SignupForm } from "@/registry/new-york-v4/blocks/signup-02/components/signup-form"
+
+export default function SignupPage() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-03/components/signup-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-03/components/signup-form.tsx
new file mode 100644
index 0000000..2b7ed59
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-03/components/signup-form.tsx
@@ -0,0 +1,80 @@
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+
+
+ Create your account
+
+ Enter your email below to create your account
+
+
+
+
+
+
+
+ By clicking continue, you agree to our Terms of Service {" "}
+ and Privacy Policy .
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-03/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-03/page.tsx
new file mode 100644
index 0000000..8d6fab0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-03/page.tsx
@@ -0,0 +1,19 @@
+import { GalleryVerticalEnd } from "lucide-react"
+
+import { SignupForm } from "@/registry/new-york-v4/blocks/signup-03/components/signup-form"
+
+export default function SignupPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-04/components/signup-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-04/components/signup-form.tsx
new file mode 100644
index 0000000..ab5e0eb
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-04/components/signup-form.tsx
@@ -0,0 +1,114 @@
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-04/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-04/page.tsx
new file mode 100644
index 0000000..1922a99
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-04/page.tsx
@@ -0,0 +1,11 @@
+import { SignupForm } from "@/registry/new-york-v4/blocks/signup-04/components/signup-form"
+
+export default function SignupPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-05/components/signup-form.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-05/components/signup-form.tsx
new file mode 100644
index 0000000..26b6214
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-05/components/signup-form.tsx
@@ -0,0 +1,78 @@
+import { GalleryVerticalEnd } from "lucide-react"
+
+import { cn } from "@/registry/new-york-v4/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export function SignupForm({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/blocks/signup-05/page.tsx b/apps/ui/src/registry/new-york-v4/blocks/signup-05/page.tsx
new file mode 100644
index 0000000..889ced0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/blocks/signup-05/page.tsx
@@ -0,0 +1,11 @@
+import { SignupForm } from "@/registry/new-york-v4/blocks/signup-05/components/signup-form"
+
+export default function SignupPage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/_registry.ts b/apps/ui/src/registry/new-york-v4/charts/_registry.ts
new file mode 100644
index 0000000..deeda2c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/_registry.ts
@@ -0,0 +1,855 @@
+import { type Registry } from "shadcn/schema"
+
+export const charts: Registry["items"] = [
+ // Area Charts
+ {
+ name: "chart-area-axes",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-axes.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-default",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-default.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-gradient",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-gradient.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-icons",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-icons.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-interactive",
+ type: "registry:block",
+ registryDependencies: ["card", "chart", "select"],
+ files: [
+ {
+ path: "charts/chart-area-interactive.tsx",
+ type: "registry:component",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-legend",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-legend.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-linear",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-linear.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-stacked-expand",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-stacked-expand.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-stacked",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-stacked.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+ {
+ name: "chart-area-step",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-area-step.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-area"],
+ },
+
+ // Bar Charts
+ {
+ name: "chart-bar-active",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-active.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-default",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-default.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-horizontal",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-horizontal.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-interactive",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-interactive.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-label-custom",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-label-custom.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-label",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-label.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-mixed",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-mixed.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-multiple",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-multiple.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-negative",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-negative.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+ {
+ name: "chart-bar-stacked",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-bar-stacked.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-bar"],
+ },
+
+ // Line Charts
+ {
+ name: "chart-line-default",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-default.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-dots-colors",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-dots-colors.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-dots-custom",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-dots-custom.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-dots",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-dots.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-interactive",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-interactive.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-label-custom",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-label-custom.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-label",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-label.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-linear",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-linear.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-multiple",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-multiple.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+ {
+ name: "chart-line-step",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-line-step.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-line"],
+ },
+
+ // Pie Charts
+ {
+ name: "chart-pie-donut-active",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-donut-active.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-donut-text",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-donut-text.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-donut",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-donut.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-interactive",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-interactive.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-label-custom",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-label-custom.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-label-list",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-label-list.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-label",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-label.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-legend",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-legend.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-separator-none",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-separator-none.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-simple",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-simple.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+ {
+ name: "chart-pie-stacked",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-pie-stacked.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-pie"],
+ },
+
+ // Radar Charts
+ {
+ name: "chart-radar-default",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-default.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-dots",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-dots.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-grid-circle-fill",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-grid-circle-fill.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-grid-circle-no-lines",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-grid-circle-no-lines.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-grid-circle",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-grid-circle.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-grid-custom",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-grid-custom.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-grid-fill",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-grid-fill.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-grid-none",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-grid-none.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-icons",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-icons.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-label-custom",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-label-custom.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-legend",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-legend.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-lines-only",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-lines-only.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-multiple",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-multiple.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+ {
+ name: "chart-radar-radius",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radar-radius.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radar"],
+ },
+
+ // Radial Charts
+ {
+ name: "chart-radial-grid",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radial-grid.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radial"],
+ },
+ {
+ name: "chart-radial-label",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radial-label.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radial"],
+ },
+ {
+ name: "chart-radial-shape",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radial-shape.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radial"],
+ },
+ {
+ name: "chart-radial-simple",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radial-simple.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radial"],
+ },
+ {
+ name: "chart-radial-stacked",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radial-stacked.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radial"],
+ },
+ {
+ name: "chart-radial-text",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-radial-text.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-radial"],
+ },
+ {
+ name: "chart-tooltip-default",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-default.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-indicator-line",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-indicator-line.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-indicator-none",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-indicator-none.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-label-none",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-label-none.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-label-custom",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-label-custom.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-label-formatter",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-label-formatter.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-formatter",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-formatter.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-icons",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-icons.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+ {
+ name: "chart-tooltip-advanced",
+ type: "registry:block",
+ registryDependencies: ["card", "chart"],
+ files: [
+ {
+ path: "charts/chart-tooltip-advanced.tsx",
+ type: "registry:block",
+ },
+ ],
+ categories: ["charts", "charts-tooltip"],
+ },
+]
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-axes.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-axes.tsx
new file mode 100644
index 0000000..c0064b4
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-axes.tsx
@@ -0,0 +1,109 @@
+
+import { TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "An area chart with axes"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaAxes() {
+ return (
+
+
+ Area Chart - Axes
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+
+ } />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-default.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-default.tsx
new file mode 100644
index 0000000..64eccbd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-default.tsx
@@ -0,0 +1,93 @@
+
+import { TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A simple area chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaDefault() {
+ return (
+
+
+ Area Chart
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-gradient.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-gradient.tsx
new file mode 100644
index 0000000..d2c0a02
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-gradient.tsx
@@ -0,0 +1,129 @@
+
+import { TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "An area chart with gradient fill"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaGradient() {
+ return (
+
+
+ Area Chart - Gradient
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ } />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-icons.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-icons.tsx
new file mode 100644
index 0000000..aca8643
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-icons.tsx
@@ -0,0 +1,111 @@
+
+import { TrendingDown, TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "An area chart with icons"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ icon: TrendingDown,
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ icon: TrendingUp,
+ },
+} satisfies ChartConfig
+
+export function ChartAreaIcons() {
+ return (
+
+
+ Area Chart - Icons
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+ } />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-interactive.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-interactive.tsx
new file mode 100644
index 0000000..355c043
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-interactive.tsx
@@ -0,0 +1,265 @@
+
+import * as React from "react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+
+export const description = "An interactive area chart"
+
+const chartData = [
+ { date: "2024-04-01", desktop: 222, mobile: 150 },
+ { date: "2024-04-02", desktop: 97, mobile: 180 },
+ { date: "2024-04-03", desktop: 167, mobile: 120 },
+ { date: "2024-04-04", desktop: 242, mobile: 260 },
+ { date: "2024-04-05", desktop: 373, mobile: 290 },
+ { date: "2024-04-06", desktop: 301, mobile: 340 },
+ { date: "2024-04-07", desktop: 245, mobile: 180 },
+ { date: "2024-04-08", desktop: 409, mobile: 320 },
+ { date: "2024-04-09", desktop: 59, mobile: 110 },
+ { date: "2024-04-10", desktop: 261, mobile: 190 },
+ { date: "2024-04-11", desktop: 327, mobile: 350 },
+ { date: "2024-04-12", desktop: 292, mobile: 210 },
+ { date: "2024-04-13", desktop: 342, mobile: 380 },
+ { date: "2024-04-14", desktop: 137, mobile: 220 },
+ { date: "2024-04-15", desktop: 120, mobile: 170 },
+ { date: "2024-04-16", desktop: 138, mobile: 190 },
+ { date: "2024-04-17", desktop: 446, mobile: 360 },
+ { date: "2024-04-18", desktop: 364, mobile: 410 },
+ { date: "2024-04-19", desktop: 243, mobile: 180 },
+ { date: "2024-04-20", desktop: 89, mobile: 150 },
+ { date: "2024-04-21", desktop: 137, mobile: 200 },
+ { date: "2024-04-22", desktop: 224, mobile: 170 },
+ { date: "2024-04-23", desktop: 138, mobile: 230 },
+ { date: "2024-04-24", desktop: 387, mobile: 290 },
+ { date: "2024-04-25", desktop: 215, mobile: 250 },
+ { date: "2024-04-26", desktop: 75, mobile: 130 },
+ { date: "2024-04-27", desktop: 383, mobile: 420 },
+ { date: "2024-04-28", desktop: 122, mobile: 180 },
+ { date: "2024-04-29", desktop: 315, mobile: 240 },
+ { date: "2024-04-30", desktop: 454, mobile: 380 },
+ { date: "2024-05-01", desktop: 165, mobile: 220 },
+ { date: "2024-05-02", desktop: 293, mobile: 310 },
+ { date: "2024-05-03", desktop: 247, mobile: 190 },
+ { date: "2024-05-04", desktop: 385, mobile: 420 },
+ { date: "2024-05-05", desktop: 481, mobile: 390 },
+ { date: "2024-05-06", desktop: 498, mobile: 520 },
+ { date: "2024-05-07", desktop: 388, mobile: 300 },
+ { date: "2024-05-08", desktop: 149, mobile: 210 },
+ { date: "2024-05-09", desktop: 227, mobile: 180 },
+ { date: "2024-05-10", desktop: 293, mobile: 330 },
+ { date: "2024-05-11", desktop: 335, mobile: 270 },
+ { date: "2024-05-12", desktop: 197, mobile: 240 },
+ { date: "2024-05-13", desktop: 197, mobile: 160 },
+ { date: "2024-05-14", desktop: 448, mobile: 490 },
+ { date: "2024-05-15", desktop: 473, mobile: 380 },
+ { date: "2024-05-16", desktop: 338, mobile: 400 },
+ { date: "2024-05-17", desktop: 499, mobile: 420 },
+ { date: "2024-05-18", desktop: 315, mobile: 350 },
+ { date: "2024-05-19", desktop: 235, mobile: 180 },
+ { date: "2024-05-20", desktop: 177, mobile: 230 },
+ { date: "2024-05-21", desktop: 82, mobile: 140 },
+ { date: "2024-05-22", desktop: 81, mobile: 120 },
+ { date: "2024-05-23", desktop: 252, mobile: 290 },
+ { date: "2024-05-24", desktop: 294, mobile: 220 },
+ { date: "2024-05-25", desktop: 201, mobile: 250 },
+ { date: "2024-05-26", desktop: 213, mobile: 170 },
+ { date: "2024-05-27", desktop: 420, mobile: 460 },
+ { date: "2024-05-28", desktop: 233, mobile: 190 },
+ { date: "2024-05-29", desktop: 78, mobile: 130 },
+ { date: "2024-05-30", desktop: 340, mobile: 280 },
+ { date: "2024-05-31", desktop: 178, mobile: 230 },
+ { date: "2024-06-01", desktop: 178, mobile: 200 },
+ { date: "2024-06-02", desktop: 470, mobile: 410 },
+ { date: "2024-06-03", desktop: 103, mobile: 160 },
+ { date: "2024-06-04", desktop: 439, mobile: 380 },
+ { date: "2024-06-05", desktop: 88, mobile: 140 },
+ { date: "2024-06-06", desktop: 294, mobile: 250 },
+ { date: "2024-06-07", desktop: 323, mobile: 370 },
+ { date: "2024-06-08", desktop: 385, mobile: 320 },
+ { date: "2024-06-09", desktop: 438, mobile: 480 },
+ { date: "2024-06-10", desktop: 155, mobile: 200 },
+ { date: "2024-06-11", desktop: 92, mobile: 150 },
+ { date: "2024-06-12", desktop: 492, mobile: 420 },
+ { date: "2024-06-13", desktop: 81, mobile: 130 },
+ { date: "2024-06-14", desktop: 426, mobile: 380 },
+ { date: "2024-06-15", desktop: 307, mobile: 350 },
+ { date: "2024-06-16", desktop: 371, mobile: 310 },
+ { date: "2024-06-17", desktop: 475, mobile: 520 },
+ { date: "2024-06-18", desktop: 107, mobile: 170 },
+ { date: "2024-06-19", desktop: 341, mobile: 290 },
+ { date: "2024-06-20", desktop: 408, mobile: 450 },
+ { date: "2024-06-21", desktop: 169, mobile: 210 },
+ { date: "2024-06-22", desktop: 317, mobile: 270 },
+ { date: "2024-06-23", desktop: 480, mobile: 530 },
+ { date: "2024-06-24", desktop: 132, mobile: 180 },
+ { date: "2024-06-25", desktop: 141, mobile: 190 },
+ { date: "2024-06-26", desktop: 434, mobile: 380 },
+ { date: "2024-06-27", desktop: 448, mobile: 490 },
+ { date: "2024-06-28", desktop: 149, mobile: 200 },
+ { date: "2024-06-29", desktop: 103, mobile: 160 },
+ { date: "2024-06-30", desktop: 446, mobile: 400 },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaInteractive() {
+ const [timeRange, setTimeRange] = React.useState("90d")
+
+ const filteredData = chartData.filter((item) => {
+ const date = new Date(item.date)
+ const referenceDate = new Date("2024-06-30")
+ let daysToSubtract = 90
+ if (timeRange === "30d") {
+ daysToSubtract = 30
+ } else if (timeRange === "7d") {
+ daysToSubtract = 7
+ }
+ const startDate = new Date(referenceDate)
+ startDate.setDate(startDate.getDate() - daysToSubtract)
+ return date >= startDate
+ })
+
+ return (
+
+
+
+ Area Chart - Interactive
+
+ Showing total visitors for the last 3 months
+
+
+
+
+
+
+
+
+ Last 3 months
+
+
+ Last 30 days
+
+
+ Last 7 days
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ const date = new Date(value)
+ return date.toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ />
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ indicator="dot"
+ />
+ }
+ />
+
+
+ } />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-legend.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-legend.tsx
new file mode 100644
index 0000000..f439272
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-legend.tsx
@@ -0,0 +1,109 @@
+
+import { TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "An area chart with a legend"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaLegend() {
+ return (
+
+
+ Area Chart - Legend
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+ } />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-linear.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-linear.tsx
new file mode 100644
index 0000000..ac477f3
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-linear.tsx
@@ -0,0 +1,93 @@
+
+import { TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A linear area chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaLinear() {
+ return (
+
+
+ Area Chart - Linear
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-stacked-expand.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-stacked-expand.tsx
new file mode 100644
index 0000000..d003f3d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-stacked-expand.tsx
@@ -0,0 +1,120 @@
+
+import { TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked area chart with expand stacking"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80, other: 45 },
+ { month: "February", desktop: 305, mobile: 200, other: 100 },
+ { month: "March", desktop: 237, mobile: 120, other: 150 },
+ { month: "April", desktop: 73, mobile: 190, other: 50 },
+ { month: "May", desktop: 209, mobile: 130, other: 100 },
+ { month: "June", desktop: 214, mobile: 140, other: 160 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-3)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaStackedExpand() {
+ return (
+
+
+ Area Chart - Stacked Expanded
+
+ Showing total visitors for the last 6months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-stacked.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-stacked.tsx
new file mode 100644
index 0000000..b4716b0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-stacked.tsx
@@ -0,0 +1,106 @@
+
+import { TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked area chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartAreaStacked() {
+ return (
+
+
+ Area Chart - Stacked
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-area-step.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-area-step.tsx
new file mode 100644
index 0000000..1ed0d95
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-area-step.tsx
@@ -0,0 +1,94 @@
+
+import { Activity, TrendingUp } from "lucide-react"
+import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A step area chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ icon: Activity,
+ },
+} satisfies ChartConfig
+
+export function ChartAreaStep() {
+ return (
+
+
+ Area Chart - Step
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-active.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-active.tsx
new file mode 100644
index 0000000..d871cbf
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-active.tsx
@@ -0,0 +1,114 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, CartesianGrid, Rectangle, XAxis } from "recharts"
+import type { BarShapeProps } from "recharts/types/cartesian/Bar"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A bar chart with an active bar"
+
+const chartData = [
+ { browser: "chrome", visitors: 187, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 275, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+const ACTIVE_INDEX = 2
+
+export function ChartBarActive() {
+ return (
+
+
+ Bar Chart - Active
+ January - June 2024
+
+
+
+
+
+
+ chartConfig[value as keyof typeof chartConfig]?.label
+ }
+ />
+ }
+ />
+
+ index === ACTIVE_INDEX ? (
+
+ ) : (
+
+ )
+ }
+ />
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-default.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-default.tsx
new file mode 100644
index 0000000..8c343f0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-default.tsx
@@ -0,0 +1,74 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A bar chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarDefault() {
+ return (
+
+
+ Bar Chart
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-horizontal.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-horizontal.tsx
new file mode 100644
index 0000000..64a76a0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-horizontal.tsx
@@ -0,0 +1,82 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, XAxis, YAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A horizontal bar chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarHorizontal() {
+ return (
+
+
+ Bar Chart - Horizontal
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-interactive.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-interactive.tsx
new file mode 100644
index 0000000..75a1c57
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-interactive.tsx
@@ -0,0 +1,220 @@
+
+import * as React from "react"
+import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "An interactive bar chart"
+
+const chartData = [
+ { date: "2024-04-01", desktop: 222, mobile: 150 },
+ { date: "2024-04-02", desktop: 97, mobile: 180 },
+ { date: "2024-04-03", desktop: 167, mobile: 120 },
+ { date: "2024-04-04", desktop: 242, mobile: 260 },
+ { date: "2024-04-05", desktop: 373, mobile: 290 },
+ { date: "2024-04-06", desktop: 301, mobile: 340 },
+ { date: "2024-04-07", desktop: 245, mobile: 180 },
+ { date: "2024-04-08", desktop: 409, mobile: 320 },
+ { date: "2024-04-09", desktop: 59, mobile: 110 },
+ { date: "2024-04-10", desktop: 261, mobile: 190 },
+ { date: "2024-04-11", desktop: 327, mobile: 350 },
+ { date: "2024-04-12", desktop: 292, mobile: 210 },
+ { date: "2024-04-13", desktop: 342, mobile: 380 },
+ { date: "2024-04-14", desktop: 137, mobile: 220 },
+ { date: "2024-04-15", desktop: 120, mobile: 170 },
+ { date: "2024-04-16", desktop: 138, mobile: 190 },
+ { date: "2024-04-17", desktop: 446, mobile: 360 },
+ { date: "2024-04-18", desktop: 364, mobile: 410 },
+ { date: "2024-04-19", desktop: 243, mobile: 180 },
+ { date: "2024-04-20", desktop: 89, mobile: 150 },
+ { date: "2024-04-21", desktop: 137, mobile: 200 },
+ { date: "2024-04-22", desktop: 224, mobile: 170 },
+ { date: "2024-04-23", desktop: 138, mobile: 230 },
+ { date: "2024-04-24", desktop: 387, mobile: 290 },
+ { date: "2024-04-25", desktop: 215, mobile: 250 },
+ { date: "2024-04-26", desktop: 75, mobile: 130 },
+ { date: "2024-04-27", desktop: 383, mobile: 420 },
+ { date: "2024-04-28", desktop: 122, mobile: 180 },
+ { date: "2024-04-29", desktop: 315, mobile: 240 },
+ { date: "2024-04-30", desktop: 454, mobile: 380 },
+ { date: "2024-05-01", desktop: 165, mobile: 220 },
+ { date: "2024-05-02", desktop: 293, mobile: 310 },
+ { date: "2024-05-03", desktop: 247, mobile: 190 },
+ { date: "2024-05-04", desktop: 385, mobile: 420 },
+ { date: "2024-05-05", desktop: 481, mobile: 390 },
+ { date: "2024-05-06", desktop: 498, mobile: 520 },
+ { date: "2024-05-07", desktop: 388, mobile: 300 },
+ { date: "2024-05-08", desktop: 149, mobile: 210 },
+ { date: "2024-05-09", desktop: 227, mobile: 180 },
+ { date: "2024-05-10", desktop: 293, mobile: 330 },
+ { date: "2024-05-11", desktop: 335, mobile: 270 },
+ { date: "2024-05-12", desktop: 197, mobile: 240 },
+ { date: "2024-05-13", desktop: 197, mobile: 160 },
+ { date: "2024-05-14", desktop: 448, mobile: 490 },
+ { date: "2024-05-15", desktop: 473, mobile: 380 },
+ { date: "2024-05-16", desktop: 338, mobile: 400 },
+ { date: "2024-05-17", desktop: 499, mobile: 420 },
+ { date: "2024-05-18", desktop: 315, mobile: 350 },
+ { date: "2024-05-19", desktop: 235, mobile: 180 },
+ { date: "2024-05-20", desktop: 177, mobile: 230 },
+ { date: "2024-05-21", desktop: 82, mobile: 140 },
+ { date: "2024-05-22", desktop: 81, mobile: 120 },
+ { date: "2024-05-23", desktop: 252, mobile: 290 },
+ { date: "2024-05-24", desktop: 294, mobile: 220 },
+ { date: "2024-05-25", desktop: 201, mobile: 250 },
+ { date: "2024-05-26", desktop: 213, mobile: 170 },
+ { date: "2024-05-27", desktop: 420, mobile: 460 },
+ { date: "2024-05-28", desktop: 233, mobile: 190 },
+ { date: "2024-05-29", desktop: 78, mobile: 130 },
+ { date: "2024-05-30", desktop: 340, mobile: 280 },
+ { date: "2024-05-31", desktop: 178, mobile: 230 },
+ { date: "2024-06-01", desktop: 178, mobile: 200 },
+ { date: "2024-06-02", desktop: 470, mobile: 410 },
+ { date: "2024-06-03", desktop: 103, mobile: 160 },
+ { date: "2024-06-04", desktop: 439, mobile: 380 },
+ { date: "2024-06-05", desktop: 88, mobile: 140 },
+ { date: "2024-06-06", desktop: 294, mobile: 250 },
+ { date: "2024-06-07", desktop: 323, mobile: 370 },
+ { date: "2024-06-08", desktop: 385, mobile: 320 },
+ { date: "2024-06-09", desktop: 438, mobile: 480 },
+ { date: "2024-06-10", desktop: 155, mobile: 200 },
+ { date: "2024-06-11", desktop: 92, mobile: 150 },
+ { date: "2024-06-12", desktop: 492, mobile: 420 },
+ { date: "2024-06-13", desktop: 81, mobile: 130 },
+ { date: "2024-06-14", desktop: 426, mobile: 380 },
+ { date: "2024-06-15", desktop: 307, mobile: 350 },
+ { date: "2024-06-16", desktop: 371, mobile: 310 },
+ { date: "2024-06-17", desktop: 475, mobile: 520 },
+ { date: "2024-06-18", desktop: 107, mobile: 170 },
+ { date: "2024-06-19", desktop: 341, mobile: 290 },
+ { date: "2024-06-20", desktop: 408, mobile: 450 },
+ { date: "2024-06-21", desktop: 169, mobile: 210 },
+ { date: "2024-06-22", desktop: 317, mobile: 270 },
+ { date: "2024-06-23", desktop: 480, mobile: 530 },
+ { date: "2024-06-24", desktop: 132, mobile: 180 },
+ { date: "2024-06-25", desktop: 141, mobile: 190 },
+ { date: "2024-06-26", desktop: 434, mobile: 380 },
+ { date: "2024-06-27", desktop: 448, mobile: 490 },
+ { date: "2024-06-28", desktop: 149, mobile: 200 },
+ { date: "2024-06-29", desktop: 103, mobile: 160 },
+ { date: "2024-06-30", desktop: 446, mobile: 400 },
+]
+
+const chartConfig = {
+ views: {
+ label: "Page Views",
+ },
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-2)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarInteractive() {
+ const [activeChart, setActiveChart] =
+ React.useState("desktop")
+
+ const total = React.useMemo(
+ () => ({
+ desktop: chartData.reduce((acc, curr) => acc + curr.desktop, 0),
+ mobile: chartData.reduce((acc, curr) => acc + curr.mobile, 0),
+ }),
+ []
+ )
+
+ return (
+
+
+
+ Bar Chart - Interactive
+
+ Showing total visitors for the last 3 months
+
+
+
+ {["desktop", "mobile"].map((key) => {
+ const chart = key as keyof typeof chartConfig
+ return (
+ setActiveChart(chart)}
+ >
+
+ {chartConfig[chart].label}
+
+
+ {total[key as keyof typeof total].toLocaleString()}
+
+
+ )
+ })}
+
+
+
+
+
+
+ {
+ const date = new Date(value)
+ return date.toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ />
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ year: "numeric",
+ })
+ }}
+ />
+ }
+ />
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-label-custom.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-label-custom.tsx
new file mode 100644
index 0000000..8ef963b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-label-custom.tsx
@@ -0,0 +1,106 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, CartesianGrid, LabelList, XAxis, YAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A bar chart with a custom label"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-2)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+ label: {
+ color: "var(--background)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarLabelCustom() {
+ return (
+
+
+ Bar Chart - Custom Label
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ hide
+ />
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-label.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-label.tsx
new file mode 100644
index 0000000..ff010ba
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-label.tsx
@@ -0,0 +1,87 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, CartesianGrid, LabelList, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A bar chart with a label"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarLabel() {
+ return (
+
+
+ Bar Chart - Label
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-mixed.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-mixed.tsx
new file mode 100644
index 0000000..dc2ecf3
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-mixed.tsx
@@ -0,0 +1,102 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, XAxis, YAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A mixed bar chart"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarMixed() {
+ return (
+
+
+ Bar Chart - Mixed
+ January - June 2024
+
+
+
+
+
+ chartConfig[value as keyof typeof chartConfig]?.label
+ }
+ />
+
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-multiple.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-multiple.tsx
new file mode 100644
index 0000000..425e02c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-multiple.tsx
@@ -0,0 +1,79 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A multiple bar chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarMultiple() {
+ return (
+
+
+ Bar Chart - Multiple
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-negative.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-negative.tsx
new file mode 100644
index 0000000..658fd27
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-negative.tsx
@@ -0,0 +1,74 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, CartesianGrid, Cell, LabelList } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A bar chart with negative values"
+
+const chartData = [
+ { month: "January", visitors: 186 },
+ { month: "February", visitors: 205 },
+ { month: "March", visitors: -207 },
+ { month: "April", visitors: 173 },
+ { month: "May", visitors: -209 },
+ { month: "June", visitors: 214 },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+} satisfies ChartConfig
+
+export function ChartBarNegative() {
+ return (
+
+
+ Bar Chart - Negative
+ January - June 2024
+
+
+
+
+
+ }
+ />
+
+
+ {chartData.map((item) => (
+ 0 ? "var(--chart-1)" : "var(--chart-2)"}
+ />
+ ))}
+ |
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-bar-stacked.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-bar-stacked.tsx
new file mode 100644
index 0000000..75af040
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-bar-stacked.tsx
@@ -0,0 +1,89 @@
+
+import { TrendingUp } from "lucide-react"
+import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartBarStacked() {
+ return (
+
+
+ Bar Chart - Stacked + Legend
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ } />
+ } />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-default.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-default.tsx
new file mode 100644
index 0000000..bed4632
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-default.tsx
@@ -0,0 +1,87 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A line chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineDefault() {
+ return (
+
+
+ Line Chart
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-dots-colors.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-dots-colors.tsx
new file mode 100644
index 0000000..d66f313
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-dots-colors.tsx
@@ -0,0 +1,117 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, Dot, Line, LineChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A line chart with dots and colors"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ color: "var(--chart-2)",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineDotsColors() {
+ return (
+
+
+ Line Chart - Dots Colors
+ January - June 2024
+
+
+
+
+
+
+ }
+ />
+ {
+ return (
+
+ )
+ }}
+ />
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-dots-custom.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-dots-custom.tsx
new file mode 100644
index 0000000..a49f64a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-dots-custom.tsx
@@ -0,0 +1,109 @@
+
+import { GitCommitVertical, TrendingUp } from "lucide-react"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A line chart with custom dots"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineDotsCustom() {
+ return (
+
+
+ Line Chart - Custom Dots
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+ {
+ if (cx == null || cy == null) {
+ return null
+ }
+
+ const r = 24
+
+ return (
+
+ )
+ }}
+ />
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-dots.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-dots.tsx
new file mode 100644
index 0000000..de14edd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-dots.tsx
@@ -0,0 +1,96 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A line chart with dots"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineDots() {
+ return (
+
+
+ Line Chart - Dots
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-interactive.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-interactive.tsx
new file mode 100644
index 0000000..9089980
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-interactive.tsx
@@ -0,0 +1,226 @@
+
+import * as React from "react"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "An interactive line chart"
+
+const chartData = [
+ { date: "2024-04-01", desktop: 222, mobile: 150 },
+ { date: "2024-04-02", desktop: 97, mobile: 180 },
+ { date: "2024-04-03", desktop: 167, mobile: 120 },
+ { date: "2024-04-04", desktop: 242, mobile: 260 },
+ { date: "2024-04-05", desktop: 373, mobile: 290 },
+ { date: "2024-04-06", desktop: 301, mobile: 340 },
+ { date: "2024-04-07", desktop: 245, mobile: 180 },
+ { date: "2024-04-08", desktop: 409, mobile: 320 },
+ { date: "2024-04-09", desktop: 59, mobile: 110 },
+ { date: "2024-04-10", desktop: 261, mobile: 190 },
+ { date: "2024-04-11", desktop: 327, mobile: 350 },
+ { date: "2024-04-12", desktop: 292, mobile: 210 },
+ { date: "2024-04-13", desktop: 342, mobile: 380 },
+ { date: "2024-04-14", desktop: 137, mobile: 220 },
+ { date: "2024-04-15", desktop: 120, mobile: 170 },
+ { date: "2024-04-16", desktop: 138, mobile: 190 },
+ { date: "2024-04-17", desktop: 446, mobile: 360 },
+ { date: "2024-04-18", desktop: 364, mobile: 410 },
+ { date: "2024-04-19", desktop: 243, mobile: 180 },
+ { date: "2024-04-20", desktop: 89, mobile: 150 },
+ { date: "2024-04-21", desktop: 137, mobile: 200 },
+ { date: "2024-04-22", desktop: 224, mobile: 170 },
+ { date: "2024-04-23", desktop: 138, mobile: 230 },
+ { date: "2024-04-24", desktop: 387, mobile: 290 },
+ { date: "2024-04-25", desktop: 215, mobile: 250 },
+ { date: "2024-04-26", desktop: 75, mobile: 130 },
+ { date: "2024-04-27", desktop: 383, mobile: 420 },
+ { date: "2024-04-28", desktop: 122, mobile: 180 },
+ { date: "2024-04-29", desktop: 315, mobile: 240 },
+ { date: "2024-04-30", desktop: 454, mobile: 380 },
+ { date: "2024-05-01", desktop: 165, mobile: 220 },
+ { date: "2024-05-02", desktop: 293, mobile: 310 },
+ { date: "2024-05-03", desktop: 247, mobile: 190 },
+ { date: "2024-05-04", desktop: 385, mobile: 420 },
+ { date: "2024-05-05", desktop: 481, mobile: 390 },
+ { date: "2024-05-06", desktop: 498, mobile: 520 },
+ { date: "2024-05-07", desktop: 388, mobile: 300 },
+ { date: "2024-05-08", desktop: 149, mobile: 210 },
+ { date: "2024-05-09", desktop: 227, mobile: 180 },
+ { date: "2024-05-10", desktop: 293, mobile: 330 },
+ { date: "2024-05-11", desktop: 335, mobile: 270 },
+ { date: "2024-05-12", desktop: 197, mobile: 240 },
+ { date: "2024-05-13", desktop: 197, mobile: 160 },
+ { date: "2024-05-14", desktop: 448, mobile: 490 },
+ { date: "2024-05-15", desktop: 473, mobile: 380 },
+ { date: "2024-05-16", desktop: 338, mobile: 400 },
+ { date: "2024-05-17", desktop: 499, mobile: 420 },
+ { date: "2024-05-18", desktop: 315, mobile: 350 },
+ { date: "2024-05-19", desktop: 235, mobile: 180 },
+ { date: "2024-05-20", desktop: 177, mobile: 230 },
+ { date: "2024-05-21", desktop: 82, mobile: 140 },
+ { date: "2024-05-22", desktop: 81, mobile: 120 },
+ { date: "2024-05-23", desktop: 252, mobile: 290 },
+ { date: "2024-05-24", desktop: 294, mobile: 220 },
+ { date: "2024-05-25", desktop: 201, mobile: 250 },
+ { date: "2024-05-26", desktop: 213, mobile: 170 },
+ { date: "2024-05-27", desktop: 420, mobile: 460 },
+ { date: "2024-05-28", desktop: 233, mobile: 190 },
+ { date: "2024-05-29", desktop: 78, mobile: 130 },
+ { date: "2024-05-30", desktop: 340, mobile: 280 },
+ { date: "2024-05-31", desktop: 178, mobile: 230 },
+ { date: "2024-06-01", desktop: 178, mobile: 200 },
+ { date: "2024-06-02", desktop: 470, mobile: 410 },
+ { date: "2024-06-03", desktop: 103, mobile: 160 },
+ { date: "2024-06-04", desktop: 439, mobile: 380 },
+ { date: "2024-06-05", desktop: 88, mobile: 140 },
+ { date: "2024-06-06", desktop: 294, mobile: 250 },
+ { date: "2024-06-07", desktop: 323, mobile: 370 },
+ { date: "2024-06-08", desktop: 385, mobile: 320 },
+ { date: "2024-06-09", desktop: 438, mobile: 480 },
+ { date: "2024-06-10", desktop: 155, mobile: 200 },
+ { date: "2024-06-11", desktop: 92, mobile: 150 },
+ { date: "2024-06-12", desktop: 492, mobile: 420 },
+ { date: "2024-06-13", desktop: 81, mobile: 130 },
+ { date: "2024-06-14", desktop: 426, mobile: 380 },
+ { date: "2024-06-15", desktop: 307, mobile: 350 },
+ { date: "2024-06-16", desktop: 371, mobile: 310 },
+ { date: "2024-06-17", desktop: 475, mobile: 520 },
+ { date: "2024-06-18", desktop: 107, mobile: 170 },
+ { date: "2024-06-19", desktop: 341, mobile: 290 },
+ { date: "2024-06-20", desktop: 408, mobile: 450 },
+ { date: "2024-06-21", desktop: 169, mobile: 210 },
+ { date: "2024-06-22", desktop: 317, mobile: 270 },
+ { date: "2024-06-23", desktop: 480, mobile: 530 },
+ { date: "2024-06-24", desktop: 132, mobile: 180 },
+ { date: "2024-06-25", desktop: 141, mobile: 190 },
+ { date: "2024-06-26", desktop: 434, mobile: 380 },
+ { date: "2024-06-27", desktop: 448, mobile: 490 },
+ { date: "2024-06-28", desktop: 149, mobile: 200 },
+ { date: "2024-06-29", desktop: 103, mobile: 160 },
+ { date: "2024-06-30", desktop: 446, mobile: 400 },
+]
+
+const chartConfig = {
+ views: {
+ label: "Page Views",
+ },
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineInteractive() {
+ const [activeChart, setActiveChart] =
+ React.useState("desktop")
+
+ const total = React.useMemo(
+ () => ({
+ desktop: chartData.reduce((acc, curr) => acc + curr.desktop, 0),
+ mobile: chartData.reduce((acc, curr) => acc + curr.mobile, 0),
+ }),
+ []
+ )
+
+ return (
+
+
+
+ Line Chart - Interactive
+
+ Showing total visitors for the last 3 months
+
+
+
+ {["desktop", "mobile"].map((key) => {
+ const chart = key as keyof typeof chartConfig
+ return (
+ setActiveChart(chart)}
+ >
+
+ {chartConfig[chart].label}
+
+
+ {total[key as keyof typeof total].toLocaleString()}
+
+
+ )
+ })}
+
+
+
+
+
+
+ {
+ const date = new Date(value)
+ return date.toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ })
+ }}
+ />
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ year: "numeric",
+ })
+ }}
+ />
+ }
+ />
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-label-custom.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-label-custom.tsx
new file mode 100644
index 0000000..710ad1c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-label-custom.tsx
@@ -0,0 +1,122 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, LabelList, Line, LineChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A line chart with a custom label"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ color: "var(--chart-2)",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineLabelCustom() {
+ return (
+
+
+ Line Chart - Custom Label
+ January - June 2024
+
+
+
+
+
+
+ }
+ />
+
+
+ chartConfig[value as keyof typeof chartConfig]?.label
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-label.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-label.tsx
new file mode 100644
index 0000000..8597f7d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-label.tsx
@@ -0,0 +1,104 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, LabelList, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A line chart with a label"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineLabel() {
+ return (
+
+
+ Line Chart - Label
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-linear.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-linear.tsx
new file mode 100644
index 0000000..de2dd5c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-linear.tsx
@@ -0,0 +1,87 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A linear line chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineLinear() {
+ return (
+
+
+ Line Chart - Linear
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-multiple.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-multiple.tsx
new file mode 100644
index 0000000..b7435db
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-multiple.tsx
@@ -0,0 +1,99 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A multiple line chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineMultiple() {
+ return (
+
+
+ Line Chart - Multiple
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ } />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-line-step.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-line-step.tsx
new file mode 100644
index 0000000..630ba89
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-line-step.tsx
@@ -0,0 +1,87 @@
+
+import { TrendingUp } from "lucide-react"
+import { CartesianGrid, Line, LineChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A line chart with step"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 73 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartLineStep() {
+ return (
+
+
+ Line Chart - Step
+ January - June 2024
+
+
+
+
+
+ value.slice(0, 3)}
+ />
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut-active.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut-active.tsx
new file mode 100644
index 0000000..e1ee899
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut-active.tsx
@@ -0,0 +1,107 @@
+
+import { TrendingUp } from "lucide-react"
+import { Label, Pie, PieChart, Sector } from "recharts"
+import type { PieSectorShapeProps } from "recharts/types/polar/Pie"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A donut chart with an active sector"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+const ACTIVE_INDEX = 0
+
+export function ChartPieDonutActive() {
+ return (
+
+
+ Pie Chart - Donut Active
+ January - June 2024
+
+
+
+
+ }
+ />
+
+ index === ACTIVE_INDEX ? (
+
+ ) : (
+
+ )
+ }
+ />
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut-text.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut-text.tsx
new file mode 100644
index 0000000..6dcadd3
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut-text.tsx
@@ -0,0 +1,128 @@
+
+import * as React from "react"
+import { TrendingUp } from "lucide-react"
+import { Label, Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A donut chart with text"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 287, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 190, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieDonutText() {
+ const totalVisitors = React.useMemo(() => {
+ return chartData.reduce((acc, curr) => acc + curr.visitors, 0)
+ }, [])
+
+ return (
+
+
+ Pie Chart - Donut with Text
+ January - June 2024
+
+
+
+
+ }
+ />
+
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {totalVisitors.toLocaleString()}
+
+
+ Visitors
+
+
+ )
+ }
+ }}
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut.tsx
new file mode 100644
index 0000000..271ee98
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-donut.tsx
@@ -0,0 +1,92 @@
+
+import { TrendingUp } from "lucide-react"
+import { Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A donut chart"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieDonut() {
+ return (
+
+
+ Pie Chart - Donut
+ January - June 2024
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-interactive.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-interactive.tsx
new file mode 100644
index 0000000..ee5f290
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-interactive.tsx
@@ -0,0 +1,201 @@
+
+import * as React from "react"
+import { Label, Pie, PieChart, Sector } from "recharts"
+import type {
+ PieSectorDataItem,
+ PieSectorShapeProps,
+} from "recharts/types/polar/Pie"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartStyle,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+
+export const description = "An interactive pie chart"
+
+const desktopData = [
+ { month: "january", desktop: 186, fill: "var(--color-january)" },
+ { month: "february", desktop: 305, fill: "var(--color-february)" },
+ { month: "march", desktop: 237, fill: "var(--color-march)" },
+ { month: "april", desktop: 173, fill: "var(--color-april)" },
+ { month: "may", desktop: 209, fill: "var(--color-may)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ desktop: {
+ label: "Desktop",
+ },
+ mobile: {
+ label: "Mobile",
+ },
+ january: {
+ label: "January",
+ color: "var(--chart-1)",
+ },
+ february: {
+ label: "February",
+ color: "var(--chart-2)",
+ },
+ march: {
+ label: "March",
+ color: "var(--chart-3)",
+ },
+ april: {
+ label: "April",
+ color: "var(--chart-4)",
+ },
+ may: {
+ label: "May",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieInteractive() {
+ const id = "pie-interactive"
+ const [activeMonth, setActiveMonth] = React.useState(desktopData[0].month)
+
+ const activeIndex = React.useMemo(
+ () => desktopData.findIndex((item) => item.month === activeMonth),
+ [activeMonth]
+ )
+ const months = React.useMemo(() => desktopData.map((item) => item.month), [])
+
+ const renderPieShape = React.useCallback(
+ ({ index, outerRadius = 0, ...props }: PieSectorShapeProps) => {
+ if (index === activeIndex) {
+ return (
+
+
+
+
+ )
+ }
+
+ return
+ },
+ [activeIndex]
+ )
+
+ return (
+
+
+
+
+ Pie Chart - Interactive
+ January - June 2024
+
+
+
+
+
+
+ {months.map((key) => {
+ const config = chartConfig[key as keyof typeof chartConfig]
+
+ if (!config) {
+ return null
+ }
+
+ return (
+
+
+
+ {config?.label}
+
+
+ )
+ })}
+
+
+
+
+
+
+ }
+ />
+
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {desktopData[activeIndex].desktop.toLocaleString()}
+
+
+ Visitors
+
+
+ )
+ }
+ }}
+ />
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-label-custom.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-label-custom.tsx
new file mode 100644
index 0000000..0f80bda
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-label-custom.tsx
@@ -0,0 +1,106 @@
+
+import { TrendingUp } from "lucide-react"
+import { Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A pie chart with a custom label"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieLabelCustom() {
+ return (
+
+
+ Pie Chart - Custom Label
+ January - June 2024
+
+
+
+
+ }
+ />
+ {
+ return (
+
+ {payload.visitors}
+
+ )
+ }}
+ nameKey="browser"
+ />
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-label-list.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-label-list.tsx
new file mode 100644
index 0000000..aecb7dd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-label-list.tsx
@@ -0,0 +1,96 @@
+
+import { TrendingUp } from "lucide-react"
+import { LabelList, Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A pie chart with a label list"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieLabelList() {
+ return (
+
+
+ Pie Chart - Label List
+ January - June 2024
+
+
+
+
+ }
+ />
+
+
+ chartConfig[value as keyof typeof chartConfig]?.label
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-label.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-label.tsx
new file mode 100644
index 0000000..2e69a21
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-label.tsx
@@ -0,0 +1,84 @@
+
+import { TrendingUp } from "lucide-react"
+import { Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A pie chart with a label"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieLabel() {
+ return (
+
+
+ Pie Chart - Label
+ January - June 2024
+
+
+
+
+ } />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-legend.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-legend.tsx
new file mode 100644
index 0000000..6d7bc7a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-legend.tsx
@@ -0,0 +1,77 @@
+
+import { Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A pie chart with a legend"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieLegend() {
+ return (
+
+
+ Pie Chart - Legend
+ January - June 2024
+
+
+
+
+
+ }
+ className="-translate-y-2 flex-wrap gap-2 *:basis-1/4 *:justify-center"
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-separator-none.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-separator-none.tsx
new file mode 100644
index 0000000..d51bbf1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-separator-none.tsx
@@ -0,0 +1,92 @@
+
+import { TrendingUp } from "lucide-react"
+import { Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A pie chart with no separator"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieSeparatorNone() {
+ return (
+
+
+ Pie Chart - Separator None
+ January - June 2024
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-simple.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-simple.tsx
new file mode 100644
index 0000000..9ac4c42
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-simple.tsx
@@ -0,0 +1,87 @@
+
+import { TrendingUp } from "lucide-react"
+import { Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A simple pie chart"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieSimple() {
+ return (
+
+
+ Pie Chart
+ January - June 2024
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-pie-stacked.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-pie-stacked.tsx
new file mode 100644
index 0000000..b552f86
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-pie-stacked.tsx
@@ -0,0 +1,118 @@
+
+import * as React from "react"
+import { TrendingUp } from "lucide-react"
+import { Label, Pie, PieChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A pie chart with stacked sections"
+
+const desktopData = [
+ { month: "january", desktop: 186, fill: "var(--color-january)" },
+ { month: "february", desktop: 305, fill: "var(--color-february)" },
+ { month: "march", desktop: 237, fill: "var(--color-march)" },
+ { month: "april", desktop: 173, fill: "var(--color-april)" },
+ { month: "may", desktop: 209, fill: "var(--color-may)" },
+]
+
+const mobileData = [
+ { month: "january", mobile: 80, fill: "var(--color-january)" },
+ { month: "february", mobile: 200, fill: "var(--color-february)" },
+ { month: "march", mobile: 120, fill: "var(--color-march)" },
+ { month: "april", mobile: 190, fill: "var(--color-april)" },
+ { month: "may", mobile: 130, fill: "var(--color-may)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ desktop: {
+ label: "Desktop",
+ },
+ mobile: {
+ label: "Mobile",
+ },
+ january: {
+ label: "January",
+ color: "var(--chart-1)",
+ },
+ february: {
+ label: "February",
+ color: "var(--chart-2)",
+ },
+ march: {
+ label: "March",
+ color: "var(--chart-3)",
+ },
+ april: {
+ label: "April",
+ color: "var(--chart-4)",
+ },
+ may: {
+ label: "May",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartPieStacked() {
+ return (
+
+
+ Pie Chart - Stacked
+ January - June 2024
+
+
+
+
+ {
+ return chartConfig[
+ payload?.[0].dataKey as keyof typeof chartConfig
+ ].label
+ }}
+ />
+ }
+ />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-default.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-default.tsx
new file mode 100644
index 0000000..3fabc7f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-default.tsx
@@ -0,0 +1,74 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 273 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarDefault() {
+ return (
+
+
+ Radar Chart
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-dots.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-dots.tsx
new file mode 100644
index 0000000..b66557b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-dots.tsx
@@ -0,0 +1,78 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with dots"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 273 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarDots() {
+ return (
+
+
+ Radar Chart - Dots
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle-fill.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle-fill.tsx
new file mode 100644
index 0000000..bc38288
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle-fill.tsx
@@ -0,0 +1,77 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a grid and circle fill"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 285 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 203 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 264 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarGridCircleFill() {
+ return (
+
+
+ Radar Chart - Grid Circle Filled
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ } />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle-no-lines.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle-no-lines.tsx
new file mode 100644
index 0000000..397cbdc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle-no-lines.tsx
@@ -0,0 +1,81 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a grid and circle fill"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 203 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarGridCircleNoLines() {
+ return (
+
+
+ Radar Chart - Grid Circle - No lines
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle.tsx
new file mode 100644
index 0000000..b6ddfe8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-circle.tsx
@@ -0,0 +1,81 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a grid and circle"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 273 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarGridCircle() {
+ return (
+
+
+ Radar Chart - Grid Circle
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-custom.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-custom.tsx
new file mode 100644
index 0000000..20317ea
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-custom.tsx
@@ -0,0 +1,77 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a custom grid"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 273 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarGridCustom() {
+ return (
+
+
+ Radar Chart - Grid Custom
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-fill.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-fill.tsx
new file mode 100644
index 0000000..7ecda67
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-fill.tsx
@@ -0,0 +1,77 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a grid filled"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 285 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 203 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 264 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarGridFill() {
+ return (
+
+
+ Radar Chart - Grid Filled
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-none.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-none.tsx
new file mode 100644
index 0000000..c41aa8c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-grid-none.tsx
@@ -0,0 +1,80 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with no grid"
+
+const chartData = [
+ { month: "January", desktop: 186 },
+ { month: "February", desktop: 305 },
+ { month: "March", desktop: 237 },
+ { month: "April", desktop: 273 },
+ { month: "May", desktop: 209 },
+ { month: "June", desktop: 214 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarGridNone() {
+ return (
+
+
+ Radar Chart - Grid None
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-icons.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-icons.tsx
new file mode 100644
index 0000000..bf1f985
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-icons.tsx
@@ -0,0 +1,95 @@
+
+import { ArrowDownFromLine, ArrowUpFromLine, TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with icons"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ icon: ArrowDownFromLine,
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ icon: ArrowUpFromLine,
+ },
+} satisfies ChartConfig
+
+export function ChartRadarIcons() {
+ return (
+
+
+ Radar Chart - Icons
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+ } />
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-label-custom.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-label-custom.tsx
new file mode 100644
index 0000000..4168cb9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-label-custom.tsx
@@ -0,0 +1,120 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a custom label"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarLabelCustom() {
+ return (
+
+
+ Radar Chart - Custom Label
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+ {
+ const data = chartData[index]
+ const yValue = typeof y === "number" ? y : 0
+
+ return (
+
+ {data.desktop}
+ /
+ {data.mobile}
+
+ {data.month}
+
+
+ )
+ }}
+ />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-legend.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-legend.tsx
new file mode 100644
index 0000000..c987e7a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-legend.tsx
@@ -0,0 +1,93 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a legend"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarLegend() {
+ return (
+
+
+ Radar Chart - Legend
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+ } />
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-lines-only.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-lines-only.tsx
new file mode 100644
index 0000000..46c4885
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-lines-only.tsx
@@ -0,0 +1,90 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with lines only"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 160 },
+ { month: "February", desktop: 185, mobile: 170 },
+ { month: "March", desktop: 207, mobile: 180 },
+ { month: "April", desktop: 173, mobile: 160 },
+ { month: "May", desktop: 160, mobile: 190 },
+ { month: "June", desktop: 174, mobile: 204 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarLinesOnly() {
+ return (
+
+
+ Radar Chart - Lines Only
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-multiple.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-multiple.tsx
new file mode 100644
index 0000000..34af089
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-multiple.tsx
@@ -0,0 +1,82 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarAngleAxis, PolarGrid, Radar, RadarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with multiple data"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarMultiple() {
+ return (
+
+
+ Radar Chart - Multiple
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radar-radius.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radar-radius.tsx
new file mode 100644
index 0000000..07dd1a2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radar-radius.tsx
@@ -0,0 +1,95 @@
+
+import { TrendingUp } from "lucide-react"
+import {
+ PolarAngleAxis,
+ PolarGrid,
+ PolarRadiusAxis,
+ Radar,
+ RadarChart,
+} from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radar chart with a radius axis"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadarRadius() {
+ return (
+
+
+ Radar Chart - Radius Axis
+
+ Showing total visitors for the last 6 months
+
+
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ January - June 2024
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radial-grid.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radial-grid.tsx
new file mode 100644
index 0000000..ecce3db
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radial-grid.tsx
@@ -0,0 +1,88 @@
+
+import { TrendingUp } from "lucide-react"
+import { PolarGrid, RadialBar, RadialBarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radial chart with a grid"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadialGrid() {
+ return (
+
+
+ Radial Chart - Grid
+ January - June 2024
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radial-label.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radial-label.tsx
new file mode 100644
index 0000000..1b6c2cf
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radial-label.tsx
@@ -0,0 +1,100 @@
+
+import { TrendingUp } from "lucide-react"
+import { LabelList, RadialBar, RadialBarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radial chart with a label"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadialLabel() {
+ return (
+
+
+ Radial Chart - Label
+ January - June 2024
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radial-shape.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radial-shape.tsx
new file mode 100644
index 0000000..6ff290e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radial-shape.tsx
@@ -0,0 +1,110 @@
+
+import { TrendingUp } from "lucide-react"
+import {
+ Label,
+ PolarGrid,
+ PolarRadiusAxis,
+ RadialBar,
+ RadialBarChart,
+} from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radial chart with a custom shape"
+
+const chartData = [
+ { browser: "safari", visitors: 1260, fill: "var(--color-safari)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadialShape() {
+ return (
+
+
+ Radial Chart - Shape
+ January - June 2024
+
+
+
+
+
+
+
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {chartData[0].visitors.toLocaleString()}
+
+
+ Visitors
+
+
+ )
+ }
+ }}
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radial-simple.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radial-simple.tsx
new file mode 100644
index 0000000..8cf8e50
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radial-simple.tsx
@@ -0,0 +1,87 @@
+
+import { TrendingUp } from "lucide-react"
+import { RadialBar, RadialBarChart } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radial chart"
+
+const chartData = [
+ { browser: "chrome", visitors: 275, fill: "var(--color-chrome)" },
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+ { browser: "firefox", visitors: 187, fill: "var(--color-firefox)" },
+ { browser: "edge", visitors: 173, fill: "var(--color-edge)" },
+ { browser: "other", visitors: 90, fill: "var(--color-other)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ chrome: {
+ label: "Chrome",
+ color: "var(--chart-1)",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+ firefox: {
+ label: "Firefox",
+ color: "var(--chart-3)",
+ },
+ edge: {
+ label: "Edge",
+ color: "var(--chart-4)",
+ },
+ other: {
+ label: "Other",
+ color: "var(--chart-5)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadialSimple() {
+ return (
+
+
+ Radial Chart
+ January - June 2024
+
+
+
+
+ }
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radial-stacked.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radial-stacked.tsx
new file mode 100644
index 0000000..aabcbb6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radial-stacked.tsx
@@ -0,0 +1,118 @@
+
+import { TrendingUp } from "lucide-react"
+import {
+ Label,
+ PolarGrid,
+ PolarRadiusAxis,
+ RadialBar,
+ RadialBarChart,
+} from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radial chart with stacked sections"
+
+const chartData = [{ month: "january", mobile: 570, desktop: 1260 }]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "var(--chart-1)",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadialStacked() {
+ const totalVisitors = chartData[0].desktop + chartData[0].mobile
+
+ return (
+
+
+ Radial Chart - Stacked
+ January - June 2024
+
+
+
+
+
+
+ }
+ />
+
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {totalVisitors.toLocaleString()}
+
+
+ Visitors
+
+
+ )
+ }
+ }}
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-radial-text.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-radial-text.tsx
new file mode 100644
index 0000000..5c9b015
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-radial-text.tsx
@@ -0,0 +1,111 @@
+
+import { TrendingUp } from "lucide-react"
+import {
+ Label,
+ PolarGrid,
+ PolarRadiusAxis,
+ RadialBar,
+ RadialBarChart,
+} from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A radial chart with text"
+
+const chartData = [
+ { browser: "safari", visitors: 200, fill: "var(--color-safari)" },
+]
+
+const chartConfig = {
+ visitors: {
+ label: "Visitors",
+ },
+ safari: {
+ label: "Safari",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartRadialText() {
+ return (
+
+
+ Radial Chart - Text
+ January - June 2024
+
+
+
+
+
+
+
+ {
+ if (viewBox && "cx" in viewBox && "cy" in viewBox) {
+ return (
+
+
+ {chartData[0].visitors.toLocaleString()}
+
+
+ Visitors
+
+
+ )
+ }
+ }}
+ />
+
+
+
+
+
+
+ Trending up by 5.2% this month
+
+
+ Showing total visitors for the last 6 months
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-advanced.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-advanced.tsx
new file mode 100644
index 0000000..b250333
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-advanced.tsx
@@ -0,0 +1,122 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipAdvanced() {
+ return (
+
+
+ Tooltip - Advanced
+
+ Tooltip with custom formatter and total.
+
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ (
+ <>
+
+ {chartConfig[name as keyof typeof chartConfig]?.label ||
+ name}
+
+ {value}
+
+ kcal
+
+
+ {/* Add this after the last item */}
+ {index === 1 && (
+
+ Total
+
+ {item.payload.running + item.payload.swimming}
+
+ kcal
+
+
+
+ )}
+ >
+ )}
+ />
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-default.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-default.tsx
new file mode 100644
index 0000000..78bc321
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-default.tsx
@@ -0,0 +1,88 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+export const iframeHeight = "600px"
+export const containerClassName =
+ "[&>div]:w-full [&>div]:max-w-md flex items-center justify-center min-h-svh"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipDefault() {
+ return (
+
+
+ Tooltip - Default
+
+ Default tooltip with ChartTooltipContent.
+
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-formatter.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-formatter.tsx
new file mode 100644
index 0000000..b8b9a19
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-formatter.tsx
@@ -0,0 +1,99 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipFormatter() {
+ return (
+
+
+ Tooltip - Formatter
+ Tooltip with custom formatter .
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ (
+
+ {chartConfig[name as keyof typeof chartConfig]?.label ||
+ name}
+
+ {value}
+
+ kcal
+
+
+
+ )}
+ />
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-icons.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-icons.tsx
new file mode 100644
index 0000000..156baf0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-icons.tsx
@@ -0,0 +1,86 @@
+
+import { Footprints, Waves } from "lucide-react"
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ icon: Footprints,
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ icon: Waves,
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipIcons() {
+ return (
+
+
+ Tooltip - Icons
+ Tooltip with icons.
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-indicator-line.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-indicator-line.tsx
new file mode 100644
index 0000000..8790c45
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-indicator-line.tsx
@@ -0,0 +1,86 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+export const iframeHeight = "600px"
+export const containerClassName =
+ "[&>div]:w-full [&>div]:max-w-md flex items-center justify-center min-h-svh"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipIndicatorLine() {
+ return (
+
+
+ Tooltip - Line Indicator
+ Tooltip with line indicator.
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-indicator-none.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-indicator-none.tsx
new file mode 100644
index 0000000..4a3e36b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-indicator-none.tsx
@@ -0,0 +1,83 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipIndicatorNone() {
+ return (
+
+
+ Tooltip - No Indicator
+ Tooltip with no indicator.
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-custom.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-custom.tsx
new file mode 100644
index 0000000..4f7f6bf
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-custom.tsx
@@ -0,0 +1,90 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ activities: {
+ label: "Activities",
+ },
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipLabelCustom() {
+ return (
+
+
+ Tooltip - Custom label
+
+ Tooltip with custom label from chartConfig.
+
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-formatter.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-formatter.tsx
new file mode 100644
index 0000000..f861eb5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-formatter.tsx
@@ -0,0 +1,93 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipLabelFormatter() {
+ return (
+
+
+ Tooltip - Label Formatter
+ Tooltip with label formatter.
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ day: "numeric",
+ month: "long",
+ year: "numeric",
+ })
+ }}
+ />
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-none.tsx b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-none.tsx
new file mode 100644
index 0000000..e2a5c3a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/charts/chart-tooltip-label-none.tsx
@@ -0,0 +1,83 @@
+
+import { Bar, BarChart, XAxis } from "recharts"
+
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+export const description = "A stacked bar chart with a legend"
+
+const chartData = [
+ { date: "2024-07-15", running: 450, swimming: 300 },
+ { date: "2024-07-16", running: 380, swimming: 420 },
+ { date: "2024-07-17", running: 520, swimming: 120 },
+ { date: "2024-07-18", running: 140, swimming: 550 },
+ { date: "2024-07-19", running: 600, swimming: 350 },
+ { date: "2024-07-20", running: 480, swimming: 400 },
+]
+
+const chartConfig = {
+ running: {
+ label: "Running",
+ color: "var(--chart-1)",
+ },
+ swimming: {
+ label: "Swimming",
+ color: "var(--chart-2)",
+ },
+} satisfies ChartConfig
+
+export function ChartTooltipLabelNone() {
+ return (
+
+
+ Tooltip - No Label
+ Tooltip with no label.
+
+
+
+
+ {
+ return new Date(value).toLocaleDateString("en-US", {
+ weekday: "short",
+ })
+ }}
+ />
+
+
+ }
+ cursor={false}
+ defaultIndex={1}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/_registry.ts b/apps/ui/src/registry/new-york-v4/examples/_registry.ts
new file mode 100644
index 0000000..ac78c23
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/_registry.ts
@@ -0,0 +1,2651 @@
+import { type Registry } from "shadcn/schema"
+
+export const examples: Registry["items"] = [
+ {
+ name: "accordion-demo",
+ type: "registry:example",
+ registryDependencies: ["accordion"],
+ files: [
+ {
+ path: "examples/accordion-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "alert-demo",
+ type: "registry:example",
+ registryDependencies: ["alert"],
+ files: [
+ {
+ path: "examples/alert-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "alert-destructive",
+ type: "registry:example",
+ registryDependencies: ["alert"],
+ files: [
+ {
+ path: "examples/alert-destructive.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "alert-dialog-demo",
+ type: "registry:example",
+ registryDependencies: ["alert-dialog", "button"],
+ files: [
+ {
+ path: "examples/alert-dialog-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "aspect-ratio-demo",
+ type: "registry:example",
+ registryDependencies: ["aspect-ratio"],
+ files: [
+ {
+ path: "examples/aspect-ratio-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "avatar-demo",
+ type: "registry:example",
+ registryDependencies: ["avatar"],
+ files: [
+ {
+ path: "examples/avatar-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "badge-demo",
+ type: "registry:example",
+ registryDependencies: ["badge"],
+ files: [
+ {
+ path: "examples/badge-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "badge-destructive",
+ type: "registry:example",
+ registryDependencies: ["badge"],
+ files: [
+ {
+ path: "examples/badge-destructive.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "badge-outline",
+ type: "registry:example",
+ registryDependencies: ["badge"],
+ files: [
+ {
+ path: "examples/badge-outline.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "badge-secondary",
+ type: "registry:example",
+ registryDependencies: ["badge"],
+ files: [
+ {
+ path: "examples/badge-secondary.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb-demo",
+ type: "registry:example",
+ registryDependencies: ["breadcrumb"],
+ files: [
+ {
+ path: "examples/breadcrumb-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb-separator",
+ type: "registry:example",
+ registryDependencies: ["breadcrumb"],
+ files: [
+ {
+ path: "examples/breadcrumb-separator.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb-dropdown",
+ type: "registry:example",
+ registryDependencies: ["breadcrumb"],
+ files: [
+ {
+ path: "examples/breadcrumb-dropdown.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb-ellipsis",
+ type: "registry:example",
+ registryDependencies: ["breadcrumb"],
+ files: [
+ {
+ path: "examples/breadcrumb-ellipsis.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb-link",
+ type: "registry:example",
+ registryDependencies: ["breadcrumb"],
+ files: [
+ {
+ path: "examples/breadcrumb-link.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb-responsive",
+ type: "registry:example",
+ registryDependencies: ["breadcrumb"],
+ files: [
+ {
+ path: "examples/breadcrumb-responsive.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-demo",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-default",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-default.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-secondary",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-secondary.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-destructive",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-destructive.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-outline",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-outline.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-ghost",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-ghost.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-link",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-link.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-with-icon",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-with-icon.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-loading",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-loading.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-icon",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-icon.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-as-child",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-as-child.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-rounded",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-rounded.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-size",
+ type: "registry:example",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "examples/button-size.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-demo",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-nested",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-nested.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-size",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-size.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-separator",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-separator.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-split",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-split.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-input",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-input.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-dropdown",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-dropdown.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-select",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-select.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-popover",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-popover.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-input-group",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-input-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "button-group-orientation",
+ type: "registry:example",
+ registryDependencies: ["button-group"],
+ files: [
+ {
+ path: "examples/button-group-orientation.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "calendar-demo",
+ type: "registry:example",
+ registryDependencies: ["calendar"],
+ files: [
+ {
+ path: "examples/calendar-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "card-demo",
+ type: "registry:example",
+ registryDependencies: ["card", "button", "switch"],
+ files: [
+ {
+ path: "examples/card-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "card-with-form",
+ type: "registry:example",
+ registryDependencies: ["button", "card", "input", "label", "select"],
+ files: [
+ {
+ path: "examples/card-with-form.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "carousel-demo",
+ type: "registry:example",
+ registryDependencies: ["carousel"],
+ files: [
+ {
+ path: "examples/carousel-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "carousel-size",
+ type: "registry:example",
+ registryDependencies: ["carousel"],
+ files: [
+ {
+ path: "examples/carousel-size.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "carousel-spacing",
+ type: "registry:example",
+ registryDependencies: ["carousel"],
+ files: [
+ {
+ path: "examples/carousel-spacing.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "carousel-orientation",
+ type: "registry:example",
+ registryDependencies: ["carousel"],
+ files: [
+ {
+ path: "examples/carousel-orientation.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "carousel-api",
+ type: "registry:example",
+ registryDependencies: ["carousel"],
+ files: [
+ {
+ path: "examples/carousel-api.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "carousel-plugin",
+ type: "registry:example",
+ registryDependencies: ["carousel"],
+ files: [
+ {
+ path: "examples/carousel-plugin.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "checkbox-demo",
+ type: "registry:example",
+ registryDependencies: ["checkbox"],
+ files: [
+ {
+ path: "examples/checkbox-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "checkbox-disabled",
+ type: "registry:example",
+ registryDependencies: ["checkbox"],
+ files: [
+ {
+ path: "examples/checkbox-disabled.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "checkbox-with-text",
+ type: "registry:example",
+ registryDependencies: ["checkbox"],
+ files: [
+ {
+ path: "examples/checkbox-with-text.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "collapsible-demo",
+ type: "registry:example",
+ registryDependencies: ["collapsible"],
+ files: [
+ {
+ path: "examples/collapsible-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "combobox-demo",
+ type: "registry:example",
+ registryDependencies: ["command"],
+ files: [
+ {
+ path: "examples/combobox-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "combobox-dropdown-menu",
+ type: "registry:example",
+ registryDependencies: ["command", "dropdown-menu", "button"],
+ files: [
+ {
+ path: "examples/combobox-dropdown-menu.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "combobox-popover",
+ type: "registry:example",
+ registryDependencies: ["combobox", "popover"],
+ files: [
+ {
+ path: "examples/combobox-popover.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "combobox-responsive",
+ type: "registry:example",
+ registryDependencies: ["combobox", "popover", "drawer"],
+ files: [
+ {
+ path: "examples/combobox-responsive.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "command-demo",
+ type: "registry:example",
+ registryDependencies: ["command"],
+ files: [
+ {
+ path: "examples/command-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "command-dialog",
+ type: "registry:example",
+ registryDependencies: ["command", "dialog"],
+ files: [
+ {
+ path: "examples/command-dialog.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "context-menu-demo",
+ type: "registry:example",
+ registryDependencies: ["context-menu"],
+ files: [
+ {
+ path: "examples/context-menu-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "data-table-demo",
+ type: "registry:example",
+ registryDependencies: ["data-table"],
+ files: [
+ {
+ path: "examples/data-table-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "date-picker-demo",
+ type: "registry:example",
+ registryDependencies: ["button", "calendar", "popover"],
+ files: [
+ {
+ path: "examples/date-picker-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["date-fns"],
+ },
+ {
+ name: "date-picker-with-presets",
+ type: "registry:example",
+ registryDependencies: ["button", "calendar", "popover", "select"],
+ files: [
+ {
+ path: "examples/date-picker-with-presets.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["date-fns"],
+ },
+ {
+ name: "date-picker-with-range",
+ type: "registry:example",
+ registryDependencies: ["button", "calendar", "popover"],
+ files: [
+ {
+ path: "examples/date-picker-with-range.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["date-fns"],
+ },
+ {
+ name: "dialog-demo",
+ type: "registry:example",
+ registryDependencies: ["dialog"],
+ files: [
+ {
+ path: "examples/dialog-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "dialog-close-button",
+ type: "registry:example",
+ registryDependencies: ["dialog", "button"],
+ files: [
+ {
+ path: "examples/dialog-close-button.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "drawer-demo",
+ type: "registry:example",
+ registryDependencies: ["drawer"],
+ files: [
+ {
+ path: "examples/drawer-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-demo",
+ type: "registry:example",
+ registryDependencies: ["empty", "button"],
+ files: [
+ {
+ path: "examples/empty-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-icon",
+ type: "registry:example",
+ registryDependencies: ["empty"],
+ files: [
+ {
+ path: "examples/empty-icon.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-avatar",
+ type: "registry:example",
+ registryDependencies: ["empty", "button", "avatar"],
+ files: [
+ {
+ path: "examples/empty-avatar.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-avatar-group",
+ type: "registry:example",
+ registryDependencies: ["empty", "button", "avatar"],
+ files: [
+ {
+ path: "examples/empty-avatar-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-input-group",
+ type: "registry:example",
+ registryDependencies: ["empty", "button", "input"],
+ files: [
+ {
+ path: "examples/empty-input-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-outline",
+ type: "registry:example",
+ registryDependencies: ["empty", "button"],
+ files: [
+ {
+ path: "examples/empty-outline.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "empty-background",
+ type: "registry:example",
+ registryDependencies: ["empty"],
+ files: [
+ {
+ path: "examples/empty-background.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-demo",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-input",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-input.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-textarea",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-textarea.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-fieldset",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-fieldset.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-radio",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-radio.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-checkbox",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-checkbox.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-switch",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-switch.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-slider",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-slider.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-select",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-select.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-choice-card",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-choice-card.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-group",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "field-responsive",
+ type: "registry:example",
+ registryDependencies: ["field"],
+ files: [
+ {
+ path: "examples/field-responsive.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "form-next-demo",
+ type: "registry:example",
+ registryDependencies: [
+ "field",
+ "input",
+ "textarea",
+ "button",
+ "card",
+ "spinner",
+ ],
+ files: [
+ {
+ path: "examples/form-next-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "form-next-complex",
+ type: "registry:example",
+ registryDependencies: [
+ "field",
+ "input",
+ "textarea",
+ "button",
+ "card",
+ "spinner",
+ "checkbox",
+ "dialog",
+ "radio-group",
+ "select",
+ "switch",
+ ],
+ files: [
+ {
+ path: "examples/form-next-complex.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "form-rhf-demo",
+ type: "registry:example",
+ registryDependencies: ["field", "input", "input-group", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-input",
+ type: "registry:example",
+ registryDependencies: ["field", "input", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-input.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-select",
+ type: "registry:example",
+ registryDependencies: ["field", "select", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-select.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-checkbox",
+ type: "registry:example",
+ registryDependencies: ["field", "checkbox", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-checkbox.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-switch",
+ type: "registry:example",
+ registryDependencies: ["field", "switch", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-switch.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-textarea",
+ type: "registry:example",
+ registryDependencies: ["field", "textarea", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-textarea.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-radiogroup",
+ type: "registry:example",
+ registryDependencies: ["field", "radio-group", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-radiogroup.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-array",
+ type: "registry:example",
+ registryDependencies: ["field", "input", "input-group", "button", "card"],
+ files: [
+ {
+ path: "examples/form-rhf-array.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-complex",
+ type: "registry:example",
+ registryDependencies: [
+ "field",
+ "button",
+ "card",
+ "checkbox",
+ "radio-group",
+ "select",
+ "switch",
+ ],
+ files: [
+ {
+ path: "examples/form-rhf-complex.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-rhf-password",
+ type: "registry:example",
+ registryDependencies: [
+ "field",
+ "input-group",
+ "progress",
+ "button",
+ "card",
+ ],
+ files: [
+ {
+ path: "examples/form-rhf-password.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["react-hook-form", "@hookform/resolvers", "zod"],
+ },
+ {
+ name: "form-tanstack-demo",
+ type: "registry:example",
+ registryDependencies: ["field", "input", "input-group", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-input",
+ type: "registry:example",
+ registryDependencies: ["field", "input", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-input.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-textarea",
+ type: "registry:example",
+ registryDependencies: ["field", "textarea", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-textarea.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-select",
+ type: "registry:example",
+ registryDependencies: ["field", "select", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-select.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-checkbox",
+ type: "registry:example",
+ registryDependencies: ["field", "checkbox", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-checkbox.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-switch",
+ type: "registry:example",
+ registryDependencies: ["field", "switch", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-switch.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-radiogroup",
+ type: "registry:example",
+ registryDependencies: ["field", "radio-group", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-radiogroup.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-array",
+ type: "registry:example",
+ registryDependencies: ["field", "input", "input-group", "button", "card"],
+ files: [
+ {
+ path: "examples/form-tanstack-array.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "form-tanstack-complex",
+ type: "registry:example",
+ registryDependencies: [
+ "field",
+ "button",
+ "card",
+ "checkbox",
+ "radio-group",
+ "select",
+ "switch",
+ ],
+ files: [
+ {
+ path: "examples/form-tanstack-complex.tsx",
+ type: "registry:example",
+ },
+ ],
+ dependencies: ["@tanstack/react-form", "zod"],
+ },
+ {
+ name: "drawer-dialog",
+ type: "registry:example",
+ registryDependencies: ["drawer", "dialog"],
+ files: [
+ {
+ path: "examples/drawer-dialog.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "dropdown-menu-demo",
+ type: "registry:example",
+ registryDependencies: ["dropdown-menu"],
+ files: [
+ {
+ path: "examples/dropdown-menu-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "dropdown-menu-checkboxes",
+ type: "registry:example",
+ registryDependencies: ["dropdown-menu", "checkbox"],
+ files: [
+ {
+ path: "examples/dropdown-menu-checkboxes.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "dropdown-menu-radio-group",
+ type: "registry:example",
+ registryDependencies: ["dropdown-menu", "radio-group"],
+ files: [
+ {
+ path: "examples/dropdown-menu-radio-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "dropdown-menu-dialog",
+ type: "registry:example",
+ registryDependencies: [
+ "dropdown-menu",
+ "dialog",
+ "button",
+ "input",
+ "label",
+ ],
+ files: [
+ {
+ path: "examples/dropdown-menu-dialog.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "hover-card-demo",
+ type: "registry:example",
+ registryDependencies: ["hover-card"],
+ files: [
+ {
+ path: "examples/hover-card-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-demo",
+ type: "registry:example",
+ registryDependencies: ["input"],
+ files: [
+ {
+ path: "examples/input-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-disabled",
+ type: "registry:example",
+ registryDependencies: ["input"],
+ files: [
+ {
+ path: "examples/input-disabled.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-file",
+ type: "registry:example",
+ registryDependencies: ["input"],
+ files: [
+ {
+ path: "examples/input-file.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-with-button",
+ type: "registry:example",
+ registryDependencies: ["input", "button"],
+ files: [
+ {
+ path: "examples/input-with-button.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-with-label",
+ type: "registry:example",
+ registryDependencies: ["input", "button", "label"],
+ files: [
+ {
+ path: "examples/input-with-label.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-with-text",
+ type: "registry:example",
+ registryDependencies: ["input", "button", "label"],
+ files: [
+ {
+ path: "examples/input-with-text.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-demo",
+ type: "registry:example",
+ registryDependencies: ["input-group"],
+ files: [
+ {
+ path: "examples/input-group-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-label",
+ type: "registry:example",
+ registryDependencies: ["input-group", "label"],
+ files: [
+ {
+ path: "examples/input-group-label.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-text",
+ type: "registry:example",
+ registryDependencies: ["input-group"],
+ files: [
+ {
+ path: "examples/input-group-text.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-tooltip",
+ type: "registry:example",
+ registryDependencies: ["input-group", "tooltip"],
+ files: [
+ {
+ path: "examples/input-group-tooltip.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-button",
+ type: "registry:example",
+ registryDependencies: ["input-group"],
+ files: [
+ {
+ path: "examples/input-group-button.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-button-group",
+ type: "registry:example",
+ registryDependencies: ["input-group", "button-group"],
+ files: [
+ {
+ path: "examples/input-group-button-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-dropdown",
+ type: "registry:example",
+ registryDependencies: ["input-group", "dropdown-menu", "button"],
+ files: [
+ {
+ path: "examples/input-group-dropdown.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-spinner",
+ type: "registry:example",
+ registryDependencies: ["input-group", "spinner"],
+ files: [
+ {
+ path: "examples/input-group-spinner.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-textarea",
+ type: "registry:example",
+ registryDependencies: ["input-group", "textarea"],
+ files: [
+ {
+ path: "examples/input-group-textarea.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-icon",
+ type: "registry:example",
+ registryDependencies: ["input-group"],
+ files: [
+ {
+ path: "examples/input-group-icon.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-group-custom",
+ type: "registry:example",
+ registryDependencies: [
+ "input-group",
+ "badge",
+ "progress",
+ "dropdown-menu",
+ "tooltip",
+ ],
+ files: [
+ {
+ path: "examples/input-group-custom.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-otp-demo",
+ type: "registry:example",
+ registryDependencies: ["input-otp"],
+ files: [
+ {
+ path: "examples/input-otp-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-otp-pattern",
+ type: "registry:example",
+ registryDependencies: ["input-otp"],
+ files: [
+ {
+ path: "examples/input-otp-pattern.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-otp-separator",
+ type: "registry:example",
+ registryDependencies: ["input-otp"],
+ files: [
+ {
+ path: "examples/input-otp-separator.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "input-otp-controlled",
+ type: "registry:example",
+ registryDependencies: ["input-otp"],
+ files: [
+ {
+ path: "examples/input-otp-controlled.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-demo",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-size",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-size.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-variant",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-variant.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-icon",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-icon.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-image",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-image.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-avatar",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-avatar.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-group",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-header",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-header.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-dropdown",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-dropdown.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "item-link",
+ type: "registry:example",
+ registryDependencies: ["item"],
+ files: [
+ {
+ path: "examples/item-link.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "kbd-demo",
+ type: "registry:example",
+ registryDependencies: ["kbd"],
+ files: [
+ {
+ path: "examples/kbd-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "kbd-tooltip",
+ type: "registry:example",
+ registryDependencies: ["kbd", "tooltip", "button"],
+ files: [
+ {
+ path: "examples/kbd-tooltip.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "kbd-input-group",
+ type: "registry:example",
+ registryDependencies: ["kbd", "input", "button", "label"],
+ files: [
+ {
+ path: "examples/kbd-input-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "kbd-button",
+ type: "registry:example",
+ registryDependencies: ["kbd", "button"],
+ files: [
+ {
+ path: "examples/kbd-button.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "kbd-group",
+ type: "registry:example",
+ registryDependencies: ["kbd"],
+ files: [
+ {
+ path: "examples/kbd-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "label-demo",
+ type: "registry:example",
+ registryDependencies: ["label"],
+ files: [
+ {
+ path: "examples/label-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "menubar-demo",
+ type: "registry:example",
+ registryDependencies: ["menubar"],
+ files: [
+ {
+ path: "examples/menubar-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "navigation-menu-demo",
+ type: "registry:example",
+ registryDependencies: ["navigation-menu"],
+ files: [
+ {
+ path: "examples/navigation-menu-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "native-select-demo",
+ type: "registry:example",
+ registryDependencies: ["native-select"],
+ files: [
+ {
+ path: "examples/native-select-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "native-select-groups",
+ type: "registry:example",
+ registryDependencies: ["native-select"],
+ files: [
+ {
+ path: "examples/native-select-groups.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "native-select-disabled",
+ type: "registry:example",
+ registryDependencies: ["native-select"],
+ files: [
+ {
+ path: "examples/native-select-disabled.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "native-select-invalid",
+ type: "registry:example",
+ registryDependencies: ["native-select"],
+ files: [
+ {
+ path: "examples/native-select-invalid.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "pagination-demo",
+ type: "registry:example",
+ registryDependencies: ["pagination"],
+ files: [
+ {
+ path: "examples/pagination-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "popover-demo",
+ type: "registry:example",
+ registryDependencies: ["popover"],
+ files: [
+ {
+ path: "examples/popover-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "progress-demo",
+ type: "registry:example",
+ registryDependencies: ["progress"],
+ files: [
+ {
+ path: "examples/progress-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "radio-group-demo",
+ type: "registry:example",
+ registryDependencies: ["radio-group"],
+ files: [
+ {
+ path: "examples/radio-group-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "resizable-demo",
+ type: "registry:example",
+ registryDependencies: ["resizable"],
+ files: [
+ {
+ path: "examples/resizable-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "resizable-demo-with-handle",
+ type: "registry:example",
+ registryDependencies: ["resizable"],
+ files: [
+ {
+ path: "examples/resizable-demo-with-handle.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "resizable-vertical",
+ type: "registry:example",
+ registryDependencies: ["resizable"],
+ files: [
+ {
+ path: "examples/resizable-vertical.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "resizable-handle",
+ type: "registry:example",
+ registryDependencies: ["resizable"],
+ files: [
+ {
+ path: "examples/resizable-handle.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "scroll-area-demo",
+ type: "registry:example",
+ registryDependencies: ["scroll-area"],
+ files: [
+ {
+ path: "examples/scroll-area-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "scroll-area-horizontal-demo",
+ type: "registry:example",
+ registryDependencies: ["scroll-area"],
+ files: [
+ {
+ path: "examples/scroll-area-horizontal-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "select-demo",
+ type: "registry:example",
+ registryDependencies: ["select"],
+ files: [
+ {
+ path: "examples/select-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "select-scrollable",
+ type: "registry:example",
+ registryDependencies: ["select"],
+ files: [
+ {
+ path: "examples/select-scrollable.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "separator-demo",
+ type: "registry:example",
+ registryDependencies: ["separator"],
+ files: [
+ {
+ path: "examples/separator-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sheet-demo",
+ type: "registry:example",
+ registryDependencies: ["sheet"],
+ files: [
+ {
+ path: "examples/sheet-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sheet-side",
+ type: "registry:example",
+ registryDependencies: ["sheet"],
+ files: [
+ {
+ path: "examples/sheet-side.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "skeleton-demo",
+ type: "registry:example",
+ registryDependencies: ["skeleton"],
+ files: [
+ {
+ path: "examples/skeleton-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "skeleton-card",
+ type: "registry:example",
+ registryDependencies: ["skeleton"],
+ files: [
+ {
+ path: "examples/skeleton-card.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "slider-demo",
+ type: "registry:example",
+ registryDependencies: ["slider"],
+ files: [
+ {
+ path: "examples/slider-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sonner-demo",
+ type: "registry:example",
+ registryDependencies: ["sonner"],
+ files: [
+ {
+ path: "examples/sonner-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "sonner-types",
+ type: "registry:example",
+ registryDependencies: ["sonner"],
+ files: [
+ {
+ path: "examples/sonner-types.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-demo",
+ type: "registry:example",
+ registryDependencies: ["spinner"],
+ files: [
+ {
+ path: "examples/spinner-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-basic",
+ type: "registry:example",
+ registryDependencies: ["spinner", "button"],
+ files: [
+ {
+ path: "examples/spinner-basic.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-button",
+ type: "registry:example",
+ registryDependencies: ["spinner", "button"],
+ files: [
+ {
+ path: "examples/spinner-button.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-badge",
+ type: "registry:example",
+ registryDependencies: ["spinner", "badge"],
+ files: [
+ {
+ path: "examples/spinner-badge.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-input-group",
+ type: "registry:example",
+ registryDependencies: ["spinner", "button", "input"],
+ files: [
+ {
+ path: "examples/spinner-input-group.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-empty",
+ type: "registry:example",
+ registryDependencies: ["spinner"],
+ files: [
+ {
+ path: "examples/spinner-empty.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-color",
+ type: "registry:example",
+ registryDependencies: ["spinner"],
+ files: [
+ {
+ path: "examples/spinner-color.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-custom",
+ type: "registry:example",
+ registryDependencies: ["spinner"],
+ files: [
+ {
+ path: "examples/spinner-custom.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-size",
+ type: "registry:example",
+ registryDependencies: ["spinner"],
+ files: [
+ {
+ path: "examples/spinner-size.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "spinner-item",
+ type: "registry:example",
+ registryDependencies: ["spinner"],
+ files: [
+ {
+ path: "examples/spinner-item.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "switch-demo",
+ type: "registry:example",
+ registryDependencies: ["switch"],
+ files: [
+ {
+ path: "examples/switch-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "table-demo",
+ type: "registry:example",
+ registryDependencies: ["table"],
+ files: [
+ {
+ path: "examples/table-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "tabs-demo",
+ type: "registry:example",
+ registryDependencies: ["tabs"],
+ files: [
+ {
+ path: "examples/tabs-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "textarea-demo",
+ type: "registry:example",
+ registryDependencies: ["textarea"],
+ files: [
+ {
+ path: "examples/textarea-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "textarea-disabled",
+ type: "registry:example",
+ registryDependencies: ["textarea"],
+ files: [
+ {
+ path: "examples/textarea-disabled.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "textarea-with-button",
+ type: "registry:example",
+ registryDependencies: ["textarea", "button"],
+ files: [
+ {
+ path: "examples/textarea-with-button.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "textarea-with-label",
+ type: "registry:example",
+ registryDependencies: ["textarea", "label"],
+ files: [
+ {
+ path: "examples/textarea-with-label.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "textarea-with-text",
+ type: "registry:example",
+ registryDependencies: ["textarea", "label"],
+ files: [
+ {
+ path: "examples/textarea-with-text.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toast-demo",
+ type: "registry:example",
+ registryDependencies: ["toast"],
+ files: [
+ {
+ path: "examples/toast-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toast-destructive",
+ type: "registry:example",
+ registryDependencies: ["toast"],
+ files: [
+ {
+ path: "examples/toast-destructive.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toast-simple",
+ type: "registry:example",
+ registryDependencies: ["toast"],
+ files: [
+ {
+ path: "examples/toast-simple.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toast-with-action",
+ type: "registry:example",
+ registryDependencies: ["toast"],
+ files: [
+ {
+ path: "examples/toast-with-action.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toast-with-title",
+ type: "registry:example",
+ registryDependencies: ["toast"],
+ files: [
+ {
+ path: "examples/toast-with-title.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-demo",
+ type: "registry:example",
+ registryDependencies: ["toggle-group"],
+ files: [
+ {
+ path: "examples/toggle-group-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-disabled",
+ type: "registry:example",
+ registryDependencies: ["toggle-group"],
+ files: [
+ {
+ path: "examples/toggle-group-disabled.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-lg",
+ type: "registry:example",
+ registryDependencies: ["toggle-group"],
+ files: [
+ {
+ path: "examples/toggle-group-lg.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-outline",
+ type: "registry:example",
+ registryDependencies: ["toggle-group"],
+ files: [
+ {
+ path: "examples/toggle-group-outline.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-sm",
+ type: "registry:example",
+ registryDependencies: ["toggle-group"],
+ files: [
+ {
+ path: "examples/toggle-group-sm.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-single",
+ type: "registry:example",
+ registryDependencies: ["toggle-group"],
+ files: [
+ {
+ path: "examples/toggle-group-single.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-group-spacing",
+ type: "registry:example",
+ registryDependencies: ["toggle-group"],
+ files: [
+ {
+ path: "examples/toggle-group-spacing.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-demo",
+ type: "registry:example",
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "examples/toggle-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-disabled",
+ type: "registry:example",
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "examples/toggle-disabled.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-lg",
+ type: "registry:example",
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "examples/toggle-lg.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-outline",
+ type: "registry:example",
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "examples/toggle-outline.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-sm",
+ type: "registry:example",
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "examples/toggle-sm.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "toggle-with-text",
+ type: "registry:example",
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "examples/toggle-with-text.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "tooltip-demo",
+ type: "registry:example",
+ registryDependencies: ["tooltip"],
+ files: [
+ {
+ path: "examples/tooltip-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-blockquote",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-blockquote.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-demo",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-h1",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-h1.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-h2",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-h2.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-h3",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-h3.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-h4",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-h4.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-inline-code",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-inline-code.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-large",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-large.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-lead",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-lead.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-list",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-list.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-muted",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-muted.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-p",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-p.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-small",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-small.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "typography-table",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/typography-table.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "mode-toggle",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/mode-toggle.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "chart-bar-demo",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/chart-bar-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "chart-bar-demo-grid",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/chart-bar-demo-grid.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "chart-bar-demo-axis",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/chart-bar-demo-axis.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "chart-bar-demo-tooltip",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/chart-bar-demo-tooltip.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "chart-bar-demo-legend",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/chart-bar-demo-legend.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "chart-tooltip-demo",
+ type: "registry:example",
+ files: [
+ {
+ path: "examples/chart-tooltip-demo.tsx",
+ type: "registry:example",
+ },
+ ],
+ },
+ {
+ name: "calendar-hijri",
+ description: "A Persian calendar.",
+ type: "registry:example",
+ registryDependencies: ["calendar"],
+ files: [
+ {
+ path: "examples/calendar-hijri.tsx",
+ type: "registry:example",
+ },
+ ],
+ categories: ["calendar", "date"],
+ meta: {
+ iframeHeight: "600px",
+ container:
+ "w-full bg-surface min-h-svh flex px-4 py-12 items-start md:py-20 justify-center min-w-0",
+ mobile: "component",
+ },
+ },
+]
diff --git a/apps/ui/src/registry/new-york-v4/examples/accordion-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/accordion-demo.tsx
new file mode 100644
index 0000000..5ced7f9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/accordion-demo.tsx
@@ -0,0 +1,61 @@
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@/registry/new-york-v4/ui/accordion"
+
+export default function AccordionDemo() {
+ return (
+
+
+ Product Information
+
+
+ Our flagship product combines cutting-edge technology with sleek
+ design. Built with premium materials, it offers unparalleled
+ performance and reliability.
+
+
+ Key features include advanced processing capabilities, and an
+ intuitive user interface designed for both beginners and experts.
+
+
+
+
+ Shipping Details
+
+
+ We offer worldwide shipping through trusted courier partners.
+ Standard delivery takes 3-5 business days, while express shipping
+ ensures delivery within 1-2 business days.
+
+
+ All orders are carefully packaged and fully insured. Track your
+ shipment in real-time through our dedicated tracking portal.
+
+
+
+
+ Return Policy
+
+
+ We stand behind our products with a comprehensive 30-day return
+ policy. If you're not completely satisfied, simply return the
+ item in its original condition.
+
+
+ Our hassle-free return process includes free return shipping and
+ full refunds processed within 48 hours of receiving the returned
+ item.
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/alert-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/alert-demo.tsx
new file mode 100644
index 0000000..049109f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/alert-demo.tsx
@@ -0,0 +1,39 @@
+import { AlertCircleIcon, CheckCircle2Icon, PopcornIcon } from "lucide-react"
+
+import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
+} from "@/registry/new-york-v4/ui/alert"
+
+export default function AlertDemo() {
+ return (
+
+
+
+ Success! Your changes have been saved
+
+ This is an alert with icon, title and description.
+
+
+
+
+
+ This Alert has a title and an icon. No description.
+
+
+
+
+ Unable to process your payment.
+
+ Please verify your billing information and try again.
+
+ Check your card details
+ Ensure sufficient funds
+ Verify billing address
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/alert-destructive.tsx b/apps/ui/src/registry/new-york-v4/examples/alert-destructive.tsx
new file mode 100644
index 0000000..c709afa
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/alert-destructive.tsx
@@ -0,0 +1,19 @@
+import { AlertCircleIcon } from "lucide-react"
+
+import {
+ Alert,
+ AlertDescription,
+ AlertTitle,
+} from "@/registry/new-york-v4/ui/alert"
+
+export default function AlertDestructive() {
+ return (
+
+
+ Error
+
+ Your session has expired. Please log in again.
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/alert-dialog-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/alert-dialog-demo.tsx
new file mode 100644
index 0000000..86ebbfc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/alert-dialog-demo.tsx
@@ -0,0 +1,35 @@
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "@/registry/new-york-v4/ui/alert-dialog"
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function AlertDialogDemo() {
+ return (
+
+
+ Show Dialog
+
+
+
+ Are you absolutely sure?
+
+ This action cannot be undone. This will permanently delete your
+ account and remove your data from our servers.
+
+
+
+ Cancel
+ Continue
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/aspect-ratio-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/aspect-ratio-demo.tsx
new file mode 100644
index 0000000..ff2d53d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/aspect-ratio-demo.tsx
@@ -0,0 +1,16 @@
+import Image from "next/image"
+
+import { AspectRatio } from "@/registry/new-york-v4/ui/aspect-ratio"
+
+export default function AspectRatioDemo() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/avatar-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/avatar-demo.tsx
new file mode 100644
index 0000000..cd6d1b9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/avatar-demo.tsx
@@ -0,0 +1,43 @@
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+
+export default function AvatarDemo() {
+ return (
+
+
+
+ CN
+
+
+
+ ER
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/badge-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/badge-demo.tsx
new file mode 100644
index 0000000..9af75e6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/badge-demo.tsx
@@ -0,0 +1,40 @@
+import { AlertCircleIcon, BadgeCheckIcon, CheckIcon } from "lucide-react"
+
+import { Badge } from "@/registry/new-york-v4/ui/badge"
+
+export default function BadgeDemo() {
+ return (
+
+
+ Badge
+ Secondary
+ Destructive
+ Outline
+
+
+
+
+ Verified
+
+
+ 8
+
+
+ 99
+
+
+ 20+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/badge-destructive.tsx b/apps/ui/src/registry/new-york-v4/examples/badge-destructive.tsx
new file mode 100644
index 0000000..baf5547
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/badge-destructive.tsx
@@ -0,0 +1,5 @@
+import { Badge } from "@/registry/new-york-v4/ui/badge"
+
+export default function BadgeDestructive() {
+ return Destructive
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/badge-outline.tsx b/apps/ui/src/registry/new-york-v4/examples/badge-outline.tsx
new file mode 100644
index 0000000..9faa91a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/badge-outline.tsx
@@ -0,0 +1,5 @@
+import { Badge } from "@/registry/new-york-v4/ui/badge"
+
+export default function BadgeOutline() {
+ return Outline
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/badge-secondary.tsx b/apps/ui/src/registry/new-york-v4/examples/badge-secondary.tsx
new file mode 100644
index 0000000..3ec92cd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/badge-secondary.tsx
@@ -0,0 +1,5 @@
+import { Badge } from "@/registry/new-york-v4/ui/badge"
+
+export default function BadgeSecondary() {
+ return Secondary
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/breadcrumb-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-demo.tsx
new file mode 100644
index 0000000..78aab4b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-demo.tsx
@@ -0,0 +1,55 @@
+import Link from "next/link"
+
+import {
+ Breadcrumb,
+ BreadcrumbEllipsis,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+export default function BreadcrumbDemo() {
+ return (
+
+
+
+
+ Home
+
+
+
+
+
+
+
+ Toggle menu
+
+
+ Documentation
+ Themes
+ GitHub
+
+
+
+
+
+
+ Components
+
+
+
+
+ Breadcrumb
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/breadcrumb-dropdown.tsx b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-dropdown.tsx
new file mode 100644
index 0000000..7803723
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-dropdown.tsx
@@ -0,0 +1,53 @@
+import Link from "next/link"
+import { ChevronDownIcon, SlashIcon } from "lucide-react"
+
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+export default function BreadcrumbWithDropdown() {
+ return (
+
+
+
+
+ Home
+
+
+
+
+
+
+
+
+ Components
+
+
+
+ Documentation
+ Themes
+ GitHub
+
+
+
+
+
+
+
+ Breadcrumb
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/breadcrumb-ellipsis.tsx b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-ellipsis.tsx
new file mode 100644
index 0000000..824e46d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-ellipsis.tsx
@@ -0,0 +1,39 @@
+import Link from "next/link"
+
+import {
+ Breadcrumb,
+ BreadcrumbEllipsis,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+
+export default function BreadcrumbCollapsed() {
+ return (
+
+
+
+
+ Home
+
+
+
+
+
+
+
+
+
+ Components
+
+
+
+
+ Breadcrumb
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/breadcrumb-link.tsx b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-link.tsx
new file mode 100644
index 0000000..fdd66b6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-link.tsx
@@ -0,0 +1,34 @@
+import Link from "next/link"
+
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+
+export default function BreadcrumbWithCustomSeparator() {
+ return (
+
+
+
+
+ Home
+
+
+
+
+
+ Components
+
+
+
+
+ Breadcrumb
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/breadcrumb-responsive.tsx b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-responsive.tsx
new file mode 100644
index 0000000..30a3f60
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-responsive.tsx
@@ -0,0 +1,134 @@
+
+import * as React from "react"
+import Link from "next/link"
+
+import { useMediaQuery } from "@/hooks/use-media-query"
+import {
+ Breadcrumb,
+ BreadcrumbEllipsis,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/registry/new-york-v4/ui/drawer"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+const items = [
+ { href: "#", label: "Home" },
+ { href: "#", label: "Documentation" },
+ { href: "#", label: "Build Your Application" },
+ { href: "#", label: "Data Fetching" },
+ { label: "Caching and Revalidating" },
+]
+
+const ITEMS_TO_DISPLAY = 3
+
+export default function BreadcrumbResponsive() {
+ const [open, setOpen] = React.useState(false)
+ const isDesktop = useMediaQuery("(min-width: 768px)")
+
+ return (
+
+
+
+
+ {items[0].label}
+
+
+
+ {items.length > ITEMS_TO_DISPLAY ? (
+ <>
+
+ {isDesktop ? (
+
+
+
+
+
+ {items.slice(1, -2).map((item, index) => (
+
+
+ {item.label}
+
+
+ ))}
+
+
+ ) : (
+
+
+
+
+
+
+ Navigate to
+
+ Select a page to navigate to.
+
+
+
+ {items.slice(1, -2).map((item, index) => (
+
+ {item.label}
+
+ ))}
+
+
+
+ Close
+
+
+
+
+ )}
+
+
+ >
+ ) : null}
+ {items.slice(-ITEMS_TO_DISPLAY + 1).map((item, index) => (
+
+ {item.href ? (
+ <>
+
+ {item.label}
+
+
+ >
+ ) : (
+
+ {item.label}
+
+ )}
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/breadcrumb-separator.tsx b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-separator.tsx
new file mode 100644
index 0000000..5983fea
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/breadcrumb-separator.tsx
@@ -0,0 +1,39 @@
+import Link from "next/link"
+import { SlashIcon } from "lucide-react"
+
+import {
+ Breadcrumb,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbList,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+} from "@/registry/new-york-v4/ui/breadcrumb"
+
+export default function BreadcrumbWithCustomSeparator() {
+ return (
+
+
+
+
+ Home
+
+
+
+
+
+
+
+ Components
+
+
+
+
+
+
+ Breadcrumb
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-as-child.tsx b/apps/ui/src/registry/new-york-v4/examples/button-as-child.tsx
new file mode 100644
index 0000000..a656c30
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-as-child.tsx
@@ -0,0 +1,11 @@
+import Link from "next/link"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonAsChild() {
+ return (
+
+ Login
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-default.tsx b/apps/ui/src/registry/new-york-v4/examples/button-default.tsx
new file mode 100644
index 0000000..de85f64
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-default.tsx
@@ -0,0 +1,5 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonDefault() {
+ return Button
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/button-demo.tsx
new file mode 100644
index 0000000..ed864cc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-demo.tsx
@@ -0,0 +1,14 @@
+import { ArrowUpIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonDemo() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-destructive.tsx b/apps/ui/src/registry/new-york-v4/examples/button-destructive.tsx
new file mode 100644
index 0000000..df78eb9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-destructive.tsx
@@ -0,0 +1,5 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonDestructive() {
+ return Destructive
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-ghost.tsx b/apps/ui/src/registry/new-york-v4/examples/button-ghost.tsx
new file mode 100644
index 0000000..d11b6f6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-ghost.tsx
@@ -0,0 +1,5 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonGhost() {
+ return Ghost
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-demo.tsx
new file mode 100644
index 0000000..a4b88e7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-demo.tsx
@@ -0,0 +1,113 @@
+
+import * as React from "react"
+import {
+ ArchiveIcon,
+ ArrowLeftIcon,
+ CalendarPlusIcon,
+ ClockIcon,
+ ListFilterIcon,
+ MailCheckIcon,
+ MoreHorizontalIcon,
+ TagIcon,
+ Trash2Icon,
+} from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+export default function ButtonGroupDemo() {
+ const [label, setLabel] = React.useState("personal")
+
+ return (
+
+
+
+
+
+
+
+ Archive
+ Report
+
+
+ Snooze
+
+
+
+
+
+
+
+
+
+
+ Mark as Read
+
+
+
+ Archive
+
+
+
+
+
+
+ Snooze
+
+
+
+ Add to Calendar
+
+
+
+ Add to List
+
+
+
+
+ Label As...
+
+
+
+
+ Personal
+
+
+ Work
+
+
+ Other
+
+
+
+
+
+
+
+
+
+ Trash
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-dropdown.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-dropdown.tsx
new file mode 100644
index 0000000..f916133
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-dropdown.tsx
@@ -0,0 +1,72 @@
+
+import {
+ AlertTriangleIcon,
+ CheckIcon,
+ ChevronDownIcon,
+ CopyIcon,
+ ShareIcon,
+ TrashIcon,
+ UserRoundXIcon,
+ VolumeOffIcon,
+} from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+export default function ButtonGroupDropdown() {
+ return (
+
+ Follow
+
+
+
+
+
+
+
+
+
+
+ Mute Conversation
+
+
+
+ Mark as Read
+
+
+
+ Report Conversation
+
+
+
+ Block User
+
+
+
+ Share Conversation
+
+
+
+ Copy Conversation
+
+
+
+
+
+
+ Delete Conversation
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-input-group.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-input-group.tsx
new file mode 100644
index 0000000..40112bf
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-input-group.tsx
@@ -0,0 +1,57 @@
+
+import * as React from "react"
+import { AudioLinesIcon, PlusIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/new-york-v4/ui/tooltip"
+
+export default function ButtonGroupInputGroup() {
+ const [voiceEnabled, setVoiceEnabled] = React.useState(false)
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ setVoiceEnabled(!voiceEnabled)}
+ size="icon-xs"
+ data-active={voiceEnabled}
+ className="data-[active=true]:bg-orange-100 data-[active=true]:text-orange-700 dark:data-[active=true]:bg-orange-800 dark:data-[active=true]:text-orange-100"
+ aria-pressed={voiceEnabled}
+ >
+
+
+
+ Voice Mode
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-input.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-input.tsx
new file mode 100644
index 0000000..73426d5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-input.tsx
@@ -0,0 +1,16 @@
+import { SearchIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export default function ButtonGroupInput() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-nested.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-nested.tsx
new file mode 100644
index 0000000..d3d95dc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-nested.tsx
@@ -0,0 +1,37 @@
+
+import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+
+export default function ButtonGroupNested() {
+ return (
+
+
+
+ 1
+
+
+ 2
+
+
+ 3
+
+
+ 4
+
+
+ 5
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-orientation.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-orientation.tsx
new file mode 100644
index 0000000..52aa1b7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-orientation.tsx
@@ -0,0 +1,21 @@
+import { MinusIcon, PlusIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+
+export default function ButtonGroupOrientation() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-popover.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-popover.tsx
new file mode 100644
index 0000000..ef9790b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-popover.tsx
@@ -0,0 +1,45 @@
+import { BotIcon, ChevronDownIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function ButtonGroupPopover() {
+ return (
+
+
+ Copilot
+
+
+
+
+
+
+
+
+
+
+
+
+
Start a new task with Copilot
+
+ Describe your task in natural language. Copilot will work in the
+ background and open a pull request for your review.
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-select.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-select.tsx
new file mode 100644
index 0000000..0eabc5f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-select.tsx
@@ -0,0 +1,56 @@
+
+import * as React from "react"
+import { ArrowRightIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+} from "@/registry/new-york-v4/ui/select"
+
+const CURRENCIES = [
+ {
+ value: "$",
+ label: "US Dollar",
+ },
+ {
+ value: "€",
+ label: "Euro",
+ },
+ {
+ value: "£",
+ label: "British Pound",
+ },
+]
+
+export default function ButtonGroupSelect() {
+ const [currency, setCurrency] = React.useState("$")
+
+ return (
+
+
+
+ {currency}
+
+ {CURRENCIES.map((currency) => (
+
+ {currency.value}{" "}
+ {currency.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-separator.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-separator.tsx
new file mode 100644
index 0000000..05d12a7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-separator.tsx
@@ -0,0 +1,19 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ ButtonGroup,
+ ButtonGroupSeparator,
+} from "@/registry/new-york-v4/ui/button-group"
+
+export default function ButtonGroupSeparatorDemo() {
+ return (
+
+
+ Copy
+
+
+
+ Paste
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-size.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-size.tsx
new file mode 100644
index 0000000..28aa4eb
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-size.tsx
@@ -0,0 +1,47 @@
+import { PlusIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+
+export default function ButtonGroupSize() {
+ return (
+
+
+
+ Small
+
+
+ Button
+
+
+ Group
+
+
+
+
+
+
+ Default
+ Button
+ Group
+
+
+
+
+
+
+ Large
+
+
+ Button
+
+
+ Group
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-group-split.tsx b/apps/ui/src/registry/new-york-v4/examples/button-group-split.tsx
new file mode 100644
index 0000000..e5d58f2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-group-split.tsx
@@ -0,0 +1,19 @@
+import { IconPlus } from "@tabler/icons-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ ButtonGroup,
+ ButtonGroupSeparator,
+} from "@/registry/new-york-v4/ui/button-group"
+
+export default function ButtonGroupSplit() {
+ return (
+
+ Button
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-icon.tsx b/apps/ui/src/registry/new-york-v4/examples/button-icon.tsx
new file mode 100644
index 0000000..d403fa6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-icon.tsx
@@ -0,0 +1,11 @@
+import { CircleFadingArrowUpIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonIcon() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-link.tsx b/apps/ui/src/registry/new-york-v4/examples/button-link.tsx
new file mode 100644
index 0000000..97bccaa
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-link.tsx
@@ -0,0 +1,5 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonLink() {
+ return Link
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-loading.tsx b/apps/ui/src/registry/new-york-v4/examples/button-loading.tsx
new file mode 100644
index 0000000..f201d30
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-loading.tsx
@@ -0,0 +1,11 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function ButtonLoading() {
+ return (
+
+
+ Submit
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-outline.tsx b/apps/ui/src/registry/new-york-v4/examples/button-outline.tsx
new file mode 100644
index 0000000..1477d86
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-outline.tsx
@@ -0,0 +1,5 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonOutline() {
+ return Outline
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-rounded.tsx b/apps/ui/src/registry/new-york-v4/examples/button-rounded.tsx
new file mode 100644
index 0000000..aa81e09
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-rounded.tsx
@@ -0,0 +1,13 @@
+import { ArrowUpIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonRounded() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-secondary.tsx b/apps/ui/src/registry/new-york-v4/examples/button-secondary.tsx
new file mode 100644
index 0000000..bea0cff
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-secondary.tsx
@@ -0,0 +1,5 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonSecondary() {
+ return Secondary
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-size.tsx b/apps/ui/src/registry/new-york-v4/examples/button-size.tsx
new file mode 100644
index 0000000..89eda1d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-size.tsx
@@ -0,0 +1,32 @@
+import { ArrowUpRightIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonSize() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/button-with-icon.tsx b/apps/ui/src/registry/new-york-v4/examples/button-with-icon.tsx
new file mode 100644
index 0000000..f9ab8f5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/button-with-icon.tsx
@@ -0,0 +1,11 @@
+import { IconGitBranch } from "@tabler/icons-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function ButtonWithIcon() {
+ return (
+
+ New Branch
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/calendar-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/calendar-demo.tsx
new file mode 100644
index 0000000..76b0222
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/calendar-demo.tsx
@@ -0,0 +1,18 @@
+
+import * as React from "react"
+
+import { Calendar } from "@/registry/new-york-v4/ui/calendar"
+
+export default function CalendarDemo() {
+ const [date, setDate] = React.useState(new Date())
+
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/calendar-hijri.tsx b/apps/ui/src/registry/new-york-v4/examples/calendar-hijri.tsx
new file mode 100644
index 0000000..a67ba45
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/calendar-hijri.tsx
@@ -0,0 +1,233 @@
+
+import * as React from "react"
+import { Vazirmatn } from "next/font/google"
+import {
+ ChevronDownIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon,
+} from "lucide-react"
+import { getDefaultClassNames, type DayButton } from "react-day-picker"
+import { DayPicker } from "react-day-picker/persian"
+
+import { cn } from "@/lib/utils"
+import { Button, buttonVariants } from "@/registry/new-york-v4/ui/button"
+
+const vazirmatn = Vazirmatn({ subsets: ["arabic"] })
+
+export default function CalendarHijri() {
+ const [date, setDate] = React.useState(
+ new Date(2025, 5, 12)
+ )
+
+ return (
+
+
+
+ )
+}
+
+// ----------------------------------------------------------------------------
+// The code below is for this example only.
+// For your own calendar, you would edit the calendar.tsx component directly.
+// ----------------------------------------------------------------------------
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = "label",
+ buttonVariant = "ghost",
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps["variant"]
+}) {
+ const defaultClassNames = getDefaultClassNames()
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className
+ )}
+ captionLayout={captionLayout}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString("default", { month: "short" }),
+ ...formatters,
+ }}
+ classNames={{
+ root: cn("w-fit", defaultClassNames.root),
+ months: cn(
+ "relative flex flex-col gap-4 md:flex-row",
+ defaultClassNames.months
+ ),
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
+ nav: cn(
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
+ defaultClassNames.nav
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_previous
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_next
+ ),
+ month_caption: cn(
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
+ defaultClassNames.month_caption
+ ),
+ dropdowns: cn(
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
+ defaultClassNames.dropdowns
+ ),
+ dropdown_root: cn(
+ "relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
+ defaultClassNames.dropdown_root
+ ),
+ dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown),
+ caption_label: cn(
+ "font-medium select-none",
+ captionLayout === "label"
+ ? "text-sm"
+ : "flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
+ defaultClassNames.caption_label
+ ),
+ table: "w-full border-collapse",
+ weekdays: cn("flex", defaultClassNames.weekdays),
+ weekday: cn(
+ "flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
+ defaultClassNames.weekday
+ ),
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
+ week_number_header: cn(
+ "w-(--cell-size) select-none",
+ defaultClassNames.week_number_header
+ ),
+ week_number: cn(
+ "text-[0.8rem] text-muted-foreground select-none",
+ defaultClassNames.week_number
+ ),
+ day: cn(
+ "group/day relative aspect-square h-full w-full p-0 text-center select-none [&:first-child[data-selected=true]_button]:rounded-l-md [&:last-child[data-selected=true]_button]:rounded-r-md",
+ defaultClassNames.day
+ ),
+ range_start: cn(
+ "rounded-l-md bg-accent",
+ defaultClassNames.range_start
+ ),
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
+ range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
+ today: cn(
+ "rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
+ defaultClassNames.today
+ ),
+ outside: cn(
+ "text-muted-foreground aria-selected:text-muted-foreground",
+ defaultClassNames.outside
+ ),
+ disabled: cn(
+ "text-muted-foreground opacity-50",
+ defaultClassNames.disabled
+ ),
+ hidden: cn("invisible", defaultClassNames.hidden),
+ ...classNames,
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ )
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === "left") {
+ return (
+
+ )
+ }
+
+ if (orientation === "right") {
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+ },
+ DayButton: CalendarDayButton,
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+
+ )
+ },
+ ...components,
+ }}
+ {...props}
+ />
+ )
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ ...props
+}: React.ComponentProps) {
+ const defaultClassNames = getDefaultClassNames()
+
+ const ref = React.useRef(null)
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus()
+ }, [modifiers.focused])
+
+ return (
+ span]:text-xs [&>span]:opacity-70",
+ defaultClassNames.day,
+ className
+ )}
+ {...props}
+ />
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/card-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/card-demo.tsx
new file mode 100644
index 0000000..9e50eb1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/card-demo.tsx
@@ -0,0 +1,63 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardAction,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function CardDemo() {
+ return (
+
+
+ Login to your account
+
+ Enter your email below to login to your account
+
+
+ Sign Up
+
+
+
+
+
+
+
+ Login
+
+
+ Login with Google
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/card-with-form.tsx b/apps/ui/src/registry/new-york-v4/examples/card-with-form.tsx
new file mode 100644
index 0000000..5655896
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/card-with-form.tsx
@@ -0,0 +1,59 @@
+import * as React from "react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+
+export default function CardWithForm() {
+ return (
+
+
+ Create project
+ Deploy your new project in one-click.
+
+
+
+
+
+ Cancel
+ Deploy
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/carousel-api.tsx b/apps/ui/src/registry/new-york-v4/examples/carousel-api.tsx
new file mode 100644
index 0000000..b416ac1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/carousel-api.tsx
@@ -0,0 +1,54 @@
+
+import * as React from "react"
+
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+ type CarouselApi,
+} from "@/registry/new-york-v4/ui/carousel"
+
+export default function CarouselDApiDemo() {
+ const [api, setApi] = React.useState()
+ const [current, setCurrent] = React.useState(0)
+ const [count, setCount] = React.useState(0)
+
+ React.useEffect(() => {
+ if (!api) {
+ return
+ }
+
+ setCount(api.scrollSnapList().length)
+ setCurrent(api.selectedScrollSnap() + 1)
+
+ api.on("select", () => {
+ setCurrent(api.selectedScrollSnap() + 1)
+ })
+ }, [api])
+
+ return (
+
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+ {index + 1}
+
+
+
+ ))}
+
+
+
+
+
+ Slide {current} of {count}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/carousel-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/carousel-demo.tsx
new file mode 100644
index 0000000..bcd4702
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/carousel-demo.tsx
@@ -0,0 +1,32 @@
+import * as React from "react"
+
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/registry/new-york-v4/ui/carousel"
+
+export default function CarouselDemo() {
+ return (
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/carousel-orientation.tsx b/apps/ui/src/registry/new-york-v4/examples/carousel-orientation.tsx
new file mode 100644
index 0000000..b466545
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/carousel-orientation.tsx
@@ -0,0 +1,38 @@
+import * as React from "react"
+
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/registry/new-york-v4/ui/carousel"
+
+export default function CarouselOrientation() {
+ return (
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/carousel-plugin.tsx b/apps/ui/src/registry/new-york-v4/examples/carousel-plugin.tsx
new file mode 100644
index 0000000..5a0de83
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/carousel-plugin.tsx
@@ -0,0 +1,43 @@
+
+import * as React from "react"
+import Autoplay from "embla-carousel-autoplay"
+
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/registry/new-york-v4/ui/carousel"
+
+export default function CarouselPlugin() {
+ const plugin = React.useRef(
+ Autoplay({ delay: 2000, stopOnInteraction: true })
+ )
+
+ return (
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/carousel-size.tsx b/apps/ui/src/registry/new-york-v4/examples/carousel-size.tsx
new file mode 100644
index 0000000..b036be6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/carousel-size.tsx
@@ -0,0 +1,37 @@
+import * as React from "react"
+
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/registry/new-york-v4/ui/carousel"
+
+export default function CarouselSize() {
+ return (
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/carousel-spacing.tsx b/apps/ui/src/registry/new-york-v4/examples/carousel-spacing.tsx
new file mode 100644
index 0000000..6771b13
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/carousel-spacing.tsx
@@ -0,0 +1,32 @@
+import * as React from "react"
+
+import { Card, CardContent } from "@/registry/new-york-v4/ui/card"
+import {
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselNext,
+ CarouselPrevious,
+} from "@/registry/new-york-v4/ui/carousel"
+
+export default function CarouselSpacing() {
+ return (
+
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+
+ {index + 1}
+
+
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-axis.tsx b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-axis.tsx
new file mode 100644
index 0000000..0f27567
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-axis.tsx
@@ -0,0 +1,46 @@
+
+import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ ChartContainer,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "#2563eb",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "#60a5fa",
+ },
+} satisfies ChartConfig
+
+export default function Component() {
+ return (
+
+
+
+ value.slice(0, 3)}
+ />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-grid.tsx b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-grid.tsx
new file mode 100644
index 0000000..655016d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-grid.tsx
@@ -0,0 +1,39 @@
+
+import { Bar, BarChart, CartesianGrid } from "recharts"
+
+import {
+ ChartContainer,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "#2563eb",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "#60a5fa",
+ },
+} satisfies ChartConfig
+
+export default function Component() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-legend.tsx b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-legend.tsx
new file mode 100644
index 0000000..ac8670f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-legend.tsx
@@ -0,0 +1,52 @@
+
+import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ ChartContainer,
+ ChartLegend,
+ ChartLegendContent,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "#2563eb",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "#60a5fa",
+ },
+} satisfies ChartConfig
+
+export default function Component() {
+ return (
+
+
+
+ value.slice(0, 3)}
+ />
+ } />
+ } />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-tooltip.tsx b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-tooltip.tsx
new file mode 100644
index 0000000..90c29ce
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo-tooltip.tsx
@@ -0,0 +1,49 @@
+
+import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"
+
+import {
+ ChartContainer,
+ ChartTooltip,
+ ChartTooltipContent,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "#2563eb",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "#60a5fa",
+ },
+} satisfies ChartConfig
+
+export default function Component() {
+ return (
+
+
+
+ value.slice(0, 3)}
+ />
+ } />
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo.tsx
new file mode 100644
index 0000000..ce79f2f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/chart-bar-demo.tsx
@@ -0,0 +1,38 @@
+
+import { Bar, BarChart } from "recharts"
+
+import {
+ ChartContainer,
+ type ChartConfig,
+} from "@/registry/new-york-v4/ui/chart"
+
+const chartData = [
+ { month: "January", desktop: 186, mobile: 80 },
+ { month: "February", desktop: 305, mobile: 200 },
+ { month: "March", desktop: 237, mobile: 120 },
+ { month: "April", desktop: 73, mobile: 190 },
+ { month: "May", desktop: 209, mobile: 130 },
+ { month: "June", desktop: 214, mobile: 140 },
+]
+
+const chartConfig = {
+ desktop: {
+ label: "Desktop",
+ color: "#2563eb",
+ },
+ mobile: {
+ label: "Mobile",
+ color: "#60a5fa",
+ },
+} satisfies ChartConfig
+
+export default function Component() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/chart-tooltip-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/chart-tooltip-demo.tsx
new file mode 100644
index 0000000..ed08579
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/chart-tooltip-demo.tsx
@@ -0,0 +1,213 @@
+
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+export default function Component() {
+ return (
+
+
+
+ Label
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Name
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Indicator
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function TooltipDemo({
+ indicator = "dot",
+ label,
+ payload,
+ hideLabel,
+ hideIndicator,
+ className,
+}: {
+ label: string
+ hideLabel?: boolean
+ hideIndicator?: boolean
+ indicator?: "line" | "dot" | "dashed"
+ payload: {
+ name: string
+ value: number
+ fill: string
+ }[]
+ nameKey?: string
+ labelKey?: string
+} & React.ComponentProps<"div">) {
+ const tooltipLabel = hideLabel ? null : (
+ {label}
+ )
+
+ if (!payload?.length) {
+ return null
+ }
+
+ const nestLabel = payload.length === 1 && indicator !== "dot"
+
+ return (
+
+ {!nestLabel ? tooltipLabel : null}
+
+ {payload.map((item, index) => {
+ const indicatorColor = item.fill
+
+ return (
+
svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
+ indicator === "dot" && "items-center"
+ )}
+ >
+ <>
+ {!hideIndicator && (
+
+ )}
+
+
+ {nestLabel ? tooltipLabel : null}
+ {item.name}
+
+
+ {item.value.toLocaleString()}
+
+
+ >
+
+ )
+ })}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/checkbox-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/checkbox-demo.tsx
new file mode 100644
index 0000000..8bf1f46
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/checkbox-demo.tsx
@@ -0,0 +1,42 @@
+
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function CheckboxDemo() {
+ return (
+
+
+
+ Accept terms and conditions
+
+
+
+
+
Accept terms and conditions
+
+ By clicking this checkbox, you agree to the terms and conditions.
+
+
+
+
+
+ Enable notifications
+
+
+
+
+
+ Enable notifications
+
+
+ You can enable or disable notifications at any time.
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/checkbox-disabled.tsx b/apps/ui/src/registry/new-york-v4/examples/checkbox-disabled.tsx
new file mode 100644
index 0000000..632d821
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/checkbox-disabled.tsx
@@ -0,0 +1,15 @@
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+
+export default function CheckboxDisabled() {
+ return (
+
+
+
+ Accept terms and conditions
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/checkbox-with-text.tsx b/apps/ui/src/registry/new-york-v4/examples/checkbox-with-text.tsx
new file mode 100644
index 0000000..6337ba5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/checkbox-with-text.tsx
@@ -0,0 +1,21 @@
+
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+
+export default function CheckboxWithText() {
+ return (
+
+
+
+
+ Accept terms and conditions
+
+
+ You agree to our Terms of Service and Privacy Policy.
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/collapsible-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/collapsible-demo.tsx
new file mode 100644
index 0000000..1620f14
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/collapsible-demo.tsx
@@ -0,0 +1,45 @@
+
+import * as React from "react"
+import { ChevronsUpDown } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+
+export default function CollapsibleDemo() {
+ const [isOpen, setIsOpen] = React.useState(false)
+
+ return (
+
+
+
+ @peduarte starred 3 repositories
+
+
+
+
+ Toggle
+
+
+
+
+ @radix-ui/primitives
+
+
+
+ @radix-ui/colors
+
+
+ @stitches/react
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/combobox-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/combobox-demo.tsx
new file mode 100644
index 0000000..c6de856
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/combobox-demo.tsx
@@ -0,0 +1,93 @@
+
+import * as React from "react"
+import { Check, ChevronsUpDown } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/registry/new-york-v4/ui/command"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+
+const frameworks = [
+ {
+ value: "next.js",
+ label: "Next.js",
+ },
+ {
+ value: "sveltekit",
+ label: "SvelteKit",
+ },
+ {
+ value: "nuxt.js",
+ label: "Nuxt.js",
+ },
+ {
+ value: "remix",
+ label: "Remix",
+ },
+ {
+ value: "astro",
+ label: "Astro",
+ },
+]
+
+export default function ComboboxDemo() {
+ const [open, setOpen] = React.useState(false)
+ const [value, setValue] = React.useState("")
+
+ return (
+
+
+
+ {value
+ ? frameworks.find((framework) => framework.value === value)?.label
+ : "Select framework..."}
+
+
+
+
+
+
+
+ No framework found.
+
+ {frameworks.map((framework) => (
+ {
+ setValue(currentValue === value ? "" : currentValue)
+ setOpen(false)
+ }}
+ >
+ {framework.label}
+
+
+ ))}
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/combobox-dropdown-menu.tsx b/apps/ui/src/registry/new-york-v4/examples/combobox-dropdown-menu.tsx
new file mode 100644
index 0000000..76d4cf4
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/combobox-dropdown-menu.tsx
@@ -0,0 +1,101 @@
+
+import * as React from "react"
+import { MoreHorizontal } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/registry/new-york-v4/ui/command"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+const labels = [
+ "feature",
+ "bug",
+ "enhancement",
+ "documentation",
+ "design",
+ "question",
+ "maintenance",
+]
+
+export default function ComboboxDropdownMenu() {
+ const [label, setLabel] = React.useState("feature")
+ const [open, setOpen] = React.useState(false)
+
+ return (
+
+
+
+ {label}
+
+ Create a new project
+
+
+
+
+
+
+
+
+ Actions
+
+ Assign to...
+ Set due date...
+
+
+ Apply label
+
+
+
+
+ No label found.
+
+ {labels.map((label) => (
+ {
+ setLabel(value)
+ setOpen(false)
+ }}
+ >
+ {label}
+
+ ))}
+
+
+
+
+
+
+
+ Delete
+ ⌘⌫
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/combobox-popover.tsx b/apps/ui/src/registry/new-york-v4/examples/combobox-popover.tsx
new file mode 100644
index 0000000..2a792f7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/combobox-popover.tsx
@@ -0,0 +1,90 @@
+
+import * as React from "react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/registry/new-york-v4/ui/command"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+
+type Status = {
+ value: string
+ label: string
+}
+
+const statuses: Status[] = [
+ {
+ value: "backlog",
+ label: "Backlog",
+ },
+ {
+ value: "todo",
+ label: "Todo",
+ },
+ {
+ value: "in progress",
+ label: "In Progress",
+ },
+ {
+ value: "done",
+ label: "Done",
+ },
+ {
+ value: "canceled",
+ label: "Canceled",
+ },
+]
+
+export default function ComboboxPopover() {
+ const [open, setOpen] = React.useState(false)
+ const [selectedStatus, setSelectedStatus] = React.useState(
+ null
+ )
+
+ return (
+
+
Status
+
+
+
+ {selectedStatus ? <>{selectedStatus.label}> : <>+ Set status>}
+
+
+
+
+
+
+ No results found.
+
+ {statuses.map((status) => (
+ {
+ setSelectedStatus(
+ statuses.find((priority) => priority.value === value) ||
+ null
+ )
+ setOpen(false)
+ }}
+ >
+ {status.label}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/combobox-responsive.tsx b/apps/ui/src/registry/new-york-v4/examples/combobox-responsive.tsx
new file mode 100644
index 0000000..2c9d1d0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/combobox-responsive.tsx
@@ -0,0 +1,122 @@
+
+import * as React from "react"
+
+import { useMediaQuery } from "@/hooks/use-media-query"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/registry/new-york-v4/ui/command"
+import {
+ Drawer,
+ DrawerContent,
+ DrawerTrigger,
+} from "@/registry/new-york-v4/ui/drawer"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+
+type Status = {
+ value: string
+ label: string
+}
+
+const statuses: Status[] = [
+ {
+ value: "backlog",
+ label: "Backlog",
+ },
+ {
+ value: "todo",
+ label: "Todo",
+ },
+ {
+ value: "in progress",
+ label: "In Progress",
+ },
+ {
+ value: "done",
+ label: "Done",
+ },
+ {
+ value: "canceled",
+ label: "Canceled",
+ },
+]
+
+export default function ComboBoxResponsive() {
+ const [open, setOpen] = React.useState(false)
+ const isDesktop = useMediaQuery("(min-width: 768px)")
+ const [selectedStatus, setSelectedStatus] = React.useState(
+ null
+ )
+
+ if (isDesktop) {
+ return (
+
+
+
+ {selectedStatus ? <>{selectedStatus.label}> : <>+ Set status>}
+
+
+
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ {selectedStatus ? <>{selectedStatus.label}> : <>+ Set status>}
+
+
+
+
+
+
+
+
+ )
+}
+
+function StatusList({
+ setOpen,
+ setSelectedStatus,
+}: {
+ setOpen: (open: boolean) => void
+ setSelectedStatus: (status: Status | null) => void
+}) {
+ return (
+
+
+
+ No results found.
+
+ {statuses.map((status) => (
+ {
+ setSelectedStatus(
+ statuses.find((priority) => priority.value === value) || null
+ )
+ setOpen(false)
+ }}
+ >
+ {status.label}
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/command-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/command-demo.tsx
new file mode 100644
index 0000000..068e078
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/command-demo.tsx
@@ -0,0 +1,62 @@
+import {
+ Calculator,
+ Calendar,
+ CreditCard,
+ Settings,
+ Smile,
+ User,
+} from "lucide-react"
+
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+ CommandSeparator,
+ CommandShortcut,
+} from "@/registry/new-york-v4/ui/command"
+
+export default function CommandDemo() {
+ return (
+
+
+
+ No results found.
+
+
+
+ Calendar
+
+
+
+ Search Emoji
+
+
+
+ Calculator
+
+
+
+
+
+
+ Profile
+ ⌘P
+
+
+
+ Billing
+ ⌘B
+
+
+
+ Settings
+ ⌘S
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/command-dialog.tsx b/apps/ui/src/registry/new-york-v4/examples/command-dialog.tsx
new file mode 100644
index 0000000..c50ede5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/command-dialog.tsx
@@ -0,0 +1,86 @@
+
+import * as React from "react"
+import {
+ Calculator,
+ Calendar,
+ CreditCard,
+ Settings,
+ Smile,
+ User,
+} from "lucide-react"
+
+import {
+ CommandDialog,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+ CommandSeparator,
+ CommandShortcut,
+} from "@/registry/new-york-v4/ui/command"
+
+export default function CommandDialogDemo() {
+ const [open, setOpen] = React.useState(false)
+
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if (e.key === "j" && (e.metaKey || e.ctrlKey)) {
+ e.preventDefault()
+ setOpen((open) => !open)
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => document.removeEventListener("keydown", down)
+ }, [])
+
+ return (
+ <>
+
+ Press{" "}
+
+ ⌘ J
+
+
+
+
+
+ No results found.
+
+
+
+ Calendar
+
+
+
+ Search Emoji
+
+
+
+ Calculator
+
+
+
+
+
+
+ Profile
+ ⌘P
+
+
+
+ Billing
+ ⌘B
+
+
+
+ Settings
+ ⌘S
+
+
+
+
+ >
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/context-menu-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/context-menu-demo.tsx
new file mode 100644
index 0000000..2a99477
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/context-menu-demo.tsx
@@ -0,0 +1,64 @@
+import {
+ ContextMenu,
+ ContextMenuCheckboxItem,
+ ContextMenuContent,
+ ContextMenuItem,
+ ContextMenuLabel,
+ ContextMenuRadioGroup,
+ ContextMenuRadioItem,
+ ContextMenuSeparator,
+ ContextMenuShortcut,
+ ContextMenuSub,
+ ContextMenuSubContent,
+ ContextMenuSubTrigger,
+ ContextMenuTrigger,
+} from "@/registry/new-york-v4/ui/context-menu"
+
+export default function ContextMenuDemo() {
+ return (
+
+
+ Right click here
+
+
+
+ Back
+ ⌘[
+
+
+ Forward
+ ⌘]
+
+
+ Reload
+ ⌘R
+
+
+ More Tools
+
+ Save Page...
+ Create Shortcut...
+ Name Window...
+
+ Developer Tools
+
+ Delete
+
+
+
+
+ Show Bookmarks
+
+ Show Full URLs
+
+
+ People
+
+ Pedro Duarte
+
+ Colm Tuite
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/data-table-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/data-table-demo.tsx
new file mode 100644
index 0000000..c300169
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/data-table-demo.tsx
@@ -0,0 +1,311 @@
+
+import * as React from "react"
+import {
+ flexRender,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getPaginationRowModel,
+ getSortedRowModel,
+ useReactTable,
+ type ColumnDef,
+ type ColumnFiltersState,
+ type SortingState,
+ type VisibilityState,
+} from "@tanstack/react-table"
+import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/registry/new-york-v4/ui/table"
+
+const data: Payment[] = [
+ {
+ id: "m5gr84i9",
+ amount: 316,
+ status: "success",
+ email: "ken99@example.com",
+ },
+ {
+ id: "3u1reuv4",
+ amount: 242,
+ status: "success",
+ email: "Abe45@example.com",
+ },
+ {
+ id: "derv1ws0",
+ amount: 837,
+ status: "processing",
+ email: "Monserrat44@example.com",
+ },
+ {
+ id: "5kma53ae",
+ amount: 874,
+ status: "success",
+ email: "Silas22@example.com",
+ },
+ {
+ id: "bhqecj4p",
+ amount: 721,
+ status: "failed",
+ email: "carmella@example.com",
+ },
+]
+
+export type Payment = {
+ id: string
+ amount: number
+ status: "pending" | "processing" | "success" | "failed"
+ email: string
+}
+
+export const columns: ColumnDef[] = [
+ {
+ id: "select",
+ header: ({ table }) => (
+ table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Select all"
+ />
+ ),
+ cell: ({ row }) => (
+ row.toggleSelected(!!value)}
+ aria-label="Select row"
+ />
+ ),
+ enableSorting: false,
+ enableHiding: false,
+ },
+ {
+ accessorKey: "status",
+ header: "Status",
+ cell: ({ row }) => (
+ {row.getValue("status")}
+ ),
+ },
+ {
+ accessorKey: "email",
+ header: ({ column }) => {
+ return (
+ column.toggleSorting(column.getIsSorted() === "asc")}
+ >
+ Email
+
+
+ )
+ },
+ cell: ({ row }) => {row.getValue("email")}
,
+ },
+ {
+ accessorKey: "amount",
+ header: () => Amount
,
+ cell: ({ row }) => {
+ const amount = parseFloat(row.getValue("amount"))
+
+ // Format the amount as a dollar amount
+ const formatted = new Intl.NumberFormat("en-US", {
+ style: "currency",
+ currency: "USD",
+ }).format(amount)
+
+ return {formatted}
+ },
+ },
+ {
+ id: "actions",
+ enableHiding: false,
+ cell: ({ row }) => {
+ const payment = row.original
+
+ return (
+
+
+
+ Open menu
+
+
+
+
+ Actions
+ navigator.clipboard.writeText(payment.id)}
+ >
+ Copy payment ID
+
+
+ View customer
+ View payment details
+
+
+ )
+ },
+ },
+]
+
+export default function DataTableDemo() {
+ const [sorting, setSorting] = React.useState([])
+ const [columnFilters, setColumnFilters] = React.useState(
+ []
+ )
+ const [columnVisibility, setColumnVisibility] =
+ React.useState({})
+ const [rowSelection, setRowSelection] = React.useState({})
+
+ const table = useReactTable({
+ data,
+ columns,
+ onSortingChange: setSorting,
+ onColumnFiltersChange: setColumnFilters,
+ getCoreRowModel: getCoreRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ onColumnVisibilityChange: setColumnVisibility,
+ onRowSelectionChange: setRowSelection,
+ state: {
+ sorting,
+ columnFilters,
+ columnVisibility,
+ rowSelection,
+ },
+ })
+
+ return (
+
+
+
+ table.getColumn("email")?.setFilterValue(event.target.value)
+ }
+ className="max-w-sm"
+ />
+
+
+
+ Columns
+
+
+
+ {table
+ .getAllColumns()
+ .filter((column) => column.getCanHide())
+ .map((column) => {
+ return (
+
+ column.toggleVisibility(!!value)
+ }
+ >
+ {column.id}
+
+ )
+ })}
+
+
+
+
+
+
+ {table.getHeaderGroups().map((headerGroup) => (
+
+ {headerGroup.headers.map((header) => {
+ return (
+
+ {header.isPlaceholder
+ ? null
+ : flexRender(
+ header.column.columnDef.header,
+ header.getContext()
+ )}
+
+ )
+ })}
+
+ ))}
+
+
+ {table.getRowModel().rows?.length ? (
+ table.getRowModel().rows.map((row) => (
+
+ {row.getVisibleCells().map((cell) => (
+
+ {flexRender(
+ cell.column.columnDef.cell,
+ cell.getContext()
+ )}
+
+ ))}
+
+ ))
+ ) : (
+
+
+ No results.
+
+
+ )}
+
+
+
+
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "}
+ {table.getFilteredRowModel().rows.length} row(s) selected.
+
+
+ table.previousPage()}
+ disabled={!table.getCanPreviousPage()}
+ >
+ Previous
+
+ table.nextPage()}
+ disabled={!table.getCanNextPage()}
+ >
+ Next
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/date-picker-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/date-picker-demo.tsx
new file mode 100644
index 0000000..55f7650
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/date-picker-demo.tsx
@@ -0,0 +1,42 @@
+
+import * as React from "react"
+import { format } from "date-fns"
+import { CalendarIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Calendar } from "@/registry/new-york-v4/ui/calendar"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+
+export default function DatePickerDemo() {
+ const [date, setDate] = React.useState()
+
+ return (
+
+
+
+
+ {date ? format(date, "PPP") : Pick a date }
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/date-picker-with-presets.tsx b/apps/ui/src/registry/new-york-v4/examples/date-picker-with-presets.tsx
new file mode 100644
index 0000000..1f9b3be
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/date-picker-with-presets.tsx
@@ -0,0 +1,64 @@
+
+import * as React from "react"
+import { addDays, format } from "date-fns"
+import { CalendarIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Calendar } from "@/registry/new-york-v4/ui/calendar"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+
+export default function DatePickerWithPresets() {
+ const [date, setDate] = React.useState()
+
+ return (
+
+
+
+
+ {date ? format(date, "PPP") : Pick a date }
+
+
+
+
+ setDate(addDays(new Date(), parseInt(value)))
+ }
+ >
+
+
+
+
+ Today
+ Tomorrow
+ In 3 days
+ In a week
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/date-picker-with-range.tsx b/apps/ui/src/registry/new-york-v4/examples/date-picker-with-range.tsx
new file mode 100644
index 0000000..0dace6a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/date-picker-with-range.tsx
@@ -0,0 +1,64 @@
+
+import * as React from "react"
+import { addDays, format } from "date-fns"
+import { CalendarIcon } from "lucide-react"
+import { type DateRange } from "react-day-picker"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Calendar } from "@/registry/new-york-v4/ui/calendar"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+
+export default function DatePickerWithRange({
+ className,
+}: React.HTMLAttributes) {
+ const [date, setDate] = React.useState({
+ from: new Date(2022, 0, 20),
+ to: addDays(new Date(2022, 0, 20), 20),
+ })
+
+ return (
+
+
+
+
+
+ {date?.from ? (
+ date.to ? (
+ <>
+ {format(date.from, "LLL dd, y")} -{" "}
+ {format(date.to, "LLL dd, y")}
+ >
+ ) : (
+ format(date.from, "LLL dd, y")
+ )
+ ) : (
+ Pick a date
+ )}
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/dialog-close-button.tsx b/apps/ui/src/registry/new-york-v4/examples/dialog-close-button.tsx
new file mode 100644
index 0000000..628c7cd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/dialog-close-button.tsx
@@ -0,0 +1,50 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/new-york-v4/ui/dialog"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function DialogCloseButton() {
+ return (
+
+
+ Share
+
+
+
+ Share link
+
+ Anyone who has this link will be able to view this.
+
+
+
+
+
+
+ Close
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/dialog-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/dialog-demo.tsx
new file mode 100644
index 0000000..8ce9383
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/dialog-demo.tsx
@@ -0,0 +1,50 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/new-york-v4/ui/dialog"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function DialogDemo() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/drawer-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/drawer-demo.tsx
new file mode 100644
index 0000000..70334ca
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/drawer-demo.tsx
@@ -0,0 +1,135 @@
+
+import * as React from "react"
+import { Minus, Plus } from "lucide-react"
+import { Bar, BarChart, ResponsiveContainer } from "recharts"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/registry/new-york-v4/ui/drawer"
+
+const data = [
+ {
+ goal: 400,
+ },
+ {
+ goal: 300,
+ },
+ {
+ goal: 200,
+ },
+ {
+ goal: 300,
+ },
+ {
+ goal: 200,
+ },
+ {
+ goal: 278,
+ },
+ {
+ goal: 189,
+ },
+ {
+ goal: 239,
+ },
+ {
+ goal: 300,
+ },
+ {
+ goal: 200,
+ },
+ {
+ goal: 278,
+ },
+ {
+ goal: 189,
+ },
+ {
+ goal: 349,
+ },
+]
+
+export default function DrawerDemo() {
+ const [goal, setGoal] = React.useState(350)
+
+ function onClick(adjustment: number) {
+ setGoal(Math.max(200, Math.min(400, goal + adjustment)))
+ }
+
+ return (
+
+
+ Open Drawer
+
+
+
+
+ Move Goal
+ Set your daily activity goal.
+
+
+
+
onClick(-10)}
+ disabled={goal <= 200}
+ >
+
+ Decrease
+
+
+
+ {goal}
+
+
+ Calories/day
+
+
+
onClick(10)}
+ disabled={goal >= 400}
+ >
+
+ Increase
+
+
+
+
+
+
+
+
+
+
+
+ Submit
+
+ Cancel
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/drawer-dialog.tsx b/apps/ui/src/registry/new-york-v4/examples/drawer-dialog.tsx
new file mode 100644
index 0000000..7949795
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/drawer-dialog.tsx
@@ -0,0 +1,89 @@
+
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+import { useMediaQuery } from "@/hooks/use-media-query"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/registry/new-york-v4/ui/dialog"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/registry/new-york-v4/ui/drawer"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function DrawerDialogDemo() {
+ const [open, setOpen] = React.useState(false)
+ const isDesktop = useMediaQuery("(min-width: 768px)")
+
+ if (isDesktop) {
+ return (
+
+
+ Edit Profile
+
+
+
+ Edit profile
+
+ Make changes to your profile here. Click save when you're
+ done.
+
+
+
+
+
+ )
+ }
+
+ return (
+
+
+ Edit Profile
+
+
+
+ Edit profile
+
+ Make changes to your profile here. Click save when you're done.
+
+
+
+
+
+ Cancel
+
+
+
+
+ )
+}
+
+function ProfileForm({ className }: React.ComponentProps<"form">) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-checkboxes.tsx b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-checkboxes.tsx
new file mode 100644
index 0000000..d83c305
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-checkboxes.tsx
@@ -0,0 +1,54 @@
+
+import * as React from "react"
+import type { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+type Checked = React.ComponentProps<
+ typeof DropdownMenuPrimitive.CheckboxItem
+>["checked"]
+
+export default function DropdownMenuCheckboxes() {
+ const [showStatusBar, setShowStatusBar] = React.useState(true)
+ const [showActivityBar, setShowActivityBar] = React.useState(false)
+ const [showPanel, setShowPanel] = React.useState(false)
+
+ return (
+
+
+ Open
+
+
+ Appearance
+
+
+ Status Bar
+
+
+ Activity Bar
+
+
+ Panel
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-demo.tsx
new file mode 100644
index 0000000..35075e6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-demo.tsx
@@ -0,0 +1,74 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuPortal,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+export default function DropdownMenuDemo() {
+ return (
+
+
+ Open
+
+
+ My Account
+
+
+ Profile
+ ⇧⌘P
+
+
+ Billing
+ ⌘B
+
+
+ Settings
+ ⌘S
+
+
+ Keyboard shortcuts
+ ⌘K
+
+
+
+
+ Team
+
+ Invite users
+
+
+ Email
+ Message
+
+ More...
+
+
+
+
+ New Team
+ ⌘+T
+
+
+
+ GitHub
+ Support
+ API
+
+
+ Log out
+ ⇧⌘Q
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-dialog.tsx b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-dialog.tsx
new file mode 100644
index 0000000..e243ac6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-dialog.tsx
@@ -0,0 +1,114 @@
+
+import { useState } from "react"
+import { MoreHorizontalIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/registry/new-york-v4/ui/dialog"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import { Field, FieldGroup, FieldLabel } from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function DropdownMenuDialog() {
+ const [showNewDialog, setShowNewDialog] = useState(false)
+ const [showShareDialog, setShowShareDialog] = useState(false)
+
+ return (
+ <>
+
+
+
+
+
+
+
+ File Actions
+
+ setShowNewDialog(true)}>
+ New File...
+
+ setShowShareDialog(true)}>
+ Share...
+
+ Download
+
+
+
+
+
+
+ Create New File
+
+ Provide a name for your new file. Click create when you're
+ done.
+
+
+
+
+ File Name
+
+
+
+
+
+ Cancel
+
+ Create
+
+
+
+
+
+
+ Share File
+
+ Anyone with the link will be able to view this file.
+
+
+
+
+ Email Address
+
+
+
+ Message (Optional)
+
+
+
+
+
+ Cancel
+
+ Send Invite
+
+
+
+ >
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-radio-group.tsx b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-radio-group.tsx
new file mode 100644
index 0000000..e807fb5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/dropdown-menu-radio-group.tsx
@@ -0,0 +1,34 @@
+
+import * as React from "react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuLabel,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+export default function DropdownMenuRadioGroupDemo() {
+ const [position, setPosition] = React.useState("bottom")
+
+ return (
+
+
+ Open
+
+
+ Panel Position
+
+
+ Top
+ Bottom
+ Right
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/empty-avatar-group.tsx b/apps/ui/src/registry/new-york-v4/examples/empty-avatar-group.tsx
new file mode 100644
index 0000000..a1b2b7e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/empty-avatar-group.tsx
@@ -0,0 +1,57 @@
+import { PlusIcon } from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+
+export default function EmptyAvatarGroup() {
+ return (
+
+
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+ No Team Members
+
+ Invite your team to collaborate on this project.
+
+
+
+
+
+ Invite Members
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/empty-avatar.tsx b/apps/ui/src/registry/new-york-v4/examples/empty-avatar.tsx
new file mode 100644
index 0000000..e28a666
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/empty-avatar.tsx
@@ -0,0 +1,40 @@
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+
+export default function EmptyAvatar() {
+ return (
+
+
+
+
+
+ LR
+
+
+ User Offline
+
+ This user is currently offline. You can leave a message to notify them
+ or try again later.
+
+
+
+ Leave Message
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/empty-background.tsx b/apps/ui/src/registry/new-york-v4/examples/empty-background.tsx
new file mode 100644
index 0000000..ab12cb8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/empty-background.tsx
@@ -0,0 +1,34 @@
+import { IconBell } from "@tabler/icons-react"
+import { RefreshCcwIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+
+export default function EmptyMuted() {
+ return (
+
+
+
+
+
+ No Notifications
+
+ You're all caught up. New notifications will appear here.
+
+
+
+
+
+ Refresh
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/empty-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/empty-demo.tsx
new file mode 100644
index 0000000..1955127
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/empty-demo.tsx
@@ -0,0 +1,45 @@
+import { IconFolderCode } from "@tabler/icons-react"
+import { ArrowUpRightIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+
+export default function EmptyDemo() {
+ return (
+
+
+
+
+
+ No Projects Yet
+
+ You haven't created any projects yet. Get started by creating
+ your first project.
+
+
+
+
+ Create Project
+ Import Project
+
+
+
+
+ Learn More
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/empty-icon.tsx b/apps/ui/src/registry/new-york-v4/examples/empty-icon.tsx
new file mode 100644
index 0000000..f8e4f47
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/empty-icon.tsx
@@ -0,0 +1,68 @@
+import {
+ IconBookmark,
+ IconHeart,
+ IconInbox,
+ IconStar,
+} from "@tabler/icons-react"
+
+import {
+ Empty,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+
+export default function EmptyIcon() {
+ return (
+
+
+
+
+
+
+ No messages
+
+ Your inbox is empty. New messages will appear here.
+
+
+
+
+
+
+
+
+
+ No favorites
+
+ Items you mark as favorites will appear here.
+
+
+
+
+
+
+
+
+
+ No likes yet
+
+ Content you like will be saved here for easy access.
+
+
+
+
+
+
+
+
+
+ No bookmarks
+
+ Save interesting content by bookmarking it.
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/empty-input-group.tsx b/apps/ui/src/registry/new-york-v4/examples/empty-input-group.tsx
new file mode 100644
index 0000000..316cb43
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/empty-input-group.tsx
@@ -0,0 +1,43 @@
+import { SearchIcon } from "lucide-react"
+
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Kbd } from "@/registry/new-york-v4/ui/kbd"
+
+export default function EmptyInputGroup() {
+ return (
+
+
+ 404 - Not Found
+
+ The page you're looking for doesn't exist. Try searching for
+ what you need below.
+
+
+
+
+
+
+
+
+
+ /
+
+
+
+ Need help? Contact support
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/empty-outline.tsx b/apps/ui/src/registry/new-york-v4/examples/empty-outline.tsx
new file mode 100644
index 0000000..5997ead
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/empty-outline.tsx
@@ -0,0 +1,32 @@
+import { IconCloud } from "@tabler/icons-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+
+export default function EmptyOutline() {
+ return (
+
+
+
+
+
+ Cloud Storage Empty
+
+ Upload files to your cloud storage to access them anywhere.
+
+
+
+
+ Upload Files
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-checkbox.tsx b/apps/ui/src/registry/new-york-v4/examples/field-checkbox.tsx
new file mode 100644
index 0000000..9ab686b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-checkbox.tsx
@@ -0,0 +1,80 @@
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldLegend,
+ FieldSeparator,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+
+export default function FieldCheckbox() {
+ return (
+
+
+
+
+ Show these items on the desktop
+
+
+ Select the items you want to show on the desktop.
+
+
+
+
+
+ Hard disks
+
+
+
+
+
+ External disks
+
+
+
+
+
+ CDs, DVDs, and iPods
+
+
+
+
+
+ Connected servers
+
+
+
+
+
+
+
+
+
+ Sync Desktop & Documents folders
+
+
+ Your Desktop & Documents folders are being synced with iCloud
+ Drive. You can access them from other devices.
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-choice-card.tsx b/apps/ui/src/registry/new-york-v4/examples/field-choice-card.tsx
new file mode 100644
index 0000000..c3670ab
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-choice-card.tsx
@@ -0,0 +1,54 @@
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSet,
+ FieldTitle,
+} from "@/registry/new-york-v4/ui/field"
+import {
+ RadioGroup,
+ RadioGroupItem,
+} from "@/registry/new-york-v4/ui/radio-group"
+
+export default function FieldChoiceCard() {
+ return (
+
+
+
+
+ Compute Environment
+
+
+ Select the compute environment for your cluster.
+
+
+
+
+
+ Kubernetes
+
+ Run GPU workloads on a K8s configured cluster.
+
+
+
+
+
+
+
+
+ Virtual Machine
+
+ Access a VM configured cluster to run GPU workloads.
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/field-demo.tsx
new file mode 100644
index 0000000..01c4f6f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-demo.tsx
@@ -0,0 +1,151 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldLegend,
+ FieldSeparator,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function FieldDemo() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-fieldset.tsx b/apps/ui/src/registry/new-york-v4/examples/field-fieldset.tsx
new file mode 100644
index 0000000..f35a3d7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-fieldset.tsx
@@ -0,0 +1,38 @@
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldLegend,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export default function FieldFieldset() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-group.tsx b/apps/ui/src/registry/new-york-v4/examples/field-group.tsx
new file mode 100644
index 0000000..03030fd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-group.tsx
@@ -0,0 +1,55 @@
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSeparator,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+
+export default function FieldGroupExample() {
+ return (
+
+
+
+ Responses
+
+ Get notified when ChatGPT responds to requests that take time, like
+ research or image generation.
+
+
+
+
+
+ Push notifications
+
+
+
+
+
+
+ Tasks
+
+ Get notified when tasks you've created have updates.{" "}
+ Manage tasks
+
+
+
+
+
+ Push notifications
+
+
+
+
+
+ Email notifications
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-input.tsx b/apps/ui/src/registry/new-york-v4/examples/field-input.tsx
new file mode 100644
index 0000000..0a170ea
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-input.tsx
@@ -0,0 +1,33 @@
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export default function FieldInput() {
+ return (
+
+
+
+
+ Username
+
+
+ Choose a unique username for your account.
+
+
+
+ Password
+
+ Must be at least 8 characters long.
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-radio.tsx b/apps/ui/src/registry/new-york-v4/examples/field-radio.tsx
new file mode 100644
index 0000000..d032355
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-radio.tsx
@@ -0,0 +1,43 @@
+import {
+ Field,
+ FieldDescription,
+ FieldLabel,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+import {
+ RadioGroup,
+ RadioGroupItem,
+} from "@/registry/new-york-v4/ui/radio-group"
+
+export default function FieldRadio() {
+ return (
+
+
+ Subscription Plan
+
+ Yearly and lifetime plans offer significant savings.
+
+
+
+
+
+ Monthly ($9.99/month)
+
+
+
+
+
+ Yearly ($99.99/year)
+
+
+
+
+
+ Lifetime ($299.99)
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-responsive.tsx b/apps/ui/src/registry/new-york-v4/examples/field-responsive.tsx
new file mode 100644
index 0000000..8e85e98
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-responsive.tsx
@@ -0,0 +1,61 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldLegend,
+ FieldSeparator,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function FieldResponsive() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-select.tsx b/apps/ui/src/registry/new-york-v4/examples/field-select.tsx
new file mode 100644
index 0000000..5c05af1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-select.tsx
@@ -0,0 +1,40 @@
+import {
+ Field,
+ FieldDescription,
+ FieldLabel,
+} from "@/registry/new-york-v4/ui/field"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+
+export default function FieldSelect() {
+ return (
+
+
+ Department
+
+
+
+
+
+ Engineering
+ Design
+ Marketing
+ Sales
+ Customer Support
+ Human Resources
+ Finance
+ Operations
+
+
+
+ Select your department or area of work.
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-slider.tsx b/apps/ui/src/registry/new-york-v4/examples/field-slider.tsx
new file mode 100644
index 0000000..b5c885b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-slider.tsx
@@ -0,0 +1,34 @@
+
+import { useState } from "react"
+
+import {
+ Field,
+ FieldDescription,
+ FieldTitle,
+} from "@/registry/new-york-v4/ui/field"
+import { Slider } from "@/registry/new-york-v4/ui/slider"
+
+export default function FieldSlider() {
+ const [value, setValue] = useState([200, 800])
+ return (
+
+
+ Price Range
+
+ Set your budget range ($
+ {value[0]} -{" "}
+ {value[1]} ).
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-switch.tsx b/apps/ui/src/registry/new-york-v4/examples/field-switch.tsx
new file mode 100644
index 0000000..05cf537
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-switch.tsx
@@ -0,0 +1,24 @@
+import {
+ Field,
+ FieldContent,
+ FieldDescription,
+ FieldLabel,
+} from "@/registry/new-york-v4/ui/field"
+import { Switch } from "@/registry/new-york-v4/ui/switch"
+
+export default function FieldSwitch() {
+ return (
+
+
+
+ Multi-factor authentication
+
+ Enable multi-factor authentication. If you do not have a two-factor
+ device, you can use a one-time code sent to your email.
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/field-textarea.tsx b/apps/ui/src/registry/new-york-v4/examples/field-textarea.tsx
new file mode 100644
index 0000000..df9e825
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/field-textarea.tsx
@@ -0,0 +1,30 @@
+import {
+ Field,
+ FieldDescription,
+ FieldGroup,
+ FieldLabel,
+ FieldSet,
+} from "@/registry/new-york-v4/ui/field"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function FieldTextarea() {
+ return (
+
+
+
+
+ Feedback
+
+
+ Share your thoughts about our service.
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-next-complex-action.ts b/apps/ui/src/registry/new-york-v4/examples/form-next-complex-action.ts
new file mode 100644
index 0000000..e40c60b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-next-complex-action.ts
@@ -0,0 +1,36 @@
+
+import { formSchema, type FormState } from "./form-next-complex-schema"
+
+export async function complexFormAction(
+ _prevState: FormState,
+ formData: FormData
+) {
+ // Sleep for 1 second
+ await new Promise((resolve) => setTimeout(resolve, 1000))
+
+ const values = {
+ plan: formData.get("plan") as FormState["values"]["plan"],
+ billingPeriod: formData.get("billingPeriod") as string,
+ addons: formData.getAll("addons") as string[],
+ emailNotifications: formData.get("emailNotifications") === "on",
+ }
+
+ const result = formSchema.safeParse(values)
+
+ if (!result.success) {
+ return {
+ values,
+ success: false,
+ errors: result.error.flatten().fieldErrors,
+ }
+ }
+
+ // Do something with the values.
+ // Call your database or API here.
+
+ return {
+ values,
+ errors: null,
+ success: true,
+ }
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-next-complex-schema.ts b/apps/ui/src/registry/new-york-v4/examples/form-next-complex-schema.ts
new file mode 100644
index 0000000..65327d4
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-next-complex-schema.ts
@@ -0,0 +1,52 @@
+import { z } from "zod"
+
+export const formSchema = z.object({
+ plan: z
+ .string({
+ required_error: "Please select a subscription plan",
+ })
+ .min(1, "Please select a subscription plan")
+ .refine((value) => value === "basic" || value === "pro", {
+ message: "Invalid plan selection. Please choose Basic or Pro",
+ }),
+ billingPeriod: z
+ .string({
+ required_error: "Please select a billing period",
+ })
+ .min(1, "Please select a billing period"),
+ addons: z
+ .array(z.string())
+ .min(1, "Please select at least one add-on")
+ .max(3, "You can select up to 3 add-ons")
+ .refine(
+ (value) => value.every((addon) => addons.some((a) => a.id === addon)),
+ {
+ message: "You selected an invalid add-on",
+ }
+ ),
+ emailNotifications: z.boolean(),
+})
+
+export type FormState = {
+ values: z.infer
+ errors: null | Partial, string[]>>
+ success: boolean
+}
+
+export 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
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-next-complex.tsx b/apps/ui/src/registry/new-york-v4/examples/form-next-complex.tsx
new file mode 100644
index 0000000..b791bb7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-next-complex.tsx
@@ -0,0 +1,193 @@
+
+import * as React from "react"
+import Form from "next/form"
+import { toast } from "sonner"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Card, CardContent, CardFooter } 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 { Spinner } from "@/registry/new-york-v4/ui/spinner"
+import { Switch } from "@/registry/new-york-v4/ui/switch"
+
+import { complexFormAction } from "./form-next-complex-action"
+import { addons, type FormState } from "./form-next-complex-schema"
+
+export default function FormNextComplex() {
+ const [formState, formAction, pending] = React.useActionState<
+ FormState,
+ FormData
+ >(complexFormAction, {
+ values: {
+ plan: "basic",
+ billingPeriod: "monthly",
+ addons: [],
+ emailNotifications: false,
+ },
+ errors: null,
+ success: false,
+ })
+
+ React.useEffect(() => {
+ if (formState.success) {
+ toast.success("Preferences saved", {
+ description: "Your subscription plan has been updated.",
+ })
+ }
+ }, [formState.success])
+
+ return (
+
+
+
+
+
+ Subscription Plan
+
+ Choose your subscription plan.
+
+
+
+
+
+ Basic
+
+ For individuals and small teams
+
+
+
+
+
+
+
+
+ Pro
+
+ For businesses with higher demands
+
+
+
+
+
+
+ {formState.errors?.plan && (
+ {formState.errors.plan[0]}
+ )}
+
+
+
+ Billing Period
+
+
+
+
+
+ Monthly
+ Yearly
+
+
+
+ Choose how often you want to be billed.
+
+ {formState.errors?.billingPeriod && (
+ {formState.errors.billingPeriod[0]}
+ )}
+
+
+
+ Add-ons
+
+ Select additional features you'd like to include.
+
+
+ {addons.map((addon) => (
+
+
+
+ {addon.title}
+ {addon.description}
+
+
+ ))}
+
+ {formState.errors?.addons && (
+ {formState.errors.addons[0]}
+ )}
+
+
+
+
+
+ Email Notifications
+
+
+ Receive email updates about your subscription
+
+
+
+
+
+
+
+
+
+
+ {pending && }
+ Save Preferences
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-next-demo-action.ts b/apps/ui/src/registry/new-york-v4/examples/form-next-demo-action.ts
new file mode 100644
index 0000000..755eb35
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-next-demo-action.ts
@@ -0,0 +1,34 @@
+
+import { formSchema, type FormState } from "./form-next-demo-schema"
+
+export async function demoFormAction(
+ _prevState: FormState,
+ formData: FormData
+) {
+ const values = {
+ title: formData.get("title") as string,
+ description: formData.get("description") as string,
+ }
+
+ const result = formSchema.safeParse(values)
+
+ if (!result.success) {
+ return {
+ values,
+ success: false,
+ errors: result.error.flatten().fieldErrors,
+ }
+ }
+
+ // Do something with the values.
+ // Call your database or API here.
+
+ return {
+ values: {
+ title: "",
+ description: "",
+ },
+ errors: null,
+ success: true,
+ }
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-next-demo-schema.ts b/apps/ui/src/registry/new-york-v4/examples/form-next-demo-schema.ts
new file mode 100644
index 0000000..d2bceda
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-next-demo-schema.ts
@@ -0,0 +1,18 @@
+import { z } from "zod"
+
+export const formSchema = z.object({
+ title: z
+ .string()
+ .min(5, "Bug title must be at least 5 characters.")
+ .max(32, "Bug title must be at most 32 characters."),
+ description: z
+ .string()
+ .min(20, "Description must be at least 20 characters.")
+ .max(100, "Description must be at most 100 characters."),
+})
+
+export type FormState = {
+ values: z.infer
+ errors: null | Partial, string[]>>
+ success: boolean
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-next-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/form-next-demo.tsx
new file mode 100644
index 0000000..7731113
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-next-demo.tsx
@@ -0,0 +1,127 @@
+
+import * as React from "react"
+import Form from "next/form"
+import { toast } from "sonner"
+
+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"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+import { demoFormAction } from "./form-next-demo-action"
+import { type FormState } from "./form-next-demo-schema"
+
+export default function FormNextDemo() {
+ const [formState, formAction, pending] = React.useActionState<
+ FormState,
+ FormData
+ >(demoFormAction, {
+ values: {
+ title: "",
+ description: "",
+ },
+ errors: null,
+ success: false,
+ })
+ const [descriptionLength, setDescriptionLength] = React.useState(0)
+
+ React.useEffect(() => {
+ if (formState.success) {
+ toast("Thank you for your feedback", {
+ description: "We'll review your report and get back to you soon.",
+ })
+ }
+ }, [formState.success])
+
+ React.useEffect(() => {
+ setDescriptionLength(formState.values.description.length)
+ }, [formState.values.description])
+
+ return (
+
+
+ Bug Report
+
+ Help us improve by reporting bugs you encounter.
+
+
+
+
+
+
+ Bug Title
+
+ {formState.errors?.title && (
+ {formState.errors.title[0]}
+ )}
+
+
+ Description
+
+ setDescriptionLength(e.target.value.length)}
+ />
+
+
+ {descriptionLength}/100 characters
+
+
+
+
+ Include steps to reproduce, expected behavior, and what actually
+ happened.
+
+ {formState.errors?.description && (
+ {formState.errors.description[0]}
+ )}
+
+
+
+
+
+
+
+ {pending && }
+ Submit
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-array.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-array.tsx
new file mode 100644
index 0000000..e48bb3d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-array.tsx
@@ -0,0 +1,159 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { XIcon } from "lucide-react"
+import { Controller, useFieldArray, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ emails: z
+ .array(
+ z.object({
+ address: z.string().email("Enter a valid email address."),
+ })
+ )
+ .min(1, "Add at least one email address.")
+ .max(5, "You can add up to 5 email addresses."),
+})
+
+export default function FormRhfArray() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ emails: [{ address: "" }, { address: "" }],
+ },
+ })
+
+ const { fields, append, remove } = useFieldArray({
+ control: form.control,
+ name: "emails",
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Contact Emails
+ Manage your contact email addresses.
+
+
+
+
+ Email Addresses
+
+ Add up to 5 email addresses where we can contact you.
+
+
+ {fields.map((field, index) => (
+ (
+
+
+
+
+ {fields.length > 1 && (
+
+ remove(index)}
+ aria-label={`Remove email ${index + 1}`}
+ >
+
+
+
+ )}
+
+ {fieldState.invalid && (
+
+ )}
+
+
+ )}
+ />
+ ))}
+ append({ address: "" })}
+ disabled={fields.length >= 5}
+ >
+ Add Email Address
+
+
+ {form.formState.errors.emails?.root && (
+
+ )}
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-checkbox.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-checkbox.tsx
new file mode 100644
index 0000000..730aa5c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-checkbox.tsx
@@ -0,0 +1,186 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ responses: z.boolean(),
+ tasks: z
+ .array(z.string())
+ .min(1, "Please select at least one notification type.")
+ .refine(
+ (value) => value.every((task) => tasks.some((t) => t.id === task)),
+ {
+ message: "Invalid notification type selected.",
+ }
+ ),
+})
+
+export default function FormRhfCheckbox() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ responses: true,
+ tasks: [],
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Notifications
+ Manage your notification preferences.
+
+
+
+
+ (
+
+
+ Responses
+
+ Get notified for requests that take time, like research or
+ image generation.
+
+
+
+
+
+ Push notifications
+
+
+
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+ (
+
+
+ Tasks
+
+ Get notified when tasks you've created have updates.
+
+
+ {tasks.map((task) => (
+
+ {
+ const newValue = checked
+ ? [...field.value, task.id]
+ : field.value.filter(
+ (value) => value !== task.id
+ )
+ field.onChange(newValue)
+ }}
+ />
+
+ {task.label}
+
+
+ ))}
+
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-complex.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-complex.tsx
new file mode 100644
index 0000000..9f10e22
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-complex.tsx
@@ -0,0 +1,306 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ plan: z
+ .string({
+ required_error: "Please select a subscription plan",
+ })
+ .min(1, "Please select a subscription plan")
+ .refine((value) => value === "basic" || value === "pro", {
+ message: "Invalid plan selection. Please choose Basic or Pro",
+ }),
+ billingPeriod: z
+ .string({
+ required_error: "Please select a billing period",
+ })
+ .min(1, "Please select a billing period"),
+ addons: z
+ .array(z.string())
+ .min(1, "Please select at least one add-on")
+ .max(3, "You can select up to 3 add-ons")
+ .refine(
+ (value) => value.every((addon) => addons.some((a) => a.id === addon)),
+ {
+ message: "You selected an invalid add-on",
+ }
+ ),
+ emailNotifications: z.boolean(),
+})
+
+export default function FormRhfComplex() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ plan: "basic",
+ billingPeriod: "",
+ addons: [],
+ emailNotifications: false,
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ You're almost there!
+
+ Choose your subscription plan and billing period.
+
+
+
+
+
+ {
+ const isInvalid = fieldState.invalid
+ return (
+
+ Subscription Plan
+
+ Choose your subscription plan.
+
+
+
+
+
+ Basic
+
+ For individuals and small teams
+
+
+
+
+
+
+
+
+ Pro
+
+ For businesses with higher demands
+
+
+
+
+
+
+ {isInvalid && }
+
+ )
+ }}
+ />
+
+ (
+
+
+ Billing Period
+
+
+
+
+
+
+ Monthly
+ Yearly
+
+
+
+ Choose how often you want to be billed.
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+ (
+
+ Add-ons
+
+ Select additional features you'd like to include.
+
+
+ {addons.map((addon) => (
+
+ {
+ const newValue = checked
+ ? [...field.value, addon.id]
+ : field.value.filter(
+ (value) => value !== addon.id
+ )
+ field.onChange(newValue)
+ field.onBlur()
+ }}
+ />
+
+
+ {addon.title}
+
+
+ {addon.description}
+
+
+
+ ))}
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+ (
+
+
+
+ Email Notifications
+
+
+ Receive email updates about your subscription
+
+
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+
+
+
+
+
+ Save Preferences
+
+ form.reset()}>
+ Reset
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-demo.tsx
new file mode 100644
index 0000000..6242894
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-demo.tsx
@@ -0,0 +1,149 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ title: z
+ .string()
+ .min(5, "Bug title must be at least 5 characters.")
+ .max(32, "Bug title must be at most 32 characters."),
+ description: z
+ .string()
+ .min(20, "Description must be at least 20 characters.")
+ .max(100, "Description must be at most 100 characters."),
+})
+
+export default function BugReportForm() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ title: "",
+ description: "",
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Bug Report
+
+ Help us improve by reporting bugs you encounter.
+
+
+
+
+
+ (
+
+
+ Bug Title
+
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+ (
+
+
+ Description
+
+
+
+
+
+ {field.value.length}/100 characters
+
+
+
+
+ Include steps to reproduce, expected behavior, and what
+ actually happened.
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Submit
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-input.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-input.tsx
new file mode 100644
index 0000000..da1b140
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-input.tsx
@@ -0,0 +1,113 @@
+
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ username: z
+ .string()
+ .min(3, "Username must be at least 3 characters.")
+ .max(10, "Username must be at most 10 characters.")
+ .regex(
+ /^[a-zA-Z0-9_]+$/,
+ "Username can only contain letters, numbers, and underscores."
+ ),
+})
+
+export default function FormRhfInput() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ username: "",
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Profile Settings
+
+ Update your profile information below.
+
+
+
+
+
+ (
+
+
+ Username
+
+
+
+ This is your public display name. Must be between 3 and 10
+ characters. Must only contain letters, numbers, and
+ underscores.
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-password.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-password.tsx
new file mode 100644
index 0000000..a753499
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-password.tsx
@@ -0,0 +1,223 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { CheckIcon } from "lucide-react"
+import { Controller, useForm, useWatch } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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,
+ FieldError,
+ FieldGroup,
+ FieldLabel,
+} from "@/registry/new-york-v4/ui/field"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Progress } from "@/registry/new-york-v4/ui/progress"
+
+const passwordRequirements = [
+ {
+ id: "length",
+ label: "At least 8 characters",
+ test: (val: string) => val.length >= 8,
+ },
+ {
+ id: "lowercase",
+ label: "One lowercase letter",
+ test: (val: string) => /[a-z]/.test(val),
+ },
+ {
+ id: "uppercase",
+ label: "One uppercase letter",
+ test: (val: string) => /[A-Z]/.test(val),
+ },
+ { id: "number", label: "One number", test: (val: string) => /\d/.test(val) },
+ {
+ id: "special",
+ label: "One special character",
+ test: (val: string) => /[!@#$%^&*(),.?":{}|<>]/.test(val),
+ },
+]
+
+const formSchema = z.object({
+ password: z
+ .string()
+ .min(8, "Password must be at least 8 characters")
+ .refine(
+ (val) => /[a-z]/.test(val),
+ "Password must contain at least one lowercase letter"
+ )
+ .refine(
+ (val) => /[A-Z]/.test(val),
+ "Password must contain at least one uppercase letter"
+ )
+ .refine(
+ (val) => /\d/.test(val),
+ "Password must contain at least one number"
+ )
+ .refine(
+ (val) => /[!@#$%^&*(),.?":{}|<>]/.test(val),
+ "Password must contain at least one special character"
+ ),
+})
+
+export default function FormRhfPassword() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ password: "",
+ },
+ })
+
+ const password = useWatch({
+ control: form.control,
+ name: "password",
+ })
+
+ // Calculate password strength.
+ const metRequirements = passwordRequirements.filter((req) =>
+ req.test(password || "")
+ )
+ const strengthPercentage =
+ (metRequirements.length / passwordRequirements.length) * 100
+
+ // Determine strength level and color.
+ const getStrengthColor = () => {
+ if (strengthPercentage === 0) return "bg-neutral-200"
+ if (strengthPercentage <= 40) return "bg-red-500"
+ if (strengthPercentage <= 80) return "bg-yellow-500"
+ return "bg-green-500"
+ }
+
+ const allRequirementsMet =
+ metRequirements.length === passwordRequirements.length
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Create Password
+
+ Choose a strong password to secure your account.
+
+
+
+
+
+ (
+
+
+ Password
+
+
+
+
+
+
+
+
+ {/* Password strength meter. */}
+
+
+
+ {/* Requirements list. */}
+
+ {passwordRequirements.map((requirement) => {
+ const isMet = requirement.test(password || "")
+ return (
+
+
+
+ {requirement.label}
+
+
+ )
+ })}
+
+
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+
+
+
+
+
+ Create Password
+
+ form.reset()}>
+ Reset
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-radiogroup.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-radiogroup.tsx
new file mode 100644
index 0000000..febf8a6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-radiogroup.tsx
@@ -0,0 +1,151 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ plan: z.string().min(1, "You must select a subscription plan to continue."),
+})
+
+export default function FormRhfRadioGroup() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ plan: "",
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Subscription Plan
+
+ See pricing and features for each plan.
+
+
+
+
+
+ (
+
+ Plan
+
+ You can upgrade or downgrade your plan at any time.
+
+
+ {plans.map((plan) => (
+
+
+
+ {plan.title}
+
+ {plan.description}
+
+
+
+
+
+ ))}
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-select.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-select.tsx
new file mode 100644
index 0000000..28c7c11
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-select.tsx
@@ -0,0 +1,149 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ language: z
+ .string()
+ .min(1, "Please select your spoken language.")
+ .refine((val) => val !== "auto", {
+ message:
+ "Auto-detection is not allowed. Please select a specific language.",
+ }),
+})
+
+export default function FormRhfSelect() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ language: "",
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Language Preferences
+
+ Select your preferred spoken language.
+
+
+
+
+
+ (
+
+
+
+ Spoken Language
+
+
+ For best results, select the language you speak.
+
+ {fieldState.invalid && (
+
+ )}
+
+
+
+
+
+
+ Auto
+
+ {spokenLanguages.map((language) => (
+
+ {language.label}
+
+ ))}
+
+
+
+ )}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-switch.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-switch.tsx
new file mode 100644
index 0000000..ab707ad
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-switch.tsx
@@ -0,0 +1,113 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ twoFactor: z.boolean().refine((val) => val === true, {
+ message: "It is highly recommended to enable two-factor authentication.",
+ }),
+})
+
+export default function FormRhfSwitch() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ twoFactor: false,
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Security Settings
+
+ Manage your account security preferences.
+
+
+
+
+
+ (
+
+
+
+ Multi-factor authentication
+
+
+ Enable multi-factor authentication to secure your account.
+
+ {fieldState.invalid && (
+
+ )}
+
+
+
+ )}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-rhf-textarea.tsx b/apps/ui/src/registry/new-york-v4/examples/form-rhf-textarea.tsx
new file mode 100644
index 0000000..691b000
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-rhf-textarea.tsx
@@ -0,0 +1,109 @@
+
+import * as React from "react"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Controller, useForm } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ about: z
+ .string()
+ .min(10, "Please provide at least 10 characters.")
+ .max(200, "Please keep it under 200 characters."),
+})
+
+export default function FormRhfTextarea() {
+ const form = useForm>({
+ resolver: zodResolver(formSchema),
+ defaultValues: {
+ about: "",
+ },
+ })
+
+ function onSubmit(data: z.infer) {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(data, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ }
+
+ return (
+
+
+ Personalization
+
+ Customize your experience by telling us more about yourself.
+
+
+
+
+
+ (
+
+
+ More about you
+
+
+
+ Tell us more about yourself. This will be used to help us
+ personalize your experience.
+
+ {fieldState.invalid && (
+
+ )}
+
+ )}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-array.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-array.tsx
new file mode 100644
index 0000000..07974bb
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-array.tsx
@@ -0,0 +1,178 @@
+/* eslint-disable react/no-children-prop */
+
+import * as React from "react"
+import { useForm } from "@tanstack/react-form"
+import { XIcon } from "lucide-react"
+import { toast } from "sonner"
+import { z } from "zod"
+
+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 = z.object({
+ emails: z
+ .array(
+ z.object({
+ address: z.string().email("Enter a valid email address."),
+ })
+ )
+ .min(1, "Add at least one email address.")
+ .max(5, "You can add up to 5 email addresses."),
+})
+
+export default function FormTanstackArray() {
+ const form = useForm({
+ defaultValues: {
+ emails: [{ address: "" }],
+ },
+ validators: {
+ onBlur: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Contact Emails
+ Manage your contact email addresses.
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {(field) => {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+ Email Addresses
+
+ Add up to 5 email addresses where we can contact you.
+
+
+ {field.state.value.map((_, index) => (
+ {
+ const isSubFieldInvalid =
+ subField.state.meta.isTouched &&
+ !subField.state.meta.isValid
+ return (
+
+
+
+
+ subField.handleChange(e.target.value)
+ }
+ aria-invalid={isSubFieldInvalid}
+ placeholder="name@example.com"
+ type="email"
+ autoComplete="email"
+ />
+ {field.state.value.length > 1 && (
+
+ field.removeValue(index)}
+ aria-label={`Remove email ${index + 1}`}
+ >
+
+
+
+ )}
+
+ {isSubFieldInvalid && (
+
+ )}
+
+
+ )
+ }}
+ />
+ ))}
+ field.pushValue({ address: "" })}
+ disabled={field.state.value.length >= 5}
+ >
+ Add Email Address
+
+
+ {isInvalid && }
+
+ )
+ }}
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-checkbox.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-checkbox.tsx
new file mode 100644
index 0000000..cec4ef6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-checkbox.tsx
@@ -0,0 +1,209 @@
+/* eslint-disable react/no-children-prop */
+
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ responses: z.boolean(),
+ tasks: z
+ .array(z.string())
+ .min(1, "Please select at least one notification type.")
+ .refine(
+ (value) => value.every((task) => tasks.some((t) => t.id === task)),
+ {
+ message: "Invalid notification type selected.",
+ }
+ ),
+})
+
+export default function FormTanstackCheckbox() {
+ const form = useForm({
+ defaultValues: {
+ responses: true,
+ tasks: [] as string[],
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Notifications
+ Manage your notification preferences.
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+
+ Responses
+
+ Get notified for requests that take time, like research
+ or image generation.
+
+
+
+
+ field.handleChange(checked === true)
+ }
+ disabled
+ />
+
+ Push notifications
+
+
+
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+
+ Tasks
+
+ Get notified when tasks you've created have
+ updates.
+
+
+ {tasks.map((task) => (
+
+ {
+ if (checked) {
+ field.pushValue(task.id)
+ } else {
+ const index = field.state.value.indexOf(
+ task.id
+ )
+ if (index > -1) {
+ field.removeValue(index)
+ }
+ }
+ }}
+ />
+
+ {task.label}
+
+
+ ))}
+
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-complex.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-complex.tsx
new file mode 100644
index 0000000..9944d53
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-complex.tsx
@@ -0,0 +1,312 @@
+/* eslint-disable react/no-children-prop */
+
+import * as React from "react"
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Card, CardContent, CardFooter } 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 = z.object({
+ plan: z
+ .string({
+ required_error: "Please select a subscription plan",
+ })
+ .min(1, "Please select a subscription plan")
+ .refine((value) => value === "basic" || value === "pro", {
+ message: "Invalid plan selection. Please choose Basic or Pro",
+ }),
+ billingPeriod: z
+ .string({
+ required_error: "Please select a billing period",
+ })
+ .min(1, "Please select a billing period"),
+ addons: z
+ .array(z.string())
+ .min(1, "Please select at least one add-on")
+ .max(3, "You can select up to 3 add-ons")
+ .refine(
+ (value) => value.every((addon) => addons.some((a) => a.id === addon)),
+ {
+ message: "You selected an invalid add-on",
+ }
+ ),
+ emailNotifications: z.boolean(),
+})
+
+export default function FormTanstackComplex() {
+ const form = useForm({
+ defaultValues: {
+ plan: "basic",
+ billingPeriod: "monthly",
+ addons: [] as string[],
+ emailNotifications: false,
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+ Subscription Plan
+
+ Choose your subscription plan.
+
+
+
+
+
+ Basic
+
+ For individuals and small teams
+
+
+
+
+
+
+
+
+ Pro
+
+ For businesses with higher demands
+
+
+
+
+
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+ Billing Period
+
+
+
+
+
+ Monthly
+ Yearly
+
+
+
+ Choose how often you want to be billed.
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+ Add-ons
+
+ Select additional features you'd like to include.
+
+
+ {addons.map((addon) => (
+
+ {
+ if (checked) {
+ field.pushValue(addon.id)
+ } else {
+ const index = field.state.value.indexOf(
+ addon.id
+ )
+ if (index > -1) {
+ field.removeValue(index)
+ }
+ }
+ }}
+ />
+
+
+ {addon.title}
+
+
+ {addon.description}
+
+
+
+ ))}
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+
+
+ Email Notifications
+
+
+ Receive email updates about your subscription
+
+
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+
+
+
+
+
+ Save Preferences
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-demo.tsx
new file mode 100644
index 0000000..79d9218
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-demo.tsx
@@ -0,0 +1,164 @@
+/* eslint-disable react/no-children-prop */
+
+import * as React from "react"
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ title: z
+ .string()
+ .min(5, "Bug title must be at least 5 characters.")
+ .max(32, "Bug title must be at most 32 characters."),
+ description: z
+ .string()
+ .min(20, "Description must be at least 20 characters.")
+ .max(100, "Description must be at most 100 characters."),
+})
+
+export default function BugReportForm() {
+ const form = useForm({
+ defaultValues: {
+ title: "",
+ description: "",
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Bug Report
+
+ Help us improve by reporting bugs you encounter.
+
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+ Bug Title
+ field.handleChange(e.target.value)}
+ aria-invalid={isInvalid}
+ placeholder="Login button not working on mobile"
+ autoComplete="off"
+ />
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+ Description
+
+ field.handleChange(e.target.value)}
+ placeholder="I'm having an issue with the login button on mobile."
+ rows={6}
+ className="min-h-24 resize-none"
+ aria-invalid={isInvalid}
+ />
+
+
+ {field.state.value.length}/100 characters
+
+
+
+
+ Include steps to reproduce, expected behavior, and what
+ actually happened.
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Submit
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-input.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-input.tsx
new file mode 100644
index 0000000..8a8ee29
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-input.tsx
@@ -0,0 +1,126 @@
+/* eslint-disable react/no-children-prop */
+
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ username: z
+ .string()
+ .min(3, "Username must be at least 3 characters.")
+ .max(10, "Username must be at most 10 characters.")
+ .regex(
+ /^[a-zA-Z0-9_]+$/,
+ "Username can only contain letters, numbers, and underscores."
+ ),
+})
+
+export default function FormTanstackInput() {
+ const form = useForm({
+ defaultValues: {
+ username: "",
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Profile Settings
+
+ Update your profile information below.
+
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+
+ Username
+
+ field.handleChange(e.target.value)}
+ aria-invalid={isInvalid}
+ placeholder="shadcn"
+ autoComplete="username"
+ />
+
+ This is your public display name. Must be between 3 and 10
+ characters. Must only contain letters, numbers, and
+ underscores.
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-radiogroup.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-radiogroup.tsx
new file mode 100644
index 0000000..1f8b0a9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-radiogroup.tsx
@@ -0,0 +1,159 @@
+/* eslint-disable react/no-children-prop */
+
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ plan: z.string().min(1, "You must select a subscription plan to continue."),
+})
+
+export default function FormTanstackRadioGroup() {
+ const form = useForm({
+ defaultValues: {
+ plan: "",
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Subscription Plan
+
+ See pricing and features for each plan.
+
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+ Plan
+
+ You can upgrade or downgrade your plan at any time.
+
+
+ {plans.map((plan) => (
+
+
+
+ {plan.title}
+
+ {plan.description}
+
+
+
+
+
+ ))}
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-select.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-select.tsx
new file mode 100644
index 0000000..51a288a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-select.tsx
@@ -0,0 +1,158 @@
+/* eslint-disable react/no-children-prop */
+
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ language: z
+ .string()
+ .min(1, "Please select your spoken language.")
+ .refine((val) => val !== "auto", {
+ message:
+ "Auto-detection is not allowed. Please select a specific language.",
+ }),
+})
+
+export default function FormTanstackSelect() {
+ const form = useForm({
+ defaultValues: {
+ language: "",
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Language Preferences
+
+ Select your preferred spoken language.
+
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+
+
+ Spoken Language
+
+
+ For best results, select the language you speak.
+
+ {isInvalid && (
+
+ )}
+
+
+
+
+
+
+ Auto
+
+ {spokenLanguages.map((language) => (
+
+ {language.label}
+
+ ))}
+
+
+
+ )
+ }}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-switch.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-switch.tsx
new file mode 100644
index 0000000..fd8e208
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-switch.tsx
@@ -0,0 +1,120 @@
+/* eslint-disable react/no-children-prop */
+
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ twoFactor: z.boolean().refine((val) => val === true, {
+ message: "It is highly recommended to enable two-factor authentication.",
+ }),
+})
+
+export default function FormTanstackSwitch() {
+ const form = useForm({
+ defaultValues: {
+ twoFactor: false,
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Security Settings
+
+ Manage your account security preferences.
+
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+
+
+ Multi-factor authentication
+
+
+ Enable multi-factor authentication to secure your
+ account.
+
+ {isInvalid && (
+
+ )}
+
+
+
+ )
+ }}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/form-tanstack-textarea.tsx b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-textarea.tsx
new file mode 100644
index 0000000..158663e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/form-tanstack-textarea.tsx
@@ -0,0 +1,121 @@
+/* eslint-disable react/no-children-prop */
+
+import { useForm } from "@tanstack/react-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+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 = z.object({
+ about: z
+ .string()
+ .min(10, "Please provide at least 10 characters.")
+ .max(200, "Please keep it under 200 characters."),
+})
+
+export default function FormTanstackTextarea() {
+ const form = useForm({
+ defaultValues: {
+ about: "",
+ },
+ validators: {
+ onSubmit: formSchema,
+ },
+ onSubmit: async ({ value }) => {
+ toast("You submitted the following values:", {
+ description: (
+
+ {JSON.stringify(value, null, 2)}
+
+ ),
+ position: "bottom-right",
+ classNames: {
+ content: "flex flex-col gap-2",
+ },
+ style: {
+ "--border-radius": "calc(var(--radius) + 4px)",
+ } as React.CSSProperties,
+ })
+ },
+ })
+
+ return (
+
+
+ Personalization
+
+ Customize your experience by telling us more about yourself.
+
+
+
+ {
+ e.preventDefault()
+ form.handleSubmit()
+ }}
+ >
+
+ {
+ const isInvalid =
+ field.state.meta.isTouched && !field.state.meta.isValid
+ return (
+
+
+ More about you
+
+ field.handleChange(e.target.value)}
+ aria-invalid={isInvalid}
+ placeholder="I'm a software engineer..."
+ className="min-h-[120px]"
+ />
+
+ Tell us more about yourself. This will be used to help us
+ personalize your experience.
+
+ {isInvalid && (
+
+ )}
+
+ )
+ }}
+ />
+
+
+
+
+
+ form.reset()}>
+ Reset
+
+
+ Save
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/hover-card-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/hover-card-demo.tsx
new file mode 100644
index 0000000..6dc2ef4
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/hover-card-demo.tsx
@@ -0,0 +1,40 @@
+import { CalendarIcon } from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@/registry/new-york-v4/ui/hover-card"
+
+export default function HoverCardDemo() {
+ return (
+
+
+ @nextjs
+
+
+
+
+
+ VC
+
+
+
@nextjs
+
+ The React Framework – created and maintained by @vercel.
+
+
+ Joined December 2021
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/input-demo.tsx
new file mode 100644
index 0000000..4fbe0cc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-demo.tsx
@@ -0,0 +1,5 @@
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export default function InputDemo() {
+ return
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-disabled.tsx b/apps/ui/src/registry/new-york-v4/examples/input-disabled.tsx
new file mode 100644
index 0000000..b68794d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-disabled.tsx
@@ -0,0 +1,5 @@
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export default function InputDisabled() {
+ return
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-file.tsx b/apps/ui/src/registry/new-york-v4/examples/input-file.tsx
new file mode 100644
index 0000000..aa1a3f0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-file.tsx
@@ -0,0 +1,11 @@
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function InputFile() {
+ return (
+
+ Picture
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-button-group.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-button-group.tsx
new file mode 100644
index 0000000..b95c3b2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-button-group.tsx
@@ -0,0 +1,31 @@
+import { Link2Icon } from "lucide-react"
+
+import {
+ ButtonGroup,
+ ButtonGroupText,
+} from "@/registry/new-york-v4/ui/button-group"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function InputGroupButtonGroup() {
+ return (
+
+
+
+ https://
+
+
+
+
+
+
+
+ .com
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-button.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-button.tsx
new file mode 100644
index 0000000..8d812c6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-button.tsx
@@ -0,0 +1,85 @@
+
+import * as React from "react"
+import {
+ IconCheck,
+ IconCopy,
+ IconInfoCircle,
+ IconStar,
+} from "@tabler/icons-react"
+
+import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+
+export default function InputGroupButtonExample() {
+ const { copyToClipboard, isCopied } = useCopyToClipboard()
+ const [isFavorite, setIsFavorite] = React.useState(false)
+
+ return (
+
+
+
+
+ {
+ copyToClipboard("https://x.com/shadcn")
+ }}
+ >
+ {isCopied ? : }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Your connection is not secure.
+ You should not enter any sensitive information on this site.
+
+
+
+ https://
+
+
+
+ setIsFavorite(!isFavorite)}
+ size="icon-xs"
+ >
+
+
+
+
+
+
+
+ Search
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-custom.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-custom.tsx
new file mode 100644
index 0000000..fdee27a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-custom.tsx
@@ -0,0 +1,27 @@
+
+import TextareaAutosize from "react-textarea-autosize"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+} from "@/registry/new-york-v4/ui/input-group"
+
+export default function InputGroupCustom() {
+ return (
+
+
+
+
+
+ Submit
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-demo.tsx
new file mode 100644
index 0000000..c7b8d11
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-demo.tsx
@@ -0,0 +1,98 @@
+import { IconCheck, IconInfoCircle, IconPlus } from "@tabler/icons-react"
+import { ArrowUpIcon, Search } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+ InputGroupText,
+ InputGroupTextarea,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/new-york-v4/ui/tooltip"
+
+export default function InputGroupDemo() {
+ return (
+
+
+
+
+
+
+ 12 results
+
+
+
+
+ https://
+
+
+
+
+
+
+
+
+ This is content in a tooltip.
+
+
+
+
+
+
+
+
+
+
+
+ Auto
+
+
+ Auto
+ Agent
+ Manual
+
+
+ 52% used
+
+
+
+ Send
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-dropdown.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-dropdown.tsx
new file mode 100644
index 0000000..708e03a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-dropdown.tsx
@@ -0,0 +1,59 @@
+import { ChevronDownIcon, MoreHorizontal } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+
+export default function InputGroupDropdown() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ Settings
+ Copy path
+ Open location
+
+
+
+
+
+
+
+
+
+
+ Search In...
+
+
+
+ Documentation
+ Blog Posts
+ Changelog
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-icon.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-icon.tsx
new file mode 100644
index 0000000..6ef0584
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-icon.tsx
@@ -0,0 +1,49 @@
+import {
+ CheckIcon,
+ CreditCardIcon,
+ InfoIcon,
+ MailIcon,
+ SearchIcon,
+ StarIcon,
+} from "lucide-react"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+
+export default function InputGroupIcon() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-label.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-label.tsx
new file mode 100644
index 0000000..9783d4f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-label.tsx
@@ -0,0 +1,50 @@
+import { InfoIcon } from "lucide-react"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/new-york-v4/ui/tooltip"
+
+export default function InputGroupLabel() {
+ return (
+
+
+
+
+ @
+
+
+
+
+
+
+ Email
+
+
+
+
+
+
+
+
+ We'll use this to send you notifications
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-spinner.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-spinner.tsx
new file mode 100644
index 0000000..cc0a393
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-spinner.tsx
@@ -0,0 +1,46 @@
+import { LoaderIcon } from "lucide-react"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+ InputGroupText,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function InputGroupSpinner() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Saving...
+
+
+
+
+
+
+
+
+
+
+ Please wait...
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-text.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-text.tsx
new file mode 100644
index 0000000..510ae18
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-text.tsx
@@ -0,0 +1,46 @@
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+ InputGroupText,
+ InputGroupTextarea,
+} from "@/registry/new-york-v4/ui/input-group"
+
+export default function InputGroupTextExample() {
+ return (
+
+
+
+ $
+
+
+
+ USD
+
+
+
+
+ https://
+
+
+
+ .com
+
+
+
+
+
+ @company.com
+
+
+
+
+
+
+ 120 characters left
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-textarea.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-textarea.tsx
new file mode 100644
index 0000000..b766317
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-textarea.tsx
@@ -0,0 +1,46 @@
+import {
+ IconBrandJavascript,
+ IconCopy,
+ IconCornerDownLeft,
+ IconRefresh,
+} from "@tabler/icons-react"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupText,
+ InputGroupTextarea,
+} from "@/registry/new-york-v4/ui/input-group"
+
+export default function InputGroupTextareaExample() {
+ return (
+
+
+
+
+ Line 1, Column 1
+
+ Run
+
+
+
+
+
+ script.js
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-group-tooltip.tsx b/apps/ui/src/registry/new-york-v4/examples/input-group-tooltip.tsx
new file mode 100644
index 0000000..2cf0e36
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-group-tooltip.tsx
@@ -0,0 +1,77 @@
+import { HelpCircle, InfoIcon } from "lucide-react"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/new-york-v4/ui/tooltip"
+
+export default function InputGroupTooltip() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ Password must be at least 8 characters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ We'll use this to send you notifications
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Click for help with API keys
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-otp-controlled.tsx b/apps/ui/src/registry/new-york-v4/examples/input-otp-controlled.tsx
new file mode 100644
index 0000000..5c05562
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-otp-controlled.tsx
@@ -0,0 +1,38 @@
+
+import * as React from "react"
+
+import {
+ InputOTP,
+ InputOTPGroup,
+ InputOTPSlot,
+} from "@/registry/new-york-v4/ui/input-otp"
+
+export default function InputOTPControlled() {
+ const [value, setValue] = React.useState("")
+
+ return (
+
+
setValue(value)}
+ >
+
+
+
+
+
+
+
+
+
+
+ {value === "" ? (
+ <>Enter your one-time password.>
+ ) : (
+ <>You entered: {value}>
+ )}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-otp-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/input-otp-demo.tsx
new file mode 100644
index 0000000..34d82fb
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-otp-demo.tsx
@@ -0,0 +1,24 @@
+import {
+ InputOTP,
+ InputOTPGroup,
+ InputOTPSeparator,
+ InputOTPSlot,
+} from "@/registry/new-york-v4/ui/input-otp"
+
+export default function InputOTPDemo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-otp-pattern.tsx b/apps/ui/src/registry/new-york-v4/examples/input-otp-pattern.tsx
new file mode 100644
index 0000000..065d292
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-otp-pattern.tsx
@@ -0,0 +1,23 @@
+
+import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
+
+import {
+ InputOTP,
+ InputOTPGroup,
+ InputOTPSlot,
+} from "@/registry/new-york-v4/ui/input-otp"
+
+export default function InputOTPPattern() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-otp-separator.tsx b/apps/ui/src/registry/new-york-v4/examples/input-otp-separator.tsx
new file mode 100644
index 0000000..eaac7d9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-otp-separator.tsx
@@ -0,0 +1,29 @@
+import React from "react"
+
+import {
+ InputOTP,
+ InputOTPGroup,
+ InputOTPSeparator,
+ InputOTPSlot,
+} from "@/registry/new-york-v4/ui/input-otp"
+
+export default function InputOTPWithSeparator() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-with-button.tsx b/apps/ui/src/registry/new-york-v4/examples/input-with-button.tsx
new file mode 100644
index 0000000..2e6c3d1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-with-button.tsx
@@ -0,0 +1,13 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Input } from "@/registry/new-york-v4/ui/input"
+
+export default function InputWithButton() {
+ return (
+
+
+
+ Subscribe
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-with-label.tsx b/apps/ui/src/registry/new-york-v4/examples/input-with-label.tsx
new file mode 100644
index 0000000..ed798cc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-with-label.tsx
@@ -0,0 +1,11 @@
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function InputWithLabel() {
+ return (
+
+ Email
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/input-with-text.tsx b/apps/ui/src/registry/new-york-v4/examples/input-with-text.tsx
new file mode 100644
index 0000000..ee1bdb5
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/input-with-text.tsx
@@ -0,0 +1,12 @@
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function InputWithText() {
+ return (
+
+
Email
+
+
Enter your email address.
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-avatar.tsx b/apps/ui/src/registry/new-york-v4/examples/item-avatar.tsx
new file mode 100644
index 0000000..8ba20ce
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-avatar.tsx
@@ -0,0 +1,80 @@
+import { Plus } from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+export default function ItemAvatar() {
+ return (
+
+
-
+
+
+
+ ER
+
+
+
+ Evil Rabbit
+ Last seen 5 months ago
+
+
+
+
+
+
+
+
-
+
+
+
+
+ CN
+
+
+
+ LR
+
+
+
+ ER
+
+
+
+
+ No Team Members
+
+ Invite your team to collaborate on this project.
+
+
+
+
+ Invite
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/item-demo.tsx
new file mode 100644
index 0000000..7c48128
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-demo.tsx
@@ -0,0 +1,44 @@
+import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+export default function ItemDemo() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-dropdown.tsx b/apps/ui/src/registry/new-york-v4/examples/item-dropdown.tsx
new file mode 100644
index 0000000..5f5c397
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-dropdown.tsx
@@ -0,0 +1,72 @@
+
+import { ChevronDownIcon } from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+const people = [
+ {
+ username: "shadcn",
+ avatar: "https://github.com/shadcn.png",
+ email: "shadcn@vercel.com",
+ },
+ {
+ username: "maxleiter",
+ avatar: "https://github.com/maxleiter.png",
+ email: "maxleiter@vercel.com",
+ },
+ {
+ username: "evilrabbit",
+ avatar: "https://github.com/evilrabbit.png",
+ email: "evilrabbit@vercel.com",
+ },
+]
+
+export default function ItemDropdown() {
+ return (
+
+
+
+
+ Select
+
+
+
+ {people.map((person) => (
+
+ -
+
+
+
+ {person.username.charAt(0)}
+
+
+
+ {person.username}
+ {person.email}
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-group.tsx b/apps/ui/src/registry/new-york-v4/examples/item-group.tsx
new file mode 100644
index 0000000..b52da14
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-group.tsx
@@ -0,0 +1,68 @@
+import * as React from "react"
+import { PlusIcon } from "lucide-react"
+
+import {
+ Avatar,
+ AvatarFallback,
+ AvatarImage,
+} from "@/registry/new-york-v4/ui/avatar"
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemGroup,
+ ItemMedia,
+ ItemSeparator,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+const people = [
+ {
+ username: "shadcn",
+ avatar: "https://github.com/shadcn.png",
+ email: "shadcn@vercel.com",
+ },
+ {
+ username: "maxleiter",
+ avatar: "https://github.com/maxleiter.png",
+ email: "maxleiter@vercel.com",
+ },
+ {
+ username: "evilrabbit",
+ avatar: "https://github.com/evilrabbit.png",
+ email: "evilrabbit@vercel.com",
+ },
+]
+
+export default function ItemGroupExample() {
+ return (
+
+
+ {people.map((person, index) => (
+
+ -
+
+
+
+ {person.username.charAt(0)}
+
+
+
+ {person.username}
+ {person.email}
+
+
+
+
+
+
+
+ {index !== people.length - 1 && }
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-header.tsx b/apps/ui/src/registry/new-york-v4/examples/item-header.tsx
new file mode 100644
index 0000000..d374e42
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-header.tsx
@@ -0,0 +1,60 @@
+import Image from "next/image"
+
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemGroup,
+ ItemHeader,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+const models = [
+ {
+ name: "v0-1.5-sm",
+ description: "Everyday tasks and UI generation.",
+ image:
+ "https://images.unsplash.com/photo-1650804068570-7fb2e3dbf888?q=80&w=640&auto=format&fit=crop",
+ credit: "Valeria Reverdo on Unsplash",
+ },
+ {
+ name: "v0-1.5-lg",
+ description: "Advanced thinking or reasoning.",
+ image:
+ "https://images.unsplash.com/photo-1610280777472-54133d004c8c?q=80&w=640&auto=format&fit=crop",
+ credit: "Michael Oeser on Unsplash",
+ },
+ {
+ name: "v0-2.0-mini",
+ description: "Open Source model for everyone.",
+ image:
+ "https://images.unsplash.com/photo-1602146057681-08560aee8cde?q=80&w=640&auto=format&fit=crop",
+ credit: "Cherry Laithang on Unsplash",
+ },
+]
+
+export default function ItemHeaderDemo() {
+ return (
+
+
+ {models.map((model) => (
+ -
+
+
+
+
+ {model.name}
+ {model.description}
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-icon.tsx b/apps/ui/src/registry/new-york-v4/examples/item-icon.tsx
new file mode 100644
index 0000000..97aef73
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-icon.tsx
@@ -0,0 +1,34 @@
+import { ShieldAlertIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+export default function ItemIcon() {
+ return (
+
+ -
+
+
+
+
+ Security Alert
+
+ New login detected from unknown device.
+
+
+
+
+ Review
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-image.tsx b/apps/ui/src/registry/new-york-v4/examples/item-image.tsx
new file mode 100644
index 0000000..9cd7ee4
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-image.tsx
@@ -0,0 +1,65 @@
+import Image from "next/image"
+
+import {
+ Item,
+ ItemContent,
+ ItemDescription,
+ ItemGroup,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+const music = [
+ {
+ title: "Midnight City Lights",
+ artist: "Neon Dreams",
+ album: "Electric Nights",
+ duration: "3:45",
+ },
+ {
+ title: "Coffee Shop Conversations",
+ artist: "The Morning Brew",
+ album: "Urban Stories",
+ duration: "4:05",
+ },
+ {
+ title: "Digital Rain",
+ artist: "Cyber Symphony",
+ album: "Binary Beats",
+ duration: "3:30",
+ },
+]
+
+export default function ItemImage() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-link.tsx b/apps/ui/src/registry/new-york-v4/examples/item-link.tsx
new file mode 100644
index 0000000..4f108bf
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-link.tsx
@@ -0,0 +1,42 @@
+import { ChevronRightIcon, ExternalLinkIcon } from "lucide-react"
+
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+export default function ItemLink() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-size.tsx b/apps/ui/src/registry/new-york-v4/examples/item-size.tsx
new file mode 100644
index 0000000..8fdd32f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-size.tsx
@@ -0,0 +1,44 @@
+import { BadgeCheckIcon, ChevronRightIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+export default function ItemSizeDemo() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/item-variant.tsx b/apps/ui/src/registry/new-york-v4/examples/item-variant.tsx
new file mode 100644
index 0000000..c52c503
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/item-variant.tsx
@@ -0,0 +1,54 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+
+export default function ItemVariant() {
+ return (
+
+ -
+
+ Default Variant
+
+ Standard styling with subtle background and borders.
+
+
+
+
+ Open
+
+
+
+ -
+
+ Outline Variant
+
+ Outlined style with clear borders and transparent background.
+
+
+
+
+ Open
+
+
+
+ -
+
+ Muted Variant
+
+ Subdued appearance with muted colors for secondary content.
+
+
+
+
+ Open
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/kbd-button.tsx b/apps/ui/src/registry/new-york-v4/examples/kbd-button.tsx
new file mode 100644
index 0000000..6129f16
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/kbd-button.tsx
@@ -0,0 +1,15 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Kbd } from "@/registry/new-york-v4/ui/kbd"
+
+export default function KbdButton() {
+ return (
+
+
+ Accept ⏎
+
+
+ Cancel Esc
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/kbd-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/kbd-demo.tsx
new file mode 100644
index 0000000..2f9d14f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/kbd-demo.tsx
@@ -0,0 +1,19 @@
+import { Kbd, KbdGroup } from "@/registry/new-york-v4/ui/kbd"
+
+export default function KbdDemo() {
+ return (
+
+
+ ⌘
+ ⇧
+ ⌥
+ ⌃
+
+
+ Ctrl
+ +
+ B
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/kbd-group.tsx b/apps/ui/src/registry/new-york-v4/examples/kbd-group.tsx
new file mode 100644
index 0000000..e93368a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/kbd-group.tsx
@@ -0,0 +1,16 @@
+import { Kbd, KbdGroup } from "@/registry/new-york-v4/ui/kbd"
+
+export default function KbdGroupExample() {
+ return (
+
+
+ Use{" "}
+
+ Ctrl + B
+ Ctrl + K
+ {" "}
+ to open the command palette
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/kbd-input-group.tsx b/apps/ui/src/registry/new-york-v4/examples/kbd-input-group.tsx
new file mode 100644
index 0000000..67de27c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/kbd-input-group.tsx
@@ -0,0 +1,25 @@
+import { SearchIcon } from "lucide-react"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupInput,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Kbd } from "@/registry/new-york-v4/ui/kbd"
+
+export default function KbdInputGroup() {
+ return (
+
+
+
+
+
+
+
+ ⌘
+ K
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/kbd-tooltip.tsx b/apps/ui/src/registry/new-york-v4/examples/kbd-tooltip.tsx
new file mode 100644
index 0000000..29fb1ef
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/kbd-tooltip.tsx
@@ -0,0 +1,41 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { ButtonGroup } from "@/registry/new-york-v4/ui/button-group"
+import { Kbd, KbdGroup } from "@/registry/new-york-v4/ui/kbd"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/new-york-v4/ui/tooltip"
+
+export default function KbdTooltip() {
+ return (
+
+
+
+
+
+ Save
+
+
+
+ Save Changes S
+
+
+
+
+
+ Print
+
+
+
+ Print Document{" "}
+
+ Ctrl
+ P
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/label-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/label-demo.tsx
new file mode 100644
index 0000000..7556a14
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/label-demo.tsx
@@ -0,0 +1,13 @@
+import { Checkbox } from "@/registry/new-york-v4/ui/checkbox"
+import { Label } from "@/registry/new-york-v4/ui/label"
+
+export default function LabelDemo() {
+ return (
+
+
+
+ Accept terms and conditions
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/menubar-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/menubar-demo.tsx
new file mode 100644
index 0000000..9c821ce
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/menubar-demo.tsx
@@ -0,0 +1,107 @@
+import {
+ Menubar,
+ MenubarCheckboxItem,
+ MenubarContent,
+ MenubarItem,
+ MenubarMenu,
+ MenubarRadioGroup,
+ MenubarRadioItem,
+ MenubarSeparator,
+ MenubarShortcut,
+ MenubarSub,
+ MenubarSubContent,
+ MenubarSubTrigger,
+ MenubarTrigger,
+} from "@/registry/new-york-v4/ui/menubar"
+
+export default function MenubarDemo() {
+ return (
+
+
+ File
+
+
+ New Tab ⌘T
+
+
+ New Window ⌘N
+
+ New Incognito Window
+
+
+ Share
+
+ Email link
+ Messages
+ Notes
+
+
+
+
+ Print... ⌘P
+
+
+
+
+ Edit
+
+
+ Undo ⌘Z
+
+
+ Redo ⇧⌘Z
+
+
+
+ Find
+
+ Search the web
+
+ Find...
+ Find Next
+ Find Previous
+
+
+
+ Cut
+ Copy
+ Paste
+
+
+
+ View
+
+ Always Show Bookmarks Bar
+
+ Always Show Full URLs
+
+
+
+ Reload ⌘R
+
+
+ Force Reload ⇧⌘R
+
+
+ Toggle Fullscreen
+
+ Hide Sidebar
+
+
+
+ Profiles
+
+
+ Andy
+ Benoit
+ Luis
+
+
+ Edit...
+
+ Add Profile...
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/mode-toggle.tsx b/apps/ui/src/registry/new-york-v4/examples/mode-toggle.tsx
new file mode 100644
index 0000000..b18a404
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/mode-toggle.tsx
@@ -0,0 +1,39 @@
+
+import * as React from "react"
+import { Moon, Sun } from "lucide-react"
+import { useTheme } from "next-themes"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+
+export default function ModeToggle() {
+ const { setTheme } = useTheme()
+
+ return (
+
+
+
+
+
+ Toggle theme
+
+
+
+ setTheme("light")}>
+ Light
+
+ setTheme("dark")}>
+ Dark
+
+ setTheme("system")}>
+ System
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/native-select-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/native-select-demo.tsx
new file mode 100644
index 0000000..2763871
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/native-select-demo.tsx
@@ -0,0 +1,16 @@
+import {
+ NativeSelect,
+ NativeSelectOption,
+} from "@/registry/new-york-v4/ui/native-select"
+
+export default function NativeSelectDemo() {
+ return (
+
+ Select status
+ Todo
+ In Progress
+ Done
+ Cancelled
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/native-select-disabled.tsx b/apps/ui/src/registry/new-york-v4/examples/native-select-disabled.tsx
new file mode 100644
index 0000000..cbc72f2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/native-select-disabled.tsx
@@ -0,0 +1,16 @@
+import {
+ NativeSelect,
+ NativeSelectOption,
+} from "@/registry/new-york-v4/ui/native-select"
+
+export default function NativeSelectDisabled() {
+ return (
+
+ Select priority
+ Low
+ Medium
+ High
+ Critical
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/native-select-groups.tsx b/apps/ui/src/registry/new-york-v4/examples/native-select-groups.tsx
new file mode 100644
index 0000000..59bb621
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/native-select-groups.tsx
@@ -0,0 +1,38 @@
+import {
+ NativeSelect,
+ NativeSelectOptGroup,
+ NativeSelectOption,
+} from "@/registry/new-york-v4/ui/native-select"
+
+export default function NativeSelectGroups() {
+ return (
+
+ Select department
+
+ Frontend
+ Backend
+ DevOps
+
+
+ Sales Rep
+
+ Account Manager
+
+
+ Sales Director
+
+
+
+
+ Customer Support
+
+
+ Product Manager
+
+
+ Operations Manager
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/native-select-invalid.tsx b/apps/ui/src/registry/new-york-v4/examples/native-select-invalid.tsx
new file mode 100644
index 0000000..59ffbb2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/native-select-invalid.tsx
@@ -0,0 +1,16 @@
+import {
+ NativeSelect,
+ NativeSelectOption,
+} from "@/registry/new-york-v4/ui/native-select"
+
+export default function NativeSelectInvalid() {
+ return (
+
+ Select role
+ Admin
+ Editor
+ Viewer
+ Guest
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/navigation-menu-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/navigation-menu-demo.tsx
new file mode 100644
index 0000000..15b5410
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/navigation-menu-demo.tsx
@@ -0,0 +1,214 @@
+
+import * as React from "react"
+import Link from "next/link"
+import { CircleCheckIcon, CircleHelpIcon, CircleIcon } from "lucide-react"
+
+import { useIsMobile } from "@/registry/new-york-v4/hooks/use-mobile"
+import {
+ NavigationMenu,
+ NavigationMenuContent,
+ NavigationMenuItem,
+ NavigationMenuLink,
+ NavigationMenuList,
+ NavigationMenuTrigger,
+ navigationMenuTriggerStyle,
+} from "@/registry/new-york-v4/ui/navigation-menu"
+
+const components: { title: string; href: string; description: string }[] = [
+ {
+ title: "Alert Dialog",
+ href: "/docs/primitives/alert-dialog",
+ description:
+ "A modal dialog that interrupts the user with important content and expects a response.",
+ },
+ {
+ title: "Hover Card",
+ href: "/docs/primitives/hover-card",
+ description:
+ "For sighted users to preview content available behind a link.",
+ },
+ {
+ title: "Progress",
+ href: "/docs/primitives/progress",
+ description:
+ "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.",
+ },
+ {
+ title: "Scroll-area",
+ href: "/docs/primitives/scroll-area",
+ description: "Visually or semantically separates content.",
+ },
+ {
+ title: "Tabs",
+ href: "/docs/primitives/tabs",
+ description:
+ "A set of layered sections of content—known as tab panels—that are displayed one at a time.",
+ },
+ {
+ title: "Tooltip",
+ href: "/docs/primitives/tooltip",
+ description:
+ "A popup that displays information related to an element when the element receives keyboard focus or the mouse hovers over it.",
+ },
+]
+
+export default function NavigationMenuDemo() {
+ const isMobile = useIsMobile()
+
+ return (
+
+
+
+ Home
+
+
+
+
+
+ Components
+
+
+ {components.map((component) => (
+
+ {component.description}
+
+ ))}
+
+
+
+
+
+ Docs
+
+
+
+ List
+
+
+
+
+
+ Components
+
+ Browse all components in the library.
+
+
+
+
+
+ Documentation
+
+ Learn how to use the library.
+
+
+
+
+
+ Blog
+
+ Read our latest blog posts.
+
+
+
+
+
+
+
+
+ Simple
+
+
+
+
+ Components
+
+
+ Documentation
+
+
+ Blocks
+
+
+
+
+
+
+ With Icon
+
+
+
+
+
+
+ Backlog
+
+
+
+
+
+ To Do
+
+
+
+
+
+ Done
+
+
+
+
+
+
+
+
+ )
+}
+
+function ListItem({
+ title,
+ children,
+ href,
+ ...props
+}: React.ComponentPropsWithoutRef<"li"> & { href: string }) {
+ return (
+
+
+
+ {title}
+
+ {children}
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/pagination-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/pagination-demo.tsx
new file mode 100644
index 0000000..e746ad4
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/pagination-demo.tsx
@@ -0,0 +1,38 @@
+import {
+ Pagination,
+ PaginationContent,
+ PaginationEllipsis,
+ PaginationItem,
+ PaginationLink,
+ PaginationNext,
+ PaginationPrevious,
+} from "@/registry/new-york-v4/ui/pagination"
+
+export default function PaginationDemo() {
+ return (
+
+
+
+
+
+
+ 1
+
+
+
+ 2
+
+
+
+ 3
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/popover-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/popover-demo.tsx
new file mode 100644
index 0000000..23f1e17
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/popover-demo.tsx
@@ -0,0 +1,62 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/registry/new-york-v4/ui/popover"
+
+export default function PopoverDemo() {
+ return (
+
+
+ Open popover
+
+
+
+
+
Dimensions
+
+ Set the dimensions for the layer.
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/progress-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/progress-demo.tsx
new file mode 100644
index 0000000..a1adea6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/progress-demo.tsx
@@ -0,0 +1,15 @@
+
+import * as React from "react"
+
+import { Progress } from "@/registry/new-york-v4/ui/progress"
+
+export default function ProgressDemo() {
+ const [progress, setProgress] = React.useState(13)
+
+ React.useEffect(() => {
+ const timer = setTimeout(() => setProgress(66), 500)
+ return () => clearTimeout(timer)
+ }, [])
+
+ return
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/radio-group-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/radio-group-demo.tsx
new file mode 100644
index 0000000..297fa19
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/radio-group-demo.tsx
@@ -0,0 +1,24 @@
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ RadioGroup,
+ RadioGroupItem,
+} from "@/registry/new-york-v4/ui/radio-group"
+
+export default function RadioGroupDemo() {
+ return (
+
+
+
+ Default
+
+
+
+ Comfortable
+
+
+
+ Compact
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/resizable-demo-with-handle.tsx b/apps/ui/src/registry/new-york-v4/examples/resizable-demo-with-handle.tsx
new file mode 100644
index 0000000..01e2292
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/resizable-demo-with-handle.tsx
@@ -0,0 +1,36 @@
+import {
+ ResizableHandle,
+ ResizablePanel,
+ ResizablePanelGroup,
+} from "@/registry/new-york-v4/ui/resizable"
+
+export default function ResizableDemo() {
+ return (
+
+
+
+ One
+
+
+
+
+
+
+
+ Two
+
+
+
+
+
+ Three
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/resizable-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/resizable-demo.tsx
new file mode 100644
index 0000000..4f79910
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/resizable-demo.tsx
@@ -0,0 +1,36 @@
+import {
+ ResizableHandle,
+ ResizablePanel,
+ ResizablePanelGroup,
+} from "@/registry/new-york-v4/ui/resizable"
+
+export default function ResizableDemo() {
+ return (
+
+
+
+ One
+
+
+
+
+
+
+
+ Two
+
+
+
+
+
+ Three
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/resizable-handle.tsx b/apps/ui/src/registry/new-york-v4/examples/resizable-handle.tsx
new file mode 100644
index 0000000..658cbee
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/resizable-handle.tsx
@@ -0,0 +1,26 @@
+import {
+ ResizableHandle,
+ ResizablePanel,
+ ResizablePanelGroup,
+} from "@/registry/new-york-v4/ui/resizable"
+
+export default function ResizableHandleDemo() {
+ return (
+
+
+
+ Sidebar
+
+
+
+
+
+ Content
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/resizable-vertical.tsx b/apps/ui/src/registry/new-york-v4/examples/resizable-vertical.tsx
new file mode 100644
index 0000000..0123fce
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/resizable-vertical.tsx
@@ -0,0 +1,26 @@
+import {
+ ResizableHandle,
+ ResizablePanel,
+ ResizablePanelGroup,
+} from "@/registry/new-york-v4/ui/resizable"
+
+export default function ResizableDemo() {
+ return (
+
+
+
+ Header
+
+
+
+
+
+ Content
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/scroll-area-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/scroll-area-demo.tsx
new file mode 100644
index 0000000..f0e3622
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/scroll-area-demo.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { ScrollArea } from "@/registry/new-york-v4/ui/scroll-area"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+
+const tags = Array.from({ length: 50 }).map(
+ (_, i, a) => `v1.2.0-beta.${a.length - i}`
+)
+
+export default function ScrollAreaDemo() {
+ return (
+
+
+
Tags
+ {tags.map((tag) => (
+
+ {tag}
+
+
+ ))}
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/scroll-area-horizontal-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/scroll-area-horizontal-demo.tsx
new file mode 100644
index 0000000..ac5e691
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/scroll-area-horizontal-demo.tsx
@@ -0,0 +1,53 @@
+import * as React from "react"
+import Image from "next/image"
+
+import { ScrollArea, ScrollBar } from "@/registry/new-york-v4/ui/scroll-area"
+
+export interface Artwork {
+ artist: string
+ art: string
+}
+
+export const works: Artwork[] = [
+ {
+ artist: "Ornella Binni",
+ art: "https://images.unsplash.com/photo-1465869185982-5a1a7522cbcb?auto=format&fit=crop&w=300&q=80",
+ },
+ {
+ artist: "Tom Byrom",
+ art: "https://images.unsplash.com/photo-1548516173-3cabfa4607e9?auto=format&fit=crop&w=300&q=80",
+ },
+ {
+ artist: "Vladimir Malyavko",
+ art: "https://images.unsplash.com/photo-1494337480532-3725c85fd2ab?auto=format&fit=crop&w=300&q=80",
+ },
+]
+
+export default function ScrollAreaHorizontalDemo() {
+ return (
+
+
+ {works.map((artwork) => (
+
+
+
+
+
+ Photo by{" "}
+
+ {artwork.artist}
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/select-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/select-demo.tsx
new file mode 100644
index 0000000..4c5b1e8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/select-demo.tsx
@@ -0,0 +1,31 @@
+import * as React from "react"
+
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+
+export default function SelectDemo() {
+ return (
+
+
+
+
+
+
+ Fruits
+ Apple
+ Banana
+ Blueberry
+ Grapes
+ Pineapple
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/select-scrollable.tsx b/apps/ui/src/registry/new-york-v4/examples/select-scrollable.tsx
new file mode 100644
index 0000000..b7288dd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/select-scrollable.tsx
@@ -0,0 +1,75 @@
+import * as React from "react"
+
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectTrigger,
+ SelectValue,
+} from "@/registry/new-york-v4/ui/select"
+
+export default function SelectScrollable() {
+ return (
+
+
+
+
+
+
+ North America
+ Eastern Standard Time (EST)
+ Central Standard Time (CST)
+ Mountain Standard Time (MST)
+ Pacific Standard Time (PST)
+ Alaska Standard Time (AKST)
+ Hawaii Standard Time (HST)
+
+
+ Europe & Africa
+ Greenwich Mean Time (GMT)
+ Central European Time (CET)
+ Eastern European Time (EET)
+
+ Western European Summer Time (WEST)
+
+ Central Africa Time (CAT)
+ East Africa Time (EAT)
+
+
+ Asia
+ Moscow Time (MSK)
+ India Standard Time (IST)
+ China Standard Time (CST)
+ Japan Standard Time (JST)
+ Korea Standard Time (KST)
+
+ Indonesia Central Standard Time (WITA)
+
+
+
+ Australia & Pacific
+
+ Australian Western Standard Time (AWST)
+
+
+ Australian Central Standard Time (ACST)
+
+
+ Australian Eastern Standard Time (AEST)
+
+ New Zealand Standard Time (NZST)
+ Fiji Time (FJT)
+
+
+ South America
+ Argentina Time (ART)
+ Bolivia Time (BOT)
+ Brasilia Time (BRT)
+ Chile Standard Time (CLT)
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/separator-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/separator-demo.tsx
new file mode 100644
index 0000000..0b5369f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/separator-demo.tsx
@@ -0,0 +1,22 @@
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+
+export default function SeparatorDemo() {
+ return (
+
+
+
Radix Primitives
+
+ An open-source UI component library.
+
+
+
+
+
Blog
+
+
Docs
+
+
Source
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/sheet-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/sheet-demo.tsx
new file mode 100644
index 0000000..f78da94
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/sheet-demo.tsx
@@ -0,0 +1,47 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Sheet,
+ SheetClose,
+ SheetContent,
+ SheetDescription,
+ SheetFooter,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/registry/new-york-v4/ui/sheet"
+
+export default function SheetDemo() {
+ return (
+
+
+ Open
+
+
+
+ Edit profile
+
+ Make changes to your profile here. Click save when you're done.
+
+
+
+
+ Save changes
+
+ Close
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/sheet-side.tsx b/apps/ui/src/registry/new-york-v4/examples/sheet-side.tsx
new file mode 100644
index 0000000..ff12be6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/sheet-side.tsx
@@ -0,0 +1,60 @@
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Sheet,
+ SheetClose,
+ SheetContent,
+ SheetDescription,
+ SheetFooter,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger,
+} from "@/registry/new-york-v4/ui/sheet"
+
+const SHEET_SIDES = ["top", "right", "bottom", "left"] as const
+
+type SheetSide = (typeof SHEET_SIDES)[number]
+
+export default function SheetSide() {
+ return (
+
+ {SHEET_SIDES.map((side) => (
+
+
+ {side}
+
+
+
+ Edit profile
+
+ Make changes to your profile here. Click save when you're
+ done.
+
+
+
+
+
+ Save changes
+
+
+
+
+ ))}
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/skeleton-card.tsx b/apps/ui/src/registry/new-york-v4/examples/skeleton-card.tsx
new file mode 100644
index 0000000..c7379b8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/skeleton-card.tsx
@@ -0,0 +1,13 @@
+import { Skeleton } from "@/registry/new-york-v4/ui/skeleton"
+
+export default function SkeletonCard() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/skeleton-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/skeleton-demo.tsx
new file mode 100644
index 0000000..d55e6ba
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/skeleton-demo.tsx
@@ -0,0 +1,13 @@
+import { Skeleton } from "@/registry/new-york-v4/ui/skeleton"
+
+export default function SkeletonDemo() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/slider-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/slider-demo.tsx
new file mode 100644
index 0000000..bd5f6b2
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/slider-demo.tsx
@@ -0,0 +1,16 @@
+import { cn } from "@/lib/utils"
+import { Slider } from "@/registry/new-york-v4/ui/slider"
+
+type SliderProps = React.ComponentProps
+
+export default function SliderDemo({ className, ...props }: SliderProps) {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/sonner-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/sonner-demo.tsx
new file mode 100644
index 0000000..fca4e9a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/sonner-demo.tsx
@@ -0,0 +1,23 @@
+
+import { toast } from "sonner"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function SonnerDemo() {
+ return (
+
+ toast("Event has been created", {
+ description: "Sunday, December 03, 2023 at 9:00 AM",
+ action: {
+ label: "Undo",
+ onClick: () => console.log("Undo"),
+ },
+ })
+ }
+ >
+ Show Toast
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/sonner-types.tsx b/apps/ui/src/registry/new-york-v4/examples/sonner-types.tsx
new file mode 100644
index 0000000..e172ab4
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/sonner-types.tsx
@@ -0,0 +1,60 @@
+
+import { toast } from "sonner"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+export default function SonnerTypes() {
+ return (
+
+ toast("Event has been created")}>
+ Default
+
+ toast.success("Event has been created")}
+ >
+ Success
+
+
+ toast.info("Be at the area 10 minutes before the event time")
+ }
+ >
+ Info
+
+
+ toast.warning("Event start time cannot be earlier than 8am")
+ }
+ >
+ Warning
+
+ toast.error("Event has not been created")}
+ >
+ Error
+
+ {
+ toast.promise<{ name: string }>(
+ () =>
+ new Promise((resolve) =>
+ setTimeout(() => resolve({ name: "Event" }), 2000)
+ ),
+ {
+ loading: "Loading...",
+ success: (data) => `${data.name} has been created`,
+ error: "Error",
+ }
+ )
+ }}
+ >
+ Promise
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-badge.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-badge.tsx
new file mode 100644
index 0000000..a5a13e3
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-badge.tsx
@@ -0,0 +1,21 @@
+import { Badge } from "@/registry/new-york-v4/ui/badge"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerBadge() {
+ return (
+
+
+
+ Syncing
+
+
+
+ Updating
+
+
+
+ Processing
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-basic.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-basic.tsx
new file mode 100644
index 0000000..43bf437
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-basic.tsx
@@ -0,0 +1,9 @@
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerBasic() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-button.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-button.tsx
new file mode 100644
index 0000000..4f3e6b3
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-button.tsx
@@ -0,0 +1,21 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerButton() {
+ return (
+
+
+
+ Loading...
+
+
+
+ Please wait
+
+
+
+ Processing
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-color.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-color.tsx
new file mode 100644
index 0000000..f2b3347
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-color.tsx
@@ -0,0 +1,13 @@
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerColor() {
+ return (
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-custom.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-custom.tsx
new file mode 100644
index 0000000..7352fe9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-custom.tsx
@@ -0,0 +1,22 @@
+import { LoaderIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
+ return (
+
+ )
+}
+
+export default function SpinnerCustom() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-demo.tsx
new file mode 100644
index 0000000..d10e36f
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-demo.tsx
@@ -0,0 +1,25 @@
+import {
+ Item,
+ ItemContent,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerDemo() {
+ return (
+
+ -
+
+
+
+
+ Processing payment...
+
+
+ $100.00
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-empty.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-empty.tsx
new file mode 100644
index 0000000..f6e6195
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-empty.tsx
@@ -0,0 +1,31 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Empty,
+ EmptyContent,
+ EmptyDescription,
+ EmptyHeader,
+ EmptyMedia,
+ EmptyTitle,
+} from "@/registry/new-york-v4/ui/empty"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerEmpty() {
+ return (
+
+
+
+
+
+ Processing your request
+
+ Please wait while we process your request. Do not refresh the page.
+
+
+
+
+ Cancel
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-input-group.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-input-group.tsx
new file mode 100644
index 0000000..e49c5bc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-input-group.tsx
@@ -0,0 +1,33 @@
+import { ArrowUpIcon } from "lucide-react"
+
+import {
+ InputGroup,
+ InputGroupAddon,
+ InputGroupButton,
+ InputGroupInput,
+ InputGroupTextarea,
+} from "@/registry/new-york-v4/ui/input-group"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerInputGroup() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Validating...
+
+
+ Send
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-item.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-item.tsx
new file mode 100644
index 0000000..38440c1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-item.tsx
@@ -0,0 +1,36 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Item,
+ ItemActions,
+ ItemContent,
+ ItemDescription,
+ ItemFooter,
+ ItemMedia,
+ ItemTitle,
+} from "@/registry/new-york-v4/ui/item"
+import { Progress } from "@/registry/new-york-v4/ui/progress"
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerItem() {
+ return (
+
+
-
+
+
+
+
+ Downloading...
+ 129 MB / 1000 MB
+
+
+
+ Cancel
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/spinner-size.tsx b/apps/ui/src/registry/new-york-v4/examples/spinner-size.tsx
new file mode 100644
index 0000000..7c54ece
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/spinner-size.tsx
@@ -0,0 +1,12 @@
+import { Spinner } from "@/registry/new-york-v4/ui/spinner"
+
+export default function SpinnerSize() {
+ return (
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/switch-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/switch-demo.tsx
new file mode 100644
index 0000000..477ffdb
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/switch-demo.tsx
@@ -0,0 +1,11 @@
+import { Label } from "@/registry/new-york-v4/ui/label"
+import { Switch } from "@/registry/new-york-v4/ui/switch"
+
+export default function SwitchDemo() {
+ return (
+
+
+ Airplane Mode
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/table-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/table-demo.tsx
new file mode 100644
index 0000000..36a752e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/table-demo.tsx
@@ -0,0 +1,87 @@
+import {
+ Table,
+ TableBody,
+ TableCaption,
+ TableCell,
+ TableFooter,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/registry/new-york-v4/ui/table"
+
+const invoices = [
+ {
+ invoice: "INV001",
+ paymentStatus: "Paid",
+ totalAmount: "$250.00",
+ paymentMethod: "Credit Card",
+ },
+ {
+ invoice: "INV002",
+ paymentStatus: "Pending",
+ totalAmount: "$150.00",
+ paymentMethod: "PayPal",
+ },
+ {
+ invoice: "INV003",
+ paymentStatus: "Unpaid",
+ totalAmount: "$350.00",
+ paymentMethod: "Bank Transfer",
+ },
+ {
+ invoice: "INV004",
+ paymentStatus: "Paid",
+ totalAmount: "$450.00",
+ paymentMethod: "Credit Card",
+ },
+ {
+ invoice: "INV005",
+ paymentStatus: "Paid",
+ totalAmount: "$550.00",
+ paymentMethod: "PayPal",
+ },
+ {
+ invoice: "INV006",
+ paymentStatus: "Pending",
+ totalAmount: "$200.00",
+ paymentMethod: "Bank Transfer",
+ },
+ {
+ invoice: "INV007",
+ paymentStatus: "Unpaid",
+ totalAmount: "$300.00",
+ paymentMethod: "Credit Card",
+ },
+]
+
+export default function TableDemo() {
+ return (
+
+ A list of your recent invoices.
+
+
+ Invoice
+ Status
+ Method
+ Amount
+
+
+
+ {invoices.map((invoice) => (
+
+ {invoice.invoice}
+ {invoice.paymentStatus}
+ {invoice.paymentMethod}
+ {invoice.totalAmount}
+
+ ))}
+
+
+
+ Total
+ $2,500.00
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/tabs-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/tabs-demo.tsx
new file mode 100644
index 0000000..6a1a413
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/tabs-demo.tsx
@@ -0,0 +1,80 @@
+import { AppWindowIcon, CodeIcon } from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardFooter,
+ CardHeader,
+ CardTitle,
+} from "@/registry/new-york-v4/ui/card"
+import { Input } from "@/registry/new-york-v4/ui/input"
+import { Label } from "@/registry/new-york-v4/ui/label"
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from "@/registry/new-york-v4/ui/tabs"
+
+export default function TabsDemo() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/textarea-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/textarea-demo.tsx
new file mode 100644
index 0000000..fe3557a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/textarea-demo.tsx
@@ -0,0 +1,5 @@
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function TextareaDemo() {
+ return
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/textarea-disabled.tsx b/apps/ui/src/registry/new-york-v4/examples/textarea-disabled.tsx
new file mode 100644
index 0000000..1be5e55
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/textarea-disabled.tsx
@@ -0,0 +1,5 @@
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function TextareaDisabled() {
+ return
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/textarea-with-button.tsx b/apps/ui/src/registry/new-york-v4/examples/textarea-with-button.tsx
new file mode 100644
index 0000000..8ded0d0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/textarea-with-button.tsx
@@ -0,0 +1,11 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function TextareaWithButton() {
+ return (
+
+
+ Send message
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/textarea-with-label.tsx b/apps/ui/src/registry/new-york-v4/examples/textarea-with-label.tsx
new file mode 100644
index 0000000..4e52971
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/textarea-with-label.tsx
@@ -0,0 +1,11 @@
+import { Label } from "@/registry/new-york-v4/ui/label"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function TextareaWithLabel() {
+ return (
+
+ Your message
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/textarea-with-text.tsx b/apps/ui/src/registry/new-york-v4/examples/textarea-with-text.tsx
new file mode 100644
index 0000000..05c2099
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/textarea-with-text.tsx
@@ -0,0 +1,14 @@
+import { Label } from "@/registry/new-york-v4/ui/label"
+import { Textarea } from "@/registry/new-york-v4/ui/textarea"
+
+export default function TextareaWithText() {
+ return (
+
+
Your Message
+
+
+ Your message will be copied to the support team.
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-demo.tsx
new file mode 100644
index 0000000..449f753
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-demo.tsx
@@ -0,0 +1,17 @@
+import { BookmarkIcon } from "lucide-react"
+
+import { Toggle } from "@/registry/new-york-v4/ui/toggle"
+
+export default function ToggleDemo() {
+ return (
+
+
+ Bookmark
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-disabled.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-disabled.tsx
new file mode 100644
index 0000000..65002df
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-disabled.tsx
@@ -0,0 +1,11 @@
+import { Underline } from "lucide-react"
+
+import { Toggle } from "@/registry/new-york-v4/ui/toggle"
+
+export default function ToggleDisabled() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-group-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-group-demo.tsx
new file mode 100644
index 0000000..521f042
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-group-demo.tsx
@@ -0,0 +1,22 @@
+import { Bold, Italic, Underline } from "lucide-react"
+
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export default function ToggleGroupDemo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-group-disabled.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-group-disabled.tsx
new file mode 100644
index 0000000..2d3ed88
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-group-disabled.tsx
@@ -0,0 +1,22 @@
+import { Bold, Italic, Underline } from "lucide-react"
+
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export default function ToggleGroupDemo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-group-lg.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-group-lg.tsx
new file mode 100644
index 0000000..2a5db57
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-group-lg.tsx
@@ -0,0 +1,22 @@
+import { Bold, Italic, Underline } from "lucide-react"
+
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export default function ToggleGroupDemo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-group-outline.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-group-outline.tsx
new file mode 100644
index 0000000..f018c69
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-group-outline.tsx
@@ -0,0 +1,22 @@
+import { Bold, Italic, Underline } from "lucide-react"
+
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export default function ToggleGroupDemo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-group-single.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-group-single.tsx
new file mode 100644
index 0000000..3913a77
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-group-single.tsx
@@ -0,0 +1,22 @@
+import { Bold, Italic, Underline } from "lucide-react"
+
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export default function ToggleGroupDemo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-group-sm.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-group-sm.tsx
new file mode 100644
index 0000000..08424d7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-group-sm.tsx
@@ -0,0 +1,22 @@
+import { Bold, Italic, Underline } from "lucide-react"
+
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export default function ToggleGroupDemo() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-group-spacing.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-group-spacing.tsx
new file mode 100644
index 0000000..0394578
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-group-spacing.tsx
@@ -0,0 +1,37 @@
+import { BookmarkIcon, HeartIcon, StarIcon } from "lucide-react"
+
+import {
+ ToggleGroup,
+ ToggleGroupItem,
+} from "@/registry/new-york-v4/ui/toggle-group"
+
+export default function ToggleGroupSpacing() {
+ return (
+
+
+
+ Star
+
+
+
+ Heart
+
+
+
+ Bookmark
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-lg.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-lg.tsx
new file mode 100644
index 0000000..97c68fd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-lg.tsx
@@ -0,0 +1,11 @@
+import { Italic } from "lucide-react"
+
+import { Toggle } from "@/registry/new-york-v4/ui/toggle"
+
+export default function ToggleLg() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-outline.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-outline.tsx
new file mode 100644
index 0000000..df64553
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-outline.tsx
@@ -0,0 +1,11 @@
+import { Italic } from "lucide-react"
+
+import { Toggle } from "@/registry/new-york-v4/ui/toggle"
+
+export default function ToggleOutline() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-sm.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-sm.tsx
new file mode 100644
index 0000000..d6c64e1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-sm.tsx
@@ -0,0 +1,11 @@
+import { Italic } from "lucide-react"
+
+import { Toggle } from "@/registry/new-york-v4/ui/toggle"
+
+export default function ToggleSm() {
+ return (
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/toggle-with-text.tsx b/apps/ui/src/registry/new-york-v4/examples/toggle-with-text.tsx
new file mode 100644
index 0000000..4c52d03
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/toggle-with-text.tsx
@@ -0,0 +1,12 @@
+import { Italic } from "lucide-react"
+
+import { Toggle } from "@/registry/new-york-v4/ui/toggle"
+
+export default function ToggleWithText() {
+ return (
+
+
+ Italic
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/tooltip-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/tooltip-demo.tsx
new file mode 100644
index 0000000..8f55c33
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/tooltip-demo.tsx
@@ -0,0 +1,19 @@
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@/registry/new-york-v4/ui/tooltip"
+
+export default function TooltipDemo() {
+ return (
+
+
+ Hover
+
+
+ Add to library
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-blockquote.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-blockquote.tsx
new file mode 100644
index 0000000..c5aa079
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-blockquote.tsx
@@ -0,0 +1,8 @@
+export default function TypographyBlockquote() {
+ return (
+
+ "After all," he said, "everyone enjoys a good joke, so
+ it's only fair that they should pay for the privilege."
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-demo.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-demo.tsx
new file mode 100644
index 0000000..063ffeb
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-demo.tsx
@@ -0,0 +1,119 @@
+export default function TypographyDemo() {
+ return (
+
+
+ Taxing Laughter: The Joke Tax Chronicles
+
+
+ Once upon a time, in a far-off land, there was a very lazy king who
+ spent all day lounging on his throne. One day, his advisors came to him
+ with a problem: the kingdom was running out of money.
+
+
+ The King's Plan
+
+
+ The king thought long and hard, and finally came up with{" "}
+
+ a brilliant plan
+
+ : he would tax the jokes in the kingdom.
+
+
+ "After all," he said, "everyone enjoys a good joke, so
+ it's only fair that they should pay for the privilege."
+
+
+ The Joke Tax
+
+
+ The king's subjects were not amused. They grumbled and complained,
+ but the king was firm:
+
+
+ 1st level of puns: 5 gold coins
+ 2nd level of jokes: 10 gold coins
+ 3rd level of one-liners : 20 gold coins
+
+
+ As a result, people stopped telling jokes, and the kingdom fell into a
+ gloom. But there was one person who refused to let the king's
+ foolishness get him down: a court jester named Jokester.
+
+
+ Jokester's Revolt
+
+
+ Jokester began sneaking into the castle in the middle of the night and
+ leaving jokes all over the place: under the king's pillow, in his
+ soup, even in the royal toilet. The king was furious, but he
+ couldn't seem to stop Jokester.
+
+
+ And then, one day, the people of the kingdom discovered that the jokes
+ left by Jokester were so funny that they couldn't help but laugh.
+ And once they started laughing, they couldn't stop.
+
+
+ The People's Rebellion
+
+
+ The people of the kingdom, feeling uplifted by the laughter, started to
+ tell jokes and puns again, and soon the entire kingdom was in on the
+ joke.
+
+
+
+
+
+
+ King's Treasury
+
+
+ People's happiness
+
+
+
+
+
+
+ Empty
+
+
+ Overflowing
+
+
+
+
+ Modest
+
+
+ Satisfied
+
+
+
+
+ Full
+
+
+ Ecstatic
+
+
+
+
+
+
+ The king, seeing how much happier his subjects were, realized the error
+ of his ways and repealed the joke tax. Jokester was declared a hero, and
+ the kingdom lived happily ever after.
+
+
+ The moral of the story is: never underestimate the power of a good laugh
+ and always be careful of bad ideas.
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-h1.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-h1.tsx
new file mode 100644
index 0000000..51b2fe6
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-h1.tsx
@@ -0,0 +1,7 @@
+export default function TypographyH1() {
+ return (
+
+ Taxing Laughter: The Joke Tax Chronicles
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-h2.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-h2.tsx
new file mode 100644
index 0000000..ea79441
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-h2.tsx
@@ -0,0 +1,7 @@
+export default function TypographyH2() {
+ return (
+
+ The People of the Kingdom
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-h3.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-h3.tsx
new file mode 100644
index 0000000..4e8a185
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-h3.tsx
@@ -0,0 +1,7 @@
+export default function TypographyH3() {
+ return (
+
+ The Joke Tax
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-h4.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-h4.tsx
new file mode 100644
index 0000000..5835166
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-h4.tsx
@@ -0,0 +1,7 @@
+export default function TypographyH4() {
+ return (
+
+ People stopped telling jokes
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-inline-code.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-inline-code.tsx
new file mode 100644
index 0000000..fbbc96b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-inline-code.tsx
@@ -0,0 +1,7 @@
+export default function TypographyInlineCode() {
+ return (
+
+ @radix-ui/react-alert-dialog
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-large.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-large.tsx
new file mode 100644
index 0000000..944f447
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-large.tsx
@@ -0,0 +1,3 @@
+export default function TypographyLarge() {
+ return Are you absolutely sure?
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-lead.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-lead.tsx
new file mode 100644
index 0000000..aac39ea
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-lead.tsx
@@ -0,0 +1,8 @@
+export default function TypographyLead() {
+ return (
+
+ A modal dialog that interrupts the user with important content and expects
+ a response.
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-list.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-list.tsx
new file mode 100644
index 0000000..a814f66
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-list.tsx
@@ -0,0 +1,9 @@
+export default function TypographyList() {
+ return (
+
+ 1st level of puns: 5 gold coins
+ 2nd level of jokes: 10 gold coins
+ 3rd level of one-liners : 20 gold coins
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-muted.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-muted.tsx
new file mode 100644
index 0000000..545dcfc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-muted.tsx
@@ -0,0 +1,5 @@
+export default function TypographyMuted() {
+ return (
+ Enter your email address.
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-p.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-p.tsx
new file mode 100644
index 0000000..88fd479
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-p.tsx
@@ -0,0 +1,8 @@
+export default function TypographyP() {
+ return (
+
+ The king, seeing how much happier his subjects were, realized the error of
+ his ways and repealed the joke tax.
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-small.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-small.tsx
new file mode 100644
index 0000000..333787a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-small.tsx
@@ -0,0 +1,5 @@
+export default function TypographySmall() {
+ return (
+ Email address
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/examples/typography-table.tsx b/apps/ui/src/registry/new-york-v4/examples/typography-table.tsx
new file mode 100644
index 0000000..7a91dfd
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/examples/typography-table.tsx
@@ -0,0 +1,44 @@
+export default function TypographyTable() {
+ return (
+
+
+
+
+
+ King's Treasury
+
+
+ People's happiness
+
+
+
+
+
+
+ Empty
+
+
+ Overflowing
+
+
+
+
+ Modest
+
+
+ Satisfied
+
+
+
+
+ Full
+
+
+ Ecstatic
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/hooks/_registry.ts b/apps/ui/src/registry/new-york-v4/hooks/_registry.ts
new file mode 100644
index 0000000..5957a81
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/hooks/_registry.ts
@@ -0,0 +1,14 @@
+import { type Registry } from "shadcn/schema"
+
+export const hooks: Registry["items"] = [
+ {
+ name: "use-mobile",
+ type: "registry:hook",
+ files: [
+ {
+ path: "hooks/use-mobile.ts",
+ type: "registry:hook",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/new-york-v4/hooks/use-mobile.ts b/apps/ui/src/registry/new-york-v4/hooks/use-mobile.ts
new file mode 100644
index 0000000..2b0fe1d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/hooks/use-mobile.ts
@@ -0,0 +1,19 @@
+import * as React from "react"
+
+const MOBILE_BREAKPOINT = 768
+
+export function useIsMobile() {
+ const [isMobile, setIsMobile] = React.useState(undefined)
+
+ React.useEffect(() => {
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
+ const onChange = () => {
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ }
+ mql.addEventListener("change", onChange)
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
+ return () => mql.removeEventListener("change", onChange)
+ }, [])
+
+ return !!isMobile
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/_registry.ts b/apps/ui/src/registry/new-york-v4/internal/_registry.ts
new file mode 100644
index 0000000..31adcb0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/_registry.ts
@@ -0,0 +1,135 @@
+import { type Registry } from "shadcn/schema"
+
+export const internal: Registry["items"] = [
+ // Do not move this. They are intentionally here for registry capture.
+ {
+ name: "sidebar-demo",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-demo.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-header",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-header.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-footer",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-footer.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-group",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-group.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-group-collapsible",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-group-collapsible.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-group-action",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-group-action.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-menu",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-menu.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-menu-action",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-menu-action.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-menu-sub",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-menu-sub.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-menu-collapsible",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-menu-collapsible.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-menu-badge",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-menu-badge.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-rsc",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-rsc.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+ {
+ name: "sidebar-controlled",
+ type: "registry:internal",
+ files: [
+ {
+ path: "internal/sidebar-controlled.tsx",
+ type: "registry:component",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-controlled.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-controlled.tsx
new file mode 100644
index 0000000..d100660
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-controlled.tsx
@@ -0,0 +1,95 @@
+
+import * as React from "react"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PanelLeftCloseIcon,
+ PanelLeftOpenIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ const [open, setOpen] = React.useState(true)
+
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+ ))}
+
+
+
+
+
+
+
+ setOpen((open) => !open)}
+ size="sm"
+ variant="ghost"
+ >
+ {open ? : }
+ {open ? "Close" : "Open"} Sidebar
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-demo.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-demo.tsx
new file mode 100644
index 0000000..66964a9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-demo.tsx
@@ -0,0 +1,84 @@
+
+import {
+ CalendarIcon,
+ HomeIcon,
+ InboxIcon,
+ SearchIcon,
+ SettingsIcon,
+} from "lucide-react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+// Menu items.
+const items = [
+ {
+ title: "Home",
+ url: "#",
+ icon: HomeIcon,
+ },
+ {
+ title: "Inbox",
+ url: "#",
+ icon: InboxIcon,
+ },
+ {
+ title: "Calendar",
+ url: "#",
+ icon: CalendarIcon,
+ },
+ {
+ title: "Search",
+ url: "#",
+ icon: SearchIcon,
+ },
+ {
+ title: "Settings",
+ url: "#",
+ icon: SettingsIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Application
+
+
+ {items.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-footer.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-footer.tsx
new file mode 100644
index 0000000..c5e494a
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-footer.tsx
@@ -0,0 +1,65 @@
+
+import { ChevronUpIcon } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarFooter,
+ SidebarHeader,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ Username
+
+
+
+
+
+ Account
+
+
+ Billing
+
+
+ Sign out
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-group-action.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-group-action.tsx
new file mode 100644
index 0000000..d773589
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-group-action.tsx
@@ -0,0 +1,70 @@
+
+import { FrameIcon, MapIcon, PieChartIcon, PlusIcon } from "lucide-react"
+import { toast, Toaster } from "sonner"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupAction,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+ Projects
+ toast("You clicked the group action!")}
+ >
+ Add Project
+
+
+
+
+
+
+
+ Design Engineering
+
+
+
+
+
+
+
+ Sales & Marketing
+
+
+
+
+
+
+
+ Travel
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-group-collapsible.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-group-collapsible.tsx
new file mode 100644
index 0000000..64ad236
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-group-collapsible.tsx
@@ -0,0 +1,61 @@
+
+import { ChevronDownIcon, LifeBuoyIcon, SendIcon } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+ Help
+
+
+
+
+
+
+
+
+
+ Support
+
+
+
+
+
+ Feedback
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-group.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-group.tsx
new file mode 100644
index 0000000..05dbbcc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-group.tsx
@@ -0,0 +1,44 @@
+
+import { LifeBuoyIcon, SendIcon } from "lucide-react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Help
+
+
+
+
+
+ Support
+
+
+
+
+
+ Feedback
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-header.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-header.tsx
new file mode 100644
index 0000000..fa4a658
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-header.tsx
@@ -0,0 +1,55 @@
+
+import { ChevronDownIcon } from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarHeader,
+ SidebarInset,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+ SidebarTrigger,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+
+
+ Select Workspace
+
+
+
+
+
+ Acme Inc
+
+
+ Acme Corp.
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-action.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-action.tsx
new file mode 100644
index 0000000..154495c
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-action.tsx
@@ -0,0 +1,103 @@
+
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ MoreHorizontalIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/registry/new-york-v4/ui/dropdown-menu"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuAction,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+
+
+
+ More
+
+
+
+
+ Edit Project
+
+
+ Delete Project
+
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-badge.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-badge.tsx
new file mode 100644
index 0000000..910b722
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-badge.tsx
@@ -0,0 +1,86 @@
+
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuBadge,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ badge: "24",
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ badge: "12",
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ badge: "3",
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ badge: "21",
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ badge: "8",
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+ {project.badge}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-collapsible.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-collapsible.tsx
new file mode 100644
index 0000000..4a9d52b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-collapsible.tsx
@@ -0,0 +1,195 @@
+
+import { ChevronRightIcon } from "lucide-react"
+
+import {
+ Collapsible,
+ CollapsibleContent,
+ CollapsibleTrigger,
+} from "@/registry/new-york-v4/ui/collapsible"
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const items = [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+ {items.map((item, index) => (
+
+
+
+
+ {item.title}
+
+
+
+
+
+ {item.items.map((subItem, subIndex) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-sub.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-sub.tsx
new file mode 100644
index 0000000..c66f2f0
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu-sub.tsx
@@ -0,0 +1,179 @@
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSub,
+ SidebarMenuSubButton,
+ SidebarMenuSubItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const items = [
+ {
+ title: "Getting Started",
+ url: "#",
+ items: [
+ {
+ title: "Installation",
+ url: "#",
+ },
+ {
+ title: "Project Structure",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Build Your Application",
+ url: "#",
+ items: [
+ {
+ title: "Routing",
+ url: "#",
+ },
+ {
+ title: "Data Fetching",
+ url: "#",
+ isActive: true,
+ },
+ {
+ title: "Rendering",
+ url: "#",
+ },
+ {
+ title: "Caching",
+ url: "#",
+ },
+ {
+ title: "Styling",
+ url: "#",
+ },
+ {
+ title: "Optimizing",
+ url: "#",
+ },
+ {
+ title: "Configuring",
+ url: "#",
+ },
+ {
+ title: "Testing",
+ url: "#",
+ },
+ {
+ title: "Authentication",
+ url: "#",
+ },
+ {
+ title: "Deploying",
+ url: "#",
+ },
+ {
+ title: "Upgrading",
+ url: "#",
+ },
+ {
+ title: "Examples",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "API Reference",
+ url: "#",
+ items: [
+ {
+ title: "Components",
+ url: "#",
+ },
+ {
+ title: "File Conventions",
+ url: "#",
+ },
+ {
+ title: "Functions",
+ url: "#",
+ },
+ {
+ title: "next.config.js Options",
+ url: "#",
+ },
+ {
+ title: "CLI",
+ url: "#",
+ },
+ {
+ title: "Edge Runtime",
+ url: "#",
+ },
+ ],
+ },
+ {
+ title: "Architecture",
+ url: "#",
+ items: [
+ {
+ title: "Accessibility",
+ url: "#",
+ },
+ {
+ title: "Fast Refresh",
+ url: "#",
+ },
+ {
+ title: "Next.js Compiler",
+ url: "#",
+ },
+ {
+ title: "Supported Browsers",
+ url: "#",
+ },
+ {
+ title: "Turbopack",
+ url: "#",
+ },
+ ],
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+
+
+ {items.map((item, index) => (
+
+
+
+ {item.title}
+
+
+
+ {item.items.map((subItem, subIndex) => (
+
+
+
+ {subItem.title}
+
+
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-menu.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu.tsx
new file mode 100644
index 0000000..6f7cb3d
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-menu.tsx
@@ -0,0 +1,76 @@
+
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ },
+]
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/internal/sidebar-rsc.tsx b/apps/ui/src/registry/new-york-v4/internal/sidebar-rsc.tsx
new file mode 100644
index 0000000..17f7bfe
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/internal/sidebar-rsc.tsx
@@ -0,0 +1,110 @@
+import * as React from "react"
+import {
+ FrameIcon,
+ LifeBuoyIcon,
+ MapIcon,
+ PieChartIcon,
+ SendIcon,
+} from "lucide-react"
+
+import {
+ Sidebar,
+ SidebarContent,
+ SidebarGroup,
+ SidebarGroupContent,
+ SidebarGroupLabel,
+ SidebarMenu,
+ SidebarMenuButton,
+ SidebarMenuItem,
+ SidebarMenuSkeleton,
+ SidebarProvider,
+} from "@/registry/new-york-v4/ui/sidebar"
+
+const projects = [
+ {
+ name: "Design Engineering",
+ url: "#",
+ icon: FrameIcon,
+ badge: "24",
+ },
+ {
+ name: "Sales & Marketing",
+ url: "#",
+ icon: PieChartIcon,
+ badge: "12",
+ },
+ {
+ name: "Travel",
+ url: "#",
+ icon: MapIcon,
+ badge: "3",
+ },
+ {
+ name: "Support",
+ url: "#",
+ icon: LifeBuoyIcon,
+ badge: "21",
+ },
+ {
+ name: "Feedback",
+ url: "#",
+ icon: SendIcon,
+ badge: "8",
+ },
+]
+
+// Dummy fetch function
+async function fetchProjects() {
+ await new Promise((resolve) => setTimeout(resolve, 3000))
+ return projects
+}
+
+export default function AppSidebar() {
+ return (
+
+
+
+
+ Projects
+
+ }>
+
+
+
+
+
+
+
+ )
+}
+
+function NavProjectsSkeleton() {
+ return (
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+ ))}
+
+ )
+}
+
+async function NavProjects() {
+ const projects = await fetchProjects()
+
+ return (
+
+ {projects.map((project) => (
+
+
+
+
+ {project.name}
+
+
+
+ ))}
+
+ )
+}
diff --git a/apps/ui/src/registry/new-york-v4/lib/_registry.ts b/apps/ui/src/registry/new-york-v4/lib/_registry.ts
new file mode 100644
index 0000000..fd94cde
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/lib/_registry.ts
@@ -0,0 +1,15 @@
+import { type Registry } from "shadcn/schema"
+
+export const lib: Registry["items"] = [
+ {
+ name: "utils",
+ type: "registry:lib",
+ dependencies: ["clsx", "tailwind-merge"],
+ files: [
+ {
+ path: "lib/utils.ts",
+ type: "registry:lib",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/new-york-v4/lib/utils.ts b/apps/ui/src/registry/new-york-v4/lib/utils.ts
new file mode 100644
index 0000000..bd0c391
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/apps/ui/src/registry/new-york-v4/registry.ts b/apps/ui/src/registry/new-york-v4/registry.ts
new file mode 100644
index 0000000..ce71657
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/registry.ts
@@ -0,0 +1,69 @@
+import { registryItemSchema, type Registry } from "shadcn/schema"
+import { z } from "zod"
+
+import { themes } from "../_legacy-themes"
+import { blocks } from "./blocks/_registry"
+import { charts } from "./charts/_registry"
+import { examples } from "./examples/_registry"
+import { hooks } from "./hooks/_registry"
+import { internal } from "./internal/_registry"
+import { lib } from "./lib/_registry"
+import { ui } from "./ui/_registry"
+
+const DEPRECATED_ITEMS = [
+ "toast",
+ "toast-demo",
+ "toast-destructive",
+ "toast-simple",
+ "toast-with-action",
+ "toast-with-title",
+]
+
+// Shared between index and style for backward compatibility.
+const NEW_YORK_V4_STYLE = {
+ type: "registry:style",
+ dependencies: ["class-variance-authority", "lucide-react", "radix-ui"],
+ devDependencies: ["tw-animate-css", "shadcn"],
+ registryDependencies: ["utils"],
+ css: {
+ '@import "tw-animate-css"': {},
+ '@import "shadcn/tailwind.css"': {},
+ "@layer base": {
+ "*": {
+ "@apply border-border outline-ring/50": {},
+ },
+ body: {
+ "@apply bg-background text-foreground": {},
+ },
+ },
+ },
+ cssVars: {},
+ files: [],
+}
+
+export const registry = {
+ name: "shadcn/ui",
+ homepage: "https://ui.shadcn.com",
+ items: z.array(registryItemSchema).parse(
+ [
+ {
+ name: "index",
+ ...NEW_YORK_V4_STYLE,
+ },
+ {
+ name: "style",
+ ...NEW_YORK_V4_STYLE,
+ },
+ ...ui,
+ ...blocks,
+ ...charts,
+ ...lib,
+ ...hooks,
+ ...themes,
+ ...examples,
+ ...internal,
+ ].filter((item) => {
+ return !DEPRECATED_ITEMS.includes(item.name)
+ })
+ ),
+} satisfies Registry
diff --git a/apps/ui/src/registry/new-york-v4/ui/_registry.ts b/apps/ui/src/registry/new-york-v4/ui/_registry.ts
new file mode 100644
index 0000000..b31e372
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/_registry.ts
@@ -0,0 +1,705 @@
+import { type Registry } from "shadcn/schema"
+
+export const ui: Registry["items"] = [
+ {
+ name: "accordion",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/accordion.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "alert",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/alert.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "alert-dialog",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/alert-dialog.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "aspect-ratio",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/aspect-ratio.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "avatar",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/avatar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "badge",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/badge.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "breadcrumb",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/breadcrumb.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "button",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/button.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "button-group",
+ type: "registry:ui",
+ registryDependencies: ["button", "separator"],
+ files: [
+ {
+ path: "ui/button-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "calendar",
+ type: "registry:ui",
+ dependencies: ["react-day-picker@latest", "date-fns"],
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/calendar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "card",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/card.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "carousel",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/carousel.tsx",
+ type: "registry:ui",
+ },
+ ],
+ registryDependencies: ["button"],
+ dependencies: ["embla-carousel-react"],
+ },
+ {
+ name: "chart",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/chart.tsx",
+ type: "registry:ui",
+ },
+ ],
+ registryDependencies: ["card"],
+ dependencies: ["recharts@3.8.0", "lucide-react"],
+ },
+ {
+ name: "checkbox",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/checkbox.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "collapsible",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/collapsible.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "combobox",
+ type: "registry:ui",
+ dependencies: ["@base-ui/react"],
+ registryDependencies: ["button", "input-group"],
+ files: [
+ {
+ path: "ui/combobox.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "command",
+ type: "registry:ui",
+ dependencies: ["cmdk"],
+ registryDependencies: ["dialog"],
+ files: [
+ {
+ path: "ui/command.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "context-menu",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/context-menu.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "dialog",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/dialog.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "drawer",
+ type: "registry:ui",
+ dependencies: ["vaul"],
+ files: [
+ {
+ path: "ui/drawer.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "dropdown-menu",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/dropdown-menu.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "empty",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/empty.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "field",
+ type: "registry:ui",
+ registryDependencies: ["label", "separator"],
+ files: [
+ {
+ path: "ui/field.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "form",
+ type: "registry:ui",
+ dependencies: ["radix-ui", "@hookform/resolvers", "zod", "react-hook-form"],
+ registryDependencies: ["button", "label"],
+ files: [
+ {
+ path: "ui/form.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "hover-card",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/hover-card.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "input",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/input.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "input-group",
+ type: "registry:ui",
+ registryDependencies: ["button", "input", "textarea"],
+ files: [
+ {
+ path: "ui/input-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "input-otp",
+ type: "registry:ui",
+ dependencies: ["input-otp"],
+ files: [
+ {
+ path: "ui/input-otp.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "item",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ registryDependencies: ["separator"],
+ files: [
+ {
+ path: "ui/item.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "label",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/label.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "menubar",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/menubar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "navigation-menu",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/navigation-menu.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "pagination",
+ type: "registry:ui",
+ registryDependencies: ["button"],
+ files: [
+ {
+ path: "ui/pagination.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "popover",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/popover.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "progress",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/progress.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "radio-group",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/radio-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "resizable",
+ type: "registry:ui",
+ dependencies: ["react-resizable-panels@^4"],
+ files: [
+ {
+ path: "ui/resizable.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "scroll-area",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/scroll-area.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "select",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/select.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "separator",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/separator.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "sheet",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/sheet.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "sidebar",
+ type: "registry:ui",
+ dependencies: ["radix-ui", "class-variance-authority", "lucide-react"],
+ registryDependencies: [
+ "button",
+ "separator",
+ "sheet",
+ "tooltip",
+ "input",
+ "use-mobile",
+ "skeleton",
+ ],
+ files: [
+ {
+ path: "ui/sidebar.tsx",
+ type: "registry:ui",
+ },
+ ],
+ tailwind: {
+ config: {
+ theme: {
+ extend: {
+ colors: {
+ sidebar: {
+ DEFAULT: "hsl(var(--sidebar-background))",
+ foreground: "hsl(var(--sidebar-foreground))",
+ primary: "hsl(var(--sidebar-primary))",
+ "primary-foreground": "hsl(var(--sidebar-primary-foreground))",
+ accent: "hsl(var(--sidebar-accent))",
+ "accent-foreground": "hsl(var(--sidebar-accent-foreground))",
+ border: "hsl(var(--sidebar-border))",
+ ring: "hsl(var(--sidebar-ring))",
+ },
+ },
+ },
+ },
+ },
+ },
+ cssVars: {
+ light: {
+ "sidebar-background": "0 0% 98%",
+ "sidebar-foreground": "240 5.3% 26.1%",
+ "sidebar-primary": "240 5.9% 10%",
+ "sidebar-primary-foreground": "0 0% 98%",
+ "sidebar-accent": "240 4.8% 95.9%",
+ "sidebar-accent-foreground": "240 5.9% 10%",
+ "sidebar-border": "220 13% 91%",
+ "sidebar-ring": "217.2 91.2% 59.8%",
+ },
+ dark: {
+ "sidebar-background": "240 5.9% 10%",
+ "sidebar-foreground": "240 4.8% 95.9%",
+ "sidebar-primary": "224.3 76.3% 48%",
+ "sidebar-primary-foreground": "0 0% 100%",
+ "sidebar-accent": "240 3.7% 15.9%",
+ "sidebar-accent-foreground": "240 4.8% 95.9%",
+ "sidebar-border": "240 3.7% 15.9%",
+ "sidebar-ring": "217.2 91.2% 59.8%",
+ },
+ },
+ },
+ {
+ name: "skeleton",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/skeleton.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "slider",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/slider.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "sonner",
+ type: "registry:ui",
+ dependencies: ["sonner", "next-themes"],
+ files: [
+ {
+ path: "ui/sonner.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "spinner",
+ type: "registry:ui",
+ dependencies: ["class-variance-authority"],
+ files: [
+ {
+ path: "ui/spinner.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "switch",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/switch.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "table",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/table.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "tabs",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/tabs.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "textarea",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/textarea.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "toast",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/toast.tsx",
+ type: "registry:ui",
+ },
+ {
+ path: "hooks/use-toast.ts",
+ type: "registry:hook",
+ },
+ {
+ path: "ui/toaster.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "toggle",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ files: [
+ {
+ path: "ui/toggle.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "toggle-group",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ registryDependencies: ["toggle"],
+ files: [
+ {
+ path: "ui/toggle-group.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "tooltip",
+ type: "registry:ui",
+ dependencies: ["radix-ui"],
+ docs: `The \`tooltip\` component has been added. Remember to wrap your app with the \`TooltipProvider\` component.
+
+\`\`\`tsx title="app/layout.tsx"
+import { TooltipProvider } from "@/components/ui/tooltip"
+
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return (
+
+
+ {children}
+
+
+ )
+}
+\`\`\`
+`,
+ files: [
+ {
+ path: "ui/tooltip.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "kbd",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/kbd.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "native-select",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/native-select.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+ {
+ name: "direction",
+ type: "registry:ui",
+ files: [
+ {
+ path: "ui/direction.tsx",
+ type: "registry:ui",
+ },
+ ],
+ },
+]
diff --git a/apps/ui/src/registry/new-york-v4/ui/accordion.tsx b/apps/ui/src/registry/new-york-v4/ui/accordion.tsx
new file mode 100644
index 0000000..8e94bb7
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/accordion.tsx
@@ -0,0 +1,65 @@
+
+import * as React from "react"
+import { ChevronDownIcon } from "lucide-react"
+import { Accordion as AccordionPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Accordion({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function AccordionItem({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AccordionTrigger({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ svg]:rotate-180",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+ )
+}
+
+function AccordionContent({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ {children}
+
+ )
+}
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
diff --git a/apps/ui/src/registry/new-york-v4/ui/alert-dialog.tsx b/apps/ui/src/registry/new-york-v4/ui/alert-dialog.tsx
new file mode 100644
index 0000000..b5cc740
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/alert-dialog.tsx
@@ -0,0 +1,195 @@
+
+import * as React from "react"
+import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+function AlertDialog({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function AlertDialogTrigger({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogPortal({
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogContent({
+ className,
+ size = "default",
+ ...props
+}: React.ComponentProps & {
+ size?: "default" | "sm"
+}) {
+ return (
+
+
+
+
+ )
+}
+
+function AlertDialogHeader({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDialogFooter({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AlertDialogMedia({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDialogAction({
+ className,
+ variant = "default",
+ size = "default",
+ ...props
+}: React.ComponentProps &
+ Pick, "variant" | "size">) {
+ return (
+
+
+
+ )
+}
+
+function AlertDialogCancel({
+ className,
+ variant = "outline",
+ size = "default",
+ ...props
+}: React.ComponentProps &
+ Pick, "variant" | "size">) {
+ return (
+
+
+
+ )
+}
+
+export {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogMedia,
+ AlertDialogOverlay,
+ AlertDialogPortal,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+}
diff --git a/apps/ui/src/registry/new-york-v4/ui/alert.tsx b/apps/ui/src/registry/new-york-v4/ui/alert.tsx
new file mode 100644
index 0000000..f99164e
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/alert.tsx
@@ -0,0 +1,66 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const alertVariants = cva(
+ "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
+ {
+ variants: {
+ variant: {
+ default: "bg-card text-card-foreground",
+ destructive:
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Alert({
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ )
+}
+
+function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDescription({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export { Alert, AlertTitle, AlertDescription }
diff --git a/apps/ui/src/registry/new-york-v4/ui/aspect-ratio.tsx b/apps/ui/src/registry/new-york-v4/ui/aspect-ratio.tsx
new file mode 100644
index 0000000..43acfc9
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/aspect-ratio.tsx
@@ -0,0 +1,10 @@
+
+import { AspectRatio as AspectRatioPrimitive } from "radix-ui"
+
+function AspectRatio({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+export { AspectRatio }
diff --git a/apps/ui/src/registry/new-york-v4/ui/avatar.tsx b/apps/ui/src/registry/new-york-v4/ui/avatar.tsx
new file mode 100644
index 0000000..0c53f3b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/avatar.tsx
@@ -0,0 +1,108 @@
+
+import * as React from "react"
+import { Avatar as AvatarPrimitive } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Avatar({
+ className,
+ size = "default",
+ ...props
+}: React.ComponentProps & {
+ size?: "default" | "sm" | "lg"
+}) {
+ return (
+
+ )
+}
+
+function AvatarImage({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AvatarFallback({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+ svg]:hidden",
+ "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
+ "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AvatarGroupCount({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+ svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+export {
+ Avatar,
+ AvatarImage,
+ AvatarFallback,
+ AvatarBadge,
+ AvatarGroup,
+ AvatarGroupCount,
+}
diff --git a/apps/ui/src/registry/new-york-v4/ui/badge.tsx b/apps/ui/src/registry/new-york-v4/ui/badge.tsx
new file mode 100644
index 0000000..6eb2a05
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/badge.tsx
@@ -0,0 +1,48 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+import { Slot } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
+ secondary:
+ "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
+ destructive:
+ "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90",
+ outline:
+ "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 [a&]:hover:underline",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Badge({
+ className,
+ variant = "default",
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ VariantProps
& { asChild?: boolean }) {
+ const Comp = asChild ? Slot.Root : "span"
+
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/apps/ui/src/registry/new-york-v4/ui/breadcrumb.tsx b/apps/ui/src/registry/new-york-v4/ui/breadcrumb.tsx
new file mode 100644
index 0000000..004bb63
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/breadcrumb.tsx
@@ -0,0 +1,109 @@
+import * as React from "react"
+import { ChevronRight, MoreHorizontal } from "lucide-react"
+import { Slot } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
+ return
+}
+
+function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
+ return (
+
+ )
+}
+
+function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
+ return (
+
+ )
+}
+
+function BreadcrumbLink({
+ asChild,
+ className,
+ ...props
+}: React.ComponentProps<"a"> & {
+ asChild?: boolean
+}) {
+ const Comp = asChild ? Slot.Root : "a"
+
+ return (
+
+ )
+}
+
+function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
+ return (
+
+ )
+}
+
+function BreadcrumbSeparator({
+ children,
+ className,
+ ...props
+}: React.ComponentProps<"li">) {
+ return (
+ svg]:size-3.5", className)}
+ {...props}
+ >
+ {children ?? }
+
+ )
+}
+
+function BreadcrumbEllipsis({
+ className,
+ ...props
+}: React.ComponentProps<"span">) {
+ return (
+
+
+ More
+
+ )
+}
+
+export {
+ Breadcrumb,
+ BreadcrumbList,
+ BreadcrumbItem,
+ BreadcrumbLink,
+ BreadcrumbPage,
+ BreadcrumbSeparator,
+ BreadcrumbEllipsis,
+}
diff --git a/apps/ui/src/registry/new-york-v4/ui/button-group.tsx b/apps/ui/src/registry/new-york-v4/ui/button-group.tsx
new file mode 100644
index 0000000..e89cfc8
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/button-group.tsx
@@ -0,0 +1,83 @@
+import { cva, type VariantProps } from "class-variance-authority"
+import { Slot } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+import { Separator } from "@/registry/new-york-v4/ui/separator"
+
+const buttonGroupVariants = cva(
+ "flex w-fit items-stretch has-[>[data-slot=button-group]]:gap-2 [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
+ {
+ variants: {
+ orientation: {
+ horizontal:
+ "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none",
+ vertical:
+ "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none",
+ },
+ },
+ defaultVariants: {
+ orientation: "horizontal",
+ },
+ }
+)
+
+function ButtonGroup({
+ className,
+ orientation,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ )
+}
+
+function ButtonGroupText({
+ className,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"div"> & {
+ asChild?: boolean
+}) {
+ const Comp = asChild ? Slot.Root : "div"
+
+ return (
+
+ )
+}
+
+function ButtonGroupSeparator({
+ className,
+ orientation = "vertical",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ ButtonGroup,
+ ButtonGroupSeparator,
+ ButtonGroupText,
+ buttonGroupVariants,
+}
diff --git a/apps/ui/src/registry/new-york-v4/ui/button.tsx b/apps/ui/src/registry/new-york-v4/ui/button.tsx
new file mode 100644
index 0000000..4d38506
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/button.tsx
@@ -0,0 +1,64 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+import { Slot } from "radix-ui"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
+ sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9",
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
+ "icon-sm": "size-8",
+ "icon-lg": "size-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+function Button({
+ className,
+ variant = "default",
+ size = "default",
+ asChild = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps & {
+ asChild?: boolean
+ }) {
+ const Comp = asChild ? Slot.Root : "button"
+
+ return (
+
+ )
+}
+
+export { Button, buttonVariants }
diff --git a/apps/ui/src/registry/new-york-v4/ui/calendar.tsx b/apps/ui/src/registry/new-york-v4/ui/calendar.tsx
new file mode 100644
index 0000000..af740c1
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/calendar.tsx
@@ -0,0 +1,219 @@
+
+import * as React from "react"
+import {
+ ChevronDownIcon,
+ ChevronLeftIcon,
+ ChevronRightIcon,
+} from "lucide-react"
+import {
+ DayPicker,
+ getDefaultClassNames,
+ type DayButton,
+} from "react-day-picker"
+
+import { cn } from "@/lib/utils"
+import { Button, buttonVariants } from "@/registry/new-york-v4/ui/button"
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ captionLayout = "label",
+ buttonVariant = "ghost",
+ formatters,
+ components,
+ ...props
+}: React.ComponentProps & {
+ buttonVariant?: React.ComponentProps["variant"]
+}) {
+ const defaultClassNames = getDefaultClassNames()
+
+ return (
+ svg]:rotate-180`,
+ String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
+ className
+ )}
+ captionLayout={captionLayout}
+ formatters={{
+ formatMonthDropdown: (date) =>
+ date.toLocaleString("default", { month: "short" }),
+ ...formatters,
+ }}
+ classNames={{
+ root: cn("w-fit", defaultClassNames.root),
+ months: cn(
+ "relative flex flex-col gap-4 md:flex-row",
+ defaultClassNames.months
+ ),
+ month: cn("flex w-full flex-col gap-4", defaultClassNames.month),
+ nav: cn(
+ "absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1",
+ defaultClassNames.nav
+ ),
+ button_previous: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_previous
+ ),
+ button_next: cn(
+ buttonVariants({ variant: buttonVariant }),
+ "size-(--cell-size) p-0 select-none aria-disabled:opacity-50",
+ defaultClassNames.button_next
+ ),
+ month_caption: cn(
+ "flex h-(--cell-size) w-full items-center justify-center px-(--cell-size)",
+ defaultClassNames.month_caption
+ ),
+ dropdowns: cn(
+ "flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
+ defaultClassNames.dropdowns
+ ),
+ dropdown_root: cn(
+ "relative rounded-md border border-input shadow-xs has-focus:border-ring has-focus:ring-[3px] has-focus:ring-ring/50",
+ defaultClassNames.dropdown_root
+ ),
+ dropdown: cn(
+ "absolute inset-0 bg-popover opacity-0",
+ defaultClassNames.dropdown
+ ),
+ caption_label: cn(
+ "font-medium select-none",
+ captionLayout === "label"
+ ? "text-sm"
+ : "flex h-8 items-center gap-1 rounded-md pr-1 pl-2 text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
+ defaultClassNames.caption_label
+ ),
+ table: "w-full border-collapse",
+ weekdays: cn("flex", defaultClassNames.weekdays),
+ weekday: cn(
+ "flex-1 rounded-md text-[0.8rem] font-normal text-muted-foreground select-none",
+ defaultClassNames.weekday
+ ),
+ week: cn("mt-2 flex w-full", defaultClassNames.week),
+ week_number_header: cn(
+ "w-(--cell-size) select-none",
+ defaultClassNames.week_number_header
+ ),
+ week_number: cn(
+ "text-[0.8rem] text-muted-foreground select-none",
+ defaultClassNames.week_number
+ ),
+ day: cn(
+ "group/day relative aspect-square h-full w-full p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-md",
+ props.showWeekNumber
+ ? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-md"
+ : "[&:first-child[data-selected=true]_button]:rounded-l-md",
+ defaultClassNames.day
+ ),
+ range_start: cn(
+ "rounded-l-md bg-accent",
+ defaultClassNames.range_start
+ ),
+ range_middle: cn("rounded-none", defaultClassNames.range_middle),
+ range_end: cn("rounded-r-md bg-accent", defaultClassNames.range_end),
+ today: cn(
+ "rounded-md bg-accent text-accent-foreground data-[selected=true]:rounded-none",
+ defaultClassNames.today
+ ),
+ outside: cn(
+ "text-muted-foreground aria-selected:text-muted-foreground",
+ defaultClassNames.outside
+ ),
+ disabled: cn(
+ "text-muted-foreground opacity-50",
+ defaultClassNames.disabled
+ ),
+ hidden: cn("invisible", defaultClassNames.hidden),
+ ...classNames,
+ }}
+ components={{
+ Root: ({ className, rootRef, ...props }) => {
+ return (
+
+ )
+ },
+ Chevron: ({ className, orientation, ...props }) => {
+ if (orientation === "left") {
+ return (
+
+ )
+ }
+
+ if (orientation === "right") {
+ return (
+
+ )
+ }
+
+ return (
+
+ )
+ },
+ DayButton: CalendarDayButton,
+ WeekNumber: ({ children, ...props }) => {
+ return (
+
+
+ {children}
+
+
+ )
+ },
+ ...components,
+ }}
+ {...props}
+ />
+ )
+}
+
+function CalendarDayButton({
+ className,
+ day,
+ modifiers,
+ ...props
+}: React.ComponentProps) {
+ const defaultClassNames = getDefaultClassNames()
+
+ const ref = React.useRef(null)
+ React.useEffect(() => {
+ if (modifiers.focused) ref.current?.focus()
+ }, [modifiers.focused])
+
+ return (
+ span]:text-xs [&>span]:opacity-70",
+ defaultClassNames.day,
+ className
+ )}
+ {...props}
+ />
+ )
+}
+
+export { Calendar, CalendarDayButton }
diff --git a/apps/ui/src/registry/new-york-v4/ui/card.tsx b/apps/ui/src/registry/new-york-v4/ui/card.tsx
new file mode 100644
index 0000000..acf57dc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Card({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+}
diff --git a/apps/ui/src/registry/new-york-v4/ui/carousel.tsx b/apps/ui/src/registry/new-york-v4/ui/carousel.tsx
new file mode 100644
index 0000000..bc5667b
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/carousel.tsx
@@ -0,0 +1,240 @@
+
+import * as React from "react"
+import useEmblaCarousel, {
+ type UseEmblaCarouselType,
+} from "embla-carousel-react"
+import { ArrowLeft, ArrowRight } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/registry/new-york-v4/ui/button"
+
+type CarouselApi = UseEmblaCarouselType[1]
+type UseCarouselParameters = Parameters
+type CarouselOptions = UseCarouselParameters[0]
+type CarouselPlugin = UseCarouselParameters[1]
+
+type CarouselProps = {
+ opts?: CarouselOptions
+ plugins?: CarouselPlugin
+ orientation?: "horizontal" | "vertical"
+ setApi?: (api: CarouselApi) => void
+}
+
+type CarouselContextProps = {
+ carouselRef: ReturnType[0]
+ api: ReturnType[1]
+ scrollPrev: () => void
+ scrollNext: () => void
+ canScrollPrev: boolean
+ canScrollNext: boolean
+} & CarouselProps
+
+const CarouselContext = React.createContext(null)
+
+function useCarousel() {
+ const context = React.useContext(CarouselContext)
+
+ if (!context) {
+ throw new Error("useCarousel must be used within a ")
+ }
+
+ return context
+}
+
+function Carousel({
+ orientation = "horizontal",
+ opts,
+ setApi,
+ plugins,
+ className,
+ children,
+ ...props
+}: React.ComponentProps<"div"> & CarouselProps) {
+ const [carouselRef, api] = useEmblaCarousel(
+ {
+ ...opts,
+ axis: orientation === "horizontal" ? "x" : "y",
+ },
+ plugins
+ )
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
+
+ const onSelect = React.useCallback((api: CarouselApi) => {
+ if (!api) return
+ setCanScrollPrev(api.canScrollPrev())
+ setCanScrollNext(api.canScrollNext())
+ }, [])
+
+ const scrollPrev = React.useCallback(() => {
+ api?.scrollPrev()
+ }, [api])
+
+ const scrollNext = React.useCallback(() => {
+ api?.scrollNext()
+ }, [api])
+
+ const handleKeyDown = React.useCallback(
+ (event: React.KeyboardEvent) => {
+ if (event.key === "ArrowLeft") {
+ event.preventDefault()
+ scrollPrev()
+ } else if (event.key === "ArrowRight") {
+ event.preventDefault()
+ scrollNext()
+ }
+ },
+ [scrollPrev, scrollNext]
+ )
+
+ React.useEffect(() => {
+ if (!api || !setApi) return
+ setApi(api)
+ }, [api, setApi])
+
+ React.useEffect(() => {
+ if (!api) return
+ onSelect(api)
+ api.on("reInit", onSelect)
+ api.on("select", onSelect)
+
+ return () => {
+ api?.off("select", onSelect)
+ }
+ }, [api, onSelect])
+
+ return (
+
+
+ {children}
+
+
+ )
+}
+
+function CarouselContent({ className, ...props }: React.ComponentProps<"div">) {
+ const { carouselRef, orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+function CarouselItem({ className, ...props }: React.ComponentProps<"div">) {
+ const { orientation } = useCarousel()
+
+ return (
+
+ )
+}
+
+function CarouselPrevious({
+ className,
+ variant = "outline",
+ size = "icon",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollPrev, canScrollPrev } = useCarousel()
+
+ return (
+
+
+ Previous slide
+
+ )
+}
+
+function CarouselNext({
+ className,
+ variant = "outline",
+ size = "icon",
+ ...props
+}: React.ComponentProps) {
+ const { orientation, scrollNext, canScrollNext } = useCarousel()
+
+ return (
+
+
+ Next slide
+
+ )
+}
+
+export {
+ type CarouselApi,
+ Carousel,
+ CarouselContent,
+ CarouselItem,
+ CarouselPrevious,
+ CarouselNext,
+}
diff --git a/apps/ui/src/registry/new-york-v4/ui/chart.tsx b/apps/ui/src/registry/new-york-v4/ui/chart.tsx
new file mode 100644
index 0000000..56e58bc
--- /dev/null
+++ b/apps/ui/src/registry/new-york-v4/ui/chart.tsx
@@ -0,0 +1,373 @@
+
+import * as React from "react"
+import * as RechartsPrimitive from "recharts"
+import type { TooltipValueType } from "recharts"
+
+import { cn } from "@/lib/utils"
+
+// Format: { THEME_NAME: CSS_SELECTOR }
+const THEMES = { light: "", dark: ".dark" } as const
+
+const INITIAL_DIMENSION = { width: 320, height: 200 } as const
+type TooltipNameType = number | string
+
+export type ChartConfig = Record<
+ string,
+ {
+ label?: React.ReactNode
+ icon?: React.ComponentType
+ } & (
+ | { color?: string; theme?: never }
+ | { color?: never; theme: Record }
+ )
+>
+
+type ChartContextProps = {
+ config: ChartConfig
+}
+
+const ChartContext = React.createContext(null)
+
+function useChart() {
+ const context = React.useContext(ChartContext)
+
+ if (!context) {
+ throw new Error("useChart must be used within a ")
+ }
+
+ return context
+}
+
+function ChartContainer({
+ id,
+ className,
+ children,
+ config,
+ initialDimension = INITIAL_DIMENSION,
+ ...props
+}: React.ComponentProps<"div"> & {
+ config: ChartConfig
+ children: React.ComponentProps<
+ typeof RechartsPrimitive.ResponsiveContainer
+ >["children"]
+ initialDimension?: {
+ width: number
+ height: number
+ }
+}) {
+ const uniqueId = React.useId()
+ const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`
+
+ return (
+
+
+
+
+ {children}
+
+
+
+ )
+}
+
+const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
+ const colorConfig = Object.entries(config).filter(
+ ([, config]) => config.theme ?? config.color
+ )
+
+ if (!colorConfig.length) {
+ return null
+ }
+
+ return (
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/preview-switcher.tsx b/apps/ui/src/routes/create/components/preview-switcher.tsx
new file mode 100644
index 0000000..c4d73bd
--- /dev/null
+++ b/apps/ui/src/routes/create/components/preview-switcher.tsx
@@ -0,0 +1,39 @@
+
+import { Button } from "@/registry/new-york-v4/ui/button"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+const PREVIEW_ITEMS = [
+ { label: "01", value: "preview-02" },
+ { label: "02", value: "preview" },
+ { label: "03", value: "custom" },
+]
+
+export function PreviewSwitcher() {
+ const [params, setParams] = useDesignSystemSearchParams()
+
+ const isPreview =
+ params.item === "preview" ||
+ params.item === "custom" ||
+ params.item.startsWith("preview-0")
+
+ if (!isPreview) {
+ return null
+ }
+
+ return (
+
+ {PREVIEW_ITEMS.map((item) => (
+ setParams({ item: item.value })}
+ >
+ {item.label}
+
+ ))}
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/preview.tsx b/apps/ui/src/routes/create/components/preview.tsx
new file mode 100644
index 0000000..b55eebb
--- /dev/null
+++ b/apps/ui/src/routes/create/components/preview.tsx
@@ -0,0 +1,38 @@
+import * as React from "react";
+
+import { PreviewSwitcher } from "@/routes/create/components/preview-switcher";
+import { InlinePreview } from "@/routes/create/preview/inline-preview";
+
+export function Preview() {
+ // Local dark-mode toggle for the preview — scoped to the DesignSystemProvider
+ // wrapper, independent of the app's global next-themes setting.
+ const [isDark, setIsDark] = React.useState(false);
+
+ React.useEffect(() => {
+ const handler = (event: KeyboardEvent) => {
+ if (event.key === "d" || event.key === "D") {
+ const target = event.target as HTMLElement | null;
+ if (target?.matches("input, textarea, [contenteditable='true']")) return;
+ setIsDark((prev) => !prev);
+ }
+ };
+ window.addEventListener("keydown", handler);
+ return () => window.removeEventListener("keydown", handler);
+ }, []);
+
+ return (
+
+ );
+}
+
+// Legacy export for components that still reference the old iframe message
+// constant; they no longer dispatch. Safe to delete in Phase I polish.
+export const CMD_K_FORWARD_TYPE = "cmd-k-forward" as const;
diff --git a/apps/ui/src/routes/create/components/project-form.tsx b/apps/ui/src/routes/create/components/project-form.tsx
new file mode 100644
index 0000000..61c76f7
--- /dev/null
+++ b/apps/ui/src/routes/create/components/project-form.tsx
@@ -0,0 +1,374 @@
+
+import * as React from "react"
+import { Copy01Icon, Globe02Icon, Tick02Icon } from "@hugeicons/core-free-icons"
+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 { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog"
+import {
+ Field,
+ FieldContent,
+ FieldGroup,
+ FieldLabel,
+ FieldLegend,
+ FieldSeparator,
+ FieldSet,
+ FieldTitle,
+} from "@/components/ui/field"
+import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
+import { Switch } from "@/components/ui/switch"
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from "@/components/ui/tabs"
+import { usePresetCode } from "@/routes/create/hooks/use-design-system"
+import {
+ useDesignSystemSearchParams,
+ type DesignSystemSearchParams,
+} from "@/routes/create/lib/search-params"
+import {
+ getFramework,
+ getTemplateValue,
+ NO_MONOREPO_FRAMEWORKS,
+ TEMPLATES,
+} from "@/routes/create/lib/templates"
+
+const TURBOREPO_LOGO =
+ 'Turborepo '
+const ORIGIN = import.meta.env.VITE_APP_URL || "http://localhost:4000"
+const IS_LOCAL_DEV = ORIGIN.includes("localhost")
+const SHADCN_VERSION = import.meta.env.VITE_RC ? "@rc" : "@latest"
+const PACKAGE_MANAGERS = ["pnpm", "npm", "yarn", "bun"] as const
+type PackageManager = (typeof PACKAGE_MANAGERS)[number]
+
+export function ProjectForm({
+ className,
+}: React.ComponentProps) {
+ const [open, setOpen] = React.useState(false)
+ const [params, setParams] = useDesignSystemSearchParams()
+ const presetCode = usePresetCode()
+ const [config, setConfig] = useConfig()
+ const [hasCopied, setHasCopied] = React.useState(false)
+
+ const packageManager = (config.packageManager || "pnpm") as PackageManager
+ const framework = React.useMemo(
+ () => getFramework(params.template ?? "next"),
+ [params.template]
+ )
+ const isMonorepo = React.useMemo(
+ () => params.template?.endsWith("-monorepo") ?? false,
+ [params.template]
+ )
+
+ const hasMonorepo = !NO_MONOREPO_FRAMEWORKS.includes(
+ framework as (typeof NO_MONOREPO_FRAMEWORKS)[number]
+ )
+
+ const commands = React.useMemo(() => {
+ const presetFlag = ` --preset ${presetCode}`
+ const baseFlag = params.base !== "radix" ? ` --base ${params.base}` : ""
+ const templateFlag = ` --template ${framework}`
+ const monorepoFlag = isMonorepo ? " --monorepo" : ""
+ const rtlFlag = params.rtl ? " --rtl" : ""
+ const flags = `${presetFlag}${baseFlag}${templateFlag}${monorepoFlag}${rtlFlag}`
+
+ return IS_LOCAL_DEV
+ ? {
+ pnpm: `shadcn init${flags}`,
+ npm: `shadcn init${flags}`,
+ yarn: `shadcn init${flags}`,
+ bun: `shadcn init${flags}`,
+ }
+ : {
+ pnpm: `pnpm dlx shadcn${SHADCN_VERSION} init${flags}`,
+ npm: `npx shadcn${SHADCN_VERSION} init${flags}`,
+ yarn: `yarn dlx shadcn${SHADCN_VERSION} init${flags}`,
+ bun: `bunx --bun shadcn${SHADCN_VERSION} init${flags}`,
+ }
+ }, [framework, isMonorepo, params.base, params.rtl, presetCode])
+
+ const command = commands[packageManager]
+
+ React.useEffect(() => {
+ if (hasCopied) {
+ const timer = setTimeout(() => setHasCopied(false), 2000)
+ return () => clearTimeout(timer)
+ }
+ }, [hasCopied])
+
+ const handleCopy = React.useCallback(() => {
+ const properties: Record = {
+ command,
+ }
+ if (params.template) {
+ properties.template = params.template
+ }
+ copyToClipboardWithMeta(command, {
+ name: "copy_npm_command",
+ properties,
+ })
+ setHasCopied(true)
+ }, [command, params.template])
+
+ return (
+
+ }>
+ Create Project
+
+
+
+ Create Project
+
+ Pick a template and configure your project.
+
+
+
+
+
+
+ Template
+
+
+
+
+ Base
+
+
+
+
+
+ Options
+
+
+
+
+ Create a monorepo
+
+ {
+ const framework = getFramework(params.template ?? "next")
+ setParams({
+ template: getTemplateValue(
+ framework,
+ checked === true
+ ) as typeof params.template,
+ })
+ }}
+ />
+
+
+
+
+
+ Enable RTL support
+
+
+ setParams({ rtl: checked === true })
+ }
+ />
+
+
+
+
+
+
+
{
+ setConfig((prev) => ({
+ ...prev,
+ packageManager: value as PackageManager,
+ }))
+ }}
+ className="min-w-0 gap-0 overflow-hidden rounded-xl border-0 ring-1 ring-border"
+ >
+
+
+ {PACKAGE_MANAGERS.map((manager) => {
+ return (
+
+ {manager}
+
+ )
+ })}
+
+
+ {hasCopied ? (
+
+ ) : (
+
+ )}
+ Copy command
+
+
+ {Object.entries(commands).map(([key, cmd]) => {
+ return (
+
+
+
+ )
+ })}
+
+
+ {hasCopied ? "Copied" : "Copy Command"}
+
+
+
+
+
+ )
+}
+
+const TemplateGrid = React.memo(function TemplateGrid({
+ template,
+ setParams,
+}: {
+ template: DesignSystemSearchParams["template"]
+ setParams: ReturnType[1]
+}) {
+ const isMonorepo = template?.endsWith("-monorepo") ?? false
+ const framework = getFramework(template ?? "next")
+
+ const handleTemplateChange = React.useCallback(
+ (value: string) => {
+ setParams({
+ template: getTemplateValue(
+ value,
+ isMonorepo
+ ) as DesignSystemSearchParams["template"],
+ })
+ },
+ [isMonorepo, setParams]
+ )
+
+ return (
+
+ {TEMPLATES.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+
+ ))}
+
+ )
+})
+
+const BaseGrid = React.memo(function BaseGrid({
+ base,
+ setParams,
+}: {
+ base: DesignSystemSearchParams["base"]
+ setParams: ReturnType[1]
+}) {
+ const handleBaseChange = React.useCallback(
+ (value: string) => {
+ setParams({ base: value as BaseName })
+ },
+ [setParams]
+ )
+
+ return (
+
+ {BASES.map((item) => (
+
+
+
+
+ {item.title}
+
+
+
+
+ ))}
+
+ )
+})
diff --git a/apps/ui/src/routes/create/components/radius-picker.tsx b/apps/ui/src/routes/create/components/radius-picker.tsx
new file mode 100644
index 0000000..d4cb281
--- /dev/null
+++ b/apps/ui/src/routes/create/components/radius-picker.tsx
@@ -0,0 +1,109 @@
+
+import * as React from "react"
+
+import { RADII, type RadiusValue } from "@/registry/config"
+import { LockButton } from "@/routes/create/components/lock-button"
+import {
+ Picker,
+ PickerContent,
+ PickerGroup,
+ PickerRadioGroup,
+ PickerRadioItem,
+ PickerSeparator,
+ PickerTrigger,
+} from "@/routes/create/components/picker"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+export function RadiusPicker({
+ isMobile,
+ anchorRef,
+}: {
+ isMobile: boolean
+ anchorRef: React.RefObject
+}) {
+ const [params, setParams] = useDesignSystemSearchParams()
+ const isRadiusLocked = params.style === "lyra" || params.style === "sera"
+ const selectedRadiusName = isRadiusLocked ? "none" : params.radius
+
+ const currentRadius = RADII.find(
+ (radius) => radius.name === selectedRadiusName
+ )
+ const defaultRadius = RADII.find((radius) => radius.name === "default")
+ const otherRadii = RADII.filter((radius) => radius.name !== "default")
+
+ return (
+
+
+
+
+
Radius
+
+ {currentRadius?.label}
+
+
+
+
+
+ {
+ if (isRadiusLocked) {
+ return
+ }
+ setParams({ radius: value as RadiusValue })
+ }}
+ >
+
+ {defaultRadius && (
+
+ {defaultRadius.label}
+
+ )}
+
+
+
+ {otherRadii.map((radius) => (
+
+ {radius.label}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/random-button.tsx b/apps/ui/src/routes/create/components/random-button.tsx
new file mode 100644
index 0000000..3482305
--- /dev/null
+++ b/apps/ui/src/routes/create/components/random-button.tsx
@@ -0,0 +1,72 @@
+
+import Script from "next/script"
+import { DiceFaces05Icon } from "@hugeicons/core-free-icons"
+import { HugeiconsIcon } from "@hugeicons/react"
+
+import { cn } from "@/lib/utils"
+import { Button } from "@/components/ui/button"
+import { useRandom } from "@/routes/create/hooks/use-random"
+import { RESET_FORWARD_TYPE } from "@/routes/create/hooks/use-reset"
+
+export const RANDOMIZE_FORWARD_TYPE = "randomize-forward"
+
+export function RandomButton({
+ variant = "outline",
+ className,
+ ...props
+}: React.ComponentProps) {
+ const { randomize } = useRandom()
+
+ return (
+
+ Shuffle
+
+ )
+}
+
+export function RandomizeScript() {
+ return (
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/reset-button.tsx b/apps/ui/src/routes/create/components/reset-button.tsx
new file mode 100644
index 0000000..13f1aad
--- /dev/null
+++ b/apps/ui/src/routes/create/components/reset-button.tsx
@@ -0,0 +1,33 @@
+
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from "@/components/ui/alert-dialog"
+import { useReset } from "@/routes/create/hooks/use-reset"
+
+export function ResetDialog() {
+ const { showResetDialog, setShowResetDialog, confirmReset } = useReset()
+
+ return (
+
+
+
+ Reset to defaults?
+
+ This will reset all customization options to their default values.
+
+
+
+ Cancel
+ Reset
+
+
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/share-button.tsx b/apps/ui/src/routes/create/components/share-button.tsx
new file mode 100644
index 0000000..b89c5b1
--- /dev/null
+++ b/apps/ui/src/routes/create/components/share-button.tsx
@@ -0,0 +1,56 @@
+
+import * as React from "react"
+import { Share03Icon, Tick02Icon } from "@hugeicons/core-free-icons"
+import { HugeiconsIcon } from "@hugeicons/react"
+
+import { copyToClipboardWithMeta } from "@/components/copy-button"
+import { Button } from "@/components/ui/button"
+import { usePresetCode } from "@/routes/create/hooks/use-design-system"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+export function ShareButton() {
+ const [params] = useDesignSystemSearchParams()
+ const presetCode = usePresetCode()
+ const [hasCopied, setHasCopied] = React.useState(false)
+
+ const shareUrl = React.useMemo(() => {
+ const origin = import.meta.env.VITE_APP_URL || "http://localhost:3000"
+ return `${origin}/create?preset=${presetCode}&item=${params.item}`
+ }, [presetCode, params.item])
+
+ React.useEffect(() => {
+ if (hasCopied) {
+ const timer = setTimeout(() => setHasCopied(false), 2000)
+ return () => clearTimeout(timer)
+ }
+ }, [hasCopied])
+
+ const handleCopy = React.useCallback(() => {
+ copyToClipboardWithMeta(shareUrl, {
+ name: "copy_create_share_url",
+ properties: {
+ url: shareUrl,
+ },
+ })
+ setHasCopied(true)
+ }, [shareUrl])
+
+ return (
+
+ {hasCopied ? (
+
+ ) : (
+
+ )}
+ Share
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/style-picker.tsx b/apps/ui/src/routes/create/components/style-picker.tsx
new file mode 100644
index 0000000..6dcb8e1
--- /dev/null
+++ b/apps/ui/src/routes/create/components/style-picker.tsx
@@ -0,0 +1,95 @@
+
+import * as React from "react"
+
+import { PRESETS, type Style, type StyleName } from "@/registry/config"
+import { LockButton } from "@/routes/create/components/lock-button"
+import {
+ Picker,
+ PickerContent,
+ PickerGroup,
+ PickerRadioGroup,
+ PickerRadioItem,
+ PickerTrigger,
+} from "@/routes/create/components/picker"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+export function StylePicker({
+ styles,
+ isMobile,
+ anchorRef,
+}: {
+ styles: readonly Style[]
+ isMobile: boolean
+ anchorRef: React.RefObject
+}) {
+ const [params, setParams] = useDesignSystemSearchParams()
+
+ const currentStyle = styles.find((style) => style.name === params.style)
+
+ return (
+
+
+
+
+
Style
+
+ {currentStyle?.title}
+
+
+ {currentStyle?.icon && (
+
+ {React.cloneElement(currentStyle.icon, {
+ className: "size-4",
+ })}
+
+ )}
+
+
+ {
+ 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,
+ }),
+ })
+ }}
+ >
+
+ {styles.map((style) => (
+
+ {style.title}
+
+ ))}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/theme-picker.tsx b/apps/ui/src/routes/create/components/theme-picker.tsx
new file mode 100644
index 0000000..e0ba701
--- /dev/null
+++ b/apps/ui/src/routes/create/components/theme-picker.tsx
@@ -0,0 +1,129 @@
+
+import * as React from "react"
+
+import { useMounted } from "@/hooks/use-mounted"
+import { BASE_COLORS, type Theme, type ThemeName } from "@/registry/config"
+import { LockButton } from "@/routes/create/components/lock-button"
+import {
+ Picker,
+ PickerContent,
+ PickerGroup,
+ PickerRadioGroup,
+ PickerRadioItem,
+ PickerSeparator,
+ PickerTrigger,
+} from "@/routes/create/components/picker"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+export function ThemePicker({
+ themes,
+ isMobile,
+ anchorRef,
+}: {
+ themes: readonly Theme[]
+ isMobile: boolean
+ anchorRef: React.RefObject
+}) {
+ const mounted = useMounted()
+ const [params, setParams] = useDesignSystemSearchParams()
+
+ const currentTheme = React.useMemo(
+ () => themes.find((theme) => theme.name === params.theme),
+ [themes, params.theme]
+ )
+
+ const currentThemeIsBaseColor = React.useMemo(
+ () => BASE_COLORS.find((baseColor) => baseColor.name === params.theme),
+ [params.theme]
+ )
+
+ React.useEffect(() => {
+ if (!currentTheme && themes.length > 0) {
+ setParams({ theme: themes[0].name })
+ }
+ }, [currentTheme, themes, setParams])
+
+ return (
+
+
+
+
+
Theme
+
+ {currentTheme?.title}
+
+
+ {mounted && (
+
+ )}
+
+
+ {
+ setParams({ theme: value as ThemeName })
+ }}
+ >
+
+ {themes
+ .filter((theme) =>
+ BASE_COLORS.find((baseColor) => baseColor.name === theme.name)
+ )
+ .map((theme) => {
+ return (
+
+ {theme.title}
+
+ )
+ })}
+
+
+
+ {themes
+ .filter(
+ (theme) =>
+ !BASE_COLORS.find(
+ (baseColor) => baseColor.name === theme.name
+ )
+ )
+ .map((theme) => {
+ return (
+
+ {theme.title}
+
+ )
+ })}
+
+
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/v0-button.tsx b/apps/ui/src/routes/create/components/v0-button.tsx
new file mode 100644
index 0000000..729b9b7
--- /dev/null
+++ b/apps/ui/src/routes/create/components/v0-button.tsx
@@ -0,0 +1,56 @@
+
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+import { useIsMobile } from "@/hooks/use-mobile"
+import { useMounted } from "@/hooks/use-mounted"
+import { Icons } from "@/components/icons"
+import { Button } from "@/components/ui/button"
+import { Skeleton } from "@/components/ui/skeleton"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+export function V0Button({ className }: { className?: string }) {
+ const [params] = useDesignSystemSearchParams()
+ const isMobile = useIsMobile()
+ const isMounted = useMounted()
+
+ const url = React.useMemo(() => {
+ const searchParams = new URLSearchParams()
+
+ if (params.preset) {
+ searchParams.set("preset", params.preset)
+ }
+
+ searchParams.set("base", params.base)
+
+ return `${import.meta.env.VITE_APP_URL}/init/v0?${searchParams.toString()}`
+ }, [params.preset, params.base])
+
+ const title = React.useMemo(() => {
+ return params.base && params.style
+ ? `New ${params.base}-${params.style} project`
+ : "New Project"
+ }, [params.base, params.style])
+
+ if (!isMounted) {
+ return
+ }
+
+ return (
+
+ }
+ >
+ Open in
+
+
+ )
+}
diff --git a/apps/ui/src/routes/create/components/welcome-dialog.tsx b/apps/ui/src/routes/create/components/welcome-dialog.tsx
new file mode 100644
index 0000000..428fe7e
--- /dev/null
+++ b/apps/ui/src/routes/create/components/welcome-dialog.tsx
@@ -0,0 +1,67 @@
+
+import * as React from "react"
+
+import { Icons } from "@/components/icons"
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+} from "@/components/ui/dialog"
+
+const STORAGE_KEY = "shadcn-create-welcome-dialog"
+
+export function WelcomeDialog() {
+ const [isOpen, setIsOpen] = React.useState(false)
+
+ React.useEffect(() => {
+ const dismissed = localStorage.getItem(STORAGE_KEY)
+ if (!dismissed) {
+ setIsOpen(true)
+ }
+ }, [])
+
+ // Stable callback — avoids re-creation on every render. (rerender-functional-setstate)
+ const handleOpenChange = React.useCallback((open: boolean) => {
+ setIsOpen(open)
+ if (!open) {
+ localStorage.setItem(STORAGE_KEY, "true")
+ }
+ }, [])
+
+ return (
+
+
+
+
+
+ Build your own shadcn/ui
+
+
+ Customize everything from the ground up. Pick your component
+ library, font, color scheme, and more.
+
+
+ Available for all major React frameworks.
+
+
+
+ }>
+ Get Started
+
+
+
+
+ )
+}
diff --git a/apps/ui/src/routes/create/hooks/use-action-menu.ts b/apps/ui/src/routes/create/hooks/use-action-menu.ts
new file mode 100644
index 0000000..8969dcc
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-action-menu.ts
@@ -0,0 +1,141 @@
+
+import * as React from "react"
+import { type RegistryItem } from "shadcn/schema"
+import useSWR from "swr"
+
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+import { groupItemsByType } from "@/routes/create/lib/utils"
+
+const ACTION_MENU_OPEN_KEY = "create:action-menu-open"
+
+type ActionMenuItem = {
+ id: string
+ type: string
+ label: string
+ registryName: string
+}
+
+type ActionMenuGroup = {
+ type: string
+ title: string
+ items: ActionMenuItem[]
+}
+
+type ActionMenuSourceItem = Pick
+
+const SEARCH_KEYWORDS: Record = {
+ "registry:block": "block blocks component components",
+ "registry:item": "item items component components",
+}
+
+function sortRegistryGroups(groups: ReturnType) {
+ return [...groups].sort((a, b) => {
+ if (a.type === b.type) {
+ return a.title.localeCompare(b.title)
+ }
+ if (a.type === "registry:block") {
+ return -1
+ }
+ if (b.type === "registry:block") {
+ return 1
+ }
+ return a.title.localeCompare(b.title)
+ })
+}
+
+export function useActionMenu(
+ itemsByBase: Record
+) {
+ const [params, setParams] = useDesignSystemSearchParams()
+ const { data: open = false, mutate: setOpenData } = useSWR(
+ ACTION_MENU_OPEN_KEY,
+ {
+ fallbackData: false,
+ revalidateOnFocus: false,
+ revalidateIfStale: false,
+ revalidateOnReconnect: false,
+ }
+ )
+
+ const groups = React.useMemo(() => {
+ const currentBaseItems = itemsByBase?.[params.base] ?? []
+ const sortedRegistryGroups = sortRegistryGroups(
+ groupItemsByType(currentBaseItems)
+ )
+
+ return sortedRegistryGroups.map((group) => ({
+ type: group.type,
+ title: group.title,
+ items: group.items.map((item) => ({
+ id: `${group.type}:${item.name}`,
+ type: group.type,
+ label: item.title ?? item.name,
+ registryName: item.name,
+ })),
+ }))
+ }, [itemsByBase, params.base])
+
+ const activeRegistryName = params.item
+
+ const handleSelect = React.useCallback(
+ (registryName: string) => {
+ setParams({ item: registryName })
+ void setOpenData(false, { revalidate: false })
+ },
+ [setOpenData, setParams]
+ )
+
+ const handleOpenChange = React.useCallback(
+ (nextOpen: boolean) => {
+ void setOpenData(nextOpen, { revalidate: false })
+ },
+ [setOpenData]
+ )
+
+ const getCommandValue = React.useCallback((item: ActionMenuItem) => {
+ const keywords = SEARCH_KEYWORDS[item.type] ?? item.type.replace(":", " ")
+ return `${item.label ?? ""} ${keywords}`.trim()
+ }, [])
+
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if (e.key === "p" && (e.metaKey || e.ctrlKey)) {
+ e.preventDefault()
+ void setOpenData((currentOpen = false) => !currentOpen, {
+ revalidate: false,
+ })
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => {
+ document.removeEventListener("keydown", down)
+ }
+ }, [setOpenData])
+
+ return {
+ activeRegistryName,
+ getCommandValue,
+ groups,
+ handleSelect,
+ open,
+ setOpen: handleOpenChange,
+ }
+}
+
+export function useActionMenuTrigger() {
+ const { mutate: setOpenData } = useSWR(ACTION_MENU_OPEN_KEY, {
+ fallbackData: false,
+ revalidateOnFocus: false,
+ revalidateIfStale: false,
+ revalidateOnReconnect: false,
+ })
+
+ const openActionMenu = React.useCallback(() => {
+ void setOpenData(true, { revalidate: false })
+ }, [setOpenData])
+
+ return {
+ openActionMenu,
+ }
+}
diff --git a/apps/ui/src/routes/create/hooks/use-custom-component.tsx b/apps/ui/src/routes/create/hooks/use-custom-component.tsx
new file mode 100644
index 0000000..dd1adb6
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-custom-component.tsx
@@ -0,0 +1,112 @@
+import * as React from "react"
+
+import type { CustomComponentSpec } from "@/routes/create/lib/custom-component-spec"
+
+const STORAGE_KEY = "shadify:create:custom-component"
+
+type PersistedState = {
+ spec: CustomComponentSpec | null
+ prompt: string | null
+ version: number
+}
+
+type CustomComponentState = PersistedState & {
+ buildingTitle: string | null
+}
+
+type CustomComponentContextValue = CustomComponentState & {
+ beginBuild: (title: string) => void
+ setSpec: (spec: CustomComponentSpec, prompt?: string) => void
+ clear: () => void
+}
+
+const CustomComponentContext =
+ React.createContext(null)
+
+function loadFromStorage(): PersistedState {
+ if (typeof window === "undefined") {
+ return { spec: null, prompt: null, version: 0 }
+ }
+ try {
+ const raw = sessionStorage.getItem(STORAGE_KEY)
+ if (!raw) return { spec: null, prompt: null, version: 0 }
+ const parsed = JSON.parse(raw) as Partial
+ return {
+ spec: parsed.spec ?? null,
+ prompt: parsed.prompt ?? null,
+ version: parsed.version ?? 0,
+ }
+ } catch {
+ return { spec: null, prompt: null, version: 0 }
+ }
+}
+
+export function CustomComponentProvider({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ const [state, setState] = React.useState(() => ({
+ ...loadFromStorage(),
+ buildingTitle: null,
+ }))
+
+ React.useEffect(() => {
+ if (typeof window === "undefined") return
+ try {
+ const { spec, prompt, version } = state
+ sessionStorage.setItem(
+ STORAGE_KEY,
+ JSON.stringify({ spec, prompt, version })
+ )
+ } catch {
+ // quota or disabled storage — ignore
+ }
+ }, [state])
+
+ const beginBuild = React.useCallback((title: string) => {
+ setState({
+ spec: null,
+ prompt: null,
+ version: 0,
+ buildingTitle: title,
+ })
+ }, [])
+
+ const setSpec = React.useCallback(
+ (spec: CustomComponentSpec, prompt?: string) => {
+ setState((prev) => ({
+ spec,
+ prompt: prompt ?? prev.prompt,
+ version: prev.version + 1,
+ buildingTitle: null,
+ }))
+ },
+ []
+ )
+
+ const clear = React.useCallback(() => {
+ setState({ spec: null, prompt: null, version: 0, buildingTitle: null })
+ }, [])
+
+ const value = React.useMemo(
+ () => ({ ...state, beginBuild, setSpec, clear }),
+ [state, beginBuild, setSpec, clear]
+ )
+
+ return (
+
+ {children}
+
+ )
+}
+
+export function useCustomComponent() {
+ const ctx = React.useContext(CustomComponentContext)
+ if (!ctx) {
+ throw new Error(
+ "useCustomComponent must be used inside CustomComponentProvider"
+ )
+ }
+ return ctx
+}
diff --git a/apps/ui/src/routes/create/hooks/use-design-system.ts b/apps/ui/src/routes/create/hooks/use-design-system.ts
new file mode 100644
index 0000000..909a248
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-design-system.ts
@@ -0,0 +1,10 @@
+
+import { getPresetCode } from "@/routes/create/lib/preset-code"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+// Returns the canonical preset code derived from the current search params.
+export function usePresetCode() {
+ const [params] = useDesignSystemSearchParams()
+
+ return getPresetCode(params)
+}
diff --git a/apps/ui/src/routes/create/hooks/use-history.tsx b/apps/ui/src/routes/create/hooks/use-history.tsx
new file mode 100644
index 0000000..f0a03ca
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-history.tsx
@@ -0,0 +1,174 @@
+
+import * as React from "react"
+import { Suspense } from "react"
+import { useRouter, useSearchParams } from "next/navigation"
+
+type HistoryContextValue = {
+ canGoBack: boolean
+ canGoForward: boolean
+ goBack: () => void
+ goForward: () => void
+}
+
+const HistoryContext = React.createContext(null)
+
+// Reads useSearchParams() in its own Suspense boundary so the
+// provider never blanks out children while search params resolve.
+function PresetSync({
+ onPresetChange,
+}: {
+ onPresetChange: (preset: string) => void
+}) {
+ const searchParams = useSearchParams()
+ const preset = searchParams.get("preset") ?? ""
+
+ React.useEffect(() => {
+ onPresetChange(preset)
+ }, [preset, onPresetChange])
+
+ return null
+}
+
+export function HistoryProvider({ children }: { children: React.ReactNode }) {
+ const router = useRouter()
+
+ const [preset, setPreset] = React.useState("")
+
+ const entriesRef = React.useRef([preset])
+ const indexRef = React.useRef(0)
+ const maxIndexRef = React.useRef(0)
+ const isNavigatingRef = React.useRef(false)
+
+ const [index, setIndex] = React.useState(0)
+ const [maxIndex, setMaxIndex] = React.useState(0)
+
+ const onPresetChange = React.useCallback((nextPreset: string) => {
+ setPreset(nextPreset)
+ }, [])
+
+ React.useEffect(() => {
+ if (isNavigatingRef.current) {
+ isNavigatingRef.current = false
+ return
+ }
+
+ if (preset === entriesRef.current[indexRef.current]) {
+ return
+ }
+
+ const nextEntries = entriesRef.current.slice(0, indexRef.current + 1)
+ nextEntries.push(preset)
+ entriesRef.current = nextEntries
+
+ const nextIndex = nextEntries.length - 1
+ indexRef.current = nextIndex
+ maxIndexRef.current = nextIndex
+ setIndex(nextIndex)
+ setMaxIndex(nextIndex)
+ }, [preset])
+
+ const canGoBack = index > 0
+ const canGoForward = index < maxIndex
+
+ const goBack = React.useCallback(() => {
+ if (indexRef.current <= 0) {
+ return
+ }
+
+ isNavigatingRef.current = true
+ const nextIndex = indexRef.current - 1
+ indexRef.current = nextIndex
+ setIndex(nextIndex)
+
+ const targetPreset = entriesRef.current[nextIndex]
+ const params = new URLSearchParams(window.location.search)
+ if (targetPreset) {
+ params.set("preset", targetPreset)
+ } else {
+ params.delete("preset")
+ }
+ const pathname = window.location.pathname
+ const query = params.toString()
+ router.replace(query ? `${pathname}?${query}` : pathname)
+ }, [router])
+
+ const goForward = React.useCallback(() => {
+ if (indexRef.current >= maxIndexRef.current) {
+ return
+ }
+
+ isNavigatingRef.current = true
+ const nextIndex = indexRef.current + 1
+ indexRef.current = nextIndex
+ setIndex(nextIndex)
+
+ const targetPreset = entriesRef.current[nextIndex]
+ const params = new URLSearchParams(window.location.search)
+ if (targetPreset) {
+ params.set("preset", targetPreset)
+ } else {
+ params.delete("preset")
+ }
+ const pathname = window.location.pathname
+ const query = params.toString()
+ router.replace(query ? `${pathname}?${query}` : pathname)
+ }, [router])
+
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if (!e.metaKey && !e.ctrlKey) {
+ return
+ }
+
+ if (
+ (e.target instanceof HTMLElement && e.target.isContentEditable) ||
+ e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement ||
+ e.target instanceof HTMLSelectElement
+ ) {
+ return
+ }
+
+ const key = e.key.toLowerCase()
+
+ if ((key === "z" && e.shiftKey) || (key === "y" && e.ctrlKey)) {
+ e.preventDefault()
+ goForward()
+ return
+ }
+
+ if (key === "z") {
+ e.preventDefault()
+ goBack()
+ }
+ }
+
+ document.addEventListener("keydown", down)
+
+ return () => {
+ document.removeEventListener("keydown", down)
+ }
+ }, [goBack, goForward])
+
+ const value = React.useMemo(
+ () => ({ canGoBack, canGoForward, goBack, goForward }),
+ [canGoBack, canGoForward, goBack, goForward]
+ )
+
+ return (
+
+
+
+
+ {children}
+
+ )
+}
+
+export function useHistory() {
+ const context = React.useContext(HistoryContext)
+ if (!context) {
+ throw new Error("useHistory must be used within HistoryProvider")
+ }
+ return context
+}
diff --git a/apps/ui/src/routes/create/hooks/use-iframe-sync.tsx b/apps/ui/src/routes/create/hooks/use-iframe-sync.tsx
new file mode 100644
index 0000000..ac9dae1
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-iframe-sync.tsx
@@ -0,0 +1,68 @@
+
+import * as React from "react"
+
+import type { DesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+type ParentToIframeMessage = {
+ type: "design-system-params"
+ data: DesignSystemSearchParams
+}
+
+export const isInIframe = () => {
+ if (typeof window === "undefined") {
+ return false
+ }
+ return window.self !== window.top
+}
+
+export function useIframeMessageListener<
+ Message extends ParentToIframeMessage,
+ MessageType extends Message["type"],
+>(
+ messageType: MessageType,
+ onMessage: (data: Extract["data"]) => void
+) {
+ const onMessageRef = React.useRef(onMessage)
+
+ React.useEffect(() => {
+ onMessageRef.current = onMessage
+ }, [onMessage])
+
+ React.useEffect(() => {
+ if (!isInIframe()) {
+ return
+ }
+
+ const handleMessage = (event: MessageEvent) => {
+ if (event.data.type === messageType) {
+ onMessageRef.current(event.data.data)
+ }
+ }
+
+ window.addEventListener("message", handleMessage)
+ return () => {
+ window.removeEventListener("message", handleMessage)
+ }
+ }, [messageType])
+}
+
+export function sendToIframe<
+ Message extends ParentToIframeMessage,
+ MessageType extends Message["type"],
+>(
+ iframe: HTMLIFrameElement | null,
+ messageType: MessageType,
+ data: Extract["data"]
+) {
+ if (!iframe?.contentWindow) {
+ return
+ }
+
+ iframe.contentWindow.postMessage(
+ {
+ type: messageType,
+ data,
+ },
+ "*"
+ )
+}
diff --git a/apps/ui/src/routes/create/hooks/use-locks.tsx b/apps/ui/src/routes/create/hooks/use-locks.tsx
new file mode 100644
index 0000000..b58da60
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-locks.tsx
@@ -0,0 +1,63 @@
+
+import * as React from "react"
+
+export type LockableParam =
+ | "style"
+ | "baseColor"
+ | "theme"
+ | "chartColor"
+ | "iconLibrary"
+ | "font"
+ | "fontHeading"
+ | "menuAccent"
+ | "menuColor"
+ | "radius"
+
+type LocksContextValue = {
+ locks: Set
+ isLocked: (param: LockableParam) => boolean
+ toggleLock: (param: LockableParam) => void
+}
+
+const LocksContext = React.createContext(null)
+
+export function LocksProvider({ children }: { children: React.ReactNode }) {
+ const [locks, setLocks] = React.useState>(new Set())
+ const locksRef = React.useRef(locks)
+ React.useEffect(() => {
+ locksRef.current = locks
+ }, [locks])
+
+ // Stable callback — reads from ref so it doesn't change on every lock toggle.
+ const isLocked = React.useCallback(
+ (param: LockableParam) => locksRef.current.has(param),
+ []
+ )
+
+ const toggleLock = React.useCallback((param: LockableParam) => {
+ setLocks((prev) => {
+ const next = new Set(prev)
+ if (next.has(param)) {
+ next.delete(param)
+ } else {
+ next.add(param)
+ }
+ return next
+ })
+ }, [])
+
+ const value = React.useMemo(
+ () => ({ locks, isLocked, toggleLock }),
+ [locks, isLocked, toggleLock]
+ )
+
+ return {children}
+}
+
+export function useLocks() {
+ const context = React.useContext(LocksContext)
+ if (!context) {
+ throw new Error("useLocks must be used within LocksProvider")
+ }
+ return context
+}
diff --git a/apps/ui/src/routes/create/hooks/use-open-preset.tsx b/apps/ui/src/routes/create/hooks/use-open-preset.tsx
new file mode 100644
index 0000000..47a4bff
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-open-preset.tsx
@@ -0,0 +1,80 @@
+
+import * as React from "react"
+import useSWR from "swr"
+
+const OPEN_PRESET_KEY = "create:open-preset-open"
+export const OPEN_PRESET_FORWARD_TYPE = "open-preset-forward"
+
+function isEditableTarget(target: EventTarget | null) {
+ return (
+ (target instanceof HTMLElement && target.isContentEditable) ||
+ target instanceof HTMLInputElement ||
+ target instanceof HTMLTextAreaElement ||
+ target instanceof HTMLSelectElement
+ )
+}
+
+export function useOpenPreset() {
+ const { data: open = false, mutate: setOpenData } = useSWR(
+ OPEN_PRESET_KEY,
+ {
+ fallbackData: false,
+ revalidateOnFocus: false,
+ revalidateIfStale: false,
+ revalidateOnReconnect: false,
+ }
+ )
+
+ const handleOpenChange = React.useCallback(
+ (nextOpen: boolean) => {
+ void setOpenData(nextOpen, { revalidate: false })
+ },
+ [setOpenData]
+ )
+
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if (
+ e.key === "o" &&
+ !e.shiftKey &&
+ !e.metaKey &&
+ !e.ctrlKey &&
+ !e.altKey
+ ) {
+ if (isEditableTarget(e.target)) {
+ return
+ }
+
+ e.preventDefault()
+ void setOpenData(true, { revalidate: false })
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => {
+ document.removeEventListener("keydown", down)
+ }
+ }, [setOpenData])
+
+ return {
+ open,
+ setOpen: handleOpenChange,
+ }
+}
+
+export function useOpenPresetTrigger() {
+ const { mutate: setOpenData } = useSWR(OPEN_PRESET_KEY, {
+ fallbackData: false,
+ revalidateOnFocus: false,
+ revalidateIfStale: false,
+ revalidateOnReconnect: false,
+ })
+
+ const openPreset = React.useCallback(() => {
+ void setOpenData(true, { revalidate: false })
+ }, [setOpenData])
+
+ return {
+ openPreset,
+ }
+}
diff --git a/apps/ui/src/routes/create/hooks/use-random.tsx b/apps/ui/src/routes/create/hooks/use-random.tsx
new file mode 100644
index 0000000..b30ed6d
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-random.tsx
@@ -0,0 +1,59 @@
+
+import * as React from "react"
+
+import { useLocks } from "@/routes/create/hooks/use-locks"
+import { computeRandomParams } from "@/routes/create/lib/randomize"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+export function useRandom() {
+ const { locks } = useLocks()
+ const [params, setParams] = useDesignSystemSearchParams()
+
+ const paramsRef = React.useRef(params)
+ React.useEffect(() => {
+ paramsRef.current = params
+ }, [params])
+
+ const randomize = React.useCallback(() => {
+ const nextParams = computeRandomParams(paramsRef.current, locks)
+
+ // Keep the ref in sync so rapid repeats use the latest randomized state
+ // even before the URL state finishes committing.
+ paramsRef.current = {
+ ...paramsRef.current,
+ ...nextParams,
+ }
+
+ setParams(nextParams)
+ }, [setParams, locks])
+
+ const randomizeRef = React.useRef(randomize)
+ React.useEffect(() => {
+ randomizeRef.current = randomize
+ }, [randomize])
+
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if (e.key === "r" && !e.shiftKey && !e.metaKey && !e.ctrlKey) {
+ if (
+ (e.target instanceof HTMLElement && e.target.isContentEditable) ||
+ e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement ||
+ e.target instanceof HTMLSelectElement
+ ) {
+ return
+ }
+
+ e.preventDefault()
+ randomizeRef.current()
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => {
+ document.removeEventListener("keydown", down)
+ }
+ }, [])
+
+ return { randomize }
+}
diff --git a/apps/ui/src/routes/create/hooks/use-reset.tsx b/apps/ui/src/routes/create/hooks/use-reset.tsx
new file mode 100644
index 0000000..5012998
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-reset.tsx
@@ -0,0 +1,102 @@
+
+import * as React from "react"
+import useSWR from "swr"
+
+import { DEFAULT_CONFIG, PRESETS } from "@/registry/config"
+import { useDesignSystemSearchParams } from "@/routes/create/lib/search-params"
+
+const RESET_DIALOG_KEY = "create:reset-dialog-open"
+export const RESET_FORWARD_TYPE = "reset-forward"
+
+export function useReset() {
+ const [params, setParams] = useDesignSystemSearchParams()
+ const { data: showResetDialog = false, mutate: setShowResetDialogData } =
+ useSWR(RESET_DIALOG_KEY, {
+ fallbackData: false,
+ revalidateOnFocus: false,
+ revalidateIfStale: false,
+ revalidateOnReconnect: false,
+ })
+
+ const reset = React.useCallback(() => {
+ const preset =
+ PRESETS.find(
+ (preset) => preset.base === params.base && preset.style === params.style
+ ) ?? DEFAULT_CONFIG
+
+ setParams({
+ base: params.base,
+ style: params.style,
+ 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,
+ template: DEFAULT_CONFIG.template,
+ item: params.item,
+ })
+ }, [setParams, params.base, params.style, params.item])
+
+ const handleShowResetDialogChange = React.useCallback(
+ (open: boolean) => {
+ void setShowResetDialogData(open, { revalidate: false })
+ },
+ [setShowResetDialogData]
+ )
+
+ const confirmReset = React.useCallback(() => {
+ reset()
+ void setShowResetDialogData(false, { revalidate: false })
+ }, [reset, setShowResetDialogData])
+
+ const showResetDialogRef = React.useRef(showResetDialog)
+ React.useEffect(() => {
+ showResetDialogRef.current = showResetDialog
+ }, [showResetDialog])
+
+ const confirmResetRef = React.useRef(confirmReset)
+ React.useEffect(() => {
+ confirmResetRef.current = confirmReset
+ }, [confirmReset])
+
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if (e.key === "R" && e.shiftKey && !e.metaKey && !e.ctrlKey) {
+ if (
+ (e.target instanceof HTMLElement && e.target.isContentEditable) ||
+ e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement ||
+ e.target instanceof HTMLSelectElement
+ ) {
+ return
+ }
+
+ e.preventDefault()
+
+ // If the dialog is already open, confirm the reset.
+ if (showResetDialogRef.current) {
+ confirmResetRef.current()
+ return
+ }
+
+ handleShowResetDialogChange(true)
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => {
+ document.removeEventListener("keydown", down)
+ }
+ }, [handleShowResetDialogChange])
+
+ return {
+ reset,
+ showResetDialog,
+ setShowResetDialog: handleShowResetDialogChange,
+ confirmReset,
+ }
+}
diff --git a/apps/ui/src/routes/create/hooks/use-theme-toggle.tsx b/apps/ui/src/routes/create/hooks/use-theme-toggle.tsx
new file mode 100644
index 0000000..53b10de
--- /dev/null
+++ b/apps/ui/src/routes/create/hooks/use-theme-toggle.tsx
@@ -0,0 +1,47 @@
+
+import * as React from "react"
+import { useTheme } from "next-themes"
+
+import { useMetaColor } from "@/hooks/use-meta-color"
+
+export function useThemeToggle() {
+ const { setTheme, resolvedTheme } = useTheme()
+ const { setMetaColor, metaColor } = useMetaColor()
+
+ React.useEffect(() => {
+ setMetaColor(metaColor)
+ }, [metaColor, setMetaColor])
+
+ const toggleTheme = React.useCallback(() => {
+ setTheme(resolvedTheme === "dark" ? "light" : "dark")
+ }, [resolvedTheme, setTheme])
+
+ // Listen for the D key to toggle theme.
+ React.useEffect(() => {
+ const down = (e: KeyboardEvent) => {
+ if (
+ (e.key === "d" || e.key === "D") &&
+ !e.metaKey &&
+ !e.ctrlKey &&
+ !e.altKey
+ ) {
+ if (
+ (e.target instanceof HTMLElement && e.target.isContentEditable) ||
+ e.target instanceof HTMLInputElement ||
+ e.target instanceof HTMLTextAreaElement ||
+ e.target instanceof HTMLSelectElement
+ ) {
+ return
+ }
+
+ e.preventDefault()
+ toggleTheme()
+ }
+ }
+
+ document.addEventListener("keydown", down)
+ return () => document.removeEventListener("keydown", down)
+ }, [toggleTheme])
+
+ return { toggleTheme }
+}
diff --git a/apps/ui/src/routes/create/layout.tsx b/apps/ui/src/routes/create/layout.tsx
new file mode 100644
index 0000000..9505dae
--- /dev/null
+++ b/apps/ui/src/routes/create/layout.tsx
@@ -0,0 +1,18 @@
+import { Suspense } from "react"
+
+import { HistoryProvider } from "@/routes/create/hooks/use-history"
+import { LocksProvider } from "@/routes/create/hooks/use-locks"
+
+export function CreateLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+
+ {children}
+
+
+ )
+}
diff --git a/apps/ui/src/routes/create/lib/api.ts b/apps/ui/src/routes/create/lib/api.ts
new file mode 100644
index 0000000..ed47054
--- /dev/null
+++ b/apps/ui/src/routes/create/lib/api.ts
@@ -0,0 +1,71 @@
+
+import { registryItemSchema } from "shadcn/schema"
+
+import { BASES, getThemesForBaseColor, type BaseName } from "@/registry/config"
+import {
+ ALLOWED_ITEM_TYPES,
+ EXCLUDED_ITEMS,
+} from "@/routes/create/lib/constants"
+
+export async function getItemsForBase(base: BaseName) {
+ const { Index } = await import("@/registry/bases/__index__")
+ const index = Index[base]
+
+ if (!index) {
+ return []
+ }
+
+ return Object.values(index).filter(
+ (item) =>
+ ALLOWED_ITEM_TYPES.includes(item.type) &&
+ !EXCLUDED_ITEMS.includes(item.name)
+ )
+}
+
+export async function getBaseItem(name: string, base: BaseName) {
+ const { Index } = await import("@/registry/bases/__index__")
+ const index = Index[base]
+
+ if (!index?.[name] || EXCLUDED_ITEMS.includes(name)) {
+ return null
+ }
+
+ return registryItemSchema.parse(index[name])
+}
+
+export async function getBaseComponent(name: string, base: BaseName) {
+ const { Index } = await import("@/registry/bases/__index__")
+ const index = Index[base]
+
+ if (!index?.[name] || EXCLUDED_ITEMS.includes(name)) {
+ return null
+ }
+
+ return index[name].component
+}
+
+export async function getAllItems() {
+ const entries = await Promise.all(
+ BASES.map(async (base) => {
+ const items = await getItemsForBase(base.name as BaseName)
+ const filtered: Pick<
+ NonNullable<(typeof items)[number]>,
+ "name" | "title" | "type"
+ >[] = []
+ for (const item of items) {
+ if (item !== null && !/\d+$/.test(item.name)) {
+ filtered.push({
+ name: item.name,
+ title: item.title,
+ type: item.type,
+ })
+ }
+ }
+ return [base.name, filtered] as const
+ })
+ )
+ return Object.fromEntries(entries)
+}
+
+// Re-export for server-side use.
+export { getThemesForBaseColor }
diff --git a/apps/ui/src/routes/create/lib/constants.ts b/apps/ui/src/routes/create/lib/constants.ts
new file mode 100644
index 0000000..7871371
--- /dev/null
+++ b/apps/ui/src/routes/create/lib/constants.ts
@@ -0,0 +1,3 @@
+export const ALLOWED_ITEM_TYPES = ["registry:block", "registry:example"]
+
+export const EXCLUDED_ITEMS = ["component-example"]
diff --git a/apps/ui/src/routes/create/lib/custom-component-spec.ts b/apps/ui/src/routes/create/lib/custom-component-spec.ts
new file mode 100644
index 0000000..997f7f7
--- /dev/null
+++ b/apps/ui/src/routes/create/lib/custom-component-spec.ts
@@ -0,0 +1,171 @@
+import { z } from "zod"
+
+export const TONES = [
+ "default",
+ "muted",
+ "primary",
+ "secondary",
+ "accent",
+ "destructive",
+] as const
+export type Tone = (typeof TONES)[number]
+
+const toneSchema = z.enum(TONES).optional()
+
+const headingSchema = z.object({
+ type: z.literal("heading"),
+ text: z.string().min(1),
+ level: z.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)]).optional(),
+ tone: toneSchema,
+})
+
+const textSchema = z.object({
+ type: z.literal("text"),
+ text: z.string(),
+ tone: toneSchema,
+ size: z.enum(["xs", "sm", "base", "lg"]).optional(),
+ weight: z.enum(["normal", "medium", "semibold", "bold"]).optional(),
+})
+
+const badgeSchema = z.object({
+ type: z.literal("badge"),
+ text: z.string().min(1),
+ tone: toneSchema,
+})
+
+const iconSchema = z.object({
+ type: z.literal("icon"),
+ name: z.string().min(1),
+ size: z.number().int().min(12).max(96).optional(),
+ tone: toneSchema,
+})
+
+const statSchema = z.object({
+ type: z.literal("stat"),
+ label: z.string().min(1),
+ value: z.string().min(1),
+ trend: z.enum(["up", "down", "flat", "none"]).optional(),
+ trendValue: z.string().optional(),
+})
+
+const progressSchema = z.object({
+ type: z.literal("progress"),
+ value: z.number().min(0).max(100),
+ label: z.string().optional(),
+})
+
+const separatorSchema = z.object({
+ type: z.literal("separator"),
+ orientation: z.enum(["horizontal", "vertical"]).optional(),
+})
+
+const imageSchema = z.object({
+ type: z.literal("image"),
+ src: z.string().url(),
+ alt: z.string().default(""),
+ aspect: z.enum(["square", "video", "portrait"]).optional(),
+})
+
+// Containers use z.lazy for recursion.
+type Node =
+ | z.infer
+ | z.infer
+ | z.infer
+ | z.infer
+ | z.infer
+ | z.infer