diff options
Diffstat (limited to 'packages')
20 files changed, 280 insertions, 408 deletions
diff --git a/packages/ui-new/components.json b/packages/ui-new/components.json index 2b0833f..f9d4fcd 100644 --- a/packages/ui-new/components.json +++ b/packages/ui-new/components.json @@ -1,6 +1,6 @@ { "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", + "style": "base-lyra", "rsc": false, "tsx": true, "tailwind": { @@ -11,6 +11,7 @@ "prefix": "" }, "iconLibrary": "lucide", + "rtl": false, "aliases": { "components": "@/components", "utils": "@/lib/utils", diff --git a/packages/ui-new/package.json b/packages/ui-new/package.json index 706c12b..fcd6aed 100644 --- a/packages/ui-new/package.json +++ b/packages/ui-new/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@base-ui/react": "^1.2.0", "@radix-ui/react-checkbox": "^1.3.3", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-label": "^2.1.8", @@ -28,11 +29,12 @@ "lucide-react": "^0.562.0", "marked": "^17.0.1", "next-themes": "^0.4.6", + "radix-ui": "^1.4.3", "react": "^19.2.0", "react-dom": "^19.2.0", "react-router": "^7.12.0", "sonner": "^2.0.7", - "tailwind-merge": "^3.4.0", + "tailwind-merge": "^3.4.1", "zustand": "^5.0.10" }, "devDependencies": { @@ -42,6 +44,7 @@ "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.1", "globals": "^16.5.0", + "shadcn": "^3.8.5", "tailwindcss": "^4.1.18", "tw-animate-css": "^1.4.0", "typescript": "~5.9.3", diff --git a/packages/ui-new/src/components/download-monitor.tsx b/packages/ui-new/src/components/download-monitor.tsx index d67e173..f3902d9 100644 --- a/packages/ui-new/src/components/download-monitor.tsx +++ b/packages/ui-new/src/components/download-monitor.tsx @@ -15,6 +15,7 @@ export function DownloadMonitor() { <span className="text-sm font-medium text-white">Downloads</span> </div> <button + type="button" onClick={() => setIsVisible(false)} className="text-zinc-400 hover:text-white transition-colors p-1" > diff --git a/packages/ui-new/src/components/ui/badge.tsx b/packages/ui-new/src/components/ui/badge.tsx index ccfa4e7..425ab9e 100644 --- a/packages/ui-new/src/components/ui/badge.tsx +++ b/packages/ui-new/src/components/ui/badge.tsx @@ -1,22 +1,24 @@ -import { Slot } from "@radix-ui/react-slot"; +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 type * as React from "react"; import { cn } from "@/lib/utils"; const badgeVariants = cva( - "inline-flex items-center justify-center rounded-full border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + "h-5 gap-1 rounded-none border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! inline-flex items-center justify-center w-fit whitespace-nowrap shrink-0 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive overflow-hidden group/badge", { variants: { variant: { - default: - "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", secondary: - "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80", destructive: - "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20", outline: - "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground", + ghost: + "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50", + link: "text-primary underline-offset-4 hover:underline", }, }, defaultVariants: { @@ -27,20 +29,24 @@ const badgeVariants = cva( function Badge({ className, - variant, - asChild = false, + variant = "default", + render, ...props -}: React.ComponentProps<"span"> & - VariantProps<typeof badgeVariants> & { asChild?: boolean }) { - const Comp = asChild ? Slot : "span"; - - return ( - <Comp - data-slot="badge" - className={cn(badgeVariants({ variant }), className)} - {...props} - /> - ); +}: useRender.ComponentProps<"span"> & VariantProps<typeof badgeVariants>) { + return useRender({ + defaultTagName: "span", + props: mergeProps<"span">( + { + className: cn(badgeVariants({ variant }), className), + }, + props, + ), + render, + state: { + slot: "badge", + variant, + }, + }); } export { Badge, badgeVariants }; diff --git a/packages/ui-new/src/components/ui/button.tsx b/packages/ui-new/src/components/ui/button.tsx index be181b0..7dee494 100644 --- a/packages/ui-new/src/components/ui/button.tsx +++ b/packages/ui-new/src/components/ui/button.tsx @@ -1,32 +1,34 @@ -import { Slot } from "@radix-ui/react-slot"; +import { Button as ButtonPrimitive } from "@base-ui/react/button"; import { cva, type VariantProps } from "class-variance-authority"; -import type * as React from "react"; import { cn } from "@/lib/utils"; const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none", { variants: { variant: { - default: "bg-primary text-primary-foreground hover:bg-primary/90", - destructive: - "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80", outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground", secondary: - "bg-secondary text-secondary-foreground hover:bg-secondary/80", + "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground", ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground", + destructive: + "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30", link: "text-primary underline-offset-4 hover:underline", }, size: { - default: "h-9 px-4 py-2 has-[>svg]:px-3", - sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 rounded-md px-6 has-[>svg]:px-4", - icon: "size-9", - "icon-sm": "size-8", - "icon-lg": "size-10", + default: + "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", + xs: "h-6 gap-1 rounded-none px-2 text-xs 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-none px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5", + lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-3 has-data-[icon=inline-start]:pl-3", + icon: "size-8", + "icon-xs": "size-6 rounded-none [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-7 rounded-none", + "icon-lg": "size-9", }, }, defaultVariants: { @@ -40,19 +42,11 @@ function Button({ className, variant = "default", size = "default", - asChild = false, ...props -}: React.ComponentProps<"button"> & - VariantProps<typeof buttonVariants> & { - asChild?: boolean; - }) { - const Comp = asChild ? Slot : "button"; - +}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) { return ( - <Comp + <ButtonPrimitive data-slot="button" - data-variant={variant} - data-size={size} className={cn(buttonVariants({ variant, size, className }))} {...props} /> diff --git a/packages/ui-new/src/components/ui/card.tsx b/packages/ui-new/src/components/ui/card.tsx index cc1ff8a..b7084a0 100644 --- a/packages/ui-new/src/components/ui/card.tsx +++ b/packages/ui-new/src/components/ui/card.tsx @@ -2,12 +2,17 @@ import type * as React from "react"; import { cn } from "@/lib/utils"; -function Card({ className, ...props }: React.ComponentProps<"div">) { +function Card({ + className, + size = "default", + ...props +}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) { return ( <div data-slot="card" + data-size={size} className={cn( - "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", + "ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-none py-4 text-xs/relaxed ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-2 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-none *:[img:last-child]:rounded-none group/card flex flex-col", className, )} {...props} @@ -20,7 +25,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) { <div data-slot="card-header" className={cn( - "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6", + "gap-1 rounded-none px-4 group-data-[size=sm]/card:px-3 [.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 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]", className, )} {...props} @@ -32,7 +37,10 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-title" - className={cn("leading-none font-semibold", className)} + className={cn( + "text-sm font-medium group-data-[size=sm]/card:text-sm", + className, + )} {...props} /> ); @@ -42,7 +50,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-description" - className={cn("text-muted-foreground text-sm", className)} + className={cn("text-muted-foreground text-xs/relaxed", className)} {...props} /> ); @@ -65,7 +73,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-content" - className={cn("px-6", className)} + className={cn("px-4 group-data-[size=sm]/card:px-3", className)} {...props} /> ); @@ -75,7 +83,10 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="card-footer" - className={cn("flex items-center px-6 [.border-t]:pt-6", className)} + className={cn( + "rounded-none border-t p-4 group-data-[size=sm]/card:p-3 flex items-center", + className, + )} {...props} /> ); diff --git a/packages/ui-new/src/components/ui/checkbox.tsx b/packages/ui-new/src/components/ui/checkbox.tsx index e771797..9f22cea 100644 --- a/packages/ui-new/src/components/ui/checkbox.tsx +++ b/packages/ui-new/src/components/ui/checkbox.tsx @@ -1,29 +1,24 @@ "use client"; -import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; +import { Checkbox as CheckboxPrimitive } from "@base-ui/react/checkbox"; import { CheckIcon } from "lucide-react"; -import type * as React from "react"; - import { cn } from "@/lib/utils"; -function Checkbox({ - className, - ...props -}: React.ComponentProps<typeof CheckboxPrimitive.Root>) { +function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) { return ( <CheckboxPrimitive.Root data-slot="checkbox" className={cn( - "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", + "border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-none border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-1 aria-invalid:ring-1 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50", className, )} {...props} > <CheckboxPrimitive.Indicator data-slot="checkbox-indicator" - className="grid place-content-center text-current transition-none" + className="[&>svg]:size-3.5 grid place-content-center text-current transition-none" > - <CheckIcon className="size-3.5" /> + <CheckIcon /> </CheckboxPrimitive.Indicator> </CheckboxPrimitive.Root> ); diff --git a/packages/ui-new/src/components/ui/dialog.tsx b/packages/ui-new/src/components/ui/dialog.tsx index fc2261a..033b47c 100644 --- a/packages/ui-new/src/components/ui/dialog.tsx +++ b/packages/ui-new/src/components/ui/dialog.tsx @@ -1,42 +1,36 @@ -import * as DialogPrimitive from "@radix-ui/react-dialog"; +"use client"; + +import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"; import { XIcon } from "lucide-react"; import type * as React from "react"; - +import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; -function Dialog({ - ...props -}: React.ComponentProps<typeof DialogPrimitive.Root>) { +function Dialog({ ...props }: DialogPrimitive.Root.Props) { return <DialogPrimitive.Root data-slot="dialog" {...props} />; } -function DialogTrigger({ - ...props -}: React.ComponentProps<typeof DialogPrimitive.Trigger>) { +function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) { return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />; } -function DialogPortal({ - ...props -}: React.ComponentProps<typeof DialogPrimitive.Portal>) { +function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) { return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />; } -function DialogClose({ - ...props -}: React.ComponentProps<typeof DialogPrimitive.Close>) { +function DialogClose({ ...props }: DialogPrimitive.Close.Props) { return <DialogPrimitive.Close data-slot="dialog-close" {...props} />; } function DialogOverlay({ className, ...props -}: React.ComponentProps<typeof DialogPrimitive.Overlay>) { +}: DialogPrimitive.Backdrop.Props) { return ( - <DialogPrimitive.Overlay + <DialogPrimitive.Backdrop data-slot="dialog-overlay" className={cn( - "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50", + "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 isolate z-50", className, )} {...props} @@ -49,16 +43,16 @@ function DialogContent({ children, showCloseButton = true, ...props -}: React.ComponentProps<typeof DialogPrimitive.Content> & { +}: DialogPrimitive.Popup.Props & { showCloseButton?: boolean; }) { return ( - <DialogPortal data-slot="dialog-portal"> + <DialogPortal> <DialogOverlay /> - <DialogPrimitive.Content + <DialogPrimitive.Popup data-slot="dialog-content" className={cn( - "bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg", + "bg-background data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 ring-foreground/10 grid max-w-[calc(100%-2rem)] gap-4 rounded-none p-4 text-xs/relaxed ring-1 duration-100 sm:max-w-sm fixed top-1/2 left-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 outline-none", className, )} {...props} @@ -67,13 +61,19 @@ function DialogContent({ {showCloseButton && ( <DialogPrimitive.Close data-slot="dialog-close" - className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4" + render={ + <Button + variant="ghost" + className="absolute top-2 right-2" + size="icon-sm" + /> + } > <XIcon /> <span className="sr-only">Close</span> </DialogPrimitive.Close> )} - </DialogPrimitive.Content> + </DialogPrimitive.Popup> </DialogPortal> ); } @@ -82,13 +82,20 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { return ( <div data-slot="dialog-header" - className={cn("flex flex-col gap-2 text-center sm:text-left", className)} + className={cn("gap-1 text-left flex flex-col", className)} {...props} /> ); } -function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { +function DialogFooter({ + className, + showCloseButton = false, + children, + ...props +}: React.ComponentProps<"div"> & { + showCloseButton?: boolean; +}) { return ( <div data-slot="dialog-footer" @@ -97,18 +104,22 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { className, )} {...props} - /> + > + {children} + {showCloseButton && ( + <DialogPrimitive.Close render={<Button variant="outline" />}> + Close + </DialogPrimitive.Close> + )} + </div> ); } -function DialogTitle({ - className, - ...props -}: React.ComponentProps<typeof DialogPrimitive.Title>) { +function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) { return ( <DialogPrimitive.Title data-slot="dialog-title" - className={cn("text-lg leading-none font-semibold", className)} + className={cn("text-sm font-medium", className)} {...props} /> ); @@ -117,11 +128,14 @@ function DialogTitle({ function DialogDescription({ className, ...props -}: React.ComponentProps<typeof DialogPrimitive.Description>) { +}: DialogPrimitive.Description.Props) { return ( <DialogPrimitive.Description data-slot="dialog-description" - className={cn("text-muted-foreground text-sm", className)} + className={cn( + "text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed *:[a]:underline *:[a]:underline-offset-3", + className, + )} {...props} /> ); diff --git a/packages/ui-new/src/components/ui/input.tsx b/packages/ui-new/src/components/ui/input.tsx index 73ea867..bb0390a 100644 --- a/packages/ui-new/src/components/ui/input.tsx +++ b/packages/ui-new/src/components/ui/input.tsx @@ -1,16 +1,15 @@ +import { Input as InputPrimitive } from "@base-ui/react/input"; import type * as React from "react"; import { cn } from "@/lib/utils"; function Input({ className, type, ...props }: React.ComponentProps<"input">) { return ( - <input + <InputPrimitive type={type} data-slot="input" className={cn( - "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 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 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", - "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", - "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + "dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-none border bg-transparent px-2.5 py-1 text-xs transition-colors file:h-6 file:text-xs file:font-medium focus-visible:ring-1 aria-invalid:ring-1 md:text-xs file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50", className, )} {...props} diff --git a/packages/ui-new/src/components/ui/label.tsx b/packages/ui-new/src/components/ui/label.tsx index a3661df..9a998c7 100644 --- a/packages/ui-new/src/components/ui/label.tsx +++ b/packages/ui-new/src/components/ui/label.tsx @@ -1,19 +1,14 @@ -"use client"; - -import * as LabelPrimitive from "@radix-ui/react-label"; import type * as React from "react"; import { cn } from "@/lib/utils"; -function Label({ - className, - ...props -}: React.ComponentProps<typeof LabelPrimitive.Root>) { +function Label({ className, ...props }: React.ComponentProps<"label">) { return ( - <LabelPrimitive.Root + // biome-ignore lint/a11y/noLabelWithoutControl: shadcn component + <label data-slot="label" 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", + "gap-2 text-xs leading-none group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed", className, )} {...props} diff --git a/packages/ui-new/src/components/ui/scroll-area.tsx b/packages/ui-new/src/components/ui/scroll-area.tsx index da6b2e2..4a68eb2 100644 --- a/packages/ui-new/src/components/ui/scroll-area.tsx +++ b/packages/ui-new/src/components/ui/scroll-area.tsx @@ -1,13 +1,13 @@ -import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"; -import type * as React from "react"; +"use client"; +import { ScrollArea as ScrollAreaPrimitive } from "@base-ui/react/scroll-area"; import { cn } from "@/lib/utils"; function ScrollArea({ className, children, ...props -}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) { +}: ScrollAreaPrimitive.Root.Props) { return ( <ScrollAreaPrimitive.Root data-slot="scroll-area" @@ -30,26 +30,23 @@ function ScrollBar({ className, orientation = "vertical", ...props -}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) { +}: ScrollAreaPrimitive.Scrollbar.Props) { return ( - <ScrollAreaPrimitive.ScrollAreaScrollbar + <ScrollAreaPrimitive.Scrollbar data-slot="scroll-area-scrollbar" + data-orientation={orientation} orientation={orientation} className={cn( - "flex touch-none p-px transition-colors select-none", - orientation === "vertical" && - "h-full w-2.5 border-l border-l-transparent", - orientation === "horizontal" && - "h-2.5 flex-col border-t border-t-transparent", + "data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none", className, )} {...props} > - <ScrollAreaPrimitive.ScrollAreaThumb + <ScrollAreaPrimitive.Thumb data-slot="scroll-area-thumb" - className="bg-border relative flex-1 rounded-full" + className="rounded-none bg-border relative flex-1" /> - </ScrollAreaPrimitive.ScrollAreaScrollbar> + </ScrollAreaPrimitive.Scrollbar> ); } diff --git a/packages/ui-new/src/components/ui/select.tsx b/packages/ui-new/src/components/ui/select.tsx index c611948..210adba 100644 --- a/packages/ui-new/src/components/ui/select.tsx +++ b/packages/ui-new/src/components/ui/select.tsx @@ -1,25 +1,28 @@ -import * as SelectPrimitive from "@radix-ui/react-select"; +import { Select as SelectPrimitive } from "@base-ui/react/select"; import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"; import type * as React from "react"; - import { cn } from "@/lib/utils"; -function Select({ - ...props -}: React.ComponentProps<typeof SelectPrimitive.Root>) { - return <SelectPrimitive.Root data-slot="select" {...props} />; -} +const Select = SelectPrimitive.Root; -function SelectGroup({ - ...props -}: React.ComponentProps<typeof SelectPrimitive.Group>) { - return <SelectPrimitive.Group data-slot="select-group" {...props} />; +function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) { + return ( + <SelectPrimitive.Group + data-slot="select-group" + className={cn("scroll-my-1", className)} + {...props} + /> + ); } -function SelectValue({ - ...props -}: React.ComponentProps<typeof SelectPrimitive.Value>) { - return <SelectPrimitive.Value data-slot="select-value" {...props} />; +function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) { + return ( + <SelectPrimitive.Value + data-slot="select-value" + className={cn("flex flex-1 text-left", className)} + {...props} + /> + ); } function SelectTrigger({ @@ -27,7 +30,7 @@ function SelectTrigger({ size = "default", children, ...props -}: React.ComponentProps<typeof SelectPrimitive.Trigger> & { +}: SelectPrimitive.Trigger.Props & { size?: "sm" | "default"; }) { return ( @@ -35,15 +38,17 @@ function SelectTrigger({ data-slot="select-trigger" data-size={size} className={cn( - "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 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-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "border-input data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 gap-1.5 rounded-none border bg-transparent py-2 pr-2 pl-2.5 text-xs transition-colors select-none focus-visible:ring-1 aria-invalid:ring-1 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-none *:data-[slot=select-value]:gap-1.5 [&_svg:not([class*='size-'])]:size-4 flex w-fit items-center justify-between whitespace-nowrap outline-none disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center [&_svg]:pointer-events-none [&_svg]:shrink-0", className, )} {...props} > {children} - <SelectPrimitive.Icon asChild> - <ChevronDownIcon className="size-4 opacity-50" /> - </SelectPrimitive.Icon> + <SelectPrimitive.Icon + render={ + <ChevronDownIcon className="text-muted-foreground size-4 pointer-events-none" /> + } + /> </SelectPrimitive.Trigger> ); } @@ -51,36 +56,41 @@ function SelectTrigger({ function SelectContent({ className, children, - position = "item-aligned", + side = "bottom", + sideOffset = 4, align = "center", + alignOffset = 0, + alignItemWithTrigger = true, ...props -}: React.ComponentProps<typeof SelectPrimitive.Content>) { +}: SelectPrimitive.Popup.Props & + Pick< + SelectPrimitive.Positioner.Props, + "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger" + >) { return ( <SelectPrimitive.Portal> - <SelectPrimitive.Content - data-slot="select-content" - className={cn( - "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md", - 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} + <SelectPrimitive.Positioner + side={side} + sideOffset={sideOffset} align={align} - {...props} + alignOffset={alignOffset} + alignItemWithTrigger={alignItemWithTrigger} + className="isolate z-50" > - <SelectScrollUpButton /> - <SelectPrimitive.Viewport + <SelectPrimitive.Popup + data-slot="select-content" + data-align-trigger={alignItemWithTrigger} className={cn( - "p-1", - position === "popper" && - "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1", + "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 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 ring-foreground/10 min-w-36 rounded-none shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) origin-(--transform-origin) overflow-x-hidden overflow-y-auto data-[align-trigger=true]:animate-none", + className, )} + {...props} > - {children} - </SelectPrimitive.Viewport> - <SelectScrollDownButton /> - </SelectPrimitive.Content> + <SelectScrollUpButton /> + <SelectPrimitive.List>{children}</SelectPrimitive.List> + <SelectScrollDownButton /> + </SelectPrimitive.Popup> + </SelectPrimitive.Positioner> </SelectPrimitive.Portal> ); } @@ -88,11 +98,11 @@ function SelectContent({ function SelectLabel({ className, ...props -}: React.ComponentProps<typeof SelectPrimitive.Label>) { +}: SelectPrimitive.GroupLabel.Props) { return ( - <SelectPrimitive.Label + <SelectPrimitive.GroupLabel data-slot="select-label" - className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)} + className={cn("text-muted-foreground px-2 py-2 text-xs", className)} {...props} /> ); @@ -102,25 +112,26 @@ function SelectItem({ className, children, ...props -}: React.ComponentProps<typeof SelectPrimitive.Item>) { +}: SelectPrimitive.Item.Props) { return ( <SelectPrimitive.Item data-slot="select-item" className={cn( - "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground 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-[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", + "focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-none py-2 pr-8 pl-2 text-xs [&_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 outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", className, )} {...props} > - <span - data-slot="select-item-indicator" - className="absolute right-2 flex size-3.5 items-center justify-center" + <SelectPrimitive.ItemText className="flex flex-1 gap-2 shrink-0 whitespace-nowrap"> + {children} + </SelectPrimitive.ItemText> + <SelectPrimitive.ItemIndicator + render={ + <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" /> + } > - <SelectPrimitive.ItemIndicator> - <CheckIcon className="size-4" /> - </SelectPrimitive.ItemIndicator> - </span> - <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText> + <CheckIcon className="pointer-events-none" /> + </SelectPrimitive.ItemIndicator> </SelectPrimitive.Item> ); } @@ -128,11 +139,11 @@ function SelectItem({ function SelectSeparator({ className, ...props -}: React.ComponentProps<typeof SelectPrimitive.Separator>) { +}: SelectPrimitive.Separator.Props) { return ( <SelectPrimitive.Separator data-slot="select-separator" - className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)} + className={cn("bg-border -mx-1 h-px pointer-events-none", className)} {...props} /> ); @@ -141,36 +152,36 @@ function SelectSeparator({ function SelectScrollUpButton({ className, ...props -}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) { +}: React.ComponentProps<typeof SelectPrimitive.ScrollUpArrow>) { return ( - <SelectPrimitive.ScrollUpButton + <SelectPrimitive.ScrollUpArrow data-slot="select-scroll-up-button" className={cn( - "flex cursor-default items-center justify-center py-1", + "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 top-0 w-full", className, )} {...props} > - <ChevronUpIcon className="size-4" /> - </SelectPrimitive.ScrollUpButton> + <ChevronUpIcon /> + </SelectPrimitive.ScrollUpArrow> ); } function SelectScrollDownButton({ className, ...props -}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) { +}: React.ComponentProps<typeof SelectPrimitive.ScrollDownArrow>) { return ( - <SelectPrimitive.ScrollDownButton + <SelectPrimitive.ScrollDownArrow data-slot="select-scroll-down-button" className={cn( - "flex cursor-default items-center justify-center py-1", + "bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4 bottom-0 w-full", className, )} {...props} > - <ChevronDownIcon className="size-4" /> - </SelectPrimitive.ScrollDownButton> + <ChevronDownIcon /> + </SelectPrimitive.ScrollDownArrow> ); } diff --git a/packages/ui-new/src/components/ui/separator.tsx b/packages/ui-new/src/components/ui/separator.tsx index 50733e0..e91a862 100644 --- a/packages/ui-new/src/components/ui/separator.tsx +++ b/packages/ui-new/src/components/ui/separator.tsx @@ -1,23 +1,20 @@ "use client"; -import * as SeparatorPrimitive from "@radix-ui/react-separator"; -import type * as React from "react"; +import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"; import { cn } from "@/lib/utils"; function Separator({ className, orientation = "horizontal", - decorative = true, ...props -}: React.ComponentProps<typeof SeparatorPrimitive.Root>) { +}: SeparatorPrimitive.Props) { return ( - <SeparatorPrimitive.Root + <SeparatorPrimitive data-slot="separator" - decorative={decorative} orientation={orientation} className={cn( - "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px", + "bg-border shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch", className, )} {...props} diff --git a/packages/ui-new/src/components/ui/sonner.tsx b/packages/ui-new/src/components/ui/sonner.tsx index c9cd094..d6e293d 100644 --- a/packages/ui-new/src/components/ui/sonner.tsx +++ b/packages/ui-new/src/components/ui/sonner.tsx @@ -30,6 +30,11 @@ const Toaster = ({ ...props }: ToasterProps) => { "--border-radius": "var(--radius)", } as React.CSSProperties } + toastOptions={{ + classNames: { + toast: "cn-toast", + }, + }} {...props} /> ); diff --git a/packages/ui-new/src/components/ui/switch.tsx b/packages/ui-new/src/components/ui/switch.tsx index 14b3b5b..fef14e3 100644 --- a/packages/ui-new/src/components/ui/switch.tsx +++ b/packages/ui-new/src/components/ui/switch.tsx @@ -1,26 +1,29 @@ -import * as SwitchPrimitive from "@radix-ui/react-switch"; -import type * as React from "react"; +"use client"; + +import { Switch as SwitchPrimitive } from "@base-ui/react/switch"; import { cn } from "@/lib/utils"; function Switch({ className, + size = "default", ...props -}: React.ComponentProps<typeof SwitchPrimitive.Root>) { +}: SwitchPrimitive.Root.Props & { + size?: "sm" | "default"; +}) { return ( <SwitchPrimitive.Root data-slot="switch" + data-size={size} className={cn( - "peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", + "data-checked:bg-primary data-unchecked:bg-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 dark:data-unchecked:bg-input/80 shrink-0 rounded-full border border-transparent focus-visible:ring-1 aria-invalid:ring-1 data-[size=default]:h-[18.4px] data-[size=default]:w-[32px] data-[size=sm]:h-[14px] data-[size=sm]:w-[24px] peer group/switch relative inline-flex items-center transition-all outline-none after:absolute after:-inset-x-3 after:-inset-y-2 data-disabled:cursor-not-allowed data-disabled:opacity-50", className, )} {...props} > <SwitchPrimitive.Thumb data-slot="switch-thumb" - className={cn( - "bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0", - )} + className="bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground rounded-full group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0 pointer-events-none block ring-0 transition-transform" /> </SwitchPrimitive.Root> ); diff --git a/packages/ui-new/src/components/ui/tabs.tsx b/packages/ui-new/src/components/ui/tabs.tsx index 2da77f2..6349f40 100644 --- a/packages/ui-new/src/components/ui/tabs.tsx +++ b/packages/ui-new/src/components/ui/tabs.tsx @@ -1,48 +1,65 @@ -"use client"; - -import * as TabsPrimitive from "@radix-ui/react-tabs"; -import type * as React from "react"; +import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"; +import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; function Tabs({ className, + orientation = "horizontal", ...props -}: React.ComponentProps<typeof TabsPrimitive.Root>) { +}: TabsPrimitive.Root.Props) { return ( <TabsPrimitive.Root data-slot="tabs" - className={cn("flex flex-col gap-2", className)} + data-orientation={orientation} + className={cn( + "gap-2 group/tabs flex data-horizontal:flex-col", + className, + )} {...props} /> ); } +const tabsListVariants = cva( + "rounded-none p-[3px] group-data-horizontal/tabs:h-8 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col", + { + variants: { + variant: { + default: "bg-muted", + line: "gap-1 bg-transparent", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + function TabsList({ className, + variant = "default", ...props -}: React.ComponentProps<typeof TabsPrimitive.List>) { +}: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) { return ( <TabsPrimitive.List data-slot="tabs-list" - className={cn( - "bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]", - className, - )} + data-variant={variant} + className={cn(tabsListVariants({ variant }), className)} {...props} /> ); } -function TabsTrigger({ - className, - ...props -}: React.ComponentProps<typeof TabsPrimitive.Trigger>) { +function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) { return ( - <TabsPrimitive.Trigger + <TabsPrimitive.Tab data-slot="tabs-trigger" className={cn( - "data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground 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 transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + "gap-1.5 rounded-none border border-transparent px-1.5 py-0.5 text-xs font-medium group-data-vertical/tabs:py-[calc(--spacing(1.25))] [&_svg:not([class*='size-'])]:size-4 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center whitespace-nowrap transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0", + "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 dark:data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 data-active:text-foreground", + "after:bg-foreground after:absolute 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", className, )} {...props} @@ -50,17 +67,14 @@ function TabsTrigger({ ); } -function TabsContent({ - className, - ...props -}: React.ComponentProps<typeof TabsPrimitive.Content>) { +function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) { return ( - <TabsPrimitive.Content + <TabsPrimitive.Panel data-slot="tabs-content" - className={cn("flex-1 outline-none", className)} + className={cn("text-xs/relaxed flex-1 outline-none", className)} {...props} /> ); } -export { Tabs, TabsList, TabsTrigger, TabsContent }; +export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }; diff --git a/packages/ui-new/src/components/ui/textarea.tsx b/packages/ui-new/src/components/ui/textarea.tsx index 4f6221b..3c3e5d0 100644 --- a/packages/ui-new/src/components/ui/textarea.tsx +++ b/packages/ui-new/src/components/ui/textarea.tsx @@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) { <textarea data-slot="textarea" className={cn( - "border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", + "border-input dark:bg-input/30 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 rounded-none border bg-transparent px-2.5 py-2 text-xs transition-colors focus-visible:ring-1 aria-invalid:ring-1 md:text-xs placeholder:text-muted-foreground flex field-sizing-content min-h-16 w-full outline-none disabled:cursor-not-allowed disabled:opacity-50", className, )} {...props} diff --git a/packages/ui-new/src/index.css b/packages/ui-new/src/index.css index 917b793..8803e5e 100644 --- a/packages/ui-new/src/index.css +++ b/packages/ui-new/src/index.css @@ -1,5 +1,6 @@ @import "tailwindcss"; @import "tw-animate-css"; +@import "shadcn/tailwind.css"; @custom-variant dark (&:is(.dark *)); @@ -122,179 +123,4 @@ body { @apply bg-background text-foreground; } - - ::selection { - @apply bg-indigo-500/30; - } - - /* Custom Scrollbar */ - * { - scrollbar-width: thin; - scrollbar-color: #3f3f46 transparent; - } - - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-track { - background: transparent; - } - - ::-webkit-scrollbar-thumb { - background-color: #3f3f46; - border-radius: 4px; - border: 2px solid transparent; - background-clip: content-box; - } - - ::-webkit-scrollbar-thumb:hover { - background-color: #52525b; - } - - ::-webkit-scrollbar-corner { - background: transparent; - } - - /* Input/Form Element Consistency */ - input[type="text"], - input[type="number"], - input[type="password"], - input[type="email"], - textarea { - background-color: rgba(0, 0, 0, 0.4); - border: 1px solid rgba(255, 255, 255, 0.1); - transition: - border-color 0.2s ease, - box-shadow 0.2s ease; - } - - input[type="text"]:focus, - input[type="number"]:focus, - input[type="password"]:focus, - input[type="email"]:focus, - textarea:focus { - border-color: rgba(99, 102, 241, 0.5); - box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.1); - outline: none; - } - - /* Number input - hide spinner */ - input[type="number"]::-webkit-outer-spin-button, - input[type="number"]::-webkit-inner-spin-button { - -webkit-appearance: none; - margin: 0; - } - - input[type="number"] { - appearance: textfield; - -moz-appearance: textfield; - } - - /* Checkbox Styling */ - input[type="checkbox"] { - appearance: none; - width: 16px; - height: 16px; - border: 1px solid #3f3f46; - border-radius: 4px; - background-color: #18181b; - cursor: pointer; - position: relative; - transition: all 0.15s ease; - } - - input[type="checkbox"]:hover { - border-color: #52525b; - } - - input[type="checkbox"]:checked { - background-color: #4f46e5; - border-color: #4f46e5; - } - - input[type="checkbox"]:checked::after { - content: ""; - position: absolute; - left: 5px; - top: 2px; - width: 4px; - height: 8px; - border: solid white; - border-width: 0 2px 2px 0; - transform: rotate(45deg); - } - - input[type="checkbox"]:focus { - outline: none; - box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.3); - } - - /* Custom Select/Dropdown Styles */ - select { - appearance: none; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%2371717a'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'%3E%3C/path%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 0.5rem center; - background-size: 1rem; - padding-right: 2rem; - } - - /* Option styling - works in WebView/Chromium */ - select option { - background-color: #18181b; - color: #e4e4e7; - padding: 12px 16px; - font-size: 13px; - border: none; - } - - select option:hover, - select option:focus { - background-color: #3730a3 !important; - color: white !important; - } - - select option:checked { - background: linear-gradient(0deg, #4f46e5 0%, #4f46e5 100%); - color: white; - font-weight: 500; - } - - select option:disabled { - color: #52525b; - background-color: #18181b; - } - - /* Optgroup styling */ - select optgroup { - background-color: #18181b; - color: #a1a1aa; - font-weight: 600; - font-size: 11px; - text-transform: uppercase; - letter-spacing: 0.05em; - padding: 8px 12px 4px; - } - - /* Select focus state */ - select:focus { - outline: none; - border-color: #6366f1; - box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2); - } - - /* Global body styles from App.svelte */ - body { - margin: 0; - padding: 0; - background: #000; - } - - /* Window Drag Region */ - .drag-region { - -webkit-app-region: drag; - app-region: drag; - } } diff --git a/packages/ui-new/src/pages/index.tsx b/packages/ui-new/src/pages/index.tsx index 180cf0c..2b3c2b2 100644 --- a/packages/ui-new/src/pages/index.tsx +++ b/packages/ui-new/src/pages/index.tsx @@ -70,7 +70,7 @@ export function IndexPage() { <img src={settingsStore.settings.customBackgroundPath} alt="Background" - className="absolute inset-0 w-full h-full object-cover transition-transform duration-[20s] ease-linear hover:scale-105" + className="absolute inset-0 w-full h-full object-cover transition-transform duration-[20s] ease-linear" onError={(e) => console.error("Failed to load main background:", e)} /> )} diff --git a/packages/ui-new/tsconfig.app.json b/packages/ui-new/tsconfig.app.json index ce9121a..54f0bdf 100644 --- a/packages/ui-new/tsconfig.app.json +++ b/packages/ui-new/tsconfig.app.json @@ -27,8 +27,8 @@ /* Paths */ "baseUrl": ".", "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } }, - "include": ["src"], + "include": ["src"] } |