36 lines · 1.3 KB
import { cn } from "~/utils/cn";
import { Icon, type IconName } from "~/ui/Icon";

const SIZES = {
  inline: { wrap: "px-6 py-12", tile: "size-11", icon: "size-5", title: "text-base" },
  page: { wrap: "px-6 py-20 md:py-28", tile: "size-14", icon: "size-6", title: "text-xl" },
} as const;

type Props = {
  title: string;
  icon?: IconName;
  /** `page` for a whole-page empty state, `inline` (default) inside panels. */
  size?: keyof typeof SIZES;
  /** Buttons or links that get the person unstuck. */
  action?: React.ReactNode;
  className?: string;
  children?: React.ReactNode;
};

export function EmptyState({ title, icon = "inbox", size = "inline", action, className, children }: Props) {
  const sizing = SIZES[size];
  return (
    <div className={cn("flex flex-col items-center justify-center text-center", sizing.wrap, className)}>
      <span
        aria-hidden
        className={cn("mb-4 grid place-items-center rounded-lg bg-gray-900 text-gray-400", sizing.tile)}
      >
        <Icon name={icon} className={sizing.icon} />
      </span>
      <h3 className={cn("font-medium text-balance text-gray-100", sizing.title)}>{title}</h3>
      {children && <div className="mt-1.5 max-w-sm text-sm leading-6 text-pretty text-gray-500">{children}</div>}
      {action && <div className="mt-5 flex flex-wrap justify-center gap-2">{action}</div>}
    </div>
  );
}