{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "drawer-content",
  "title": "Content drawer",
  "description": "Styled drawer: the Content set. 5 decorative drawer variations (MenuDrawer, FormDrawer, ListDrawer, DetailDrawer, GridDrawer) 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/drawer-content.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\"\nimport {\n  BarChart3,\n  Bell,\n  Calendar,\n  Check,\n  ChevronRight,\n  Copy,\n  Hash,\n  House,\n  Link2,\n  Mail,\n  MapPin,\n  MessageCircle,\n  Search,\n  Settings,\n  Share2,\n  Tag,\n  User,\n  Users,\n  X,\n} from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\n/* Content-themed drawer family: 5 drawers opening from the bottom. They all carry\n   the same self-contained shell (NO radix/vaul/portal), the same slide-up from the\n   bottom edge and the same token colors; the DIFFERENCE IS THE INNER CONTENT LAYOUT\n   of the panel - each export brings a real usage scenario to life with tokenized\n   placeholder content (bars/icons/cards). The shell: a fixed inset-0 backdrop + a\n   fixed panel pinned to the bottom edge (z-50), sliding up from below. Internal\n   state (defaultOpen) or controlled (open + onOpenChange). A backdrop click /\n   Escape / the close button / the handle closes it. The panel takes focus on open.\n   The slide runs through AnimatePresence; only a fade under reduced-motion. Color\n   comes ONLY from tokens, with transparency via color-mix. */\n\nexport type StyledSize = \"sm\" | \"md\" | \"lg\" | \"xl\"\n\n/* size = panel yuksekligi (alt kenardan). */\nconst panelHeight: Record<StyledSize, string> = {\n  sm: \"h-[35vh]\",\n  md: \"h-[50vh]\",\n  lg: \"h-[68vh]\",\n  xl: \"h-[82vh]\",\n}\n\ninterface DrawerProps {\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\n/* Controlled/uncontrolled acik durum yonetimi. */\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\n/* An icon box that keeps both lucide <svg> and remixicon <i> children aligned. */\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 bottom-edge panel. body draws the panel's inner content per variant; defaultTitle is the variant's title. The Essentials slide and close mechanism (outwards past the bottom edge) is preserved. */\nfunction DrawerContentShell({\n  props,\n  defaultTitle,\n  body,\n}: {\n  props: DrawerProps\n  defaultTitle: React.ReactNode\n  body: React.ReactNode\n}) {\n  const { size = \"md\", trigger, title, className, open, defaultOpen, onOpenChange } = props\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  // Bottom edge: the panel slides down past its own bottom edge. This value keeps\n  // the mechanism intact for vertical edges so the top edge derives the same way.\n  const enter = { y: \"100%\" }\n  const heading = title ?? defaultTitle\n\n  return (\n    <div data-slot=\"styled-drawer\" 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-drawer-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-drawer-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 } : { 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 inset-x-0 bottom-0 flex flex-col rounded-t-2xl border-t border-border bg-card text-card-foreground shadow-lg outline-none\",\n                panelHeight[size],\n                className\n              )}\n            >\n              {/* Tutamac (grab handle) - tiklayinca kapatir. */}\n              <button\n                type=\"button\"\n                aria-label=\"Close drawer\"\n                onClick={() => setOpen(false)}\n                className=\"flex w-full shrink-0 cursor-pointer items-center justify-center py-3 outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:ring-inset\"\n              >\n                <span className=\"h-1.5 w-12 rounded-full bg-[color-mix(in_oklab,var(--color-foreground)_25%,transparent)]\" />\n              </button>\n\n              <div className=\"flex items-start justify-between gap-4 border-b border-border px-5 pb-4\">\n                <h2\n                  data-slot=\"styled-drawer-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\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/* MenuDrawer: a horizontal quick-action strip at the top (icon tiles) with a vertical nav menu below - each row an icon plus a label bar; the first item is active (primary soft). */\nexport function MenuDrawer(props: DrawerProps) {\n  const quick = [Search, Bell, Share2, Settings]\n  const items = [\n    { Icon: House, w: \"w-16\", active: true },\n    { Icon: Users, w: \"w-24\", active: false },\n    { Icon: Calendar, w: \"w-20\", active: false },\n    { Icon: BarChart3, w: \"w-28\", active: false },\n    { Icon: Settings, w: \"w-20\", active: false },\n  ]\n  return (\n    <DrawerContentShell\n      props={props}\n      defaultTitle=\"Menu\"\n      body={\n        <div className=\"flex flex-col gap-4\">\n          {/* Yatay ikon seridi. */}\n          <div className={cn(\"flex items-center gap-3\", iconBox)}>\n            {quick.map((Icon, i) => (\n              <span\n                key={i}\n                className=\"flex size-12 flex-1 items-center justify-center rounded-xl border border-border bg-muted text-foreground\"\n              >\n                <Icon />\n              </span>\n            ))}\n          </div>\n          {/* Dikey nav menusu. */}\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\n                  className={cn(\n                    w,\n                    active &&\n                      \"bg-[color-mix(in_oklab,var(--color-primary)_45%,transparent)]\"\n                  )}\n                />\n              </span>\n            ))}\n          </nav>\n        </div>\n      }\n    />\n  )\n}\n\n/* FormDrawer: token label + input placeholder satirlari + submit. Girisler bos\n   bordered kutu (gorunum yeterli). */\nexport function FormDrawer(props: DrawerProps) {\n  const fields = [\n    { label: \"w-16\", input: \"h-9\" },\n    { label: \"w-20\", input: \"h-9\" },\n    { label: \"w-14\", input: \"h-20\" },\n  ]\n  return (\n    <DrawerContentShell\n      props={props}\n      defaultTitle=\"New item\"\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/* ListDrawer: token liste satirlari - avatar daire + iki metin cubugu + chevron. */\nexport function ListDrawer(props: DrawerProps) {\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  ]\n  return (\n    <DrawerContentShell\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\n                  className={cn(\n                    \"h-1.5 bg-[color-mix(in_oklab,var(--color-muted-foreground)_25%,transparent)]\",\n                    r.sub\n                  )}\n                />\n              </div>\n              <ChevronRight className=\"text-muted-foreground\" />\n            </li>\n          ))}\n        </ul>\n      }\n    />\n  )\n}\n\n/* DetailDrawer: 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 DetailDrawer(props: DrawerProps) {\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    <DrawerContentShell\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/* GridDrawer: a token card grid - icon plus label tiles like share options, with a copyable link row at the top. */\nexport function GridDrawer(props: DrawerProps) {\n  const options = [\n    { Icon: Copy, w: \"w-10\" },\n    { Icon: Link2, w: \"w-12\" },\n    { Icon: Mail, w: \"w-8\" },\n    { Icon: MessageCircle, w: \"w-12\" },\n    { Icon: Users, w: \"w-10\" },\n    { Icon: Share2, w: \"w-8\" },\n  ]\n  return (\n    <DrawerContentShell\n      props={props}\n      defaultTitle=\"Share\"\n      body={\n        <div className=\"flex flex-col gap-4\">\n          {/* Kopyalanabilir baglanti satiri. */}\n          <div className={cn(\"flex items-center gap-3 rounded-lg border border-border bg-muted px-3 py-2.5\", iconBox)}>\n            <Link2 className=\"text-muted-foreground\" />\n            <Bar className=\"h-2 flex-1\" />\n            <span className=\"flex items-center gap-1.5 text-xs font-medium text-primary [&_svg]:size-3.5\">\n              <Copy />\n              Copy\n            </span>\n          </div>\n          {/* Grid of share options. */}\n          <div className=\"grid grid-cols-3 gap-3\">\n            {options.map(({ Icon, w }, i) => (\n              <span\n                key={i}\n                className={cn(\n                  \"flex flex-col items-center gap-2 rounded-xl border border-border bg-background p-4 text-foreground transition-colors hover:bg-muted\",\n                  iconBox\n                )}\n              >\n                <span className=\"flex size-10 items-center justify-center rounded-full bg-[color-mix(in_oklab,var(--color-primary)_12%,transparent)] text-primary\">\n                  <Icon />\n                </span>\n                <Bar className={cn(\"h-1.5\", w)} />\n              </span>\n            ))}\n          </div>\n        </div>\n      }\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "components/ui/drawer-content.tsx"
    }
  ],
  "categories": [
    "styled",
    "drawer"
  ],
  "type": "registry:component"
}