Actualización ShadCN/UI a Base UI

This commit is contained in:
David Arranz 2026-04-12 12:07:54 +02:00
parent 3a54ccb482
commit 85556056fd
60 changed files with 1715 additions and 1240 deletions

View File

@ -1,11 +1,11 @@
{ {
"$schema": "https://ui.shadcn.com/schema.json", "$schema": "https://ui.shadcn.com/schema.json",
"style": "radix-nova", "style": "base-vega",
"rsc": false, "rsc": false,
"tsx": true, "tsx": true,
"tailwind": { "tailwind": {
"config": "", "config": "",
"css": "@repo/shadcn-ui/globals.css", "css": "src/styles/globals.css",
"baseColor": "neutral", "baseColor": "neutral",
"cssVariables": true, "cssVariables": true,
"prefix": "" "prefix": ""

View File

@ -20,49 +20,75 @@
"ui:add": "pnpm dlx shadcn@latest add" "ui:add": "pnpm dlx shadcn@latest add"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^19.1.0", "react": "^19.2.5",
"react-dom": "^19.1.0" "react-dom": "^19.2.5"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.3.1", "@biomejs/biome": "^2.4.11",
"@repo/typescript-config": "workspace:*", "@repo/typescript-config": "workspace:*",
"@tailwindcss/cli": "^4.1.5", "@tailwindcss/cli": "^4.2.2",
"@tailwindcss/postcss": "^4.1.5", "@tailwindcss/postcss": "^4.2.2",
"@turbo/gen": "^2.5.2", "@turbo/gen": "^2.9.6",
"@types/node": "^22.15.12", "@types/node": "^25.6.0",
"@types/react": "^19.1.2", "@types/react": "^19.2.14",
"@types/react-dom": "^19.1.3", "@types/react-dom": "^19.2.3",
"postcss": "^8.5.3", "postcss": "^8.5.9",
"react": "^19.1.0", "react": "^19.2.5",
"react-dom": "^19.1.0", "react-dom": "^19.2.5",
"shadcn": "^4.0.8", "shadcn": "^4.2.0",
"tw-animate-css": "^1.4.0", "tw-animate-css": "^1.4.0",
"typescript": "^5.6.0" "typescript": "^6.0.2"
}, },
"dependencies": { "dependencies": {
"@base-ui/react": "^1.3.0",
"@fontsource-variable/geist": "^5.2.8", "@fontsource-variable/geist": "^5.2.8",
"@fontsource-variable/geist-mono": "^5.2.7", "@fontsource-variable/geist-mono": "^5.2.7",
"@hookform/resolvers": "^5.2.2", "@hookform/resolvers": "^5.2.2",
"@radix-ui/react-accordion": "^1.2.12",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@radix-ui/react-aspect-ratio": "^1.1.8",
"@radix-ui/react-avatar": "^1.1.11",
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-collapsible": "^1.1.12",
"@radix-ui/react-context-menu": "^2.2.16",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-menubar": "^1.1.16",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slider": "^1.3.6",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tabs": "^1.1.13",
"@radix-ui/react-tooltip": "^1.2.8",
"add": "^2.0.6", "add": "^2.0.6",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.6.0",
"framer-motion": "^12.4.7", "framer-motion": "^12.38.0",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"lucide-react": "^0.577.0", "lucide-react": "^1.8.0",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"pnpm": "^10.10.0", "pnpm": "^10.33.0",
"radix-ui": "latest", "radix-ui": "latest",
"react-day-picker": "9.14.0", "react-day-picker": "9.14.0",
"react-hook-form": "^7.72.1", "react-hook-form": "^7.72.1",
"react-resizable-panels": "^4.8.0", "react-resizable-panels": "^4.9.0",
"recharts": "2.15.4", "recharts": "3.8.0",
"sonner": "^2.0.7", "sonner": "^2.0.7",
"tailwind-merge": "^3.5.0", "tailwind-merge": "^3.5.0",
"tailwindcss": "^4.1.10", "tailwindcss": "^4.2.2",
"tailwindcss-animate": "^1.0.7",
"vaul": "^1.1.2", "vaul": "^1.1.2",
"zod": "^3.25.76" "zod": "^4.3.6"
} }
} }

View File

