{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "sheet-content",
  "title": "Content sheet",
  "description": "Styled sheet: the Content set. 5 decorative sheet variations (MenuSheet, FormSheet, ListSheet, DetailSheet, SplitSheet) on the ai2 token system, powered by framer-motion, reduced-motion aware and sized sm to xl. Part of the free styled layer.",
  "dependencies": [
    "motion@^12.42.2",
    "lucide-react@^1.23.0"
  ],
  "registryDependencies": [
    "@ai2/tokens"
  ],
  "files": [
    {
      "path": "registry/ai2/styled/sheet-content.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport {\n  BarChart3,\n  Calendar,\n  Check,\n  ChevronRight,\n  CreditCard,\n  Hash,\n  House,\n  LayoutGrid,\n  LifeBuoy,\n  MapPin,\n  Settings,\n  Tag,\n  User,\n  Users,\n  Zap,\n  X,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Content-themed sheet family: 5 right-edge panels. They all carry the same\n   self-contained shell (NO radix/portal), the same slide-in from the right and the\n   same token colors; the DIFFERENCE IS THE INNER CONTENT LAYOUT of the panel - each\n   export brings a real usage scenario to life with tokenized placeholder content\n   (bars/avatars/icons). The shell: a fixed inset-0 backdrop + a fixed panel pinned\n   to the right (z-50), sliding in from the right. Internal state (defaultOpen) or\n   controlled (open + onOpenChange). A backdrop click / Escape / the close button\n   closes it. The panel takes focus on open. The slide runs through\n   AnimatePresence; only a fade under reduced-motion. Color comes ONLY from tokens,\n   with transparency via color-mix. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\ntype Edge = \"right\" | \"left\" | \"top\" | \"bottom\"\n\nconst widthSize: Record<StyledSize, string> = {\n  sm: \"w-72\",\n  md: \"w-80\",\n  lg: \"w-96\",\n  xl: \"w-[32rem]\",\n}\n\nconst heightSize: Record<StyledSize, string> = {\n  sm: \"h-40\",\n  md: \"h-64\",\n  lg: \"h-80\",\n  xl: \"h-[32rem]\",\n}\n\nconst edgeAnchor: Record<Edge, string> = {\n  right: \"inset-y-0 right-0\",\n  left: \"inset-y-0 left-0\",\n  top: \"inset-x-0 top-0\",\n  bottom: \"inset-x-0 bottom-0\",\n}\n\n/* Essentials slideOffset: the panel slides outwards past its own edge. This family uses the right edge; the mechanism is kept so other edges derive from the same function. */\nfunction slideOffset(edge: Edge) {\n  switch (edge) {\n    case \"right\":\n      return { x: \"100%\" }\n    case \"left\":\n      return { x: \"-100%\" }\n    case \"top\":\n      return { y: \"-100%\" }\n    case \"bottom\":\n      return { y: \"100%\" }\n  }\n}\n\ninterface SheetProps {\n  size?: StyledSize\n  trigger?: React.ReactNode\n  title?: React.ReactNode\n  className?: string\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (o: boolean) => void\n}\n\nfunction useControllableOpen(\n  open: boolean | undefined,\n  defaultOpen: boolean | undefined,\n  onOpenChange: ((o: boolean) => void) | undefined\n) {\n  const [uncontrolled, setUncontrolled] = React.useState(defaultOpen ?? false)\n  const isControlled = open !== undefined\n  const value = isControlled ? open : uncontrolled\n  const setValue = React.useCallback(\n    (next: boolean) => {\n      if (!isControlled) setUncontrolled(next)\n      onOpenChange?.(next)\n    },\n    [isControlled, onOpenChange]\n  )\n  return [value, setValue] as const\n}\n\nconst iconBox =\n  \"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 [&_i]:text-base [&_i]:leading-none\"\n\n/* Shared shell: trigger plus an AnimatePresence backdrop plus a right-edge panel. body draws the panel's inner content per variant; defaultTitle is the variant's title. */\nfunction SheetContentShell({\n  props,\n  defaultTitle,\n  body,\n}: {\n  props: SheetProps\n  defaultTitle: React.ReactNode\n  body: React.ReactNode\n}) {\n  const { size = \"md\", trigger, title, className, open, defaultOpen, onOpenChange } = props\n  const edge: Edge = \"right\"\n  const reduce = useReducedMotion()\n  const [isOpen, setOpen] = useControllableOpen(open, defaultOpen, onOpenChange)\n  const panelRef = React.useRef<HTMLDivElement>(null)\n\n  React.useEffect(() => {\n    if (!isOpen) return\n    const onKey = (e: KeyboardEvent) => {\n      if (e.key === \"Escape\") setOpen(false)\n    }\n    window.addEventListener(\"keydown\", onKey)\n    return () => window.removeEventListener(\"keydown\", onKey)\n  }, [isOpen, setOpen])\n\n  // Right and left edges are horizontal: full height, width from size. This family\n  // uses the right edge; heightSize keeps the mechanism intact for vertical edges.\n  const horizontal = edge === \"right\"\n  const sizeClass = horizontal ? cn(\"h-full\", widthSize[size]) : cn(\"w-full\", heightSize[size])\n  const enter = slideOffset(edge)\n  const heading = title ?? defaultTitle\n\n  return (\n    <div data-slot=\"styled-sheet\" className=\"inline-flex\">\n      {/* The ARIA state lives on the REAL trigger. There used to be a\n          span role=\"button\" tabIndex={-1} wrapper, which produced TWO buttons\n          in the AX tree: one carrying the state (not focusable) and one that\n          took focus (stateless). A screen-reader user never heard \"has dialog\"\n          or \"expanded\" - measured with the CDP AX tree. */}\n      {React.isValidElement<React.ButtonHTMLAttributes<HTMLButtonElement>>(trigger) ? (\n        React.cloneElement(trigger, {\n          \"aria-haspopup\": \"dialog\",\n          \"aria-expanded\": isOpen,\n          onClick: (event: React.MouseEvent<HTMLButtonElement>) => {\n            trigger.props.onClick?.(event)\n            setOpen(true)\n          },\n        })\n      ) : (\n        <button\n          type=\"button\"\n          aria-haspopup=\"dialog\"\n          aria-expanded={isOpen}\n          onClick={() => setOpen(true)}\n          className={cn(\n              \"inline-flex h-9 shrink-0 select-none items-center justify-center gap-2 rounded-lg border border-border bg-secondary px-4 text-sm font-medium text-secondary-foreground outline-none whitespace-nowrap transition-[color,background-color] duration-(--motion-fast) ease-(--motion-ease) hover:bg-surface-3 focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n              iconBox\n            )}\n        >\n          {trigger ?? \"Open\"}\n        </button>\n      )}\n\n      <AnimatePresence>\n        {isOpen ? (\n          <div className=\"fixed inset-0 z-50\">\n            <motion.div\n              data-slot=\"styled-sheet-backdrop\"\n              onClick={() => setOpen(false)}\n              initial={{ opacity: 0 }}\n              animate={{ opacity: 1 }}\n              exit={{ opacity: 0 }}\n              transition={{ duration: reduce ? 0 : 0.2, ease: [0.4, 0, 0.2, 1] }}\n              className=\"absolute inset-0 bg-[color-mix(in_oklab,var(--color-foreground)_50%,transparent)]\"\n            />\n            <motion.div\n              ref={panelRef}\n              data-slot=\"styled-sheet-panel\"\n              role=\"dialog\"\n              aria-modal=\"true\"\n              tabIndex={-1}\n              onAnimationComplete={() => panelRef.current?.focus()}\n              initial={reduce ? { opacity: 0 } : enter}\n              animate={reduce ? { opacity: 1 } : { x: 0, y: 0 }}\n              exit={reduce ? { opacity: 0 } : enter}\n              transition={\n                reduce ? { duration: 0 } : { type: \"spring\", stiffness: 320, damping: 34 }\n              }\n              className={cn(\n                \"fixed flex flex-col border-l border-border bg-card text-card-foreground shadow-lg outline-none\",\n                edgeAnchor[edge],\n                sizeClass,\n                className\n              )}\n            >\n              <div className=\"flex items-start justify-between gap-4 border-b border-border px-5 py-4\">\n                <h2\n                  data-slot=\"styled-sheet-title\"\n                  className=\"text-base font-semibold text-foreground\"\n                >\n                  {heading}\n                </h2>\n                <button\n                  type=\"button\"\n                  onClick={() => setOpen(false)}\n                  aria-label=\"Close\"\n                  className={cn(\n                    \"relative -m-1 inline-flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground outline-none transition-[color,background-color] duration-(--motion-fast) ease-(--motion-ease) hover:bg-surface-3 hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/50 after:absolute after:-inset-2\",\n                    iconBox\n                  )}\n                >\n                  <X />\n                </button>\n              </div>\n              <div className=\"min-h-0 flex-1 overflow-auto p-5 text-sm text-muted-foreground\">\n                {body}\n              </div>\n            </motion.div>\n          </div>\n        ) : null}\n      </AnimatePresence>\n    </div>\n  )\n}\n\n/* A token placeholder bar: a token-colored bar instead of real text. */\nfunction Bar({ className }: { className?: string }) {\n  return <span className={cn(\"block h-2 rounded-full bg-muted\", className)} aria-hidden />\n}\n\n/* MenuSheet: dikey token nav menusu - her oge ikon + etiket cubugu; ilk oge\n   aktif (primary soft). */\nexport function MenuSheet(props: SheetProps) {\n  const items = [\n    { Icon: House, w: \"w-16\", active: true },\n    { Icon: LayoutGrid, w: \"w-24\", active: false },\n    { Icon: Users, w: \"w-20\", active: false },\n    { Icon: Calendar, w: \"w-28\", active: false },\n    { Icon: BarChart3, w: \"w-16\", active: false },\n    { Icon: Settings, w: \"w-24\", active: false },\n    { Icon: LifeBuoy, w: \"w-20\", active: false },\n  ]\n  return (\n    <SheetContentShell\n      props={props}\n      defaultTitle=\"Menu\"\n      body={\n        <nav className=\"flex flex-col gap-1\">\n          {items.map(({ Icon, w, active }, i) => (\n            <span\n              key={i}\n              className={cn(\n                \"flex items-center gap-3 rounded-lg px-3 py-2.5\",\n                iconBox,\n                active\n                  ? \"bg-[color-mix(in_oklab,var(--color-primary)_12%,transparent)] text-primary\"\n                  : \"text-foreground hover:bg-muted\"\n              )}\n            >\n              <Icon />\n              <Bar className={cn(w, active && \"bg-[color-mix(in_oklab,var(--color-primary)_45%,transparent)]\")} />\n            </span>\n          ))}\n        </nav>\n      }\n    />\n  )\n}\n\n/* FormSheet: token label + input placeholder satirlari + submit. Girisler bos\n   bordered kutu (gorunum yeterli). */\nexport function FormSheet(props: SheetProps) {\n  const fields = [\n    { label: \"w-16\", input: \"h-9\" },\n    { label: \"w-20\", input: \"h-9\" },\n    { label: \"w-14\", input: \"h-9\" },\n    { label: \"w-24\", input: \"h-20\" },\n  ]\n  return (\n    <SheetContentShell\n      props={props}\n      defaultTitle=\"Details\"\n      body={\n        <form className=\"flex flex-col gap-5\" onSubmit={(e) => e.preventDefault()}>\n          {fields.map((f, i) => (\n            <div key={i} className=\"flex flex-col gap-2\">\n              <Bar className={cn(\"h-2\", f.label)} />\n              <div className={cn(\"rounded-lg border border-border bg-background\", f.input)} />\n            </div>\n          ))}\n          <button\n            type=\"submit\"\n            className={cn(\n              \"mt-1 inline-flex h-9 w-full select-none items-center justify-center gap-2 rounded-lg bg-primary px-4 text-sm font-medium text-primary-foreground outline-none transition-[color,background-color] duration-(--motion-fast) ease-(--motion-ease) hover:bg-[color-mix(in_oklab,var(--color-primary)_88%,transparent)] focus-visible:ring-[3px] focus-visible:ring-ring/50\",\n              iconBox\n            )}\n          >\n            <Check />\n            Submit\n          </button>\n        </form>\n      }\n    />\n  )\n}\n\n/* ListSheet: token liste satirlari - avatar daire + iki metin cubugu + chevron. */\nexport function ListSheet(props: SheetProps) {\n  const rows = [\n    { name: \"w-24\", sub: \"w-32\" },\n    { name: \"w-20\", sub: \"w-28\" },\n    { name: \"w-28\", sub: \"w-24\" },\n    { name: \"w-16\", sub: \"w-36\" },\n    { name: \"w-24\", sub: \"w-20\" },\n    { name: \"w-20\", sub: \"w-32\" },\n  ]\n  return (\n    <SheetContentShell\n      props={props}\n      defaultTitle=\"Members\"\n      body={\n        <ul className=\"flex flex-col\">\n          {rows.map((r, i) => (\n            <li\n              key={i}\n              className={cn(\n                \"flex items-center gap-3 border-b border-border py-3 last:border-0\",\n                iconBox\n              )}\n            >\n              <span className=\"flex size-9 shrink-0 items-center justify-center rounded-full bg-muted text-muted-foreground\">\n                <User />\n              </span>\n              <div className=\"flex flex-1 flex-col gap-1.5\">\n                <Bar className={cn(\"h-2\", r.name)} />\n                <Bar className={cn(\"h-1.5 bg-[color-mix(in_oklab,var(--color-muted-foreground)_25%,transparent)]\", r.sub)} />\n              </div>\n              <ChevronRight className=\"text-muted-foreground\" />\n            </li>\n          ))}\n        </ul>\n      }\n    />\n  )\n}\n\n/* DetailSheet: a header block at the top (avatar plus name) with key/value detail fields below - each row an icon label plus a value bar. */\nexport function DetailSheet(props: SheetProps) {\n  const rows = [\n    { Icon: Tag, value: \"w-24\" },\n    { Icon: Calendar, value: \"w-20\" },\n    { Icon: User, value: \"w-28\" },\n    { Icon: MapPin, value: \"w-16\" },\n    { Icon: Hash, value: \"w-24\" },\n  ]\n  return (\n    <SheetContentShell\n      props={props}\n      defaultTitle=\"Overview\"\n      body={\n        <div className=\"flex flex-col gap-5\">\n          <div className=\"flex items-center gap-3\">\n            <span className=\"flex size-12 shrink-0 items-center justify-center rounded-xl bg-muted text-muted-foreground [&_svg]:size-5\">\n              <User />\n            </span>\n            <div className=\"flex flex-col gap-2\">\n              <Bar className=\"h-2.5 w-32\" />\n              <Bar className=\"h-2 w-20 bg-[color-mix(in_oklab,var(--color-muted-foreground)_25%,transparent)]\" />\n            </div>\n          </div>\n          <div className=\"flex flex-col divide-y divide-border rounded-lg border border-border\">\n            {rows.map(({ Icon, value }, i) => (\n              <div\n                key={i}\n                className={cn(\"flex items-center justify-between gap-4 px-3 py-2.5\", iconBox)}\n              >\n                <span className=\"flex items-center gap-2 text-muted-foreground\">\n                  <Icon />\n                  <Bar className=\"h-2 w-14\" />\n                </span>\n                <Bar className={cn(\"h-2\", value)} />\n              </div>\n            ))}\n          </div>\n        </div>\n      }\n    />\n  )\n}\n\n/* SplitSheet: a summary card in the top half (icon plus title plus mini statistics) and an action list in the bottom half - each row an icon plus label plus chevron. */\nexport function SplitSheet(props: SheetProps) {\n  const stats = [\"w-10\", \"w-8\", \"w-12\"]\n  const actions = [\n    { Icon: Zap, w: \"w-24\" },\n    { Icon: CreditCard, w: \"w-28\" },\n    { Icon: Settings, w: \"w-20\" },\n    { Icon: LifeBuoy, w: \"w-24\" },\n  ]\n  return (\n    <SheetContentShell\n      props={props}\n      defaultTitle=\"Summary\"\n      body={\n        <div className=\"flex h-full flex-col gap-4\">\n          <div className=\"flex flex-col gap-3 rounded-xl border border-border bg-[color-mix(in_oklab,var(--color-muted)_50%,transparent)] p-4\">\n            <div className={cn(\"flex items-center gap-3\", iconBox)}>\n              <span className=\"flex size-9 shrink-0 items-center justify-center rounded-lg bg-[color-mix(in_oklab,var(--color-primary)_14%,transparent)] text-primary\">\n                <BarChart3 />\n              </span>\n              <Bar className=\"h-2.5 w-28\" />\n            </div>\n            <Bar className=\"h-2 w-full\" />\n            <Bar className=\"h-2 w-2/3 bg-[color-mix(in_oklab,var(--color-muted-foreground)_25%,transparent)]\" />\n            <div className=\"mt-1 grid grid-cols-3 gap-3\">\n              {stats.map((w, i) => (\n                <div key={i} className=\"flex flex-col gap-1.5 rounded-lg bg-background p-2.5\">\n                  <Bar className={cn(\"h-2.5\", w)} />\n                  <Bar className=\"h-1.5 w-full bg-[color-mix(in_oklab,var(--color-muted-foreground)_25%,transparent)]\" />\n                </div>\n              ))}\n            </div>\n          </div>\n\n          <Bar className=\"mt-1 h-2 w-16\" />\n\n          <div className=\"flex flex-col gap-1\">\n            {actions.map(({ Icon, w }, i) => (\n              <span\n                key={i}\n                className={cn(\n                  \"flex items-center gap-3 rounded-lg px-3 py-2.5 text-foreground hover:bg-muted\",\n                  iconBox\n                )}\n              >\n                <Icon />\n                <Bar className={cn(\"h-2 flex-1\", w)} />\n                <ChevronRight className=\"text-muted-foreground\" />\n              </span>\n            ))}\n          </div>\n        </div>\n      }\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/sheet-content.tsx"
    }
  ],
  "categories": [
    "styled",
    "sheet"
  ],
  "type": "registry:component"
}