{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "modal",
  "title": "Modal",
  "author": "Poyraz Avsever",
  "description": "Poyraz Soft Glass modal component with standardized floating and overlay behavior.",
  "dependencies": [
    "@radix-ui/react-dialog@^1.1.15",
    "class-variance-authority@^0.7.1",
    "lucide-react@^0.574.0"
  ],
  "registryDependencies": [
    "@poyraz/poyraz-utils",
    "@poyraz/poyraz-theme",
    "@poyraz/poyraz-recipes"
  ],
  "files": [
    {
      "path": "registry/poyraz/ui/phase6/molecules/modal.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  overlaySurfaceVariants,\n  overlayVariants,\n  type OverlayProps,\n  type OverlaySurfaceProps,\n} from \"@/components/ui/recipes\";\n\n/* ================================================================== */\n/*  MODAL — opinionated wrapper around Radix Dialog                    */\n/*  Difference from Dialog:                                            */\n/*  • size variants (sm / default / lg / full)                         */\n/*  • centered by default, optional top-aligned                        */\n/*  • built-in close icon                                              */\n/* ================================================================== */\n\nconst modalContentVariants = cva(\n  [\n    \"fixed z-50 grid gap-4 p-5\",\n    \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\n    \"data-[state=open]:fade-in-0 data-[state=closed]:fade-out-0\",\n    \"data-[state=open]:[--poyraz-enter-scale:0.98] data-[state=closed]:[--poyraz-exit-scale:0.98]\",\n    \"motion-reduce:[--poyraz-enter-scale:1] motion-reduce:[--poyraz-exit-scale:1] motion-reduce:[--poyraz-enter-translate-x:0] motion-reduce:[--poyraz-enter-translate-y:0] motion-reduce:duration-100\",\n  ].join(\" \"),\n  {\n    variants: {\n      size: {\n        sm: \"w-full max-w-xs\",\n        default: \"w-full max-w-sm\",\n        lg: \"w-full max-w-lg\",\n        xl: \"w-full max-w-2xl\",\n        full: \"w-[calc(100vw-2rem)] h-[calc(100vh-2rem)]\",\n      },\n      position: {\n        center:\n          \"left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]\",\n        top: \"left-[50%] top-[10%] translate-x-[-50%] data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[5%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[5%]\",\n      },\n    },\n    defaultVariants: {\n      size: \"default\",\n      position: \"center\",\n    },\n  },\n);\n\n/* ── Root ─────────────────────────────────────────────────────────── */\n\nconst Modal = DialogPrimitive.Root;\n\nconst ModalTrigger = DialogPrimitive.Trigger;\n\nconst ModalClose = DialogPrimitive.Close;\n\n/* ── Overlay ──────────────────────────────────────────────────────── */\n\nconst ModalOverlay = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Overlay>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> & OverlayProps\n>(({ className, tone, ...props }, ref) => (\n  <DialogPrimitive.Overlay\n    ref={ref}\n    className={cn(overlayVariants({ tone }), className)}\n    {...props}\n  />\n));\nModalOverlay.displayName = \"ModalOverlay\";\n\n/* ── Content ──────────────────────────────────────────────────────── */\n\nexport interface ModalContentProps\n  extends\n    React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>,\n    VariantProps<typeof modalContentVariants>,\n    OverlaySurfaceProps {\n  /** Hide the default close (X) button */\n  hideClose?: boolean;\n  mobile?: \"floating\" | \"fullscreen\";\n  overlayTone?: NonNullable<OverlayProps[\"tone\"]>;\n  overlayClassName?: string;\n}\n\nconst ModalContent = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Content>,\n  ModalContentProps\n>(\n  (\n    {\n      className,\n      children,\n      size,\n      position,\n      surface,\n      radius,\n      hideClose = false,\n      mobile = \"floating\",\n      overlayTone,\n      overlayClassName,\n      ...props\n    },\n    ref,\n  ) => (\n    <DialogPrimitive.Portal>\n      <ModalOverlay tone={overlayTone} className={overlayClassName} />\n      <DialogPrimitive.Content\n        ref={ref}\n        className={cn(\n          overlaySurfaceVariants({ surface, radius }),\n          modalContentVariants({ size, position }),\n          mobile === \"fullscreen\" &&\n            \"max-sm:inset-0 max-sm:h-dvh max-sm:w-full max-sm:max-w-none max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-none\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        {!hideClose && (\n          <DialogPrimitive.Close className=\"absolute right-4 top-4 cursor-pointer rounded-md p-1 opacity-70 ring-offset-background transition-[color,background-color,opacity,transform] duration-[var(--poyraz-motion-duration-fast)] ease-[var(--poyraz-motion-ease-out)] hover:bg-accent hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none\">\n            <X className=\"h-4 w-4\" />\n            <span className=\"sr-only\">Close</span>\n          </DialogPrimitive.Close>\n        )}\n      </DialogPrimitive.Content>\n    </DialogPrimitive.Portal>\n  ),\n);\nModalContent.displayName = \"ModalContent\";\n\n/* ── Header / Footer / Title / Description ────────────────────────── */\n\nconst ModalHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n  <div className={cn(\"flex flex-col space-y-1.5 text-left\", className)} {...props} />\n);\nModalHeader.displayName = \"ModalHeader\";\n\nconst ModalFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (\n  <div\n    className={cn(\n      \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 pt-2 border-t border-border\",\n      className,\n    )}\n    {...props}\n  />\n);\nModalFooter.displayName = \"ModalFooter\";\n\nconst ModalTitle = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Title>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Title\n    ref={ref}\n    className={cn(\"text-base font-semibold leading-none tracking-tight\", className)}\n    {...props}\n  />\n));\nModalTitle.displayName = \"ModalTitle\";\n\nconst ModalDescription = React.forwardRef<\n  React.ElementRef<typeof DialogPrimitive.Description>,\n  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n  <DialogPrimitive.Description\n    ref={ref}\n    className={cn(\"text-sm text-muted-foreground\", className)}\n    {...props}\n  />\n));\nModalDescription.displayName = \"ModalDescription\";\n\n/* ================================================================== */\n/*  EXPORTS                                                            */\n/* ================================================================== */\n\nexport {\n  Modal,\n  ModalTrigger,\n  ModalClose,\n  ModalOverlay,\n  ModalContent,\n  ModalHeader,\n  ModalFooter,\n  ModalTitle,\n  ModalDescription,\n  modalContentVariants,\n};\n",
      "type": "registry:ui",
      "target": "@ui/molecules/modal.tsx"
    }
  ],
  "meta": {
    "category": "phase-6-interactive",
    "phase": "beta"
  },
  "type": "registry:ui"
}