48 lines · 1.3 KB
import { useId, useState } from "react";
import { Icon } from "~/ui/Icon";
import { cn } from "~/utils/cn";

/**
 * An (i) toggle that reveals an explanation inline, below its row. Inline rather than floating so it is never
 * clipped by a Panel and works the same on touch screens.
 */
export function InfoTip({
  label,
  children,
  className,
}: {
  label: string;
  children: React.ReactNode;
  className?: string;
}) {
  const [open, setOpen] = useState(false);
  const id = useId();
  return (
    <>
      <button
        type="button"
        aria-label={label}
        aria-expanded={open}
        aria-controls={id}
        onClick={() => setOpen((value) => !value)}
        className={cn(
          "inline-flex size-5 items-center justify-center rounded-full text-kumo-subtle hover:text-accent-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-accent-500",
          open && "text-accent-400",
          className,
        )}
      >
        <Icon name="info" className="size-4" />
      </button>
      {open && (
        <div
          id={id}
          role="note"
          className="basis-full rounded-md border border-kumo-hairline bg-kumo-base/70 p-3 text-xs text-kumo-default"