113 lines · 3.4 KB · TypeScript
RawDownload
import { cva, type VariantProps } from "class-variance-authority";import { Children, isValidElement } from "react";import type * as React from "react"; import { cn } from "~/lib/utils"; const alertVariants = cva("relative rounded-xl border px-3.5 py-3 text-card-foreground text-sm", {  defaultVariants: {    variant: "default",  },  variants: {    variant: {      default: "bg-transparent dark:bg-input/32 [&_svg]:text-muted-foreground",      error:        "border-destructive/32 bg-destructive/4 text-destructive-foreground [&_[data-slot=alert-description]]:text-destructive-foreground/80 [&_svg]:text-destructive",      info: "border-info/32 bg-info/4 [&_svg]:text-info",      success: "border-success/32 bg-success/4 [&_svg]:text-success",      warning: "border-warning/32 bg-warning/4 [&_svg]:text-warning",    },  },}); function alertChildSlot(child: React.ReactElement): string | undefined {  const propsSlot = (child.props as Record<string, string | undefined>)["data-slot"];  if (propsSlot) {    return propsSlot;  }   const type = child.type as { displayName?: string; name?: string };  switch (type.displayName ?? type.name) {    case "AlertAction":      return "alert-action";    case "AlertTitle":      return "alert-title";    case "AlertDescription":      return "alert-description";    default:      return undefined;  }} function Alert({  className,  variant,  children,  ...props}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {  const icon: React.ReactNode[] = [];  const content: React.ReactNode[] = [];  const action: React.ReactNode[] = [];   Children.forEach(children, (child) => {    if (!isValidElement(child)) {      content.push(child);      return;    }    const slot = alertChildSlot(child);    if (slot === "alert-action") {      action.push(child);    } else if (slot === "alert-title" || slot === "alert-description") {      content.push(child);    } else {      icon.push(child);    }  });   return (    <div      className={cn(alertVariants({ variant }), className)}      data-slot="alert"      role="alert"      {...props}    >      <div className="flex items-center gap-2">        {icon.length > 0 && (          <div className="flex size-4 shrink-0 items-center justify-center [&>svg]:size-full">            {icon}          </div>        )}        {content.length > 0 && (          <div className="flex min-w-0 flex-1 flex-col gap-0.5">{content}</div>        )}        {action.length > 0 && (          <div className="flex shrink-0 items-center self-center">{action}</div>        )}      </div>    </div>  );} function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {  return <div className={cn("font-medium", className)} data-slot="alert-title" {...props} />;} function AlertDescription({ className, ...props }: React.ComponentProps<"div">) {  return (    <div      className={cn("flex flex-col gap-2.5 text-muted-foreground", className)}      data-slot="alert-description"      {...props}    />  );} function AlertAction({ className, ...props }: React.ComponentProps<"div">) {  return <div className={cn("flex gap-1", className)} data-slot="alert-action" {...props} />;} AlertTitle.displayName = "AlertTitle";AlertDescription.displayName = "AlertDescription";AlertAction.displayName = "AlertAction"; export { Alert, AlertTitle, AlertDescription, AlertAction };