@ -1,13 +1,9 @@
import * as React from "react" import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
import { Accordion as AccordionPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react" import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
function Accordion({ function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return ( return (
<AccordionPrimitive.Root <AccordionPrimitive.Root
data-slot="accordion" data-slot="accordion"
@ -17,10 +13,7 @@ function Accordion({
) )
} }
function AccordionItem({ function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
return ( return (
<AccordionPrimitive.Item <AccordionPrimitive.Item
data-slot="accordion-item" data-slot="accordion-item"
@ -34,13 +27,13 @@ function AccordionTrigger({
className, className,
children, children,
...props ...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) { }: AccordionPrimitive.Trigger.Props) {
return ( return (
<AccordionPrimitive.Header className="flex"> <AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger <AccordionPrimitive.Trigger
data-slot="accordion-trigger" data-slot="accordion-trigger"
className={cn( className={cn(
"group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground", "group/accordion-trigger relative flex flex-1 items-start justify-between rounded-md border border-transparent py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring aria-disabled:pointer-events-none aria-disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
className className
)} )}
{...props} {...props}
@ -57,22 +50,22 @@ function AccordionContent({
className, className,
children, children,
...props ...props
}: React.ComponentProps<typeof AccordionPrimitive.Content>) { }: AccordionPrimitive.Panel.Props) {
return ( return (
<AccordionPrimitive.Content <AccordionPrimitive.Panel
data-slot="accordion-content" data-slot="accordion-content"
className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up" className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
{...props} {...props}
> >
<div <div
className={cn( className={cn(
"h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4", "h-(--accordion-panel-height) pt-0 pb-4 data-ending-style:h-0 data-starting-style:h-0 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
className className
)} )}
> >
{children} {children}
</div> </div>
</AccordionPrimitive.Content> </AccordionPrimitive.Panel>
) )
} }

View File

@ -1,26 +1,22 @@
"use client"
import * as React from "react" import * as React from "react"
import { AlertDialog as AlertDialogPrimitive } from "radix-ui" import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { Button } from "@repo/shadcn-ui/components/button" import { Button } from "@repo/shadcn-ui/components/button"
function AlertDialog({ function AlertDialog({ ...props }: AlertDialogPrimitive.Root.Props) {
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} /> return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
} }
function AlertDialogTrigger({ function AlertDialogTrigger({ ...props }: AlertDialogPrimitive.Trigger.Props) {
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
return ( return (
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} /> <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
) )
} }
function AlertDialogPortal({ function AlertDialogPortal({ ...props }: AlertDialogPrimitive.Portal.Props) {
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
return ( return (
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} /> <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
) )
@ -29,12 +25,12 @@ function AlertDialogPortal({
function AlertDialogOverlay({ function AlertDialogOverlay({
className, className,
...props ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) { }: AlertDialogPrimitive.Backdrop.Props) {
return ( return (
<AlertDialogPrimitive.Overlay <AlertDialogPrimitive.Backdrop
data-slot="alert-dialog-overlay" data-slot="alert-dialog-overlay"
className={cn( className={cn(
"fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className className
)} )}
{...props} {...props}
@ -46,17 +42,17 @@ function AlertDialogContent({
className, className,
size = "default", size = "default",
...props ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & { }: AlertDialogPrimitive.Popup.Props & {
size?: "default" | "sm" size?: "default" | "sm"
}) { }) {
return ( return (
<AlertDialogPortal> <AlertDialogPortal>
<AlertDialogOverlay /> <AlertDialogOverlay />
<AlertDialogPrimitive.Content <AlertDialogPrimitive.Popup
data-slot="alert-dialog-content" data-slot="alert-dialog-content"
data-size={size} data-size={size}
className={cn( className={cn(
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-background p-4 ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-6 rounded-xl bg-popover p-6 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}
@ -73,7 +69,7 @@ function AlertDialogHeader({
<div <div
data-slot="alert-dialog-header" data-slot="alert-dialog-header"
className={cn( className={cn(
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
className className
)} )}
{...props} {...props}
@ -89,7 +85,7 @@ function AlertDialogFooter({
<div <div
data-slot="alert-dialog-footer" data-slot="alert-dialog-footer"
className={cn( className={cn(
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
className className
)} )}
{...props} {...props}
@ -105,7 +101,7 @@ function AlertDialogMedia({
<div <div
data-slot="alert-dialog-media" data-slot="alert-dialog-media"
className={cn( className={cn(
"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", "mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
className className
)} )}
{...props} {...props}
@ -121,7 +117,7 @@ function AlertDialogTitle({
<AlertDialogPrimitive.Title <AlertDialogPrimitive.Title
data-slot="alert-dialog-title" data-slot="alert-dialog-title"
className={cn( className={cn(
"text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", "text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
className className
)} )}
{...props} {...props}
@ -147,19 +143,14 @@ function AlertDialogDescription({
function AlertDialogAction({ function AlertDialogAction({
className, className,
variant = "default",
size = "default",
...props ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> & }: React.ComponentProps<typeof Button>) {
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
return ( return (
<Button variant={variant} size={size} asChild> <Button
<AlertDialogPrimitive.Action
data-slot="alert-dialog-action" data-slot="alert-dialog-action"
className={cn(className)} className={cn(className)}
{...props} {...props}
/> />
</Button>
) )
} }
@ -168,16 +159,15 @@ function AlertDialogCancel({
variant = "outline", variant = "outline",
size = "default", size = "default",
...props ...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> & }: AlertDialogPrimitive.Close.Props &
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) { Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
return ( return (
<Button variant={variant} size={size} asChild> <AlertDialogPrimitive.Close
<AlertDialogPrimitive.Cancel
data-slot="alert-dialog-cancel" data-slot="alert-dialog-cancel"
className={cn(className)} className={cn(className)}
render={<Button variant={variant} size={size} />}
{...props} {...props}
/> />
</Button>
) )
} }

View File

@ -4,7 +4,7 @@ import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
const alertVariants = cva( const alertVariants = cva(
"group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", "group/alert relative grid w-full gap-0.5 rounded-lg border px-4 py-3 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2.5 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
{ {
variants: { variants: {
variant: { variant: {
@ -67,7 +67,7 @@ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="alert-action" data-slot="alert-action"
className={cn("absolute top-2 right-2", className)} className={cn("absolute top-2.5 right-3", className)}
{...props} {...props}
/> />
) )

View File

@ -1,9 +1,22 @@
import { AspectRatio as AspectRatioPrimitive } from "radix-ui" import { cn } from "@repo/shadcn-ui/lib/utils"
function AspectRatio({ function AspectRatio({
ratio,
className,
...props ...props
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) { }: React.ComponentProps<"div"> & { ratio: number }) {
return <AspectRatioPrimitive.Root data-slot="aspect-ratio" {...props} /> return (
<div
data-slot="aspect-ratio"
style={
{
"--ratio": ratio,
} as React.CSSProperties
}
className={cn("relative aspect-(--ratio)", className)}
{...props}
/>
)
} }
export { AspectRatio } export { AspectRatio }

View File

@ -1,5 +1,5 @@
import * as React from "react" import * as React from "react"
import { Avatar as AvatarPrimitive } from "radix-ui" import { Avatar as AvatarPrimitive } from "@base-ui/react/avatar"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -7,7 +7,7 @@ function Avatar({
className, className,
size = "default", size = "default",
...props ...props
}: React.ComponentProps<typeof AvatarPrimitive.Root> & { }: AvatarPrimitive.Root.Props & {
size?: "default" | "sm" | "lg" size?: "default" | "sm" | "lg"
}) { }) {
return ( return (
@ -23,10 +23,7 @@ function Avatar({
) )
} }
function AvatarImage({ function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
className,
...props
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
return ( return (
<AvatarPrimitive.Image <AvatarPrimitive.Image
data-slot="avatar-image" data-slot="avatar-image"
@ -42,7 +39,7 @@ function AvatarImage({
function AvatarFallback({ function AvatarFallback({
className, className,
...props ...props
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) { }: AvatarPrimitive.Fallback.Props) {
return ( return (
<AvatarPrimitive.Fallback <AvatarPrimitive.Fallback
data-slot="avatar-fallback" data-slot="avatar-fallback"

View File

@ -1,6 +1,6 @@
import * as React from "react" 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 { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -30,20 +30,23 @@ const badgeVariants = cva(
function Badge({ function Badge({
className, className,
variant = "default", variant = "default",
asChild = false, render,
...props ...props
}: React.ComponentProps<"span"> & }: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) {
VariantProps<typeof badgeVariants> & { asChild?: boolean }) { return useRender({
const Comp = asChild ? Slot.Root : "span" defaultTagName: "span",
props: mergeProps<"span">(
return ( {
<Comp className: cn(badgeVariants({ variant }), className),
data-slot="badge" },
data-variant={variant} props
className={cn(badgeVariants({ variant }), className)} ),
{...props} render,
/> state: {
) slot: "badge",
variant,
},
})
} }
export { Badge, badgeVariants } export { Badge, badgeVariants }

View File

@ -1,5 +1,6 @@
import * as React from "react" import * as React from "react"
import { Slot } from "radix-ui" import { mergeProps } from "@base-ui/react/merge-props"
import { useRender } from "@base-ui/react/use-render"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react" import { ChevronRightIcon, MoreHorizontalIcon } from "lucide-react"
@ -20,7 +21,7 @@ function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
<ol <ol
data-slot="breadcrumb-list" data-slot="breadcrumb-list"
className={cn( className={cn(
"flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground", "flex flex-wrap items-center gap-1.5 text-sm wrap-break-word text-muted-foreground sm:gap-2.5",
className className
)} )}
{...props} {...props}
@ -32,28 +33,30 @@ function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
return ( return (
<li <li
data-slot="breadcrumb-item" data-slot="breadcrumb-item"
className={cn("inline-flex items-center gap-1", className)} className={cn("inline-flex items-center gap-1.5", className)}
{...props} {...props}
/> />
) )
} }
function BreadcrumbLink({ function BreadcrumbLink({
asChild,
className, className,
render,
...props ...props
}: React.ComponentProps<"a"> & { }: useRender.ComponentProps<"a">) {
asChild?: boolean return useRender({
}) { defaultTagName: "a",
const Comp = asChild ? Slot.Root : "a" props: mergeProps<"a">(
{
return ( className: cn("transition-colors hover:text-foreground", className),
<Comp },
data-slot="breadcrumb-link" props
className={cn("transition-colors hover:text-foreground", className)} ),
{...props} render,
/> state: {
) slot: "breadcrumb-link",
},
})
} }
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) { function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {

View File

@ -1,18 +1,19 @@
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 { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { Separator } from "@repo/shadcn-ui/components/separator" import { Separator } from "@repo/shadcn-ui/components/separator"
const buttonGroupVariants = cva( const buttonGroupVariants = cva(
"flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1", "flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 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: { variants: {
orientation: { orientation: {
horizontal: horizontal:
"[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg!", "*:data-slot:rounded-r-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-md! [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
vertical: vertical:
"flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg!", "flex-col *:data-slot:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-md! [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
}, },
}, },
defaultVariants: { defaultVariants: {
@ -39,22 +40,25 @@ function ButtonGroup({
function ButtonGroupText({ function ButtonGroupText({
className, className,
asChild = false, render,
...props ...props
}: React.ComponentProps<"div"> & { }: useRender.ComponentProps<"div">) {
asChild?: boolean return useRender({
}) { defaultTagName: "div",
const Comp = asChild ? Slot.Root : "div" props: mergeProps<"div">(
{
return ( className: cn(
<Comp "flex items-center gap-2 rounded-md border bg-muted px-2.5 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className={cn(
"flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
className className
)} ),
{...props} },
/> props
) ),
render,
state: {
slot: "button-group-text",
},
})
} }
function ButtonGroupSeparator({ function ButtonGroupSeparator({

View File

@ -1,17 +1,16 @@
import * as React from "react" import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
const buttonVariants = cva( const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{ {
variants: { variants: {
variant: { variant: {
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", default: "bg-primary text-primary-foreground hover:bg-primary/80",
outline: outline:
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", "border-border bg-background shadow-xs hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
secondary: secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost: ghost:
@ -22,16 +21,16 @@ const buttonVariants = cva(
}, },
size: { size: {
default: default:
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
icon: "size-8", icon: "size-9",
"icon-xs": "icon-xs":
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3", "size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "icon-sm":
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg", "size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md",
"icon-lg": "size-9", "icon-lg": "size-10",
}, },
}, },
defaultVariants: { defaultVariants: {
@ -45,19 +44,11 @@ function Button({
className, className,
variant = "default", variant = "default",
size = "default", size = "default",
asChild = false,
...props ...props
}: React.ComponentProps<"button"> & }: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
VariantProps<typeof buttonVariants> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot.Root : "button"
return ( return (
<Comp <ButtonPrimitive
data-slot="button" data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))} className={cn(buttonVariants({ variant, size, className }))}
{...props} {...props}
/> />

View File

@ -29,7 +29,7 @@ function Calendar({
<DayPicker <DayPicker
showOutsideDays={showOutsideDays} showOutsideDays={showOutsideDays}
className={cn( className={cn(
"group/calendar bg-background p-2 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(7)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent", "group/calendar bg-background p-3 [--cell-radius:var(--radius-md)] [--cell-size:--spacing(8)] in-data-[slot=card-content]:bg-transparent in-data-[slot=popover-content]:bg-transparent",
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
className className
@ -194,7 +194,6 @@ function CalendarDayButton({
return ( return (
<Button <Button
ref={ref}
variant="ghost" variant="ghost"
size="icon" size="icon"
data-day={day.date.toLocaleDateString(locale?.code)} data-day={day.date.toLocaleDateString(locale?.code)}

View File

@ -12,7 +12,7 @@ function Card({
data-slot="card" data-slot="card"
data-size={size} data-size={size}
className={cn( className={cn(
"group/card flex flex-col gap-4 overflow-hidden rounded-xl bg-card py-4 text-sm text-card-foreground ring-1 ring-foreground/10 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", "group/card flex flex-col gap-6 overflow-hidden rounded-xl bg-card py-6 text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className className
)} )}
{...props} {...props}
@ -25,7 +25,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-header" data-slot="card-header"
className={cn( className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3", "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-6 group-data-[size=sm]/card:px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-6 group-data-[size=sm]/card:[.border-b]:pb-4",
className className
)} )}
{...props} {...props}
@ -38,7 +38,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-title" data-slot="card-title"
className={cn( className={cn(
"text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", "text-base leading-normal font-medium group-data-[size=sm]/card:text-sm",
className className
)} )}
{...props} {...props}
@ -73,7 +73,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="card-content" data-slot="card-content"
className={cn("px-4 group-data-[size=sm]/card:px-3", className)} className={cn("px-6 group-data-[size=sm]/card:px-4", className)}
{...props} {...props}
/> />
) )
@ -84,7 +84,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-footer" data-slot="card-footer"
className={cn( className={cn(
"flex items-center rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/card:p-3", "flex items-center rounded-b-xl px-6 group-data-[size=sm]/card:px-4 [.border-t]:pt-6 group-data-[size=sm]/card:[.border-t]:pt-4",
className className
)} )}
{...props} {...props}

View File

@ -1,3 +1,5 @@
"use client"
import * as React from "react" import * as React from "react"
import useEmblaCarousel, { import useEmblaCarousel, {
type UseEmblaCarouselType, type UseEmblaCarouselType,

View File

@ -1,20 +1,25 @@
import * as React from "react" import * as React from "react"
import * as RechartsPrimitive from "recharts" import * as RechartsPrimitive from "recharts"
import type { TooltipValueType } from "recharts"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
// Format: { THEME_NAME: CSS_SELECTOR } // Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const const THEMES = { light: "", dark: ".dark" } as const
export type ChartConfig = { const INITIAL_DIMENSION = { width: 320, height: 200 } as const
[k in string]: { type TooltipNameType = number | string
export type ChartConfig = Record<
string,
{
label?: React.ReactNode label?: React.ReactNode
icon?: React.ComponentType icon?: React.ComponentType
} & ( } & (
| { color?: string; theme?: never } | { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> } | { color?: never; theme: Record<keyof typeof THEMES, string> }
) )
} >
type ChartContextProps = { type ChartContextProps = {
config: ChartConfig config: ChartConfig
@ -37,15 +42,20 @@ function ChartContainer({
className, className,
children, children,
config, config,
initialDimension = INITIAL_DIMENSION,
...props ...props
}: React.ComponentProps<"div"> & { }: React.ComponentProps<"div"> & {
config: ChartConfig config: ChartConfig
children: React.ComponentProps< children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer typeof RechartsPrimitive.ResponsiveContainer
>["children"] >["children"]
initialDimension?: {
width: number
height: number
}
}) { }) {
const uniqueId = React.useId() const uniqueId = React.useId()
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` const chartId = `chart-${id ?? uniqueId.replace(/:/g, "")}`
return ( return (
<ChartContext.Provider value={{ config }}> <ChartContext.Provider value={{ config }}>
@ -59,7 +69,9 @@ function ChartContainer({
{...props} {...props}
> >
<ChartStyle id={chartId} config={config} /> <ChartStyle id={chartId} config={config} />
<RechartsPrimitive.ResponsiveContainer> <RechartsPrimitive.ResponsiveContainer
initialDimension={initialDimension}
>
{children} {children}
</RechartsPrimitive.ResponsiveContainer> </RechartsPrimitive.ResponsiveContainer>
</div> </div>
@ -69,7 +81,7 @@ function ChartContainer({
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter( const colorConfig = Object.entries(config).filter(
([, config]) => config.theme || config.color ([, config]) => config.theme ?? config.color
) )
if (!colorConfig.length) { if (!colorConfig.length) {
@ -86,7 +98,7 @@ ${prefix} [data-chart=${id}] {
${colorConfig ${colorConfig
.map(([key, itemConfig]) => { .map(([key, itemConfig]) => {
const color = const color =
itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.theme?.[theme as keyof typeof itemConfig.theme] ??
itemConfig.color itemConfig.color
return color ? ` --color-${key}: ${color};` : null return color ? ` --color-${key}: ${color};` : null
}) })
@ -123,7 +135,13 @@ function ChartTooltipContent({
indicator?: "line" | "dot" | "dashed" indicator?: "line" | "dot" | "dashed"
nameKey?: string nameKey?: string
labelKey?: string labelKey?: string
}) { } & Omit<
RechartsPrimitive.DefaultTooltipContentProps<
TooltipValueType,
TooltipNameType
>,
"accessibilityLayer"
>) {
const { config } = useChart() const { config } = useChart()
const tooltipLabel = React.useMemo(() => { const tooltipLabel = React.useMemo(() => {
@ -132,11 +150,11 @@ function ChartTooltipContent({
} }
const [item] = payload const [item] = payload
const key = `${labelKey || item?.dataKey || item?.name || "value"}` const key = `${labelKey ?? item?.dataKey ?? item?.name ?? "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value = const value =
!labelKey && typeof label === "string" !labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label ? (config[label]?.label ?? label)
: itemConfig?.label : itemConfig?.label
if (labelFormatter) { if (labelFormatter) {
@ -180,13 +198,13 @@ function ChartTooltipContent({
{payload {payload
.filter((item) => item.type !== "none") .filter((item) => item.type !== "none")
.map((item, index) => { .map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}` const key = `${nameKey ?? item.name ?? item.dataKey ?? "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color const indicatorColor = color ?? item.payload?.fill ?? item.color
return ( return (
<div <div
key={item.dataKey} key={index}
className={cn( className={cn(
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground", "flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
indicator === "dot" && "items-center" indicator === "dot" && "items-center"
@ -229,12 +247,14 @@ function ChartTooltipContent({
<div className="grid gap-1.5"> <div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null} {nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground"> <span className="text-muted-foreground">
{itemConfig?.label || item.name} {itemConfig?.label ?? item.name}
</span> </span>
</div> </div>
{item.value && ( {item.value != null && (
<span className="font-mono font-medium text-foreground tabular-nums"> <span className="font-mono font-medium text-foreground tabular-nums">
{item.value.toLocaleString()} {typeof item.value === "number"
? item.value.toLocaleString()
: String(item.value)}
</span> </span>
)} )}
</div> </div>
@ -256,11 +276,10 @@ function ChartLegendContent({
payload, payload,
verticalAlign = "bottom", verticalAlign = "bottom",
nameKey, nameKey,
}: React.ComponentProps<"div"> & }: React.ComponentProps<"div"> & {
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean hideIcon?: boolean
nameKey?: string nameKey?: string
}) { } & RechartsPrimitive.DefaultLegendContentProps) {
const { config } = useChart() const { config } = useChart()
if (!payload?.length) { if (!payload?.length) {
@ -277,13 +296,13 @@ function ChartLegendContent({
> >
{payload {payload
.filter((item) => item.type !== "none") .filter((item) => item.type !== "none")
.map((item) => { .map((item, index) => {
const key = `${nameKey || item.dataKey || "value"}` const key = `${nameKey ?? item.dataKey ?? "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key) const itemConfig = getPayloadConfigFromPayload(config, item, key)
return ( return (
<div <div
key={item.value} key={index}
className={cn( className={cn(
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground" "flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground"
)} )}
@ -339,9 +358,7 @@ function getPayloadConfigFromPayload(
] as string ] as string
} }
return configLabelKey in config return configLabelKey in config ? config[configLabelKey] : config[key]
? config[configLabelKey]
: config[key as keyof typeof config]
} }
export { export {

View File

@ -1,18 +1,16 @@
import * as React from "react" "use client"
import { Checkbox as CheckboxPrimitive } from "radix-ui"
import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { CheckIcon } from "lucide-react" import { CheckIcon } from "lucide-react"
function Checkbox({ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
className,
...props
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
return ( return (
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
data-slot="checkbox" data-slot="checkbox"
className={cn( className={cn(
"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary", "peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input shadow-xs transition-shadow outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
className className
)} )}
{...props} {...props}

View File

@ -1,30 +1,18 @@
import { Collapsible as CollapsiblePrimitive } from "radix-ui" import { Collapsible as CollapsiblePrimitive } from "@base-ui/react/collapsible"
function Collapsible({ function Collapsible({ ...props }: CollapsiblePrimitive.Root.Props) {
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} /> return <CollapsiblePrimitive.Root data-slot="collapsible" {...props} />
} }
function CollapsibleTrigger({ function CollapsibleTrigger({ ...props }: CollapsiblePrimitive.Trigger.Props) {
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
return ( return (
<CollapsiblePrimitive.CollapsibleTrigger <CollapsiblePrimitive.Trigger data-slot="collapsible-trigger" {...props} />
data-slot="collapsible-trigger"
{...props}
/>
) )
} }
function CollapsibleContent({ function CollapsibleContent({ ...props }: CollapsiblePrimitive.Panel.Props) {
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return ( return (
<CollapsiblePrimitive.CollapsibleContent <CollapsiblePrimitive.Panel data-slot="collapsible-content" {...props} />
data-slot="collapsible-content"
{...props}
/>
) )
} }

View File

@ -0,0 +1,295 @@
import * as React from "react"
import { Combobox as ComboboxPrimitive } from "@base-ui/react"
import { cn } from "@repo/shadcn-ui/lib/utils"
import { Button } from "@repo/shadcn-ui/components/button"
import {
InputGroup,
InputGroupAddon,
InputGroupButton,
InputGroupInput,
} from "@repo/shadcn-ui/components/input-group"
import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react"
const Combobox = ComboboxPrimitive.Root
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />
}
function ComboboxTrigger({
className,
children,
...props
}: ComboboxPrimitive.Trigger.Props) {
return (
<ComboboxPrimitive.Trigger
data-slot="combobox-trigger"
className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
{...props}
>
{children}
<ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
</ComboboxPrimitive.Trigger>
)
}
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
return (
<ComboboxPrimitive.Clear
data-slot="combobox-clear"
render={<InputGroupButton variant="ghost" size="icon-xs" />}
className={cn(className)}
{...props}
>
<XIcon className="pointer-events-none" />
</ComboboxPrimitive.Clear>
)
}
function ComboboxInput({
className,
children,
disabled = false,
showTrigger = true,
showClear = false,
...props
}: ComboboxPrimitive.Input.Props & {
showTrigger?: boolean
showClear?: boolean
}) {
return (
<InputGroup className={cn("w-auto", className)}>
<ComboboxPrimitive.Input
render={<InputGroupInput disabled={disabled} />}
{...props}
/>
<InputGroupAddon align="inline-end">
{showTrigger && (
<InputGroupButton
size="icon-xs"
variant="ghost"
render={<ComboboxTrigger />}
data-slot="input-group-button"
className="group-has-data-[slot=combobox-clear]/input-group:hidden data-pressed:bg-transparent"
disabled={disabled}
/>
)}
{showClear && <ComboboxClear disabled={disabled} />}
</InputGroupAddon>
{children}
</InputGroup>
)
}
function ComboboxContent({
className,
side = "bottom",
sideOffset = 6,
align = "start",
alignOffset = 0,
anchor,
...props
}: ComboboxPrimitive.Popup.Props &
Pick<
ComboboxPrimitive.Positioner.Props,
"side" | "align" | "sideOffset" | "alignOffset" | "anchor"
>) {
return (
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Positioner
side={side}
sideOffset={sideOffset}
align={align}
alignOffset={alignOffset}
anchor={anchor}
className="isolate z-50"
>
<ComboboxPrimitive.Popup
data-slot="combobox-content"
data-chips={!!anchor}
className={cn("group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props}
/>
</ComboboxPrimitive.Positioner>
</ComboboxPrimitive.Portal>
)
}
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
return (
<ComboboxPrimitive.List
data-slot="combobox-list"
className={cn(
"no-scrollbar max-h-[min(calc(--spacing(72)---spacing(9)),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0",
className
)}
{...props}
/>
)
}
function ComboboxItem({
className,
children,
...props
}: ComboboxPrimitive.Item.Props) {
return (
<ComboboxPrimitive.Item
data-slot="combobox-item"
className={cn(
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<ComboboxPrimitive.ItemIndicator
render={
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
}
>
<CheckIcon className="pointer-events-none" />
</ComboboxPrimitive.ItemIndicator>
</ComboboxPrimitive.Item>
)
}
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
return (
<ComboboxPrimitive.Group
data-slot="combobox-group"
className={cn(className)}
{...props}
/>
)
}
function ComboboxLabel({
className,
...props
}: ComboboxPrimitive.GroupLabel.Props) {
return (
<ComboboxPrimitive.GroupLabel
data-slot="combobox-label"
className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}
{...props}
/>
)
}
function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
return (
<ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
)
}
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
return (
<ComboboxPrimitive.Empty
data-slot="combobox-empty"
className={cn(
"hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex",
className
)}
{...props}
/>
)
}
function ComboboxSeparator({
className,
...props
}: ComboboxPrimitive.Separator.Props) {
return (
<ComboboxPrimitive.Separator
data-slot="combobox-separator"
className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props}
/>
)
}
function ComboboxChips({
className,
...props
}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> &
ComboboxPrimitive.Chips.Props) {
return (
<ComboboxPrimitive.Chips
data-slot="combobox-chips"
className={cn(
"flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1.5 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40",
className
)}
{...props}
/>
)
}
function ComboboxChip({
className,
children,
showRemove = true,
...props
}: ComboboxPrimitive.Chip.Props & {
showRemove?: boolean
}) {
return (
<ComboboxPrimitive.Chip
data-slot="combobox-chip"
className={cn(
"flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0",
className
)}
{...props}
>
{children}
{showRemove && (
<ComboboxPrimitive.ChipRemove
render={<Button variant="ghost" size="icon-xs" />}
className="-ml-1 opacity-50 hover:opacity-100"
data-slot="combobox-chip-remove"
>
<XIcon className="pointer-events-none" />
</ComboboxPrimitive.ChipRemove>
)}
</ComboboxPrimitive.Chip>
)
}
function ComboboxChipsInput({
className,
...props
}: ComboboxPrimitive.Input.Props) {
return (
<ComboboxPrimitive.Input
data-slot="combobox-chip-input"
className={cn("min-w-16 flex-1 outline-none", className)}
{...props}
/>
)
}
function useComboboxAnchor() {
return React.useRef<HTMLDivElement | null>(null)
}
export {
Combobox,
ComboboxInput,
ComboboxContent,
ComboboxList,
ComboboxItem,
ComboboxGroup,
ComboboxLabel,
ComboboxCollection,
ComboboxEmpty,
ComboboxSeparator,
ComboboxChips,
ComboboxChip,
ComboboxChipsInput,
ComboboxTrigger,
ComboboxValue,
useComboboxAnchor,
}

View File

@ -1,3 +1,5 @@
"use client"
import * as React from "react" import * as React from "react"
import { Command as CommandPrimitive } from "cmdk" import { Command as CommandPrimitive } from "cmdk"
@ -38,11 +40,12 @@ function CommandDialog({
className, className,
showCloseButton = false, showCloseButton = false,
...props ...props
}: React.ComponentProps<typeof Dialog> & { }: Omit<React.ComponentProps<typeof Dialog>, "children"> & {
title?: string title?: string
description?: string description?: string
className?: string className?: string
showCloseButton?: boolean showCloseButton?: boolean
children: React.ReactNode
}) { }) {
return ( return (
<Dialog {...props}> <Dialog {...props}>
@ -138,7 +141,7 @@ function CommandSeparator({
return ( return (
<CommandPrimitive.Separator <CommandPrimitive.Separator
data-slot="command-separator" data-slot="command-separator"
className={cn("-mx-1 h-px bg-border", className)} className={cn("-mx-1 h-px w-auto bg-border", className)}
{...props} {...props}
/> />
) )
@ -153,7 +156,7 @@ function CommandItem({
<CommandPrimitive.Item <CommandPrimitive.Item
data-slot="command-item" data-slot="command-item"
className={cn( className={cn(
"group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-muted data-selected:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-selected:*:[svg]:text-foreground", "group/command-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none in-data-[slot=dialog-content]:rounded-lg! data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-muted data-selected:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-selected:**:[svg]:text-foreground",
className className
)} )}
{...props} {...props}

View File

@ -1,19 +1,25 @@
"use client"
import * as React from "react" import * as React from "react"
import { ContextMenu as ContextMenuPrimitive } from "radix-ui" import { ContextMenu as ContextMenuPrimitive } from "@base-ui/react/context-menu"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { ChevronRightIcon, CheckIcon } from "lucide-react" import { ChevronRightIcon, CheckIcon } from "lucide-react"
function ContextMenu({ function ContextMenu({ ...props }: ContextMenuPrimitive.Root.Props) {
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} /> return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />
} }
function ContextMenuPortal({ ...props }: ContextMenuPrimitive.Portal.Props) {
return (
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
)
}
function ContextMenuTrigger({ function ContextMenuTrigger({
className, className,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) { }: ContextMenuPrimitive.Trigger.Props) {
return ( return (
<ContextMenuPrimitive.Trigger <ContextMenuPrimitive.Trigger
data-slot="context-menu-trigger" data-slot="context-menu-trigger"
@ -23,53 +29,60 @@ function ContextMenuTrigger({
) )
} }
function ContextMenuGroup({ function ContextMenuContent({
className,
align = "start",
alignOffset = 4,
side = "right",
sideOffset = 0,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) { }: ContextMenuPrimitive.Popup.Props &
Pick<
ContextMenuPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset"
>) {
return (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Positioner
className="isolate z-50 outline-none"
align={align}
alignOffset={alignOffset}
side={side}
sideOffset={sideOffset}
>
<ContextMenuPrimitive.Popup
data-slot="context-menu-content"
className={cn("z-50 max-h-(--available-height) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props}
/>
</ContextMenuPrimitive.Positioner>
</ContextMenuPrimitive.Portal>
)
}
function ContextMenuGroup({ ...props }: ContextMenuPrimitive.Group.Props) {
return ( return (
<ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} /> <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
) )
} }
function ContextMenuPortal({ function ContextMenuLabel({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
return (
<ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
)
}
function ContextMenuSub({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />
}
function ContextMenuRadioGroup({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
return (
<ContextMenuPrimitive.RadioGroup
data-slot="context-menu-radio-group"
{...props}
/>
)
}
function ContextMenuContent({
className, className,
inset,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Content> & { }: ContextMenuPrimitive.GroupLabel.Props & {
side?: "top" | "right" | "bottom" | "left" inset?: boolean
}) { }) {
return ( return (
<ContextMenuPrimitive.Portal> <ContextMenuPrimitive.GroupLabel
<ContextMenuPrimitive.Content data-slot="context-menu-label"
data-slot="context-menu-content" data-inset={inset}
className={cn("z-50 max-h-(--radix-context-menu-content-available-height) min-w-36 origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )} className={cn(
"px-2 py-1.5 text-xs font-medium text-muted-foreground data-inset:pl-8",
className
)}
{...props} {...props}
/> />
</ContextMenuPrimitive.Portal>
) )
} }
@ -78,7 +91,7 @@ function ContextMenuItem({
inset, inset,
variant = "default", variant = "default",
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & { }: ContextMenuPrimitive.Item.Props & {
inset?: boolean inset?: boolean
variant?: "default" | "destructive" variant?: "default" | "destructive"
}) { }) {
@ -88,7 +101,7 @@ function ContextMenuItem({
data-inset={inset} data-inset={inset}
data-variant={variant} data-variant={variant}
className={cn( className={cn(
"group/context-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive", "group/context-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:*:[svg]:text-accent-foreground data-[variant=destructive]:*:[svg]:text-destructive",
className className
)} )}
{...props} {...props}
@ -96,38 +109,44 @@ function ContextMenuItem({
) )
} }
function ContextMenuSub({ ...props }: ContextMenuPrimitive.SubmenuRoot.Props) {
return (
<ContextMenuPrimitive.SubmenuRoot data-slot="context-menu-sub" {...props} />
)
}
function ContextMenuSubTrigger({ function ContextMenuSubTrigger({
className, className,
inset, inset,
children, children,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { }: ContextMenuPrimitive.SubmenuTrigger.Props & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
<ContextMenuPrimitive.SubTrigger <ContextMenuPrimitive.SubmenuTrigger
data-slot="context-menu-sub-trigger" data-slot="context-menu-sub-trigger"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
> >
{children} {children}
<ChevronRightIcon className="ml-auto" /> <ChevronRightIcon className="ml-auto" />
</ContextMenuPrimitive.SubTrigger> </ContextMenuPrimitive.SubmenuTrigger>
) )
} }
function ContextMenuSubContent({ function ContextMenuSubContent({
className,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) { }: React.ComponentProps<typeof ContextMenuContent>) {
return ( return (
<ContextMenuPrimitive.SubContent <ContextMenuContent
data-slot="context-menu-sub-content" data-slot="context-menu-sub-content"
className={cn("z-50 min-w-32 origin-(--radix-context-menu-content-transform-origin) overflow-hidden rounded-lg border bg-popover p-1 text-popover-foreground shadow-lg duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )} className="shadow-lg"
side="right"
{...props} {...props}
/> />
) )
@ -139,7 +158,7 @@ function ContextMenuCheckboxItem({
checked, checked,
inset, inset,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem> & { }: ContextMenuPrimitive.CheckboxItem.Props & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
@ -147,29 +166,40 @@ function ContextMenuCheckboxItem({
data-slot="context-menu-checkbox-item" data-slot="context-menu-checkbox-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
checked={checked} checked={checked}
{...props} {...props}
> >
<span className="pointer-events-none absolute right-2"> <span className="pointer-events-none absolute right-2">
<ContextMenuPrimitive.ItemIndicator> <ContextMenuPrimitive.CheckboxItemIndicator>
<CheckIcon <CheckIcon
/> />
</ContextMenuPrimitive.ItemIndicator> </ContextMenuPrimitive.CheckboxItemIndicator>
</span> </span>
{children} {children}
</ContextMenuPrimitive.CheckboxItem> </ContextMenuPrimitive.CheckboxItem>
) )
} }
function ContextMenuRadioGroup({
...props
}: ContextMenuPrimitive.RadioGroup.Props) {
return (
<ContextMenuPrimitive.RadioGroup
data-slot="context-menu-radio-group"
{...props}
/>
)
}
function ContextMenuRadioItem({ function ContextMenuRadioItem({
className, className,
children, children,
inset, inset,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem> & { }: ContextMenuPrimitive.RadioItem.Props & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
@ -177,46 +207,26 @@ function ContextMenuRadioItem({
data-slot="context-menu-radio-item" data-slot="context-menu-radio-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
> >
<span className="pointer-events-none absolute right-2"> <span className="pointer-events-none absolute right-2">
<ContextMenuPrimitive.ItemIndicator> <ContextMenuPrimitive.RadioItemIndicator>
<CheckIcon <CheckIcon
/> />
</ContextMenuPrimitive.ItemIndicator> </ContextMenuPrimitive.RadioItemIndicator>
</span> </span>
{children} {children}
</ContextMenuPrimitive.RadioItem> </ContextMenuPrimitive.RadioItem>
) )
} }
function ContextMenuLabel({
className,
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
inset?: boolean
}) {
return (
<ContextMenuPrimitive.Label
data-slot="context-menu-label"
data-inset={inset}
className={cn(
"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
className
)}
{...props}
/>
)
}
function ContextMenuSeparator({ function ContextMenuSeparator({
className, className,
...props ...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) { }: ContextMenuPrimitive.Separator.Props) {
return ( return (
<ContextMenuPrimitive.Separator <ContextMenuPrimitive.Separator
data-slot="context-menu-separator" data-slot="context-menu-separator"

View File

@ -1,40 +1,32 @@
import * as React from "react" import * as React from "react"
import { Dialog as DialogPrimitive } from "radix-ui" import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { Button } from "@repo/shadcn-ui/components/button" import { Button } from "@repo/shadcn-ui/components/button"
import { XIcon } from "lucide-react" import { XIcon } from "lucide-react"
function Dialog({ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot="dialog" {...props} /> return <DialogPrimitive.Root data-slot="dialog" {...props} />
} }
function DialogTrigger({ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} /> return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
} }
function DialogPortal({ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} /> return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
} }
function DialogClose({ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot="dialog-close" {...props} /> return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
} }
function DialogOverlay({ function DialogOverlay({
className, className,
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) { }: DialogPrimitive.Backdrop.Props) {
return ( return (
<DialogPrimitive.Overlay <DialogPrimitive.Backdrop
data-slot="dialog-overlay" data-slot="dialog-overlay"
className={cn( className={cn(
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", "fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
@ -50,35 +42,38 @@ function DialogContent({
children, children,
showCloseButton = true, showCloseButton = true,
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & { }: DialogPrimitive.Popup.Props & {
showCloseButton?: boolean showCloseButton?: boolean
}) { }) {
return ( return (
<DialogPortal> <DialogPortal>
<DialogOverlay /> <DialogOverlay />
<DialogPrimitive.Content <DialogPrimitive.Popup
data-slot="dialog-content" data-slot="dialog-content"
className={cn( className={cn(
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-background p-4 text-sm ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-xl bg-popover p-6 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}
> >
{children} {children}
{showCloseButton && ( {showCloseButton && (
<DialogPrimitive.Close data-slot="dialog-close" asChild> <DialogPrimitive.Close
data-slot="dialog-close"
render={
<Button <Button
variant="ghost" variant="ghost"
className="absolute top-2 right-2" className="absolute top-4 right-4"
size="icon-sm" size="icon-sm"
/>
}
> >
<XIcon <XIcon
/> />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</Button>
</DialogPrimitive.Close> </DialogPrimitive.Close>
)} )}
</DialogPrimitive.Content> </DialogPrimitive.Popup>
</DialogPortal> </DialogPortal>
) )
} }
@ -105,29 +100,26 @@ function DialogFooter({
<div <div
data-slot="dialog-footer" data-slot="dialog-footer"
className={cn( className={cn(
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end", "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
className className
)} )}
{...props} {...props}
> >
{children} {children}
{showCloseButton && ( {showCloseButton && (
<DialogPrimitive.Close asChild> <DialogPrimitive.Close render={<Button variant="outline" />}>
<Button variant="outline">Close</Button> Close
</DialogPrimitive.Close> </DialogPrimitive.Close>
)} )}
</div> </div>
) )
} }
function DialogTitle({ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
return ( return (
<DialogPrimitive.Title <DialogPrimitive.Title
data-slot="dialog-title" data-slot="dialog-title"
className={cn("text-base leading-none font-medium", className)} className={cn("leading-none font-medium", className)}
{...props} {...props}
/> />
) )
@ -136,7 +128,7 @@ function DialogTitle({
function DialogDescription({ function DialogDescription({
className, className,
...props ...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) { }: DialogPrimitive.Description.Props) {
return ( return (
<DialogPrimitive.Description <DialogPrimitive.Description
data-slot="dialog-description" data-slot="dialog-description"

View File

@ -0,0 +1,4 @@
export {
DirectionProvider,
useDirection,
} from "@base-ui/react/direction-provider"

View File

@ -1,3 +1,5 @@
"use client"
import * as React from "react" import * as React from "react"
import { Drawer as DrawerPrimitive } from "vaul" import { Drawer as DrawerPrimitive } from "vaul"
@ -54,12 +56,12 @@ function DrawerContent({
<DrawerPrimitive.Content <DrawerPrimitive.Content
data-slot="drawer-content" data-slot="drawer-content"
className={cn( className={cn(
"group/drawer-content fixed z-50 flex h-auto flex-col bg-background text-sm data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", "group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm",
className className
)} )}
{...props} {...props}
> >
<div className="mx-auto mt-4 hidden h-1 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" /> <div className="mx-auto mt-4 hidden h-1.5 w-[100px] shrink-0 rounded-full bg-muted group-data-[vaul-drawer-direction=bottom]/drawer-content:block" />
{children} {children}
</DrawerPrimitive.Content> </DrawerPrimitive.Content>
</DrawerPortal> </DrawerPortal>
@ -71,7 +73,7 @@ function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="drawer-header" data-slot="drawer-header"
className={cn( className={cn(
"flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-0.5 md:text-left", "flex flex-col gap-0.5 p-4 group-data-[vaul-drawer-direction=bottom]/drawer-content:text-center group-data-[vaul-drawer-direction=top]/drawer-content:text-center md:gap-1.5 md:text-left",
className className
)} )}
{...props} {...props}
@ -96,7 +98,7 @@ function DrawerTitle({
return ( return (
<DrawerPrimitive.Title <DrawerPrimitive.Title
data-slot="drawer-title" data-slot="drawer-title"
className={cn("text-base font-medium text-foreground", className)} className={cn("font-medium text-foreground", className)}
{...props} {...props}
/> />
) )

View File

@ -1,58 +1,73 @@
import * as React from "react" import * as React from "react"
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui" import { Menu as MenuPrimitive } from "@base-ui/react/menu"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { CheckIcon, ChevronRightIcon } from "lucide-react" import { ChevronRightIcon, CheckIcon } from "lucide-react"
function DropdownMenu({ function DropdownMenu({ ...props }: MenuPrimitive.Root.Props) {
...props return <MenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
} }
function DropdownMenuPortal({ function DropdownMenuPortal({ ...props }: MenuPrimitive.Portal.Props) {
...props return <MenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
)
} }
function DropdownMenuTrigger({ function DropdownMenuTrigger({ ...props }: MenuPrimitive.Trigger.Props) {
...props return <MenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger"
{...props}
/>
)
} }
function DropdownMenuContent({ function DropdownMenuContent({
className,
align = "start", align = "start",
alignOffset = 0,
side = "bottom",
sideOffset = 4, sideOffset = 4,
className,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) { }: MenuPrimitive.Popup.Props &
Pick<
MenuPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset"
>) {
return ( return (
<DropdownMenuPrimitive.Portal> <MenuPrimitive.Portal>
<DropdownMenuPrimitive.Content <MenuPrimitive.Positioner
data-slot="dropdown-menu-content" className="isolate z-50 outline-none"
sideOffset={sideOffset}
align={align} align={align}
className={cn("z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:overflow-hidden data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )} alignOffset={alignOffset}
side={side}
sideOffset={sideOffset}
>
<MenuPrimitive.Popup
data-slot="dropdown-menu-content"
className={cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props} {...props}
/> />
</DropdownMenuPrimitive.Portal> </MenuPrimitive.Positioner>
</MenuPrimitive.Portal>
) )
} }
function DropdownMenuGroup({ function DropdownMenuGroup({ ...props }: MenuPrimitive.Group.Props) {
return <MenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
}
function DropdownMenuLabel({
className,
inset,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) { }: MenuPrimitive.GroupLabel.Props & {
inset?: boolean
}) {
return ( return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} /> <MenuPrimitive.GroupLabel
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-xs font-medium text-muted-foreground data-inset:pl-8",
className
)}
{...props}
/>
) )
} }
@ -61,17 +76,17 @@ function DropdownMenuItem({
inset, inset,
variant = "default", variant = "default",
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & { }: MenuPrimitive.Item.Props & {
inset?: boolean inset?: boolean
variant?: "default" | "destructive" variant?: "default" | "destructive"
}) { }) {
return ( return (
<DropdownMenuPrimitive.Item <MenuPrimitive.Item
data-slot="dropdown-menu-item" data-slot="dropdown-menu-item"
data-inset={inset} data-inset={inset}
data-variant={variant} data-variant={variant}
className={cn( className={cn(
"group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive", "group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
className className
)} )}
{...props} {...props}
@ -79,21 +94,70 @@ function DropdownMenuItem({
) )
} }
function DropdownMenuSub({ ...props }: MenuPrimitive.SubmenuRoot.Props) {
return <MenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />
}
function DropdownMenuSubTrigger({
className,
inset,
children,
...props
}: MenuPrimitive.SubmenuTrigger.Props & {
inset?: boolean
}) {
return (
<MenuPrimitive.SubmenuTrigger
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto" />
</MenuPrimitive.SubmenuTrigger>
)
}
function DropdownMenuSubContent({
align = "start",
alignOffset = -3,
side = "right",
sideOffset = 0,
className,
...props
}: React.ComponentProps<typeof DropdownMenuContent>) {
return (
<DropdownMenuContent
data-slot="dropdown-menu-sub-content"
className={cn("w-auto min-w-[96px] rounded-md bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
align={align}
alignOffset={alignOffset}
side={side}
sideOffset={sideOffset}
{...props}
/>
)
}
function DropdownMenuCheckboxItem({ function DropdownMenuCheckboxItem({
className, className,
children, children,
checked, checked,
inset, inset,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & { }: MenuPrimitive.CheckboxItem.Props & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
<DropdownMenuPrimitive.CheckboxItem <MenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item" data-slot="dropdown-menu-checkbox-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
checked={checked} checked={checked}
@ -103,21 +167,19 @@ function DropdownMenuCheckboxItem({
className="pointer-events-none absolute right-2 flex items-center justify-center" className="pointer-events-none absolute right-2 flex items-center justify-center"
data-slot="dropdown-menu-checkbox-item-indicator" data-slot="dropdown-menu-checkbox-item-indicator"
> >
<DropdownMenuPrimitive.ItemIndicator> <MenuPrimitive.CheckboxItemIndicator>
<CheckIcon <CheckIcon
/> />
</DropdownMenuPrimitive.ItemIndicator> </MenuPrimitive.CheckboxItemIndicator>
</span> </span>
{children} {children}
</DropdownMenuPrimitive.CheckboxItem> </MenuPrimitive.CheckboxItem>
) )
} }
function DropdownMenuRadioGroup({ function DropdownMenuRadioGroup({ ...props }: MenuPrimitive.RadioGroup.Props) {
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return ( return (
<DropdownMenuPrimitive.RadioGroup <MenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group" data-slot="dropdown-menu-radio-group"
{...props} {...props}
/> />
@ -129,15 +191,15 @@ function DropdownMenuRadioItem({
children, children,
inset, inset,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & { }: MenuPrimitive.RadioItem.Props & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
<DropdownMenuPrimitive.RadioItem <MenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item" data-slot="dropdown-menu-radio-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@ -146,42 +208,22 @@ function DropdownMenuRadioItem({
className="pointer-events-none absolute right-2 flex items-center justify-center" className="pointer-events-none absolute right-2 flex items-center justify-center"
data-slot="dropdown-menu-radio-item-indicator" data-slot="dropdown-menu-radio-item-indicator"
> >
<DropdownMenuPrimitive.ItemIndicator> <MenuPrimitive.RadioItemIndicator>
<CheckIcon <CheckIcon
/> />
</DropdownMenuPrimitive.ItemIndicator> </MenuPrimitive.RadioItemIndicator>
</span> </span>
{children} {children}
</DropdownMenuPrimitive.RadioItem> </MenuPrimitive.RadioItem>
)
}
function DropdownMenuLabel({
className,
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
className
)}
{...props}
/>
) )
} }
function DropdownMenuSeparator({ function DropdownMenuSeparator({
className, className,
...props ...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) { }: MenuPrimitive.Separator.Props) {
return ( return (
<DropdownMenuPrimitive.Separator <MenuPrimitive.Separator
data-slot="dropdown-menu-separator" data-slot="dropdown-menu-separator"
className={cn("-mx-1 my-1 h-px bg-border", className)} className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props} {...props}
@ -205,49 +247,6 @@ function DropdownMenuShortcut({
) )
} }
function DropdownMenuSub({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
}
function DropdownMenuSubTrigger({
className,
inset,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto" />
</DropdownMenuPrimitive.SubTrigger>
)
}
function DropdownMenuSubContent({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return (
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn("z-50 min-w-[96px] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props}
/>
)
}
export { export {
DropdownMenu, DropdownMenu,
DropdownMenuPortal, DropdownMenuPortal,

View File

@ -7,7 +7,7 @@ function Empty({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="empty" data-slot="empty"
className={cn( className={cn(
"flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance", "flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-lg border-dashed p-12 text-center text-balance",
className className
)} )}
{...props} {...props}
@ -31,7 +31,7 @@ const emptyMediaVariants = cva(
variants: { variants: {
variant: { variant: {
default: "bg-transparent", default: "bg-transparent",
icon: "flex size-8 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-4", icon: "flex size-10 shrink-0 items-center justify-center rounded-lg bg-muted text-foreground [&_svg:not([class*='size-'])]:size-6",
}, },
}, },
defaultVariants: { defaultVariants: {
@ -59,7 +59,10 @@ function EmptyTitle({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="empty-title" data-slot="empty-title"
className={cn("text-sm font-medium tracking-tight", className)} className={cn(
"text-lg font-medium tracking-tight",
className
)}
{...props} {...props}
/> />
) )
@ -83,7 +86,7 @@ function EmptyContent({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="empty-content" data-slot="empty-content"
className={cn( className={cn(
"flex w-full max-w-sm min-w-0 flex-col items-center gap-2.5 text-sm text-balance", "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
className className
)} )}
{...props} {...props}

View File

@ -10,7 +10,7 @@ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
<fieldset <fieldset
data-slot="field-set" data-slot="field-set"
className={cn( className={cn(
"flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3", "flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
className className
)} )}
{...props} {...props}
@ -28,7 +28,7 @@ function FieldLegend({
data-slot="field-legend" data-slot="field-legend"
data-variant={variant} data-variant={variant}
className={cn( className={cn(
"mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base", "mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
className className
)} )}
{...props} {...props}
@ -41,7 +41,7 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="field-group" data-slot="field-group"
className={cn( className={cn(
"group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4", "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
className className
)} )}
{...props} {...props}
@ -50,7 +50,7 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
} }
const fieldVariants = cva( const fieldVariants = cva(
"group/field flex w-full gap-2 data-[invalid=true]:text-destructive", "group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
{ {
variants: { variants: {
orientation: { orientation: {
@ -88,7 +88,7 @@ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="field-content" data-slot="field-content"
className={cn( className={cn(
"group/field-content flex flex-1 flex-col gap-0.5 leading-snug", "group/field-content flex flex-1 flex-col gap-1 leading-snug",
className className
)} )}
{...props} {...props}
@ -104,7 +104,7 @@ function FieldLabel({
<Label <Label
data-slot="field-label" data-slot="field-label"
className={cn( className={cn(
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10", "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border *:data-[slot=field]:p-3 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col", "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
className className
)} )}

View File

@ -1,13 +1,12 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
import { Label as LabelPrimitive, Slot as SlotPrimitive } from "radix-ui" import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import { import {
Controller, Controller,
FormProvider, FormProvider,
useFormContext, useFormContext,
useFormState,
type ControllerProps, type ControllerProps,
type FieldPath, type FieldPath,
type FieldValues, type FieldValues,
@ -20,18 +19,16 @@ const Form = FormProvider
type FormFieldContextValue< type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues, TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = { > = {
name: TName name: TName
} }
const FormFieldContext = React.createContext<FormFieldContextValue>( const FormFieldContext = React.createContext<FormFieldContextValue | null>(null)
{} as FormFieldContextValue
)
const FormField = < const FormField = <
TFieldValues extends FieldValues = FieldValues, TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({ >({
...props ...props
}: ControllerProps<TFieldValues, TName>) => { }: ControllerProps<TFieldValues, TName>) => {
@ -45,14 +42,18 @@ const FormField = <
const useFormField = () => { const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext) const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext) const itemContext = React.useContext(FormItemContext)
const { getFieldState } = useFormContext() const { getFieldState, formState } = useFormContext()
const formState = useFormState({ name: fieldContext.name })
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) { if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>") throw new Error("useFormField should be used within <FormField>")
} }
if (!itemContext) {
throw new Error("useFormField should be used within <FormItem>")
}
const fieldState = getFieldState(fieldContext.name, formState)
const { id } = itemContext const { id } = itemContext
return { return {
@ -69,47 +70,48 @@ type FormItemContextValue = {
id: string id: string
} }
const FormItemContext = React.createContext<FormItemContextValue>( const FormItemContext = React.createContext<FormItemContextValue | null>(null)
{} as FormItemContextValue
)
function FormItem({ className, ...props }: React.ComponentProps<"div">) { const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId() const id = React.useId()
return ( return (
<FormItemContext.Provider value={{ id }}> <FormItemContext.Provider value={{ id }}>
<div <div ref={ref} className={cn("space-y-2", className)} {...props} />
data-slot="form-item"
className={cn("grid gap-2", className)}
{...props}
/>
</FormItemContext.Provider> </FormItemContext.Provider>
) )
} })
FormItem.displayName = "FormItem"
function FormLabel({ const FormLabel = React.forwardRef<
className, React.ElementRef<typeof LabelPrimitive.Root>,
...props React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
}: React.ComponentProps<typeof LabelPrimitive.Root>) { >(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField() const { error, formItemId } = useFormField()
return ( return (
<Label <Label
data-slot="form-label" ref={ref}
data-error={!!error} className={cn(error && "text-destructive", className)}
className={cn("data-[error=true]:text-destructive", className)}
htmlFor={formItemId} htmlFor={formItemId}
{...props} {...props}
/> />
) )
} })
FormLabel.displayName = "FormLabel"
function FormControl({ ...props }: React.ComponentProps<typeof SlotPrimitive.Slot>) { const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField() const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return ( return (
<SlotPrimitive.Slot <Slot
data-slot="form-control" ref={ref}
id={formItemId} id={formItemId}
aria-describedby={ aria-describedby={
!error !error
@ -120,24 +122,32 @@ function FormControl({ ...props }: React.ComponentProps<typeof SlotPrimitive.Slo
{...props} {...props}
/> />
) )
} })
FormControl.displayName = "FormControl"
function FormDescription({ className, ...props }: React.ComponentProps<"p">) { const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField() const { formDescriptionId } = useFormField()
return ( return (
<p <p
data-slot="form-description" ref={ref}
id={formDescriptionId} id={formDescriptionId}
className={cn("text-muted-foreground text-sm", className)} className={cn("text-sm text-muted-foreground", className)}
{...props} {...props}
/> />
) )
} })
FormDescription.displayName = "FormDescription"
function FormMessage({ className, ...props }: React.ComponentProps<"p">) { const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField() const { error, formMessageId } = useFormField()
const body = error ? String(error?.message ?? "") : props.children const body = error ? String(error?.message ?? "") : children
if (!body) { if (!body) {
return null return null
@ -145,15 +155,16 @@ function FormMessage({ className, ...props }: React.ComponentProps<"p">) {
return ( return (
<p <p
data-slot="form-message" ref={ref}
id={formMessageId} id={formMessageId}
className={cn("text-destructive text-sm", className)} className={cn("text-sm font-medium text-destructive", className)}
{...props} {...props}
> >
{body} {body}
</p> </p>
) )
} })
FormMessage.displayName = "FormMessage"
export { export {
useFormField, useFormField,

View File

@ -1,35 +1,51 @@
import { cn } from "@repo/shadcn-ui/lib/utils"; "use client"
import { HoverCard as HoverCardPrimitive } from "radix-ui";
import type * as React from "react";
function HoverCard({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Root>) { import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
import { cn } from "@repo/shadcn-ui/lib/utils"
function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
} }
function HoverCardTrigger({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) { function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
return <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />; return (
<PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
)
} }
function HoverCardContent({ function HoverCardContent({
className, className,
align = "center", side = "bottom",
sideOffset = 4, sideOffset = 4,
align = "center",
alignOffset = 4,
...props ...props
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) { }: PreviewCardPrimitive.Popup.Props &
Pick<
PreviewCardPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset"
>) {
return ( return (
<HoverCardPrimitive.Portal data-slot="hover-card-portal"> <PreviewCardPrimitive.Portal data-slot="hover-card-portal">
<HoverCardPrimitive.Content <PreviewCardPrimitive.Positioner
align={align} align={align}
alignOffset={alignOffset}
side={side}
sideOffset={sideOffset}
className="isolate z-50"
>
<PreviewCardPrimitive.Popup
data-slot="hover-card-content"
className={cn( className={cn(
"z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "z-50 w-64 origin-(--transform-origin) rounded-lg bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
data-slot="hover-card-content"
sideOffset={sideOffset}
{...props} {...props}
/> />
</HoverCardPrimitive.Portal> </PreviewCardPrimitive.Positioner>
); </PreviewCardPrimitive.Portal>
)
} }
export { HoverCard, HoverCardTrigger, HoverCardContent }; export { HoverCard, HoverCardTrigger, HoverCardContent }

View File

@ -1,3 +1,5 @@
"use client"
import * as React from "react" import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
@ -12,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="input-group" data-slot="input-group"
role="group" role="group"
className={cn( className={cn(
"group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", "group/input-group relative flex h-9 w-full min-w-0 items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
className className
)} )}
{...props} {...props}
@ -26,9 +28,9 @@ const inputGroupAddonVariants = cva(
variants: { variants: {
align: { align: {
"inline-start": "inline-start":
"order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]", "order-first pl-2 has-[>button]:-ml-1 has-[>kbd]:ml-[-0.15rem]",
"inline-end": "inline-end":
"order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]", "order-last pr-2 has-[>button]:-mr-1 has-[>kbd]:mr-[-0.15rem]",
"block-start": "block-start":
"order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2", "order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2",
"block-end": "block-end":
@ -68,10 +70,10 @@ const inputGroupButtonVariants = cva(
{ {
variants: { variants: {
size: { size: {
xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5", xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
sm: "", sm: "",
"icon-xs": "icon-xs":
"size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0", "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
"icon-sm": "size-8 p-0 has-[>svg]:p-0", "icon-sm": "size-8 p-0 has-[>svg]:p-0",
}, },
}, },
@ -87,8 +89,10 @@ function InputGroupButton({
variant = "ghost", variant = "ghost",
size = "xs", size = "xs",
...props ...props
}: Omit<React.ComponentProps<typeof Button>, "size"> & }: Omit<React.ComponentProps<typeof Button>, "size" | "type"> &
VariantProps<typeof inputGroupButtonVariants>) { VariantProps<typeof inputGroupButtonVariants> & {
type?: "button" | "submit" | "reset"
}) {
return ( return (
<Button <Button
type={type} type={type}
@ -120,7 +124,7 @@ function InputGroupInput({
<Input <Input
data-slot="input-group-control" data-slot="input-group-control"
className={cn( className={cn(
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent", "flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
className className
)} )}
{...props} {...props}
@ -136,7 +140,7 @@ function InputGroupTextarea({
<Textarea <Textarea
data-slot="input-group-control" data-slot="input-group-control"
className={cn( className={cn(
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent", "flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
className className
)} )}
{...props} {...props}

View File

@ -30,7 +30,7 @@ function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="input-otp-group" data-slot="input-otp-group"
className={cn( className={cn(
"flex items-center rounded-lg has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40", "flex items-center rounded-md has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40",
className className
)} )}
{...props} {...props}
@ -53,7 +53,7 @@ function InputOTPSlot({
data-slot="input-otp-slot" data-slot="input-otp-slot"
data-active={isActive} data-active={isActive}
className={cn( className={cn(
"relative flex size-8 items-center justify-center border-y border-r border-input text-sm transition-all outline-none first:rounded-l-lg first:border-l last:rounded-r-lg aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-3 data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40", "relative flex size-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-3 data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40",
className className
)} )}
{...props} {...props}

View File

@ -1,18 +1,20 @@
import { cn } from "@repo/shadcn-ui/lib/utils"; import * as React from "react"
import type * as React from "react"; import { Input as InputPrimitive } from "@base-ui/react/input"
import { cn } from "@repo/shadcn-ui/lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) { function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return ( return (
<input <InputPrimitive
type={type}
data-slot="input"
className={cn( className={cn(
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", "h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className className
)} )}
data-slot="input"
type={type}
{...props} {...props}
/> />
); )
} }
export { Input }; export { Input }

View File

@ -1,6 +1,7 @@
import * as React from "react" import * as React from "react"
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 { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { Separator } from "@repo/shadcn-ui/components/separator" import { Separator } from "@repo/shadcn-ui/components/separator"
@ -34,7 +35,7 @@ function ItemSeparator({
} }
const itemVariants = cva( const itemVariants = cva(
"group/item flex w-full flex-wrap items-center rounded-lg border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted", "group/item flex w-full flex-wrap items-center rounded-md border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted",
{ {
variants: { variants: {
variant: { variant: {
@ -43,7 +44,7 @@ const itemVariants = cva(
muted: "border-transparent bg-muted/50", muted: "border-transparent bg-muted/50",
}, },
size: { size: {
default: "gap-2.5 px-3 py-2.5", default: "gap-3.5 px-4 py-3.5",
sm: "gap-2.5 px-3 py-2.5", sm: "gap-2.5 px-3 py-2.5",
xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0", xs: "gap-2 px-2.5 py-2 in-data-[slot=dropdown-menu-content]:p-0",
}, },
@ -59,20 +60,24 @@ function Item({
className, className,
variant = "default", variant = "default",
size = "default", size = "default",
asChild = false, render,
...props ...props
}: React.ComponentProps<"div"> & }: useRender.ComponentProps<"div"> & VariantProps<typeof itemVariants>) {
VariantProps<typeof itemVariants> & { asChild?: boolean }) { return useRender({
const Comp = asChild ? Slot.Root : "div" defaultTagName: "div",
return ( props: mergeProps<"div">(
<Comp {
data-slot="item" className: cn(itemVariants({ variant, size, className })),
data-variant={variant} },
data-size={size} props
className={cn(itemVariants({ variant, size, className }))} ),
{...props} render,
/> state: {
) slot: "item",
variant,
size,
},
})
} }
const itemMediaVariants = cva( const itemMediaVariants = cva(

View File

@ -1,14 +1,12 @@
"use client"
import * as React from "react" import * as React from "react"
import { Label as LabelPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
function Label({ function Label({ className, ...props }: React.ComponentProps<"label">) {
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
return ( return (
<LabelPrimitive.Root <label
data-slot="label" data-slot="label"
className={cn( className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",

View File

@ -1,18 +1,33 @@
"use client"
import * as React from "react" import * as React from "react"
import { Menubar as MenubarPrimitive } from "radix-ui" import { Menu as MenuPrimitive } from "@base-ui/react/menu"
import { Menubar as MenubarPrimitive } from "@base-ui/react/menubar"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { CheckIcon, ChevronRightIcon } from "lucide-react" import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuPortal,
DropdownMenuRadioGroup,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@repo/shadcn-ui/components/dropdown-menu"
import { CheckIcon } from "lucide-react"
function Menubar({ function Menubar({ className, ...props }: MenubarPrimitive.Props) {
className,
...props
}: React.ComponentProps<typeof MenubarPrimitive.Root>) {
return ( return (
<MenubarPrimitive.Root <MenubarPrimitive
data-slot="menubar" data-slot="menubar"
className={cn( className={cn(
"flex h-8 items-center gap-0.5 rounded-lg border p-[3px]", "flex h-9 items-center gap-1 rounded-md border p-1 shadow-xs",
className className
)} )}
{...props} {...props}
@ -20,41 +35,31 @@ function Menubar({
) )
} }
function MenubarMenu({ function MenubarMenu({ ...props }: React.ComponentProps<typeof DropdownMenu>) {
...props return <DropdownMenu data-slot="menubar-menu" {...props} />
}: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
return <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />
} }
function MenubarGroup({ function MenubarGroup({
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Group>) { }: React.ComponentProps<typeof DropdownMenuGroup>) {
return <MenubarPrimitive.Group data-slot="menubar-group" {...props} /> return <DropdownMenuGroup data-slot="menubar-group" {...props} />
} }
function MenubarPortal({ function MenubarPortal({
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Portal>) { }: React.ComponentProps<typeof DropdownMenuPortal>) {
return <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} /> return <DropdownMenuPortal data-slot="menubar-portal" {...props} />
}
function MenubarRadioGroup({
...props
}: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
return (
<MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />
)
} }
function MenubarTrigger({ function MenubarTrigger({
className, className,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Trigger>) { }: React.ComponentProps<typeof DropdownMenuTrigger>) {
return ( return (
<MenubarPrimitive.Trigger <DropdownMenuTrigger
data-slot="menubar-trigger" data-slot="menubar-trigger"
className={cn( className={cn(
"flex items-center rounded-sm px-1.5 py-[2px] text-sm font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted", "flex items-center rounded-sm px-2 py-1 text-sm font-medium outline-hidden select-none hover:bg-muted aria-expanded:bg-muted",
className className
)} )}
{...props} {...props}
@ -68,18 +73,16 @@ function MenubarContent({
alignOffset = -4, alignOffset = -4,
sideOffset = 8, sideOffset = 8,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Content>) { }: React.ComponentProps<typeof DropdownMenuContent>) {
return ( return (
<MenubarPortal> <DropdownMenuContent
<MenubarPrimitive.Content
data-slot="menubar-content" data-slot="menubar-content"
align={align} align={align}
alignOffset={alignOffset} alignOffset={alignOffset}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn("z-50 min-w-36 origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95", className )} className={cn("min-w-36 rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95", className )}
{...props} {...props}
/> />
</MenubarPortal>
) )
} }
@ -88,17 +91,14 @@ function MenubarItem({
inset, inset,
variant = "default", variant = "default",
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Item> & { }: React.ComponentProps<typeof DropdownMenuItem>) {
inset?: boolean
variant?: "default" | "destructive"
}) {
return ( return (
<MenubarPrimitive.Item <DropdownMenuItem
data-slot="menubar-item" data-slot="menubar-item"
data-inset={inset} data-inset={inset}
data-variant={variant} data-variant={variant}
className={cn( className={cn(
"group/menubar-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive!", "group/menubar-item gap-2 rounded-sm px-2 py-1.5 text-sm focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:opacity-50 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive!",
className className
)} )}
{...props} {...props}
@ -112,57 +112,63 @@ function MenubarCheckboxItem({
checked, checked,
inset, inset,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem> & { }: MenuPrimitive.CheckboxItem.Props & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
<MenubarPrimitive.CheckboxItem <MenuPrimitive.CheckboxItem
data-slot="menubar-checkbox-item" data-slot="menubar-checkbox-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0", "relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className className
)} )}
checked={checked} checked={checked}
{...props} {...props}
> >
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"> <span className="pointer-events-none absolute left-2 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
<MenubarPrimitive.ItemIndicator> <MenuPrimitive.CheckboxItemIndicator>
<CheckIcon <CheckIcon
/> />
</MenubarPrimitive.ItemIndicator> </MenuPrimitive.CheckboxItemIndicator>
</span> </span>
{children} {children}
</MenubarPrimitive.CheckboxItem> </MenuPrimitive.CheckboxItem>
) )
} }
function MenubarRadioGroup({
...props
}: React.ComponentProps<typeof DropdownMenuRadioGroup>) {
return <DropdownMenuRadioGroup data-slot="menubar-radio-group" {...props} />
}
function MenubarRadioItem({ function MenubarRadioItem({
className, className,
children, children,
inset, inset,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.RadioItem> & { }: MenuPrimitive.RadioItem.Props & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
<MenubarPrimitive.RadioItem <MenuPrimitive.RadioItem
data-slot="menubar-radio-item" data-slot="menubar-radio-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-1.5 pl-7 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex cursor-default items-center gap-2 rounded-md py-1.5 pr-2 pl-8 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
> >
<span className="pointer-events-none absolute left-1.5 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4"> <span className="pointer-events-none absolute left-2 flex size-4 items-center justify-center [&_svg:not([class*='size-'])]:size-4">
<MenubarPrimitive.ItemIndicator> <MenuPrimitive.RadioItemIndicator>
<CheckIcon <CheckIcon
/> />
</MenubarPrimitive.ItemIndicator> </MenuPrimitive.RadioItemIndicator>
</span> </span>
{children} {children}
</MenubarPrimitive.RadioItem> </MenuPrimitive.RadioItem>
) )
} }
@ -170,15 +176,15 @@ function MenubarLabel({
className, className,
inset, inset,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Label> & { }: React.ComponentProps<typeof DropdownMenuLabel> & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
<MenubarPrimitive.Label <DropdownMenuLabel
data-slot="menubar-label" data-slot="menubar-label"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"px-1.5 py-1 text-sm font-medium data-inset:pl-7", "px-2 py-1.5 text-sm font-medium data-inset:pl-8",
className className
)} )}
{...props} {...props}
@ -189,9 +195,9 @@ function MenubarLabel({
function MenubarSeparator({ function MenubarSeparator({
className, className,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Separator>) { }: React.ComponentProps<typeof DropdownMenuSeparator>) {
return ( return (
<MenubarPrimitive.Separator <DropdownMenuSeparator
data-slot="menubar-separator" data-slot="menubar-separator"
className={cn("-mx-1 my-1 h-px bg-border", className)} className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props} {...props}
@ -202,9 +208,9 @@ function MenubarSeparator({
function MenubarShortcut({ function MenubarShortcut({
className, className,
...props ...props
}: React.ComponentProps<"span">) { }: React.ComponentProps<typeof DropdownMenuShortcut>) {
return ( return (
<span <DropdownMenuShortcut
data-slot="menubar-shortcut" data-slot="menubar-shortcut"
className={cn( className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground", "ml-auto text-xs tracking-widest text-muted-foreground group-focus/menubar-item:text-accent-foreground",
@ -217,42 +223,38 @@ function MenubarShortcut({
function MenubarSub({ function MenubarSub({
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.Sub>) { }: React.ComponentProps<typeof DropdownMenuSub>) {
return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} /> return <DropdownMenuSub data-slot="menubar-sub" {...props} />
} }
function MenubarSubTrigger({ function MenubarSubTrigger({
className, className,
inset, inset,
children,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & { }: React.ComponentProps<typeof DropdownMenuSubTrigger> & {
inset?: boolean inset?: boolean
}) { }) {
return ( return (
<MenubarPrimitive.SubTrigger <DropdownMenuSubTrigger
data-slot="menubar-sub-trigger" data-slot="menubar-sub-trigger"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-7 data-open:bg-accent data-open:text-accent-foreground [&_svg:not([class*='size-'])]:size-4", "gap-2 rounded-sm px-2 py-1.5 text-sm focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-open:bg-accent data-open:text-accent-foreground [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
> />
{children}
<ChevronRightIcon className="ml-auto size-4" />
</MenubarPrimitive.SubTrigger>
) )
} }
function MenubarSubContent({ function MenubarSubContent({
className, className,
...props ...props
}: React.ComponentProps<typeof MenubarPrimitive.SubContent>) { }: React.ComponentProps<typeof DropdownMenuSubContent>) {
return ( return (
<MenubarPrimitive.SubContent <DropdownMenuSubContent
data-slot="menubar-sub-content" data-slot="menubar-sub-content"
className={cn("z-50 min-w-32 origin-(--radix-menubar-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )} className={cn("min-w-32 rounded-md bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props} {...props}
/> />
) )

View File

@ -0,0 +1,61 @@
import * as React from "react"
import { cn } from "@repo/shadcn-ui/lib/utils"
import { ChevronDownIcon } from "lucide-react"
type NativeSelectProps = Omit<React.ComponentProps<"select">, "size"> & {
size?: "sm" | "default"
}
function NativeSelect({
className,
size = "default",
...props
}: NativeSelectProps) {
return (
<div
className={cn(
"group/native-select relative w-fit has-[select:disabled]:opacity-50",
className
)}
data-slot="native-select-wrapper"
data-size={size}
>
<select
data-slot="native-select"
data-size={size}
className="h-9 w-full min-w-0 appearance-none rounded-md border border-input bg-transparent py-1 pr-8 pl-2.5 text-sm shadow-xs transition-[color,box-shadow] outline-none select-none selection:bg-primary selection:text-primary-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=sm]:h-8 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40"
{...props}
/>
<ChevronDownIcon className="pointer-events-none absolute top-1/2 right-2.5 size-4 -translate-y-1/2 text-muted-foreground select-none" aria-hidden="true" data-slot="native-select-icon" />
</div>
)
}
function NativeSelectOption({
className,
...props
}: React.ComponentProps<"option">) {
return (
<option
data-slot="native-select-option"
className={cn("bg-[Canvas] text-[CanvasText]", className)}
{...props}
/>
)
}
function NativeSelectOptGroup({
className,
...props
}: React.ComponentProps<"optgroup">) {
return (
<optgroup
data-slot="native-select-optgroup"
className={cn("bg-[Canvas] text-[CanvasText]", className)}
{...props}
/>
)
}
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption }

View File

@ -1,22 +1,19 @@
import * as React from "react" import { NavigationMenu as NavigationMenuPrimitive } from "@base-ui/react/navigation-menu"
import { cva } from "class-variance-authority" import { cva } from "class-variance-authority"
import { NavigationMenu as NavigationMenuPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { ChevronDownIcon } from "lucide-react" import { ChevronDownIcon } from "lucide-react"
function NavigationMenu({ function NavigationMenu({
align = "start",
className, className,
children, children,
viewport = true,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & { }: NavigationMenuPrimitive.Root.Props &
viewport?: boolean Pick<NavigationMenuPrimitive.Positioner.Props, "align">) {
}) {
return ( return (
<NavigationMenuPrimitive.Root <NavigationMenuPrimitive.Root
data-slot="navigation-menu" data-slot="navigation-menu"
data-viewport={viewport}
className={cn( className={cn(
"group/navigation-menu relative flex max-w-max flex-1 items-center justify-center", "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
className className
@ -24,7 +21,7 @@ function NavigationMenu({
{...props} {...props}
> >
{children} {children}
{viewport && <NavigationMenuViewport />} <NavigationMenuPositioner align={align} />
</NavigationMenuPrimitive.Root> </NavigationMenuPrimitive.Root>
) )
} }
@ -32,7 +29,7 @@ function NavigationMenu({
function NavigationMenuList({ function NavigationMenuList({
className, className,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) { }: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.List>) {
return ( return (
<NavigationMenuPrimitive.List <NavigationMenuPrimitive.List
data-slot="navigation-menu-list" data-slot="navigation-menu-list"
@ -48,7 +45,7 @@ function NavigationMenuList({
function NavigationMenuItem({ function NavigationMenuItem({
className, className,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) { }: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Item>) {
return ( return (
<NavigationMenuPrimitive.Item <NavigationMenuPrimitive.Item
data-slot="navigation-menu-item" data-slot="navigation-menu-item"
@ -59,14 +56,14 @@ function NavigationMenuItem({
} }
const navigationMenuTriggerStyle = cva( const navigationMenuTriggerStyle = cva(
"group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center rounded-lg bg-background px-2.5 py-1.5 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted data-open:bg-muted/50 data-open:hover:bg-muted data-open:focus:bg-muted" "group/navigation-menu-trigger inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-popup-open:bg-muted/50 data-popup-open:hover:bg-muted data-open:bg-muted/50 data-open:hover:bg-muted data-open:focus:bg-muted"
) )
function NavigationMenuTrigger({ function NavigationMenuTrigger({
className, className,
children, children,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) { }: NavigationMenuPrimitive.Trigger.Props) {
return ( return (
<NavigationMenuPrimitive.Trigger <NavigationMenuPrimitive.Trigger
data-slot="navigation-menu-trigger" data-slot="navigation-menu-trigger"
@ -82,12 +79,12 @@ function NavigationMenuTrigger({
function NavigationMenuContent({ function NavigationMenuContent({
className, className,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) { }: NavigationMenuPrimitive.Content.Props) {
return ( return (
<NavigationMenuPrimitive.Content <NavigationMenuPrimitive.Content
data-slot="navigation-menu-content" data-slot="navigation-menu-content"
className={cn( className={cn(
"top-0 left-0 w-full p-1 ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-lg group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none md:absolute md:w-auto group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95", "data-ending-style:data-activation-direction=left:translate-x-[50%] data-ending-style:data-activation-direction=right:translate-x-[-50%] data-starting-style:data-activation-direction=left:translate-x-[-50%] data-starting-style:data-activation-direction=right:translate-x-[50%] h-full w-auto p-2 pr-2.5 transition-[opacity,transform,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:ring-1 group-data-[viewport=false]/navigation-menu:ring-foreground/10 group-data-[viewport=false]/navigation-menu:duration-300 data-ending-style:opacity-0 data-starting-style:opacity-0 data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 data-[motion^=from-]:animate-in data-[motion^=from-]:fade-in data-[motion^=to-]:animate-out data-[motion^=to-]:fade-out **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none group-data-[viewport=false]/navigation-menu:data-open:animate-in group-data-[viewport=false]/navigation-menu:data-open:fade-in-0 group-data-[viewport=false]/navigation-menu:data-open:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-closed:animate-out group-data-[viewport=false]/navigation-menu:data-closed:fade-out-0 group-data-[viewport=false]/navigation-menu:data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}
@ -95,37 +92,44 @@ function NavigationMenuContent({
) )
} }
function NavigationMenuViewport({ function NavigationMenuPositioner({
className, className,
side = "bottom",
sideOffset = 8,
align = "start",
alignOffset = 0,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) { }: NavigationMenuPrimitive.Positioner.Props) {
return ( return (
<div <NavigationMenuPrimitive.Portal>
<NavigationMenuPrimitive.Positioner
side={side}
sideOffset={sideOffset}
align={align}
alignOffset={alignOffset}
className={cn( className={cn(
"absolute top-full left-0 isolate z-50 flex justify-center" "isolate z-50 h-(--positioner-height) w-(--positioner-width) max-w-(--available-width) transition-[top,left,right,bottom] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] data-instant:transition-none data-[side=bottom]:before:top-[-10px] data-[side=bottom]:before:right-0 data-[side=bottom]:before:left-0",
)}
>
<NavigationMenuPrimitive.Viewport
data-slot="navigation-menu-viewport"
className={cn(
"origin-top-center relative mt-1.5 h-(--radix-navigation-menu-viewport-height) w-full overflow-hidden rounded-lg bg-popover text-popover-foreground shadow ring-1 ring-foreground/10 duration-100 md:w-(--radix-navigation-menu-viewport-width) data-open:animate-in data-open:zoom-in-90 data-closed:animate-out data-closed:zoom-out-90",
className className
)} )}
{...props} {...props}
/> >
</div> <NavigationMenuPrimitive.Popup className="data-[ending-style]:easing-[ease] xs:w-(--popup-width) relative h-(--popup-height) w-(--popup-width) origin-(--transform-origin) rounded-lg bg-popover text-popover-foreground shadow ring-1 ring-foreground/10 transition-[opacity,transform,width,height,scale,translate] duration-[0.35s] ease-[cubic-bezier(0.22,1,0.36,1)] outline-none data-ending-style:scale-90 data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:scale-90 data-starting-style:opacity-0">
<NavigationMenuPrimitive.Viewport className="relative size-full overflow-hidden" />
</NavigationMenuPrimitive.Popup>
</NavigationMenuPrimitive.Positioner>
</NavigationMenuPrimitive.Portal>
) )
} }
function NavigationMenuLink({ function NavigationMenuLink({
className, className,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) { }: NavigationMenuPrimitive.Link.Props) {
return ( return (
<NavigationMenuPrimitive.Link <NavigationMenuPrimitive.Link
data-slot="navigation-menu-link" data-slot="navigation-menu-link"
className={cn( className={cn(
"flex items-center gap-2 rounded-lg p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-md data-active:bg-muted/50 data-active:hover:bg-muted data-active:focus:bg-muted [&_svg:not([class*='size-'])]:size-4", "flex items-center gap-1.5 rounded-md p-2 text-sm transition-all outline-none hover:bg-muted focus:bg-muted focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-1 in-data-[slot=navigation-menu-content]:rounded-sm data-[active=true]:bg-muted/50 data-[active=true]:hover:bg-muted data-[active=true]:focus:bg-muted [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@ -136,9 +140,9 @@ function NavigationMenuLink({
function NavigationMenuIndicator({ function NavigationMenuIndicator({
className, className,
...props ...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) { }: React.ComponentPropsWithRef<typeof NavigationMenuPrimitive.Icon>) {
return ( return (
<NavigationMenuPrimitive.Indicator <NavigationMenuPrimitive.Icon
data-slot="navigation-menu-indicator" data-slot="navigation-menu-indicator"
className={cn( className={cn(
"top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in", "top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:animate-in data-[state=visible]:fade-in",
@ -147,18 +151,18 @@ function NavigationMenuIndicator({
{...props} {...props}
> >
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" /> <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
</NavigationMenuPrimitive.Indicator> </NavigationMenuPrimitive.Icon>
) )
} }
export { export {
NavigationMenu, NavigationMenu,
NavigationMenuList,
NavigationMenuItem,
NavigationMenuContent, NavigationMenuContent,
NavigationMenuTrigger,
NavigationMenuLink,
NavigationMenuIndicator, NavigationMenuIndicator,
NavigationMenuViewport, NavigationMenuItem,
NavigationMenuLink,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle, navigationMenuTriggerStyle,
NavigationMenuPositioner,
} }

View File

@ -23,7 +23,7 @@ function PaginationContent({
return ( return (
<ul <ul
data-slot="pagination-content" data-slot="pagination-content"
className={cn("flex items-center gap-0.5", className)} className={cn("flex items-center gap-1", className)}
{...props} {...props}
/> />
) )
@ -46,18 +46,19 @@ function PaginationLink({
}: PaginationLinkProps) { }: PaginationLinkProps) {
return ( return (
<Button <Button
asChild
variant={isActive ? "outline" : "ghost"} variant={isActive ? "outline" : "ghost"}
size={size} size={size}
className={cn(className)} className={cn(className)}
> nativeButton={false}
render={
<a <a
aria-current={isActive ? "page" : undefined} aria-current={isActive ? "page" : undefined}
data-slot="pagination-link" data-slot="pagination-link"
data-active={isActive} data-active={isActive}
{...props} {...props}
/> />
</Button> }
/>
) )
} }
@ -70,7 +71,7 @@ function PaginationPrevious({
<PaginationLink <PaginationLink
aria-label="Go to previous page" aria-label="Go to previous page"
size="default" size="default"
className={cn("pl-1.5!", className)} className={cn("pl-2!", className)}
{...props} {...props}
> >
<ChevronLeftIcon data-icon="inline-start" /> <ChevronLeftIcon data-icon="inline-start" />
@ -88,7 +89,7 @@ function PaginationNext({
<PaginationLink <PaginationLink
aria-label="Go to next page" aria-label="Go to next page"
size="default" size="default"
className={cn("pr-1.5!", className)} className={cn("pr-2!", className)}
{...props} {...props}
> >
<span className="hidden sm:block">{text}</span> <span className="hidden sm:block">{text}</span>
@ -106,7 +107,7 @@ function PaginationEllipsis({
aria-hidden aria-hidden
data-slot="pagination-ellipsis" data-slot="pagination-ellipsis"
className={cn( className={cn(
"flex size-8 items-center justify-center [&_svg:not([class*='size-'])]:size-4", "flex size-9 items-center justify-center [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}

View File

@ -1,61 +1,63 @@
import * as React from "react" import * as React from "react"
import { Popover as PopoverPrimitive } from "radix-ui" import { Popover as PopoverPrimitive } from "@base-ui/react/popover"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
function Popover({ function Popover({ ...props }: PopoverPrimitive.Root.Props) {
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root data-slot="popover" {...props} /> return <PopoverPrimitive.Root data-slot="popover" {...props} />
} }
function PopoverTrigger({ function PopoverTrigger({ ...props }: PopoverPrimitive.Trigger.Props) {
...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} /> return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />
} }
function PopoverContent({ function PopoverContent({
className, className,
align = "center", align = "center",
alignOffset = 0,
side = "bottom",
sideOffset = 4, sideOffset = 4,
...props ...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) { }: PopoverPrimitive.Popup.Props &
Pick<
PopoverPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset"
>) {
return ( return (
<PopoverPrimitive.Portal> <PopoverPrimitive.Portal>
<PopoverPrimitive.Content <PopoverPrimitive.Positioner
data-slot="popover-content"
align={align} align={align}
alignOffset={alignOffset}
side={side}
sideOffset={sideOffset} sideOffset={sideOffset}
className="isolate z-50"
>
<PopoverPrimitive.Popup
data-slot="popover-content"
className={cn( className={cn(
"z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "z-50 flex w-72 origin-(--transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}
/> />
</PopoverPrimitive.Positioner>
</PopoverPrimitive.Portal> </PopoverPrimitive.Portal>
) )
} }
function PopoverAnchor({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />
}
function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) { function PopoverHeader({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="popover-header" data-slot="popover-header"
className={cn("flex flex-col gap-0.5 text-sm", className)} className={cn("flex flex-col gap-1 text-sm", className)}
{...props} {...props}
/> />
) )
} }
function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) { function PopoverTitle({ className, ...props }: PopoverPrimitive.Title.Props) {
return ( return (
<div <PopoverPrimitive.Title
data-slot="popover-title" data-slot="popover-title"
className={cn("font-medium", className)} className={cn("font-medium", className)}
{...props} {...props}
@ -66,9 +68,9 @@ function PopoverTitle({ className, ...props }: React.ComponentProps<"h2">) {
function PopoverDescription({ function PopoverDescription({
className, className,
...props ...props
}: React.ComponentProps<"p">) { }: PopoverPrimitive.Description.Props) {
return ( return (
<p <PopoverPrimitive.Description
data-slot="popover-description" data-slot="popover-description"
className={cn("text-muted-foreground", className)} className={cn("text-muted-foreground", className)}
{...props} {...props}
@ -78,7 +80,6 @@ function PopoverDescription({
export { export {
Popover, Popover,
PopoverAnchor,
PopoverContent, PopoverContent,
PopoverDescription, PopoverDescription,
PopoverHeader, PopoverHeader,

View File

@ -1,29 +1,83 @@
import * as React from "react" "use client"
import { Progress as ProgressPrimitive } from "radix-ui"
import { Progress as ProgressPrimitive } from "@base-ui/react/progress"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
function Progress({ function Progress({
className, className,
children,
value, value,
...props ...props
}: React.ComponentProps<typeof ProgressPrimitive.Root>) { }: ProgressPrimitive.Root.Props) {
return ( return (
<ProgressPrimitive.Root <ProgressPrimitive.Root
value={value}
data-slot="progress" data-slot="progress"
className={cn( className={cn("flex flex-wrap gap-3", className)}
"relative flex h-1 w-full items-center overflow-x-hidden rounded-full bg-muted",
className
)}
{...props} {...props}
> >
<ProgressPrimitive.Indicator {children}
data-slot="progress-indicator" <ProgressTrack>
className="size-full flex-1 bg-primary transition-all" <ProgressIndicator />
style={{ transform: `translateX(-${100 - (value || 0)}%)` }} </ProgressTrack>
/>
</ProgressPrimitive.Root> </ProgressPrimitive.Root>
) )
} }
export { Progress } function ProgressTrack({ className, ...props }: ProgressPrimitive.Track.Props) {
return (
<ProgressPrimitive.Track
className={cn(
"relative flex h-1.5 w-full items-center overflow-x-hidden rounded-full bg-muted",
className
)}
data-slot="progress-track"
{...props}
/>
)
}
function ProgressIndicator({
className,
...props
}: ProgressPrimitive.Indicator.Props) {
return (
<ProgressPrimitive.Indicator
data-slot="progress-indicator"
className={cn("h-full bg-primary transition-all", className)}
{...props}
/>
)
}
function ProgressLabel({ className, ...props }: ProgressPrimitive.Label.Props) {
return (
<ProgressPrimitive.Label
className={cn("text-sm font-medium", className)}
data-slot="progress-label"
{...props}
/>
)
}
function ProgressValue({ className, ...props }: ProgressPrimitive.Value.Props) {
return (
<ProgressPrimitive.Value
className={cn(
"ml-auto text-sm text-muted-foreground tabular-nums",
className
)}
data-slot="progress-value"
{...props}
/>
)
}
export {
Progress,
ProgressTrack,
ProgressIndicator,
ProgressLabel,
ProgressValue,
}

View File

@ -1,27 +1,21 @@
import * as React from "react" import { Radio as RadioPrimitive } from "@base-ui/react/radio"
import { RadioGroup as RadioGroupPrimitive } from "radix-ui" import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
function RadioGroup({ function RadioGroup({ className, ...props }: RadioGroupPrimitive.Props) {
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
return ( return (
<RadioGroupPrimitive.Root <RadioGroupPrimitive
data-slot="radio-group" data-slot="radio-group"
className={cn("grid w-full gap-2", className)} className={cn("grid w-full gap-3", className)}
{...props} {...props}
/> />
) )
} }
function RadioGroupItem({ function RadioGroupItem({ className, ...props }: RadioPrimitive.Root.Props) {
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
return ( return (
<RadioGroupPrimitive.Item <RadioPrimitive.Root
data-slot="radio-group-item" data-slot="radio-group-item"
className={cn( className={cn(
"group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary", "group/radio-group-item peer relative flex aspect-square size-4 shrink-0 rounded-full border border-input outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
@ -29,13 +23,13 @@ function RadioGroupItem({
)} )}
{...props} {...props}
> >
<RadioGroupPrimitive.Indicator <RadioPrimitive.Indicator
data-slot="radio-group-indicator" data-slot="radio-group-indicator"
className="flex size-4 items-center justify-center" className="flex size-4 items-center justify-center"
> >
<span className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" /> <span className="absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 rounded-full bg-primary-foreground" />
</RadioGroupPrimitive.Indicator> </RadioPrimitive.Indicator>
</RadioGroupPrimitive.Item> </RadioPrimitive.Root>
) )
} }

View File

@ -1,3 +1,5 @@
"use client"
import * as ResizablePrimitive from "react-resizable-panels" import * as ResizablePrimitive from "react-resizable-panels"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"

View File

@ -1,5 +1,5 @@
import * as React from "react" import * as React from "react"
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui" import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -7,7 +7,7 @@ function ScrollArea({
className, className,
children, children,
...props ...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) { }: ScrollAreaPrimitive.Root.Props) {
return ( return (
<ScrollAreaPrimitive.Root <ScrollAreaPrimitive.Root
data-slot="scroll-area" data-slot="scroll-area"
@ -30,9 +30,9 @@ function ScrollBar({
className, className,
orientation = "vertical", orientation = "vertical",
...props ...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) { }: ScrollAreaPrimitive.Scrollbar.Props) {
return ( return (
<ScrollAreaPrimitive.ScrollAreaScrollbar <ScrollAreaPrimitive.Scrollbar
data-slot="scroll-area-scrollbar" data-slot="scroll-area-scrollbar"
data-orientation={orientation} data-orientation={orientation}
orientation={orientation} orientation={orientation}
@ -42,11 +42,11 @@ function ScrollBar({
)} )}
{...props} {...props}
> >
<ScrollAreaPrimitive.ScrollAreaThumb <ScrollAreaPrimitive.Thumb
data-slot="scroll-area-thumb" data-slot="scroll-area-thumb"
className="relative flex-1 rounded-full bg-border" className="relative flex-1 rounded-full bg-border"
/> />
</ScrollAreaPrimitive.ScrollAreaScrollbar> </ScrollAreaPrimitive.Scrollbar>
) )
} }

View File

@ -1,19 +1,14 @@
"use client"
import * as React from "react" import * as React from "react"
import { Select as SelectPrimitive } from "radix-ui" import { Select as SelectPrimitive } from "@base-ui/react/select"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react" import { ChevronDownIcon, CheckIcon, ChevronUpIcon } from "lucide-react"
function Select({ const Select = SelectPrimitive.Root
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot="select" {...props} />
}
function SelectGroup({ function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) {
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
return ( return (
<SelectPrimitive.Group <SelectPrimitive.Group
data-slot="select-group" data-slot="select-group"
@ -23,10 +18,14 @@ function SelectGroup({
) )
} }
function SelectValue({ function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) {
...props return (
}: React.ComponentProps<typeof SelectPrimitive.Value>) { <SelectPrimitive.Value
return <SelectPrimitive.Value data-slot="select-value" {...props} /> data-slot="select-value"
className={cn("flex flex-1 text-left", className)}
{...props}
/>
)
} }
function SelectTrigger({ function SelectTrigger({
@ -34,7 +33,7 @@ function SelectTrigger({
size = "default", size = "default",
children, children,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & { }: SelectPrimitive.Trigger.Props & {
size?: "sm" | "default" size?: "sm" | "default"
}) { }) {
return ( return (
@ -42,15 +41,17 @@ function SelectTrigger({
data-slot="select-trigger" data-slot="select-trigger"
data-size={size} data-size={size}
className={cn( className={cn(
"flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "flex w-fit items-center justify-between gap-1.5 rounded-md border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
> >
{children} {children}
<SelectPrimitive.Icon asChild> <SelectPrimitive.Icon
render={
<ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" /> <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
</SelectPrimitive.Icon> }
/>
</SelectPrimitive.Trigger> </SelectPrimitive.Trigger>
) )
} }
@ -58,32 +59,38 @@ function SelectTrigger({
function SelectContent({ function SelectContent({
className, className,
children, children,
position = "item-aligned", side = "bottom",
sideOffset = 4,
align = "center", align = "center",
alignOffset = 0,
alignItemWithTrigger = true,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) { }: SelectPrimitive.Popup.Props &
Pick<
SelectPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger"
>) {
return ( return (
<SelectPrimitive.Portal> <SelectPrimitive.Portal>
<SelectPrimitive.Content <SelectPrimitive.Positioner
data-slot="select-content" side={side}
data-align-trigger={position === "item-aligned"} sideOffset={sideOffset}
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
position={position}
align={align} align={align}
alignOffset={alignOffset}
alignItemWithTrigger={alignItemWithTrigger}
className="isolate z-50"
>
<SelectPrimitive.Popup
data-slot="select-content"
data-align-trigger={alignItemWithTrigger}
className={cn("relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props} {...props}
> >
<SelectScrollUpButton /> <SelectScrollUpButton />
<SelectPrimitive.Viewport <SelectPrimitive.List>{children}</SelectPrimitive.List>
data-position={position}
className={cn(
"data-[position=popper]:h-(--radix-select-trigger-height) data-[position=popper]:w-full data-[position=popper]:min-w-(--radix-select-trigger-width)",
position === "popper" && ""
)}
>
{children}
</SelectPrimitive.Viewport>
<SelectScrollDownButton /> <SelectScrollDownButton />
</SelectPrimitive.Content> </SelectPrimitive.Popup>
</SelectPrimitive.Positioner>
</SelectPrimitive.Portal> </SelectPrimitive.Portal>
) )
} }
@ -91,11 +98,11 @@ function SelectContent({
function SelectLabel({ function SelectLabel({
className, className,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.Label>) { }: SelectPrimitive.GroupLabel.Props) {
return ( return (
<SelectPrimitive.Label <SelectPrimitive.GroupLabel
data-slot="select-label" data-slot="select-label"
className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)} className={cn("px-2 py-1.5 text-xs text-muted-foreground", className)}
{...props} {...props}
/> />
) )
@ -105,22 +112,26 @@ function SelectItem({
className, className,
children, children,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) { }: SelectPrimitive.Item.Props) {
return ( return (
<SelectPrimitive.Item <SelectPrimitive.Item
data-slot="select-item" data-slot="select-item"
className={cn( className={cn(
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", "relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className className
)} )}
{...props} {...props}
> >
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center"> <SelectPrimitive.ItemText className="flex flex-1 shrink-0 gap-2 whitespace-nowrap">
<SelectPrimitive.ItemIndicator> {children}
</SelectPrimitive.ItemText>
<SelectPrimitive.ItemIndicator
render={
<span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
}
>
<CheckIcon className="pointer-events-none" /> <CheckIcon className="pointer-events-none" />
</SelectPrimitive.ItemIndicator> </SelectPrimitive.ItemIndicator>
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item> </SelectPrimitive.Item>
) )
} }
@ -128,7 +139,7 @@ function SelectItem({
function SelectSeparator({ function SelectSeparator({
className, className,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) { }: SelectPrimitive.Separator.Props) {
return ( return (
<SelectPrimitive.Separator <SelectPrimitive.Separator
data-slot="select-separator" data-slot="select-separator"
@ -141,38 +152,38 @@ function SelectSeparator({
function SelectScrollUpButton({ function SelectScrollUpButton({
className, className,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) { }: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) {
return ( return (
<SelectPrimitive.ScrollUpButton <SelectPrimitive.ScrollUpArrow
data-slot="select-scroll-up-button" data-slot="select-scroll-up-button"
className={cn( className={cn(
"z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4", "top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
> >
<ChevronUpIcon <ChevronUpIcon
/> />
</SelectPrimitive.ScrollUpButton> </SelectPrimitive.ScrollUpArrow>
) )
} }
function SelectScrollDownButton({ function SelectScrollDownButton({
className, className,
...props ...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) { }: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) {
return ( return (
<SelectPrimitive.ScrollDownButton <SelectPrimitive.ScrollDownArrow
data-slot="select-scroll-down-button" data-slot="select-scroll-down-button"
className={cn( className={cn(
"z-10 flex cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4", "bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
> >
<ChevronDownIcon <ChevronDownIcon
/> />
</SelectPrimitive.ScrollDownButton> </SelectPrimitive.ScrollDownArrow>
) )
} }

View File

@ -1,25 +1,23 @@
import { cn } from "@repo/shadcn-ui/lib/utils"; import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
import { Separator as SeparatorPrimitive } from "radix-ui";
import type * as React from "react"; import { cn } from "@repo/shadcn-ui/lib/utils"
function Separator({ function Separator({
className, className,
orientation = "horizontal", orientation = "horizontal",
decorative = true,
...props ...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) { }: SeparatorPrimitive.Props) {
return ( return (
<SeparatorPrimitive.Root <SeparatorPrimitive
data-slot="separator"
orientation={orientation}
className={cn( className={cn(
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch h-px", "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
className className
)} )}
data-slot="separator"
decorative={decorative}
orientation={orientation}
{...props} {...props}
/> />
); )
} }
export { Separator }; export { Separator }

View File

@ -1,41 +1,34 @@
"use client"
import * as React from "react" import * as React from "react"
import { Dialog as SheetPrimitive } from "radix-ui" import { Dialog as SheetPrimitive } from "@base-ui/react/dialog"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { Button } from "@repo/shadcn-ui/components/button" import { Button } from "@repo/shadcn-ui/components/button"
import { XIcon } from "lucide-react" import { XIcon } from "lucide-react"
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) { function Sheet({ ...props }: SheetPrimitive.Root.Props) {
return <SheetPrimitive.Root data-slot="sheet" {...props} /> return <SheetPrimitive.Root data-slot="sheet" {...props} />
} }
function SheetTrigger({ function SheetTrigger({ ...props }: SheetPrimitive.Trigger.Props) {
...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} /> return <SheetPrimitive.Trigger data-slot="sheet-trigger" {...props} />
} }
function SheetClose({ function SheetClose({ ...props }: SheetPrimitive.Close.Props) {
...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
return <SheetPrimitive.Close data-slot="sheet-close" {...props} /> return <SheetPrimitive.Close data-slot="sheet-close" {...props} />
} }
function SheetPortal({ function SheetPortal({ ...props }: SheetPrimitive.Portal.Props) {
...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} /> return <SheetPrimitive.Portal data-slot="sheet-portal" {...props} />
} }
function SheetOverlay({ function SheetOverlay({ className, ...props }: SheetPrimitive.Backdrop.Props) {
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
return ( return (
<SheetPrimitive.Overlay <SheetPrimitive.Backdrop
data-slot="sheet-overlay" data-slot="sheet-overlay"
className={cn( className={cn(
"fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0", "fixed inset-0 z-50 bg-black/10 transition-opacity duration-150 data-ending-style:opacity-0 data-starting-style:opacity-0 supports-backdrop-filter:backdrop-blur-xs",
className className
)} )}
{...props} {...props}
@ -49,37 +42,40 @@ function SheetContent({
side = "right", side = "right",
showCloseButton = true, showCloseButton = true,
...props ...props
}: React.ComponentProps<typeof SheetPrimitive.Content> & { }: SheetPrimitive.Popup.Props & {
side?: "top" | "right" | "bottom" | "left" side?: "top" | "right" | "bottom" | "left"
showCloseButton?: boolean showCloseButton?: boolean
}) { }) {
return ( return (
<SheetPortal> <SheetPortal>
<SheetOverlay /> <SheetOverlay />
<SheetPrimitive.Content <SheetPrimitive.Popup
data-slot="sheet-content" data-slot="sheet-content"
data-side={side} data-side={side}
className={cn( className={cn(
"fixed z-50 flex flex-col gap-4 bg-background bg-clip-padding text-sm shadow-lg transition duration-200 ease-in-out data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-[side=bottom]:data-open:slide-in-from-bottom-10 data-[side=left]:data-open:slide-in-from-left-10 data-[side=right]:data-open:slide-in-from-right-10 data-[side=top]:data-open:slide-in-from-top-10 data-closed:animate-out data-closed:fade-out-0 data-[side=bottom]:data-closed:slide-out-to-bottom-10 data-[side=left]:data-closed:slide-out-to-left-10 data-[side=right]:data-closed:slide-out-to-right-10 data-[side=top]:data-closed:slide-out-to-top-10", "fixed z-50 flex flex-col gap-4 bg-popover bg-clip-padding text-sm text-popover-foreground shadow-lg transition duration-200 ease-in-out data-ending-style:opacity-0 data-starting-style:opacity-0 data-[side=bottom]:inset-x-0 data-[side=bottom]:bottom-0 data-[side=bottom]:h-auto data-[side=bottom]:border-t data-[side=bottom]:data-ending-style:translate-y-[2.5rem] data-[side=bottom]:data-starting-style:translate-y-[2.5rem] data-[side=left]:inset-y-0 data-[side=left]:left-0 data-[side=left]:h-full data-[side=left]:w-3/4 data-[side=left]:border-r data-[side=left]:data-ending-style:translate-x-[-2.5rem] data-[side=left]:data-starting-style:translate-x-[-2.5rem] data-[side=right]:inset-y-0 data-[side=right]:right-0 data-[side=right]:h-full data-[side=right]:w-3/4 data-[side=right]:border-l data-[side=right]:data-ending-style:translate-x-[2.5rem] data-[side=right]:data-starting-style:translate-x-[2.5rem] data-[side=top]:inset-x-0 data-[side=top]:top-0 data-[side=top]:h-auto data-[side=top]:border-b data-[side=top]:data-ending-style:translate-y-[-2.5rem] data-[side=top]:data-starting-style:translate-y-[-2.5rem] data-[side=left]:sm:max-w-sm data-[side=right]:sm:max-w-sm",
className className
)} )}
{...props} {...props}
> >
{children} {children}
{showCloseButton && ( {showCloseButton && (
<SheetPrimitive.Close data-slot="sheet-close" asChild> <SheetPrimitive.Close
data-slot="sheet-close"
render={
<Button <Button
variant="ghost" variant="ghost"
className="absolute top-3 right-3" className="absolute top-4 right-4"
size="icon-sm" size="icon-sm"
/>
}
> >
<XIcon <XIcon
/> />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</Button>
</SheetPrimitive.Close> </SheetPrimitive.Close>
)} )}
</SheetPrimitive.Content> </SheetPrimitive.Popup>
</SheetPortal> </SheetPortal>
) )
} }
@ -88,7 +84,7 @@ function SheetHeader({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="sheet-header" data-slot="sheet-header"
className={cn("flex flex-col gap-0.5 p-4", className)} className={cn("flex flex-col gap-1.5 p-4", className)}
{...props} {...props}
/> />
) )
@ -104,14 +100,11 @@ function SheetFooter({ className, ...props }: React.ComponentProps<"div">) {
) )
} }
function SheetTitle({ function SheetTitle({ className, ...props }: SheetPrimitive.Title.Props) {
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
return ( return (
<SheetPrimitive.Title <SheetPrimitive.Title
data-slot="sheet-title" data-slot="sheet-title"
className={cn("text-base font-medium text-foreground", className)} className={cn("font-medium text-foreground", className)}
{...props} {...props}
/> />
) )
@ -120,7 +113,7 @@ function SheetTitle({
function SheetDescription({ function SheetDescription({
className, className,
...props ...props
}: React.ComponentProps<typeof SheetPrimitive.Description>) { }: SheetPrimitive.Description.Props) {
return ( return (
<SheetPrimitive.Description <SheetPrimitive.Description
data-slot="sheet-description" data-slot="sheet-description"

View File

@ -1,8 +1,9 @@
"use client" "use client"
import * as React from "react" import * as React from "react"
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 { cva, type VariantProps } from "class-variance-authority"
import { Slot } from "radix-ui"
import { useIsMobile } from "@repo/shadcn-ui/hooks/use-mobile" import { useIsMobile } from "@repo/shadcn-ui/hooks/use-mobile"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -370,7 +371,7 @@ function SidebarContent({ className, ...props }: React.ComponentProps<"div">) {
data-slot="sidebar-content" data-slot="sidebar-content"
data-sidebar="content" data-sidebar="content"
className={cn( className={cn(
"no-scrollbar flex min-h-0 flex-1 flex-col gap-0 overflow-auto group-data-[collapsible=icon]:overflow-hidden", "no-scrollbar flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
className className
)} )}
{...props} {...props}
@ -391,42 +392,50 @@ function SidebarGroup({ className, ...props }: React.ComponentProps<"div">) {
function SidebarGroupLabel({ function SidebarGroupLabel({
className, className,
asChild = false, render,
...props ...props
}: React.ComponentProps<"div"> & { asChild?: boolean }) { }: useRender.ComponentProps<"div"> & React.ComponentProps<"div">) {
const Comp = asChild ? Slot.Root : "div" return useRender({
defaultTagName: "div",
return ( props: mergeProps<"div">(
<Comp {
data-slot="sidebar-group-label" className: cn(
data-sidebar="group-label"
className={cn(
"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", "flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
className className
)} ),
{...props} },
/> props
) ),
render,
state: {
slot: "sidebar-group-label",
sidebar: "group-label",
},
})
} }
function SidebarGroupAction({ function SidebarGroupAction({
className, className,
asChild = false, render,
...props ...props
}: React.ComponentProps<"button"> & { asChild?: boolean }) { }: useRender.ComponentProps<"button"> & React.ComponentProps<"button">) {
const Comp = asChild ? Slot.Root : "button" return useRender({
defaultTagName: "button",
return ( props: mergeProps<"button">(
<Comp {
data-slot="sidebar-group-action" className: cn(
data-sidebar="group-action"
className={cn(
"absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0", "absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
className className
)} ),
{...props} },
/> props
) ),
render,
state: {
slot: "sidebar-group-action",
sidebar: "group-action",
},
})
} }
function SidebarGroupContent({ function SidebarGroupContent({
@ -448,7 +457,7 @@ function SidebarMenu({ className, ...props }: React.ComponentProps<"ul">) {
<ul <ul
data-slot="sidebar-menu" data-slot="sidebar-menu"
data-sidebar="menu" data-sidebar="menu"
className={cn("flex w-full min-w-0 flex-col gap-0", className)} className={cn("flex w-full min-w-0 flex-col gap-1", className)}
{...props} {...props}
/> />
) )
@ -488,34 +497,38 @@ const sidebarMenuButtonVariants = cva(
) )
function SidebarMenuButton({ function SidebarMenuButton({
asChild = false, render,
isActive = false, isActive = false,
variant = "default", variant = "default",
size = "default", size = "default",
tooltip, tooltip,
className, className,
...props ...props
}: React.ComponentProps<"button"> & { }: useRender.ComponentProps<"button"> &
asChild?: boolean React.ComponentProps<"button"> & {
isActive?: boolean isActive?: boolean
tooltip?: string | React.ComponentProps<typeof TooltipContent> tooltip?: string | React.ComponentProps<typeof TooltipContent>
} & VariantProps<typeof sidebarMenuButtonVariants>) { } & VariantProps<typeof sidebarMenuButtonVariants>) {
const Comp = asChild ? Slot.Root : "button"
const { isMobile, state } = useSidebar() const { isMobile, state } = useSidebar()
const comp = useRender({
const button = ( defaultTagName: "button",
<Comp props: mergeProps<"button">(
data-slot="sidebar-menu-button" {
data-sidebar="menu-button" className: cn(sidebarMenuButtonVariants({ variant, size }), className),
data-size={size} },
data-active={isActive} props
className={cn(sidebarMenuButtonVariants({ variant, size }), className)} ),
{...props} render: !tooltip ? render : <TooltipTrigger render={render} />,
/> state: {
) slot: "sidebar-menu-button",
sidebar: "menu-button",
size,
active: isActive,
},
})
if (!tooltip) { if (!tooltip) {
return button return comp
} }
if (typeof tooltip === "string") { if (typeof tooltip === "string") {
@ -526,7 +539,7 @@ function SidebarMenuButton({
return ( return (
<Tooltip> <Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger> {comp}
<TooltipContent <TooltipContent
side="right" side="right"
align="center" align="center"
@ -539,28 +552,32 @@ function SidebarMenuButton({
function SidebarMenuAction({ function SidebarMenuAction({
className, className,
asChild = false, render,
showOnHover = false, showOnHover = false,
...props ...props
}: React.ComponentProps<"button"> & { }: useRender.ComponentProps<"button"> &
asChild?: boolean React.ComponentProps<"button"> & {
showOnHover?: boolean showOnHover?: boolean
}) { }) {
const Comp = asChild ? Slot.Root : "button" return useRender({
defaultTagName: "button",
return ( props: mergeProps<"button">(
<Comp {
data-slot="sidebar-menu-action" className: cn(
data-sidebar="menu-action"
className={cn(
"absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0", "absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
showOnHover && showOnHover &&
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0", "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0",
className className
)} ),
{...props} },
/> props
) ),
render,
state: {
slot: "sidebar-menu-action",
sidebar: "menu-action",
},
})
} }
function SidebarMenuBadge({ function SidebarMenuBadge({
@ -647,31 +664,35 @@ function SidebarMenuSubItem({
} }
function SidebarMenuSubButton({ function SidebarMenuSubButton({
asChild = false, render,
size = "md", size = "md",
isActive = false, isActive = false,
className, className,
...props ...props
}: React.ComponentProps<"a"> & { }: useRender.ComponentProps<"a"> &
asChild?: boolean React.ComponentProps<"a"> & {
size?: "sm" | "md" size?: "sm" | "md"
isActive?: boolean isActive?: boolean
}) { }) {
const Comp = asChild ? Slot.Root : "a" return useRender({
defaultTagName: "a",
return ( props: mergeProps<"a">(
<Comp {
data-slot="sidebar-menu-sub-button" className: cn(
data-sidebar="menu-sub-button"
data-size={size}
data-active={isActive}
className={cn(
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
className className
)} ),
{...props} },
/> props
) ),
render,
state: {
slot: "sidebar-menu-sub-button",
sidebar: "menu-sub-button",
size,
active: isActive,
},
})
} }
export { export {

View File

@ -1,5 +1,4 @@
import * as React from "react" import { Slider as SliderPrimitive } from "@base-ui/react/slider"
import { Slider as SliderPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -10,46 +9,42 @@ function Slider({
min = 0, min = 0,
max = 100, max = 100,
...props ...props
}: React.ComponentProps<typeof SliderPrimitive.Root>) { }: SliderPrimitive.Root.Props) {
const _values = React.useMemo( const _values = Array.isArray(value)
() =>
Array.isArray(value)
? value ? value
: Array.isArray(defaultValue) : Array.isArray(defaultValue)
? defaultValue ? defaultValue
: [min, max], : [min, max]
[value, defaultValue, min, max]
)
return ( return (
<SliderPrimitive.Root <SliderPrimitive.Root
className={cn("data-horizontal:w-full data-vertical:h-full", className)}
data-slot="slider" data-slot="slider"
defaultValue={defaultValue} defaultValue={defaultValue}
value={value} value={value}
min={min} min={min}
max={max} max={max}
className={cn( thumbAlignment="edge"
"relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col",
className
)}
{...props} {...props}
> >
<SliderPrimitive.Control className="relative flex w-full touch-none items-center select-none data-disabled:opacity-50 data-vertical:h-full data-vertical:min-h-40 data-vertical:w-auto data-vertical:flex-col">
<SliderPrimitive.Track <SliderPrimitive.Track
data-slot="slider-track" data-slot="slider-track"
className="relative grow overflow-hidden rounded-full bg-muted data-horizontal:h-1 data-horizontal:w-full data-vertical:h-full data-vertical:w-1" className="relative grow overflow-hidden rounded-full bg-muted select-none data-horizontal:h-1.5 data-horizontal:w-full data-vertical:h-full data-vertical:w-1.5"
> >
<SliderPrimitive.Range <SliderPrimitive.Indicator
data-slot="slider-range" data-slot="slider-range"
className="absolute bg-primary select-none data-horizontal:h-full data-vertical:w-full" className="bg-primary select-none data-horizontal:h-full data-vertical:w-full"
/> />
</SliderPrimitive.Track> </SliderPrimitive.Track>
{Array.from({ length: _values.length }, (_, index) => ( {Array.from({ length: _values.length }, (_, index) => (
<SliderPrimitive.Thumb <SliderPrimitive.Thumb
data-slot="slider-thumb" data-slot="slider-thumb"
key={index} key={index}
className="relative block size-3 shrink-0 rounded-full border border-ring bg-white ring-ring/50 transition-[color,box-shadow] select-none after:absolute after:-inset-2 hover:ring-3 focus-visible:ring-3 focus-visible:outline-hidden active:ring-3 disabled:pointer-events-none disabled:opacity-50" className="block size-4 shrink-0 rounded-full border border-primary bg-white shadow-sm ring-ring/50 transition-[color,box-shadow] select-none hover:ring-4 focus-visible:ring-4 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50"
/> />
))} ))}
</SliderPrimitive.Control>
</SliderPrimitive.Root> </SliderPrimitive.Root>
) )
} }

View File

@ -1,3 +1,5 @@
"use client"
import { useTheme } from "next-themes" import { useTheme } from "next-themes"
import { Toaster as Sonner, type ToasterProps } from "sonner" import { Toaster as Sonner, type ToasterProps } from "sonner"
import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react" import { CircleCheckIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, Loader2Icon } from "lucide-react"

View File

@ -1,5 +1,4 @@
import * as React from "react" import { Switch as SwitchPrimitive } from "@base-ui/react/switch"
import { Switch as SwitchPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -7,7 +6,7 @@ function Switch({
className, className,
size = "default", size = "default",
...props ...props
}: React.ComponentProps<typeof SwitchPrimitive.Root> & { }: SwitchPrimitive.Root.Props & {
size?: "sm" | "default" size?: "sm" | "default"
}) { }) {
return ( return (
@ -15,7 +14,7 @@ function Switch({
data-slot="switch" data-slot="switch"
data-size={size} data-size={size}
className={cn( className={cn(
"peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50", "peer group/switch relative inline-flex shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:bg-primary data-unchecked:bg-input dark:data-unchecked:bg-input/80 data-disabled:cursor-not-allowed data-disabled:opacity-50",
className className
)} )}
{...props} {...props}

View File

@ -1,3 +1,5 @@
"use client"
import * as React from "react" import * as React from "react"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -55,7 +57,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
<tr <tr
data-slot="table-row" data-slot="table-row"
className={cn( className={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", "border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted",
className className
)} )}
{...props} {...props}

View File

@ -1,6 +1,5 @@
import * as React from "react" import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
import { Tabs as TabsPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
@ -8,7 +7,7 @@ function Tabs({
className, className,
orientation = "horizontal", orientation = "horizontal",
...props ...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) { }: TabsPrimitive.Root.Props) {
return ( return (
<TabsPrimitive.Root <TabsPrimitive.Root
data-slot="tabs" data-slot="tabs"
@ -23,7 +22,7 @@ function Tabs({
} }
const tabsListVariants = cva( const tabsListVariants = cva(
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none", "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-9 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
{ {
variants: { variants: {
variant: { variant: {
@ -41,8 +40,7 @@ function TabsList({
className, className,
variant = "default", variant = "default",
...props ...props
}: React.ComponentProps<typeof TabsPrimitive.List> & }: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
VariantProps<typeof tabsListVariants>) {
return ( return (
<TabsPrimitive.List <TabsPrimitive.List
data-slot="tabs-list" data-slot="tabs-list"
@ -53,15 +51,12 @@ function TabsList({
) )
} }
function TabsTrigger({ function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return ( return (
<TabsPrimitive.Trigger <TabsPrimitive.Tab
data-slot="tabs-trigger" data-slot="tabs-trigger"
className={cn( className={cn(
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground", "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100", "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
@ -72,12 +67,9 @@ function TabsTrigger({
) )
} }
function TabsContent({ function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
return ( return (
<TabsPrimitive.Content <TabsPrimitive.Panel
data-slot="tabs-content" data-slot="tabs-content"
className={cn("flex-1 text-sm outline-none", className)} className={cn("flex-1 text-sm outline-none", className)}
{...props} {...props}

View File

@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
<textarea <textarea
data-slot="textarea" data-slot="textarea"
className={cn( className={cn(
"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", "flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className className
)} )}
{...props} {...props}

View File

@ -1,8 +1,7 @@
"use client"
import * as React from "react" import * as React from "react"
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group"
import { type VariantProps } from "class-variance-authority" import { type VariantProps } from "class-variance-authority"
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
import { toggleVariants } from "@repo/shadcn-ui/components/toggle" import { toggleVariants } from "@repo/shadcn-ui/components/toggle"
@ -27,13 +26,13 @@ function ToggleGroup({
orientation = "horizontal", orientation = "horizontal",
children, children,
...props ...props
}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> & }: ToggleGroupPrimitive.Props &
VariantProps<typeof toggleVariants> & { VariantProps<typeof toggleVariants> & {
spacing?: number spacing?: number
orientation?: "horizontal" | "vertical" orientation?: "horizontal" | "vertical"
}) { }) {
return ( return (
<ToggleGroupPrimitive.Root <ToggleGroupPrimitive
data-slot="toggle-group" data-slot="toggle-group"
data-variant={variant} data-variant={variant}
data-size={size} data-size={size}
@ -41,7 +40,7 @@ function ToggleGroup({
data-orientation={orientation} data-orientation={orientation}
style={{ "--gap": spacing } as React.CSSProperties} style={{ "--gap": spacing } as React.CSSProperties}
className={cn( className={cn(
"group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-lg data-[size=sm]:rounded-[min(var(--radius-md),10px)] data-vertical:flex-col data-vertical:items-stretch", "group/toggle-group flex w-fit flex-row items-center gap-[--spacing(var(--gap))] rounded-md data-[spacing=0]:data-[variant=outline]:shadow-xs data-vertical:flex-col data-vertical:items-stretch",
className className
)} )}
{...props} {...props}
@ -51,7 +50,7 @@ function ToggleGroup({
> >
{children} {children}
</ToggleGroupContext.Provider> </ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root> </ToggleGroupPrimitive>
) )
} }
@ -61,18 +60,17 @@ function ToggleGroupItem({
variant = "default", variant = "default",
size = "default", size = "default",
...props ...props
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> & }: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
VariantProps<typeof toggleVariants>) {
const context = React.useContext(ToggleGroupContext) const context = React.useContext(ToggleGroupContext)
return ( return (
<ToggleGroupPrimitive.Item <TogglePrimitive
data-slot="toggle-group-item" data-slot="toggle-group-item"
data-variant={context.variant || variant} data-variant={context.variant || variant}
data-size={context.size || size} data-size={context.size || size}
data-spacing={context.spacing} data-spacing={context.spacing}
className={cn( className={cn(
"shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-lg group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-lg group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-lg group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-lg group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", "shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:px-2 group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-end]:pr-1.5 group-data-[spacing=0]/toggle-group:has-data-[icon=inline-start]:pl-1.5 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-l-md group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-t-md group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-r-md group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-b-md data-[state=on]:bg-muted group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t",
toggleVariants({ toggleVariants({
variant: context.variant || variant, variant: context.variant || variant,
size: context.size || size, size: context.size || size,
@ -82,7 +80,7 @@ function ToggleGroupItem({
{...props} {...props}
> >
{children} {children}
</ToggleGroupPrimitive.Item> </TogglePrimitive>
) )
} }

View File

@ -1,21 +1,23 @@
import * as React from "react" "use client"
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle"
import { cva, type VariantProps } from "class-variance-authority" import { cva, type VariantProps } from "class-variance-authority"
import { Toggle as TogglePrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
const toggleVariants = cva( const toggleVariants = cva(
"group/toggle inline-flex items-center justify-center gap-1 rounded-lg text-sm font-medium whitespace-nowrap transition-all outline-none hover:bg-muted hover:text-foreground 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 aria-pressed:bg-muted data-[state=on]:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "group/toggle inline-flex items-center justify-center gap-1 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none hover:bg-muted hover:text-foreground 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 aria-pressed:bg-muted dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{ {
variants: { variants: {
variant: { variant: {
default: "bg-transparent", default: "bg-transparent",
outline: "border border-input bg-transparent hover:bg-muted", outline: "border border-input bg-transparent shadow-xs hover:bg-muted",
}, },
size: { size: {
default: "h-8 min-w-8 px-2", default:
sm: "h-7 min-w-7 rounded-[min(var(--radius-md),12px)] px-1.5 text-[0.8rem]", "h-9 min-w-9 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
lg: "h-9 min-w-9 px-2.5", sm: "h-8 min-w-8 px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
lg: "h-10 min-w-10 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
}, },
}, },
defaultVariants: { defaultVariants: {
@ -30,10 +32,9 @@ function Toggle({
variant = "default", variant = "default",
size = "default", size = "default",
...props ...props
}: React.ComponentProps<typeof TogglePrimitive.Root> & }: TogglePrimitive.Props & VariantProps<typeof toggleVariants>) {
VariantProps<typeof toggleVariants>) {
return ( return (
<TogglePrimitive.Root <TogglePrimitive
data-slot="toggle" data-slot="toggle"
className={cn(toggleVariants({ variant, size, className }))} className={cn(toggleVariants({ variant, size, className }))}
{...props} {...props}

View File

@ -1,55 +1,64 @@
import * as React from "react" import { Tooltip as TooltipPrimitive } from "@base-ui/react/tooltip"
import { Tooltip as TooltipPrimitive } from "radix-ui"
import { cn } from "@repo/shadcn-ui/lib/utils" import { cn } from "@repo/shadcn-ui/lib/utils"
function TooltipProvider({ function TooltipProvider({
delayDuration = 0, delay = 0,
...props ...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) { }: TooltipPrimitive.Provider.Props) {
return ( return (
<TooltipPrimitive.Provider <TooltipPrimitive.Provider
data-slot="tooltip-provider" data-slot="tooltip-provider"
delayDuration={delayDuration} delay={delay}
{...props} {...props}
/> />
) )
} }
function Tooltip({ function Tooltip({ ...props }: TooltipPrimitive.Root.Props) {
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} /> return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
} }
function TooltipTrigger({ function TooltipTrigger({ ...props }: TooltipPrimitive.Trigger.Props) {
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} /> return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
} }
function TooltipContent({ function TooltipContent({
className, className,
sideOffset = 0, side = "top",
sideOffset = 4,
align = "center",
alignOffset = 0,
children, children,
...props ...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) { }: TooltipPrimitive.Popup.Props &
Pick<
TooltipPrimitive.Positioner.Props,
"align" | "alignOffset" | "side" | "sideOffset"
>) {
return ( return (
<TooltipPrimitive.Portal> <TooltipPrimitive.Portal>
<TooltipPrimitive.Content <TooltipPrimitive.Positioner
data-slot="tooltip-content" align={align}
alignOffset={alignOffset}
side={side}
sideOffset={sideOffset} sideOffset={sideOffset}
className="isolate z-50"
>
<TooltipPrimitive.Popup
data-slot="tooltip-content"
className={cn( className={cn(
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}
> >
{children} {children}
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" /> <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
</TooltipPrimitive.Content> </TooltipPrimitive.Popup>
</TooltipPrimitive.Positioner>
</TooltipPrimitive.Portal> </TooltipPrimitive.Portal>
) )
} }
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }

View File

@ -0,0 +1,19 @@
import * as React from "react"
const MOBILE_BREAKPOINT = 768
export function useIsMobile() {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(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
}

View File

@ -1,10 +1,112 @@
@import "tailwindcss"; @import "tailwindcss";
@plugin "tailwindcss-animate";
@import "tw-animate-css"; @import "tw-animate-css";
@import "shadcn/tailwind.css";
@import "@fontsource-variable/geist"; @import "@fontsource-variable/geist";
@import "@fontsource-variable/geist-mono"; @import "@fontsource-variable/geist-mono";
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));
@source "../../../../apps/**/*.{ts,tsx}";
@source "../../../../modules/**/*.{ts,tsx}";
@source "../../../../packages/**/*.{ts,tsx}";
@source "../components/**/*.{ts,tsx}";
@source "../**/*.{ts,tsx}";
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
--font-sans: "Geist Variable", sans-serif;
--font-serif: "Geist Variable", serif;
--font-mono: "Geist Mono Variable", monospace;
--radius: 0.625rem;
--shadow-2xs: 0 1px 3px 0px oklch(0 0 0 / 0.05);
--shadow-xs: 0 1px 3px 0px oklch(0 0 0 / 0.05);
--shadow-sm: 0 1px 3px 0px oklch(0 0 0 / 0.1), 0 1px 2px -1px oklch(0 0 0 / 0.1);
--shadow: 0 1px 3px 0px oklch(0 0 0 / 0.1), 0 1px 2px -1px oklch(0 0 0 / 0.1);
--shadow-md: 0 1px 3px 0px oklch(0 0 0 / 0.1), 0 2px 4px -1px oklch(0 0 0 / 0.1);
--shadow-lg: 0 1px 3px 0px oklch(0 0 0 / 0.1), 0 4px 6px -1px oklch(0 0 0 / 0.1);
--shadow-xl: 0 1px 3px 0px oklch(0 0 0 / 0.1), 0 8px 10px -1px oklch(0 0 0 / 0.1);
--shadow-2xl: 0 1px 3px 0px oklch(0 0 0 / 0.25);
--spacing: 0.24rem;
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
--shadow-2xs: 1px 1px 6px 0px oklch(0 0 0 / 0.05);
--shadow-xs: 1px 1px 6px 0px oklch(0 0 0 / 0.05);
--shadow-sm: 1px 1px 6px 0px oklch(0 0 0 / 0.1), 1px 1px 2px -1px oklch(0 0 0 / 0.1);
--shadow: 1px 1px 6px 0px oklch(0 0 0 / 0.1), 1px 1px 2px -1px oklch(0 0 0 / 0.1);
--shadow-md: 1px 1px 6px 0px oklch(0 0 0 / 0.1), 1px 2px 4px -1px oklch(0 0 0 / 0.1);
--shadow-lg: 1px 1px 6px 0px oklch(0 0 0 / 0.1), 1px 4px 6px -1px oklch(0 0 0 / 0.1);
--shadow-xl: 1px 1px 6px 0px oklch(0 0 0 / 0.1), 1px 8px 10px -1px oklch(0 0 0 / 0.1);
--shadow-2xl: 1px 1px 6px 0px oklch(0 0 0 / 0.25);
}
@theme inline { @theme inline {
--color-background: var(--background); --color-background: var(--background);
@ -22,7 +124,6 @@
--color-accent: var(--accent); --color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground); --color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive); --color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border); --color-border: var(--border);
--color-input: var(--input); --color-input: var(--input);
--color-ring: var(--ring); --color-ring: var(--ring);
@ -31,7 +132,6 @@
--color-chart-3: var(--chart-3); --color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4); --color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5); --color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar); --color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground); --color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary); --color-sidebar-primary: var(--sidebar-primary);
@ -45,13 +145,10 @@
--font-mono: var(--font-mono); --font-mono: var(--font-mono);
--font-serif: var(--font-serif); --font-serif: var(--font-serif);
--radius-sm: calc(var(--radius) * 0.6); --radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) * 0.8); --radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius); --radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4); --radius-xl: calc(var(--radius) + 4px);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
--shadow-2xs: var(--shadow-2xs); --shadow-2xs: var(--shadow-2xs);
--shadow-xs: var(--shadow-xs); --shadow-xs: var(--shadow-xs);
@ -61,174 +158,21 @@
--shadow-lg: var(--shadow-lg); --shadow-lg: var(--shadow-lg);
--shadow-xl: var(--shadow-xl); --shadow-xl: var(--shadow-xl);
--shadow-2xl: var(--shadow-2xl); --shadow-2xl: var(--shadow-2xl);
--radius-2xl: calc(var(--radius) * 1.8);
@keyframes accordion-down { --radius-3xl: calc(var(--radius) * 2.2);
from { --radius-4xl: calc(var(--radius) * 2.6);
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
}
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
button:not(:disabled),
[role="button"]:not(:disabled) {
cursor: pointer;
}
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
@layer utilities {
body {
font-family: var(--font-sans);
font-variant-ligatures: none;
}
}
/**
*
* https://tweakcn.com/
* https://themux.vercel.app/shadcn-themes
* https://shadcnstudio.com/theme-generator
*
**/
:root {
--font-sans: "Geist Variable", ui-sans-serif, sans-serif, system-ui;
--font-mono: "Geist Mono Variable", ui-monospace, monospace;
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.575 0.1533 256.4357);
--primary-foreground: oklch(1 0 0);
--secondary: oklch(0.6427 0.1407 253.94);
--secondary-foreground: oklch(0.9543 0.0166 250.8425);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.6427 0.1407 253.94);
--accent-foreground: oklch(0.97 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.822 0 0);
--ring: oklch(0.575 0.1533 256.4357); /* oklch(0.708 0 0); */
--chart-1: oklch(0.809 0.105 251.813);
--chart-2: oklch(0.623 0.214 259.815);
--chart-3: oklch(0.546 0.245 262.881);
--chart-4: oklch(0.488 0.243 264.376);
--chart-5: oklch(0.424 0.199 265.638);
--sidebar: oklch(0.575 0.1533 256.4357);
--sidebar-foreground: oklch(1 0 0);
--sidebar-primary: oklch(1 0 0);
--sidebar-primary-foreground: oklch(0.575 0.1533 256.4357);
--sidebar-accent: oklch(0.6427 0.1407 253.94);
--sidebar-accent-foreground: oklch(1 0 0);
--sidebar-border: oklch(0.6427 0.1407 253.94);
--sidebar-ring: oklch(1 0 0);
--radius: 0.625rem;
--shadow-x: 0;
--shadow-y: 1px;
--shadow-blur: 3px;
--shadow-spread: 0px;
--shadow-opacity: 0.1;
--shadow-color: oklch(0 0 0);
--shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
--shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 1px 2px -1px hsl(0 0% 0% / 0.1);
--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 2px 4px -1px hsl(0 0% 0% / 0.1);
--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 4px 6px -1px hsl(0 0% 0% / 0.1);
--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.1), 0 8px 10px -1px hsl(0 0% 0% / 0.1);
--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);
--tracking-normal: 0em;
}
.dark {
--background: oklch(0.1448 0 0);
--foreground: oklch(0.9851 0 0);
--card: oklch(0.2046 0 0);
--card-foreground: oklch(0.9851 0 0);
--popover: oklch(0.2046 0 0);
--popover-foreground: oklch(0.9851 0 0);
--primary: oklch(0.5471 0.2506 262.8726);
--primary-foreground: oklch(0.9705 0.0142 254.6042);
--secondary: oklch(0.2707 0.0092 285.7705);
--secondary-foreground: oklch(0.9851 0 0);
--muted: oklch(0.2686 0 0);
--muted-foreground: oklch(0.709 0 0);
--accent: oklch(0.3729 0.0306 259.7328);
--accent-foreground: oklch(0.8717 0.0093 258.3382);
--destructive: oklch(0.7022 0.1892 22.2279);
--destructive-foreground: oklch(0.2558 0.0412 235.1561);
--border: oklch(1 0 0);
--input: oklch(1 0 0);
--ring: oklch(0.4915 0.2776 263.8724);
--chart-1: oklch(0.4915 0.2776 263.8724);
--chart-2: oklch(0.7019 0.1577 160.4375);
--chart-3: oklch(0.7724 0.1728 65.367);
--chart-4: oklch(0.6217 0.2589 305.309);
--chart-5: oklch(0.6435 0.2452 16.501);
--sidebar: oklch(0.2046 0 0);
--sidebar-foreground: oklch(0.9851 0 0);
--sidebar-primary: oklch(0.5471 0.2506 262.8726);
--sidebar-primary-foreground: oklch(0.9705 0.0142 254.6042);
--sidebar-accent: oklch(0.2686 0 0);
--sidebar-accent-foreground: oklch(0.9851 0 0);
--sidebar-border: oklch(1 0 0);
--sidebar-ring: oklch(0.4915 0.2776 263.8724);
--radius: 0.625rem;
--shadow-2xs: 1px 1px 6px 0px hsl(0 0% 0% / 0.05);
--shadow-xs: 1px 1px 6px 0px hsl(0 0% 0% / 0.05);
--shadow-sm: 1px 1px 6px 0px hsl(0 0% 0% / 0.1), 1px 1px 2px -1px hsl(0 0% 0% / 0.1);
--shadow: 1px 1px 6px 0px hsl(0 0% 0% / 0.1), 1px 1px 2px -1px hsl(0 0% 0% / 0.1);
--shadow-md: 1px 1px 6px 0px hsl(0 0% 0% / 0.1), 1px 2px 4px -1px hsl(0 0% 0% / 0.1);
--shadow-lg: 1px 1px 6px 0px hsl(0 0% 0% / 0.1), 1px 4px 6px -1px hsl(0 0% 0% / 0.1);
--shadow-xl: 1px 1px 6px 0px hsl(0 0% 0% / 0.1), 1px 8px 10px -1px hsl(0 0% 0% / 0.1);
--shadow-2xl: 1px 1px 6px 0px hsl(0 0% 0% / 0.25);
} }
@layer base { @layer base {
* { * {
@apply border-border outline-ring/50; @apply border-border outline-ring/50;
@apply transition-colors duration-300; /* Added transition for smooth color changes */
} }
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
input { button:not(:disabled),
@apply bg-input; [role="button"]:not(:disabled) {
cursor: pointer;
} }
} }
@source "../components";

View File

@ -7,6 +7,10 @@ const config = {
"../../packages/shadcn-ui/src/**/*.{ts,tsx}", "../../packages/shadcn-ui/src/**/*.{ts,tsx}",
"../../modules/**/*.{ts,tsx}", "../../modules/**/*.{ts,tsx}",
], ],
theme: {
extend: {},
},
plugins: [],
}; };
export default config; export default config;

View File

@ -4,7 +4,6 @@
"module": "node16", "module": "node16",
"target": "esnext", "target": "esnext",
"moduleResolution": "node16", "moduleResolution": "node16",
"baseUrl": ".",
"paths": { "paths": {
"@repo/shadcn-ui/*": ["./src/*"] "@repo/shadcn-ui/*": ["./src/*"]
} }

View File

@ -1,2 +1,3 @@
#/bin/sh #/bin/sh
for file in src/components/*.tsx; do pnpm dlx shadcn@latest add -y -o $(basename "$file" .tsx); done #for file in src/components/*.tsx; do pnpm dlx shadcn@latest add -y -o $(basename "$file" .tsx); done
pnpm dlx shadcn@latest add --all --overwrite