mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
style: enhance UI components with improved styling and layout
- Updated global CSS to include new status colors for better visual feedback. - Refined button, badge, card, and input components with enhanced styles and transitions for a more polished user experience. - Adjusted sidebar and dialog components for improved aesthetics and usability. - Implemented gradient backgrounds and shadow effects across various sections to elevate the overall design. - Enhanced keyboard shortcuts and settings views with consistent styling and layout adjustments for better accessibility.
This commit is contained in:
@@ -4,21 +4,42 @@ import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const badgeVariants = cva(
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
||||
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 shadow-sm",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default:
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
||||
"border-transparent bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
secondary:
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
outline: "text-foreground",
|
||||
"border-transparent bg-destructive text-white hover:bg-destructive/90",
|
||||
outline:
|
||||
"text-foreground border-border bg-background/50 backdrop-blur-sm",
|
||||
// Semantic status variants using CSS variables
|
||||
success:
|
||||
"border-transparent bg-[var(--status-success-bg)] text-[var(--status-success)] border border-[var(--status-success)]/30",
|
||||
warning:
|
||||
"border-transparent bg-[var(--status-warning-bg)] text-[var(--status-warning)] border border-[var(--status-warning)]/30",
|
||||
error:
|
||||
"border-transparent bg-[var(--status-error-bg)] text-[var(--status-error)] border border-[var(--status-error)]/30",
|
||||
info:
|
||||
"border-transparent bg-[var(--status-info-bg)] text-[var(--status-info)] border border-[var(--status-info)]/30",
|
||||
// Muted variants for subtle indication
|
||||
muted:
|
||||
"border-border/50 bg-muted/50 text-muted-foreground",
|
||||
brand:
|
||||
"border-transparent bg-brand-500/15 text-brand-500 border border-brand-500/30",
|
||||
},
|
||||
size: {
|
||||
default: "px-2.5 py-0.5 text-xs",
|
||||
sm: "px-2 py-0.5 text-[10px]",
|
||||
lg: "px-3 py-1 text-sm",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -27,9 +48,9 @@ export interface BadgeProps
|
||||
extends React.HTMLAttributes<HTMLDivElement>,
|
||||
VariantProps<typeof badgeVariants> {}
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
function Badge({ className, variant, size, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
<div className={cn(badgeVariants({ variant, size }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import * as React from "react";
|
||||
import { Slot } from "@radix-ui/react-slot";
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import { Loader2 } from "lucide-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 cursor-pointer disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_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",
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 cursor-pointer disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_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 active:scale-[0.98]",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
default:
|
||||
"bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 hover:shadow-md hover:shadow-primary/25",
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
"bg-destructive text-white shadow-sm hover:bg-destructive/90 hover:shadow-md hover:shadow-destructive/25 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
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",
|
||||
secondary:
|
||||
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||
ghost:
|
||||
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
link: "text-primary underline-offset-4 hover:underline active:scale-100",
|
||||
"animated-outline":
|
||||
"relative overflow-hidden rounded-xl hover:bg-transparent shadow-none",
|
||||
},
|
||||
@@ -38,17 +40,32 @@ const buttonVariants = cva(
|
||||
}
|
||||
);
|
||||
|
||||
// Loading spinner component
|
||||
function ButtonSpinner({ className }: { className?: string }) {
|
||||
return (
|
||||
<Loader2
|
||||
className={cn("size-4 animate-spin", className)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
asChild = false,
|
||||
loading = false,
|
||||
disabled,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<"button"> &
|
||||
VariantProps<typeof buttonVariants> & {
|
||||
asChild?: boolean;
|
||||
loading?: boolean;
|
||||
}) {
|
||||
const isDisabled = disabled || loading;
|
||||
|
||||
// Special handling for animated-outline variant
|
||||
if (variant === "animated-outline" && !asChild) {
|
||||
return (
|
||||
@@ -59,20 +76,22 @@ function Button({
|
||||
className
|
||||
)}
|
||||
data-slot="button"
|
||||
disabled={isDisabled}
|
||||
{...props}
|
||||
>
|
||||
{/* Animated rotating gradient border */}
|
||||
<span className="absolute inset-[-1000%] animate-[spin_2s_linear_infinite] animated-outline-gradient" />
|
||||
{/* Animated rotating gradient border - smoother animation */}
|
||||
<span className="absolute inset-[-1000%] animate-[spin_3s_linear_infinite] animated-outline-gradient opacity-75 transition-opacity duration-300 group-hover:opacity-100" />
|
||||
|
||||
{/* Inner content container */}
|
||||
<span
|
||||
className={cn(
|
||||
"animated-outline-inner inline-flex h-full w-full cursor-pointer items-center justify-center gap-2 rounded-[10px] px-4 py-1 text-sm font-medium backdrop-blur-3xl transition-all",
|
||||
"animated-outline-inner inline-flex h-full w-full cursor-pointer items-center justify-center gap-2 rounded-[10px] px-4 py-1 text-sm font-medium backdrop-blur-3xl transition-all duration-200",
|
||||
size === "sm" && "px-3 text-xs gap-1.5",
|
||||
size === "lg" && "px-8",
|
||||
size === "icon" && "p-0 gap-0"
|
||||
)}
|
||||
>
|
||||
{loading && <ButtonSpinner />}
|
||||
{children}
|
||||
</span>
|
||||
</button>
|
||||
@@ -85,8 +104,10 @@ function Button({
|
||||
<Comp
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
disabled={isDisabled}
|
||||
{...props}
|
||||
>
|
||||
{loading && <ButtonSpinner />}
|
||||
{children}
|
||||
</Comp>
|
||||
);
|
||||
|
||||
@@ -2,12 +2,20 @@ import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
interface CardProps extends React.ComponentProps<"div"> {
|
||||
gradient?: boolean;
|
||||
}
|
||||
|
||||
function Card({ className, gradient = false, ...props }: CardProps) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border border-white/10 backdrop-blur-sm py-6 shadow-sm",
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border border-white/10 backdrop-blur-md py-6",
|
||||
// Premium layered shadow
|
||||
"shadow-[0_1px_2px_rgba(0,0,0,0.05),0_4px_6px_rgba(0,0,0,0.05),0_10px_20px_rgba(0,0,0,0.04)]",
|
||||
// Gradient border option
|
||||
gradient && "relative before:absolute before:inset-0 before:rounded-xl before:p-[1px] before:bg-gradient-to-br before:from-white/20 before:to-transparent before:pointer-events-none before:-z-10",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -20,7 +28,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",
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -32,7 +40,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
className={cn("leading-none font-semibold tracking-tight", 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-sm leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -75,7 +83,7 @@ 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("flex items-center gap-3 px-6 [.border-t]:pt-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -38,7 +38,10 @@ function DialogOverlay({
|
||||
<DialogPrimitive.Overlay
|
||||
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",
|
||||
"fixed inset-0 z-50 bg-black/60 backdrop-blur-sm",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
||||
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"duration-200",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -66,7 +69,17 @@ function DialogContent({
|
||||
<DialogPrimitive.Content
|
||||
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 flex flex-col w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200 max-h-[calc(100vh-4rem)]",
|
||||
"fixed top-[50%] left-[50%] z-50 translate-x-[-50%] translate-y-[-50%]",
|
||||
"flex flex-col w-full max-w-[calc(100%-2rem)] max-h-[calc(100vh-4rem)]",
|
||||
"bg-card border border-border rounded-xl shadow-2xl",
|
||||
// Premium shadow
|
||||
"shadow-[0_25px_50px_-12px_rgba(0,0,0,0.25)]",
|
||||
// Animations - smoother with scale
|
||||
"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-[state=closed]:slide-out-to-top-[2%] data-[state=open]:slide-in-from-top-[2%]",
|
||||
"duration-200",
|
||||
compact
|
||||
? "max-w-4xl p-4"
|
||||
: !hasCustomMaxWidth
|
||||
@@ -81,8 +94,13 @@ function DialogContent({
|
||||
<DialogPrimitive.Close
|
||||
data-slot="dialog-close"
|
||||
className={cn(
|
||||
"ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute rounded-xs opacity-70 transition-opacity cursor-pointer hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
compact ? "top-2 right-3" : "top-3 right-5"
|
||||
"absolute rounded-lg opacity-60 transition-all duration-200 cursor-pointer",
|
||||
"hover:opacity-100 hover:bg-muted",
|
||||
"focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-none",
|
||||
"disabled:pointer-events-none disabled:cursor-not-allowed",
|
||||
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-4",
|
||||
"p-1.5",
|
||||
compact ? "top-2 right-3" : "top-4 right-4"
|
||||
)}
|
||||
>
|
||||
<XIcon />
|
||||
@@ -109,7 +127,7 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="dialog-footer"
|
||||
className={cn(
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end mt-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@@ -124,7 +142,7 @@ function DialogTitle({
|
||||
return (
|
||||
<DialogPrimitive.Title
|
||||
data-slot="dialog-title"
|
||||
className={cn("text-lg leading-none font-semibold", className)}
|
||||
className={cn("text-lg leading-none font-semibold tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
@@ -137,7 +155,7 @@ function DialogDescription({
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
className={cn("text-muted-foreground text-sm leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -2,20 +2,64 @@ import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||
return (
|
||||
interface InputProps extends React.ComponentProps<"input"> {
|
||||
startAddon?: React.ReactNode;
|
||||
endAddon?: React.ReactNode;
|
||||
}
|
||||
|
||||
function Input({ className, type, startAddon, endAddon, ...props }: InputProps) {
|
||||
const hasAddons = startAddon || endAddon;
|
||||
|
||||
const inputElement = (
|
||||
<input
|
||||
type={type}
|
||||
data-slot="input"
|
||||
className={cn(
|
||||
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground bg-input border-input h-9 w-full min-w-0 rounded-md border 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",
|
||||
"file:text-foreground placeholder:text-muted-foreground/60 selection:bg-primary selection:text-primary-foreground bg-input border-input h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base shadow-xs 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",
|
||||
// Inner shadow for depth
|
||||
"shadow-[inset_0_1px_2px_rgba(0,0,0,0.05)]",
|
||||
// Animated focus ring
|
||||
"transition-[color,box-shadow,border-color] duration-200 ease-out",
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||
"aria-invalid:ring-destructive/20 aria-invalid:border-destructive",
|
||||
// Adjust padding for addons
|
||||
startAddon && "pl-0",
|
||||
endAddon && "pr-0",
|
||||
hasAddons && "border-0 shadow-none focus-visible:ring-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
if (!hasAddons) {
|
||||
return inputElement;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center h-9 w-full rounded-md border border-input bg-input shadow-xs",
|
||||
"shadow-[inset_0_1px_2px_rgba(0,0,0,0.05)]",
|
||||
"transition-[box-shadow,border-color] duration-200 ease-out",
|
||||
"focus-within:border-ring focus-within:ring-ring/50 focus-within:ring-[3px]",
|
||||
"has-[input:disabled]:opacity-50 has-[input:disabled]:cursor-not-allowed",
|
||||
"has-[input[aria-invalid]]:ring-destructive/20 has-[input[aria-invalid]]:border-destructive"
|
||||
)}
|
||||
>
|
||||
{startAddon && (
|
||||
<span className="flex items-center justify-center px-3 text-muted-foreground text-sm">
|
||||
{startAddon}
|
||||
</span>
|
||||
)}
|
||||
{inputElement}
|
||||
{endAddon && (
|
||||
<span className="flex items-center justify-center px-3 text-muted-foreground text-sm">
|
||||
{endAddon}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export { Input }
|
||||
|
||||
@@ -7,7 +7,11 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
<textarea
|
||||
data-slot="textarea"
|
||||
className={cn(
|
||||
"placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input min-h-[80px] w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm resize-none",
|
||||
"placeholder:text-muted-foreground/60 selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input min-h-[80px] w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-base outline-none disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm resize-none",
|
||||
// Inner shadow for depth
|
||||
"shadow-[inset_0_1px_2px_rgba(0,0,0,0.05)]",
|
||||
// Animated focus ring
|
||||
"transition-[color,box-shadow,border-color] duration-200 ease-out",
|
||||
"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",
|
||||
className
|
||||
|
||||
@@ -14,13 +14,23 @@ const TooltipTrigger = TooltipPrimitive.Trigger
|
||||
const TooltipContent = React.forwardRef<
|
||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||
>(({ className, sideOffset = 6, ...props }, ref) => (
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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",
|
||||
"z-50 overflow-hidden rounded-lg border border-border bg-popover px-3 py-1.5 text-xs font-medium text-popover-foreground",
|
||||
// Premium shadow
|
||||
"shadow-lg shadow-black/10",
|
||||
// Faster, snappier animations
|
||||
"animate-in fade-in-0 zoom-in-95 duration-150",
|
||||
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=closed]:duration-100",
|
||||
// Slide from edge
|
||||
"data-[side=bottom]:slide-in-from-top-1",
|
||||
"data-[side=left]:slide-in-from-right-1",
|
||||
"data-[side=right]:slide-in-from-left-1",
|
||||
"data-[side=top]:slide-in-from-bottom-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
Reference in New Issue
Block a user