@tribepad/themis 1.9.2 → 1.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/dist/elements/AlertDialog/index.js +1 -1
- package/dist/elements/AlertDialog/index.js.map +1 -1
- package/dist/elements/AlertDialog/index.mjs +1 -1
- package/dist/elements/AlertDialog/index.mjs.map +1 -1
- package/dist/elements/Modal/Modal.d.ts.map +1 -1
- package/dist/elements/Modal/index.js +1 -1
- package/dist/elements/Modal/index.js.map +1 -1
- package/dist/elements/Modal/index.mjs +1 -1
- package/dist/elements/Modal/index.mjs.map +1 -1
- package/dist/elements/OTPInput/index.js +1 -1
- package/dist/elements/OTPInput/index.js.map +1 -1
- package/dist/elements/OTPInput/index.mjs +1 -1
- package/dist/elements/OTPInput/index.mjs.map +1 -1
- package/dist/elements/index.js +1 -1
- package/dist/elements/index.js.map +1 -1
- package/dist/elements/index.mjs +1 -1
- package/dist/elements/index.mjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/elements/Modal/Modal.stories.tsx +70 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/utils/browserExtensionOverlay.ts","../../../src/hooks/useMediaQuery.ts","../../../src/styles/shared-variants.ts","../../../src/elements/Modal/Modal.styles.ts","../../../src/elements/Modal/Modal.tsx"],"names":["cn","inputs","twMerge","clsx","isExtensionInjectedElement","element","node","depth","tag","root","custom","parent","createInteractOutsideGuard","consumerShouldClose","useMediaQuery","query","subscribe","useCallback","callback","mql","getSnapshot","getServerSnapshot","useSyncExternalStore","MODAL_ANIMATION_IN","MODAL_ANIMATION_OUT","MODAL_SLIDE_IN","MODAL_SLIDE_OUT","MODAL_SLIDE_IN_LEFT","MODAL_SLIDE_OUT_LEFT","MODAL_SLIDE_IN_RIGHT","MODAL_SLIDE_OUT_RIGHT","MODAL_SLIDE_IN_TOP","MODAL_SLIDE_OUT_TOP","MODAL_SLIDE_IN_BOTTOM","MODAL_SLIDE_OUT_BOTTOM","MODAL_OVERLAY_IN","MODAL_OVERLAY_OUT","REDUCED_MOTION","modalContentVariants","cva","modalOverlayVariants","modalPanelVariants","MODAL_PANEL_SLIDE","modalBodyVariants","modalTabsVariants","ModalContext","createContext","ModalRoot","children","defaultOpen","isOpen","onOpenChange","role","childArray","Children","child","isValidElement","ModalContent","triggerChild","ModalTrigger","unwrappedContent","jsx","AriaDialogTrigger","triggerElement","unwrappedTrigger","jsxs","size","animation","position","mobileBreakpoint","animationDuration","isDismissable","isKeyboardDismissDisabled","shouldCloseOnInteractOutside","showClose","className","useContext","isPanel","isMobile","effectivePosition","overlayClasses","mergedModalClasses","edge","box","slide","AriaModalOverlay","AriaModal","AriaDialog","close","Fragment","AriaButton","X","ModalOverlay","_props","ModalHeader","ModalTitle","as","Heading","ModalDescription","ModalTabs","ModalBody","ModalFooter","ModalClose","state","OverlayTriggerStateContext","childElement","handlePress","existingOnPress","cloneElement","Modal"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * Detection + dismissal guard for in-page writing-assistant browser extensions\n * (Grammarly, LanguageTool, Wordtune, …).\n *\n * These extensions inject their suggestion UI as elements appended to `<body>`,\n * outside any open overlay's DOM subtree. React Aria's `isDismissable` behaviour\n * treats a click on that injected UI as an \"outside click\" and dismisses the\n * overlay — so clicking a spelling suggestion silently closes the form pop-up\n * and discards the user's in-progress correction.\n *\n * The guard below lets overlays keep open when the interaction lands on an\n * extension-injected node, while every other outside click, Escape, and the\n * close button behave exactly as before.\n */\n\nconst MAX_ANCESTOR_DEPTH = 100;\n\n/**\n * Returns `true` when `element` belongs to an injected in-page writing\n * assistant (Grammarly, LanguageTool, Wordtune, …).\n *\n * UX-ONLY heuristic. This is a deliberately spoofable DOM sniff used solely to\n * keep a form pop-up open when a writing-assistant suggestion is clicked. It is\n * NOT a security/authorization control and must never gate access to data or\n * actions (the frontend is not a security boundary). Pure function — never log\n * the element (it can reference arbitrary DOM / user content).\n */\nexport function isExtensionInjectedElement(element: Element): boolean {\n if (!(element instanceof Element)) return false;\n\n // 1. Known-vendor markers (authoritative). Walk up via parentElement, and\n // climb shadow boundaries via getRootNode().host so detection still finds\n // the <grammarly-extension> host if React Aria's shadowDOM flag is ever\n // enabled (it defaults to false today → the document-level listener\n // already receives the retargeted host, so the parentElement walk alone\n // suffices currently). Depth-bounded as a defensive guard against a\n // crafted/overridden getRootNode/host returning a cycle (which would\n // otherwise hang the tab — a client-side DoS).\n let node: Element | null = element;\n for (let depth = 0; node && depth < MAX_ANCESTOR_DEPTH; depth++) {\n const tag = node.tagName?.toLowerCase() ?? '';\n if (tag.startsWith('grammarly-')) return true; // Grammarly\n if (tag.startsWith('lt-')) return true; // LanguageTool\n if (node.hasAttribute('data-grammarly-shadow-root')) return true;\n\n if (node.parentElement) {\n node = node.parentElement;\n } else {\n const root = node.getRootNode();\n node = root instanceof ShadowRoot ? root.host : null;\n }\n }\n\n // 2. Structural fallback (best-effort, future-proofs the long tail).\n // Extensions inject their UI as a custom element (hyphenated tag name)\n // mounted as a top-level sibling of the React root — i.e. a direct child\n // of <body>/<html>. Our app never renders body-level custom elements, and\n // React Aria portals render plain <div>s, so this stays safe for any\n // future first-party web component nested inside the app tree.\n let custom: Element | null = element;\n while (custom && !custom.tagName.includes('-')) {\n custom = custom.parentElement;\n }\n if (!custom) return false;\n\n const parent = custom.parentElement;\n return parent === document.body || parent === document.documentElement;\n}\n\n/**\n * Builds a `shouldCloseOnInteractOutside` predicate for React Aria overlays\n * that keeps the overlay open when the interaction targets an extension-injected\n * element, while preserving any consumer-supplied predicate.\n *\n * React Aria semantics: the predicate returns `true` ⇒ the overlay should\n * close. So we close only when the element is NOT extension-injected AND the\n * consumer (if any) also agrees it should close.\n */\nexport function createInteractOutsideGuard(\n consumerShouldClose?: (element: Element) => boolean,\n): (element: Element) => boolean {\n return (element: Element): boolean =>\n !isExtensionInjectedElement(element) && (consumerShouldClose?.(element) ?? true);\n}\n","'use client';\n\nimport { useCallback, useSyncExternalStore } from 'react';\n\n/**\n * SSR-safe media query hook using useSyncExternalStore.\n *\n * - No hydration mismatch flicker (getServerSnapshot returns false)\n * - Listener registered once per query value (no dependency bugs)\n * - Reacts to changes in real time\n *\n * @param query - CSS media query string, e.g. '(max-width: 767px)'\n * @returns Whether the media query currently matches\n */\nexport function useMediaQuery(query: string): boolean {\n const subscribe = useCallback(\n (callback: () => void) => {\n const mql = window.matchMedia(query);\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n },\n [query],\n );\n\n const getSnapshot = useCallback(\n () => window.matchMedia(query).matches,\n [query],\n );\n\n const getServerSnapshot = useCallback(() => false, []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","/**\n * Shared CVA Variant Utilities\n *\n * Common patterns extracted from component variants for consistency and reduced bundle size.\n * Use these constants in CVA definitions to ensure consistent styling across Themis.\n *\n * @see interaction-states.ts for interaction-specific styles (focus, hover, pressed)\n */\n\n// =============================================================================\n// Focus Ring Patterns\n// =============================================================================\n\n/**\n * Focus-within ring (for container elements with focusable children)\n * Use when the container should show focus when any child is focused\n */\nexport const FOCUS_WITHIN_RING = [\n 'focus-within:outline-none',\n 'focus-within:ring-2',\n 'focus-within:ring-[var(--ring)]',\n 'focus-within:ring-offset-2',\n] as const;\n\n/**\n * Focus-visible ring (for directly focusable elements)\n * Use for buttons, inputs, and other interactive elements\n */\nexport const FOCUS_VISIBLE_RING = [\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n] as const;\n\n/**\n * Standard focus ring (for elements using :focus pseudo-class)\n * Prefer focus-visible when possible for better UX\n */\nexport const FOCUS_RING = [\n 'focus:outline-none',\n 'focus:ring-2',\n 'focus:ring-[var(--ring)]',\n] as const;\n\n/**\n * Focus with background change (for segments, cells, menu items)\n */\nexport const FOCUS_HIGHLIGHT = [\n 'focus:outline-none',\n 'focus:bg-[var(--accent)]',\n 'focus:text-[var(--accent-foreground)]',\n] as const;\n\n// =============================================================================\n// Disabled State Patterns\n// =============================================================================\n\n/**\n * Standard disabled state using disabled attribute\n */\nexport const DISABLED_STANDARD = [\n 'disabled:pointer-events-none',\n 'disabled:opacity-50',\n] as const;\n\n/**\n * Disabled state using data attribute (React Aria pattern)\n */\nexport const DISABLED_DATA_ATTR = [\n 'data-[disabled]:pointer-events-none',\n 'data-[disabled]:opacity-50',\n 'data-[disabled]:cursor-not-allowed',\n] as const;\n\n// =============================================================================\n// Size-Based Text Variants\n// =============================================================================\n\n/**\n * Small text size scale (xs -> sm -> base)\n */\nexport const TEXT_SIZE_SMALL_SCALE = {\n sm: 'text-xs',\n default: 'text-sm',\n lg: 'text-base',\n} as const;\n\n/**\n * Medium text size scale (sm -> base -> lg)\n */\nexport const TEXT_SIZE_MEDIUM_SCALE = {\n sm: 'text-sm',\n default: 'text-base',\n lg: 'text-lg',\n} as const;\n\n// =============================================================================\n// Touch Target Utilities\n// =============================================================================\n\n/**\n * WCAG 2.2 AAA minimum touch target (44x44px)\n */\nexport const TOUCH_TARGET_MIN = [\n 'min-h-[44px]',\n 'min-w-[44px]',\n] as const;\n\n/**\n * Common button/cell sizes with touch target compliance\n */\nexport const INTERACTIVE_SIZES = {\n sm: 'h-9 w-9', // 36px - desktop only, NOT AAA compliant\n default: 'h-11 w-11', // 44px - AAA compliant\n lg: 'h-14 w-14', // 56px - AAA compliant, enhanced\n} as const;\n\n/**\n * Height-only sizes for fields and inputs\n */\nexport const FIELD_HEIGHTS = {\n sm: 'h-9', // 36px\n default: 'h-10', // 40px\n lg: 'h-11', // 44px\n} as const;\n\n/**\n * Layer 2: Visual sizing for form field inputs (height + padding + font).\n * Use on the <input>, <select>, or trigger element itself.\n *\n * Components may append additional classes (e.g. gap-* for segmented fields, py-* for text inputs).\n */\nexport const FIELD_INPUT_SIZES = {\n sm: `${FIELD_HEIGHTS.sm} px-2 text-xs`,\n default: `${FIELD_HEIGHTS.default} px-3 text-sm`,\n lg: `${FIELD_HEIGHTS.lg} px-4 text-base`,\n} as const;\n\n/**\n * Layer 1: Invisible touch target wrapper for form fields.\n * Apply to a <label> (or wrapper div) around the input element.\n * The <label> natively routes clicks/taps to the contained input\n * without JavaScript — achieving a 44px touch target while the\n * input stays at its visual height (h-9/h-10/h-11).\n */\nexport const FIELD_TOUCH_TARGET = 'min-h-[44px] flex items-center' as const;\n\n// =============================================================================\n// Message/Feedback Patterns\n// =============================================================================\n\n/**\n * Error message styling\n */\nexport const ERROR_MESSAGE_BASE = [\n 'flex',\n 'items-center',\n 'gap-1.5',\n 'text-[var(--destructive-background)]',\n] as const;\n\n/**\n * Success message styling\n */\nexport const SUCCESS_MESSAGE_BASE = [\n 'flex',\n 'items-center',\n 'gap-1.5',\n 'text-[var(--success-background)]',\n] as const;\n\n/**\n * Description/helper text styling\n */\nexport const DESCRIPTION_BASE = [\n 'text-[var(--menu-muted)]',\n] as const;\n\n/**\n * Label base styling\n */\nexport const LABEL_BASE = [\n 'font-medium',\n 'text-[var(--content-foreground)]',\n] as const;\n\n/**\n * Required indicator pattern\n */\nexport const REQUIRED_INDICATOR = \"after:content-['*'] after:ml-0.5 after:text-[var(--destructive-background)]\";\n\n// =============================================================================\n// Animation Patterns\n// =============================================================================\n\n/**\n * Popover/dropdown entry animation\n */\nexport const POPOVER_ANIMATION_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:zoom-in-95',\n] as const;\n\n/**\n * Popover/dropdown exit animation\n */\nexport const POPOVER_ANIMATION_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:zoom-out-95',\n] as const;\n\n/**\n * Modal content enter animation (fade + zoom)\n * Uses React Aria data-[entering]/data-[exiting] attributes\n */\nexport const MODAL_ANIMATION_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:zoom-in-95',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\n/**\n * Modal content exit animation (fade + zoom)\n * Uses React Aria data-[entering]/data-[exiting] attributes\n */\nexport const MODAL_ANIMATION_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:zoom-out-95',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\n/**\n * Modal slide enter animation (fade + slide up)\n */\nexport const MODAL_SLIDE_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:slide-in-from-bottom-4',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\n/**\n * Modal slide exit animation (fade + slide down)\n */\nexport const MODAL_SLIDE_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:slide-out-to-bottom-4',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\n/**\n * Directional slide animations for edge-anchored drawers (Modal `position` variant).\n *\n * Unitless `slide-in-from-*` / `slide-out-to-*` translate the full extent of the\n * element (100%), so the drawer slides all the way in from / out to its anchored\n * edge — distinct from `MODAL_SLIDE_*` above, which is the small 1rem nudge used by\n * the centred `slide` animation. No `fade` is paired here: a drawer slides, it\n * does not fade. Honour `prefers-reduced-motion` via REDUCED_MOTION on the element.\n */\nexport const MODAL_SLIDE_IN_LEFT = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-left',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_LEFT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-left',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\nexport const MODAL_SLIDE_IN_RIGHT = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-right',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_RIGHT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-right',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\nexport const MODAL_SLIDE_IN_TOP = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-top',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_TOP = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-top',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\nexport const MODAL_SLIDE_IN_BOTTOM = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-bottom',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_BOTTOM = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-bottom',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\n/**\n * Modal overlay enter/exit animation (fade only)\n */\nexport const MODAL_OVERLAY_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:duration-200',\n] as const;\n\nexport const MODAL_OVERLAY_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:duration-150',\n] as const;\n\n/**\n * Reduced motion support (WCAG 2.2)\n */\nexport const REDUCED_MOTION = [\n 'motion-reduce:transition-none',\n 'motion-reduce:animate-none',\n] as const;\n\n/**\n * Standard transition for colors\n */\nexport const TRANSITION_COLORS = [\n 'transition-colors',\n 'duration-200',\n] as const;\n\n/**\n * Fast transition for interactions\n */\nexport const TRANSITION_FAST = [\n 'transition-colors',\n 'duration-150',\n] as const;\n\n// =============================================================================\n// Hover State Patterns\n// =============================================================================\n\n/**\n * Accent background on hover (for interactive items)\n */\nexport const HOVER_ACCENT = [\n 'hover:bg-[var(--accent)]',\n 'hover:text-[var(--accent-foreground)]',\n] as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Combines multiple style arrays into a flat array for CVA base styles\n */\nexport function combineStyles(...styles: (readonly string[] | string)[]): string[] {\n return styles.flatMap(s => Array.isArray(s) ? [...s] : [s]);\n}\n","import { cva, type VariantProps } from 'class-variance-authority';\nimport {\n MODAL_ANIMATION_IN,\n MODAL_ANIMATION_OUT,\n MODAL_SLIDE_IN,\n MODAL_SLIDE_OUT,\n MODAL_SLIDE_IN_LEFT,\n MODAL_SLIDE_OUT_LEFT,\n MODAL_SLIDE_IN_RIGHT,\n MODAL_SLIDE_OUT_RIGHT,\n MODAL_SLIDE_IN_TOP,\n MODAL_SLIDE_OUT_TOP,\n MODAL_SLIDE_IN_BOTTOM,\n MODAL_SLIDE_OUT_BOTTOM,\n MODAL_OVERLAY_IN,\n MODAL_OVERLAY_OUT,\n REDUCED_MOTION,\n} from '../../styles/shared-variants';\n\n/**\n * CVA Variants for Modal.Content\n *\n * Size and animation variant definitions for the modal content container.\n * Uses React Aria data-[entering]/data-[exiting] attributes for enter+exit animations.\n * Uses CSS custom properties for all colours to support theming.\n *\n * @see Modal.types.ts (ModalSize, ModalAnimation)\n */\nexport const modalContentVariants = cva(\n [\n // Base styles\n 'relative',\n 'bg-[var(--content-background)]',\n 'text-[var(--content-foreground)]',\n 'rounded-lg',\n // No box padding — each region (Header/Tabs/Body/Footer) owns its own\n // padding so the tab strip can sit flush, edge-to-edge. The box is a\n // vertical flex column; `overflow-hidden` clips the full-bleed tab divider\n // and bottom-sheet rounded corners. Body content must be wrapped in\n // `Modal.Body` (which supplies the padding + scroll).\n 'flex',\n 'flex-col',\n 'overflow-hidden',\n 'w-full',\n 'outline-none',\n // Responsive: full-width on mobile with padding, constrained on desktop\n 'mx-4',\n 'sm:mx-0',\n // Reduced motion support\n ...REDUCED_MOTION,\n ],\n {\n variants: {\n // Centred dialogs are capped at 85vh so a long `Modal.Body` scrolls\n // within the box while the header/tabs/footer stay pinned.\n size: {\n sm: 'max-w-sm max-h-[85vh]', // 300px\n md: 'max-w-md max-h-[85vh]', // 425px\n lg: 'max-w-lg max-h-[85vh]', // 600px\n xl: 'max-w-2xl max-h-[85vh]', // 800px\n full: 'max-w-full min-h-screen max-h-none rounded-none', // Full screen\n },\n animation: {\n 'fade-zoom': [\n ...MODAL_ANIMATION_IN,\n ...MODAL_ANIMATION_OUT,\n ],\n fade: [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n ],\n slide: [\n ...MODAL_SLIDE_IN,\n ...MODAL_SLIDE_OUT,\n ],\n none: '',\n },\n },\n defaultVariants: {\n size: 'md',\n animation: 'fade-zoom',\n },\n }\n);\n\n/**\n * CVA Variants for Modal Overlay\n *\n * Animation variant definitions for the modal backdrop overlay.\n * Uses React Aria data-[entering]/data-[exiting] attributes for enter+exit animations.\n * Uses CSS custom properties for all colours to support theming.\n */\nexport const modalOverlayVariants = cva(\n [\n // Base overlay styles\n 'fixed',\n 'inset-0',\n 'z-50',\n 'flex',\n 'bg-black/50',\n 'backdrop-blur-sm',\n // Reduced motion support\n ...REDUCED_MOTION,\n ],\n {\n variants: {\n animation: {\n 'fade-zoom': [...MODAL_OVERLAY_IN, ...MODAL_OVERLAY_OUT],\n fade: [...MODAL_OVERLAY_IN, ...MODAL_OVERLAY_OUT],\n slide: [...MODAL_OVERLAY_IN, ...MODAL_OVERLAY_OUT],\n none: '',\n },\n // How the dialog is anchored within the full-screen backdrop.\n position: {\n center: 'items-center justify-center',\n left: 'items-stretch justify-start',\n right: 'items-stretch justify-end',\n top: 'items-start justify-center',\n bottom: 'items-end justify-center',\n },\n },\n defaultVariants: {\n animation: 'fade-zoom',\n position: 'center',\n },\n }\n);\n\n/**\n * CVA Variants for an edge-anchored Modal drawer/sheet (Modal.Content `position`)\n *\n * The box styling for `position` ∈ {left, right, top, bottom}. `position` sets the\n * full extent on the anchored axis plus the inner-edge border; `size` sets the\n * cross-axis dimension (width for left/right, height for top/bottom) via compound\n * variants. The directional slide animation is applied separately (see\n * MODAL_PANEL_SLIDE) so it can be suppressed for `animation=\"none\"`.\n */\nexport const modalPanelVariants = cva(\n [\n // Base styles — `relative` anchors the absolute close button (as the centred box does)\n 'relative',\n 'bg-[var(--content-background)]',\n 'text-[var(--content-foreground)]',\n // Like the centred box: no box padding (regions own it) and a flex column\n // so `Modal.Body` scrolls within the panel while header/tabs/footer stay\n // pinned. `overflow-hidden` clips the full-bleed tab divider.\n 'flex',\n 'flex-col',\n 'overflow-hidden',\n 'outline-none',\n // Reduced motion support\n ...REDUCED_MOTION,\n ],\n {\n variants: {\n // Only the two exposed corners are rounded (`rounded-lg`, matching the\n // centred modal); the anchored edge stays square against the viewport.\n position: {\n left: 'h-full border-r border-[var(--border)] rounded-r-lg',\n right: 'h-full border-l border-[var(--border)] rounded-l-lg',\n top: 'w-full border-b border-[var(--border)] rounded-b-lg',\n bottom: 'w-full border-t border-[var(--border)] rounded-t-lg',\n },\n // Cross-axis dimension; meaning depends on `position` (see compoundVariants).\n size: {\n sm: '',\n md: '',\n lg: '',\n xl: '',\n full: '',\n },\n },\n compoundVariants: [\n // left/right → width\n { position: ['left', 'right'], size: 'sm', class: 'w-[300px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'md', class: 'w-[400px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'lg', class: 'w-[500px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'xl', class: 'w-[600px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'full', class: 'w-screen' },\n // top/bottom → height\n { position: ['top', 'bottom'], size: 'sm', class: 'h-[200px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'md', class: 'h-[320px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'lg', class: 'h-[440px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'xl', class: 'h-[560px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'full', class: 'h-screen' },\n ],\n defaultVariants: {\n position: 'right',\n size: 'md',\n },\n }\n);\n\n/**\n * Directional slide classes per anchored edge, keyed by position.\n * Applied to the drawer box unless `animation=\"none\"`.\n */\nexport const MODAL_PANEL_SLIDE = {\n left: [...MODAL_SLIDE_IN_LEFT, ...MODAL_SLIDE_OUT_LEFT],\n right: [...MODAL_SLIDE_IN_RIGHT, ...MODAL_SLIDE_OUT_RIGHT],\n top: [...MODAL_SLIDE_IN_TOP, ...MODAL_SLIDE_OUT_TOP],\n bottom: [...MODAL_SLIDE_IN_BOTTOM, ...MODAL_SLIDE_OUT_BOTTOM],\n} as const;\n\n/**\n * CVA Variants for Modal.Body\n *\n * The scrollable content region. Owns the horizontal padding the box used to\n * provide (`p-6` → `px-6`) plus vertical padding, and grows to fill the box\n * (`flex-1 min-h-0`) so long content scrolls here while the header/tabs/footer\n * stay pinned. Holds `TabPanel`s in the tabbed case, arbitrary content\n * otherwise.\n */\nexport const modalBodyVariants = cva([\n 'px-6',\n 'py-4',\n 'flex-1',\n 'min-h-0',\n 'overflow-y-auto',\n]);\n\n/**\n * CVA Variants for Modal.Tabs\n *\n * Full-width tab strip that sits below the header. Stretches the inner\n * `[role=tablist]` to the full box width so its own `border-b` becomes the\n * seamless divider (edge-to-edge — the box has no padding to inset it). The\n * tablist gets `px-2` (8px) which, combined with the default Tab's own `px-4`\n * (16px), lands the first tab's text at the 24px inset used by the `px-6`\n * header and body. The border lives on the tablist box, so it stays flush at\n * both edges regardless of that interior padding. Designed for the `default`\n * Tabs variant (matching PageTitle); other variants inset differently.\n */\nexport const modalTabsVariants = cva([\n '[&_[role=tablist]]:flex',\n '[&_[role=tablist]]:w-full',\n '[&_[role=tablist]]:px-2',\n]);\n\n/**\n * Type exports for variant props\n * Allows TypeScript inference of variant combinations\n */\nexport type ModalContentVariantProps = VariantProps<typeof modalContentVariants>;\nexport type ModalOverlayVariantProps = VariantProps<typeof modalOverlayVariants>;\nexport type ModalPanelVariantProps = VariantProps<typeof modalPanelVariants>;\n","'use client';\n\n/**\n * Modal Component - Implementation\n *\n * Accessible modal dialog component combining React Aria primitives with ShadCN styling.\n * Follows Themis library patterns with compound component structure.\n *\n * @see PRD.md (Full requirements)\n * @see Modal.types.ts (Zod schemas)\n * @see RESEARCH.md (React Aria patterns)\n */\n\nimport {\n Children,\n isValidElement,\n cloneElement,\n useContext,\n createContext,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport {\n DialogTrigger as AriaDialogTrigger,\n Modal as AriaModal,\n ModalOverlay as AriaModalOverlay,\n Dialog as AriaDialog,\n Heading,\n Button as AriaButton,\n OverlayTriggerStateContext,\n} from 'react-aria-components';\nimport { X } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { createInteractOutsideGuard } from '../../utils/browserExtensionOverlay';\nimport { useMediaQuery } from '../../hooks/useMediaQuery';\nimport {\n modalContentVariants,\n modalOverlayVariants,\n modalPanelVariants,\n modalBodyVariants,\n modalTabsVariants,\n MODAL_PANEL_SLIDE,\n} from './Modal.styles';\nimport type {\n ModalProps,\n ModalTriggerProps,\n ModalContentProps,\n ModalOverlayProps,\n ModalHeaderProps,\n ModalTitleProps,\n ModalDescriptionProps,\n ModalBodyProps,\n ModalTabsProps,\n ModalFooterProps,\n ModalCloseProps,\n} from './Modal.types';\n\n/**\n * Modal Context\n * Passes role prop from Modal root to Modal.Content\n */\ninterface ModalContextValue {\n role?: 'dialog' | 'alertdialog';\n}\n\nconst ModalContext = createContext<ModalContextValue | undefined>(undefined);\n\n/**\n * Modal Root Component\n *\n * Manages modal open/close state and validates required children structure.\n * Must contain one Modal.Content. Modal.Trigger is optional — when omitted,\n * use controlled mode (isOpen + onOpenChange) to manage the modal.\n *\n * Unwraps compound component children and passes them to React Aria's DialogTrigger.\n *\n * @see PRD.md FR-001 (Modal Root Requirements)\n * @see PRD.md FR-012 (Controlled Mode)\n * @see PRD.md FR-013 (Uncontrolled Mode)\n */\nfunction ModalRoot({ children, defaultOpen, isOpen, onOpenChange, role = 'dialog' }: ModalProps): ReactElement {\n const childArray = Children.toArray(children);\n\n // Validate children structure in development only\n if (process.env.NODE_ENV !== 'production') {\n const hasContent = childArray.some(\n (child) =>\n isValidElement(child) &&\n (child.type === ModalContent ||\n (child.type as { displayName?: string }).displayName === 'ModalContent')\n );\n\n if (!hasContent) {\n throw new Error(\n 'Modal requires a Modal.Content as a child'\n );\n }\n }\n\n // Find trigger and content children\n const triggerChild = childArray.find(\n (child) =>\n isValidElement(child) &&\n (child.type === ModalTrigger ||\n (child.type as { displayName?: string }).displayName === 'ModalTrigger')\n );\n\n const contentChild = childArray.find(\n (child) =>\n isValidElement(child) &&\n (child.type === ModalContent ||\n (child.type as { displayName?: string }).displayName === 'ModalContent')\n );\n\n const unwrappedContent = contentChild as ReactElement;\n\n // When no trigger is provided, render content directly (controlled mode)\n if (!triggerChild) {\n return (\n <ModalContext.Provider value={{ role }}>\n <AriaDialogTrigger\n defaultOpen={defaultOpen}\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n >\n {unwrappedContent}\n </AriaDialogTrigger>\n </ModalContext.Provider>\n );\n }\n\n // Extract the actual children from Modal.Trigger\n // React Aria's DialogTrigger expects direct Button and Modal children\n const triggerElement = triggerChild as ReactElement;\n const unwrappedTrigger = isValidElement(triggerElement)\n ? ((triggerElement.props as unknown as { children?: ReactNode }).children as ReactNode)\n : null;\n\n return (\n <ModalContext.Provider value={{ role }}>\n <AriaDialogTrigger\n defaultOpen={defaultOpen}\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n >\n {unwrappedTrigger}\n {unwrappedContent}\n </AriaDialogTrigger>\n </ModalContext.Provider>\n );\n}\n\nModalRoot.displayName = 'Modal';\n\n/**\n * Modal.Trigger Component\n *\n * Wraps a single child element (typically Button) with modal trigger behavior.\n * Handles click events to open modal and manages ARIA attributes.\n *\n * React Aria's DialogTrigger (used in Modal root) automatically applies:\n * - aria-haspopup=\"dialog\"\n * - aria-expanded (true/false based on state)\n * - onClick handler to toggle modal\n *\n * @see PRD.md FR-002 (Trigger Requirements)\n * @see RESEARCH.md Section 2 (React Aria Integration)\n */\nfunction ModalTrigger({ children }: ModalTriggerProps): ReactElement {\n // React Aria's DialogTrigger (in Modal root) automatically adds ARIA attributes\n // to the first child component. We pass through the child element directly.\n return children as ReactElement;\n}\n\nModalTrigger.displayName = 'ModalTrigger';\n\n/**\n * Modal.Content Component\n *\n * Renders the modal content with overlay backdrop.\n * Uses React Aria's Modal and ModalOverlay for accessibility.\n * Applies CVA variants for size and animation.\n *\n * @see PRD.md FR-003 (Content Requirements)\n * @see PRD.md TR-001 (CVA Variant Styling)\n * @see RESEARCH.md Section 2 (React Aria Integration)\n */\nfunction ModalContent({\n children,\n size = 'md',\n animation = 'fade-zoom',\n position = 'center',\n mobileBreakpoint = 768,\n animationDuration = 250,\n isDismissable = true,\n isKeyboardDismissDisabled = false,\n shouldCloseOnInteractOutside,\n showClose = true,\n className,\n}: ModalContentProps): ReactElement {\n // Get role from context\n const context = useContext(ModalContext);\n const role = context?.role ?? 'dialog';\n\n // Edge-anchored drawer/sheet when position is not 'center'. Below the\n // breakpoint every drawer collapses to a bottom sheet (matching Panel).\n const isPanel = position !== 'center';\n const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint - 1}px)`);\n const effectivePosition = isPanel && isMobile ? 'bottom' : position;\n\n // Overlay anchors the dialog to the effective edge (or centres it).\n const overlayClasses = modalOverlayVariants({ animation, position: effectivePosition });\n\n // Box classes: the centred dialog keeps its size→max-width behaviour; a drawer\n // uses the panel variant (full extent on the anchored axis + size→cross-axis),\n // with the directional slide applied unless animation is disabled. On mobile the\n // drawer renders as a content-height bottom sheet with a rounded top edge.\n let mergedModalClasses: string;\n if (isPanel) {\n // Within this branch position is never 'center', so effectivePosition is a\n // concrete edge — narrow it for the edge-keyed CVA + slide map.\n const edge = effectivePosition as 'left' | 'right' | 'top' | 'bottom';\n const box = isMobile\n ? cn(modalPanelVariants({ position: 'bottom', size }), 'h-auto max-h-[85vh]')\n : modalPanelVariants({ position: edge, size });\n const slide = animation === 'none' ? undefined : MODAL_PANEL_SLIDE[edge];\n mergedModalClasses = cn(box, slide, className);\n } else {\n mergedModalClasses = cn(modalContentVariants({ size, animation }), className);\n }\n\n return (\n <AriaModalOverlay\n isDismissable={isDismissable}\n isKeyboardDismissDisabled={isKeyboardDismissDisabled}\n shouldCloseOnInteractOutside={createInteractOutsideGuard(shouldCloseOnInteractOutside)}\n className={overlayClasses}\n >\n <AriaModal\n className={mergedModalClasses}\n style={{\n transitionDuration: `${animationDuration}ms`,\n }}\n >\n <AriaDialog role={role} className=\"outline-none\">\n {({ close }) => (\n <>\n {showClose && (\n <AriaButton\n onPress={close}\n className=\"absolute right-0 top-0 flex items-center justify-center rounded-sm opacity-70 ring-offset-[var(--content-background)] transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-[var(--ring)] focus:ring-offset-2 disabled:pointer-events-none min-h-[44px] min-w-[44px]\"\n aria-label=\"Close modal\"\n >\n <X className=\"h-4 w-4\" aria-hidden=\"true\" />\n </AriaButton>\n )}\n {children}\n </>\n )}\n </AriaDialog>\n </AriaModal>\n </AriaModalOverlay>\n );\n}\n\nModalContent.displayName = 'ModalContent';\n\n/**\n * Modal.Overlay Component (Placeholder - not exposed in public API)\n * Overlay is managed internally by Modal.Content via AriaModalOverlay\n */\nfunction ModalOverlay(_props: ModalOverlayProps): null {\n return null;\n}\n\nModalOverlay.displayName = 'ModalOverlay';\n\n/**\n * Modal.Header Component\n *\n * Container for modal header content (typically Title and Description).\n * Applies consistent spacing and layout.\n *\n * @see PRD.md FR-005 (Header Requirements)\n */\nfunction ModalHeader({ children, className }: ModalHeaderProps): ReactElement {\n // px-6 pt-6 pb-4 replaces the padding the box used to supply (Modal.Content\n // is now padding-less so the tab strip can sit flush). pr keeps the close\n // button (absolute, top-right) clear of long titles.\n return (\n <div\n className={cn(\n 'flex flex-col space-y-1.5 px-6 pt-6 pb-4 pr-12 text-center sm:text-left',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nModalHeader.displayName = 'ModalHeader';\n\n/**\n * Modal.Title Component\n *\n * Renders modal title using React Aria's Heading component.\n * Automatically links to modal via aria-labelledby.\n *\n * @see PRD.md FR-006 (Title Requirements)\n * @see PRD.md AR-002 (ARIA Attributes - auto-labelledby)\n */\nfunction ModalTitle({ children, as = 'h2', className }: ModalTitleProps): ReactElement {\n return (\n <Heading slot=\"title\" level={parseInt(as[1] ?? '2')} className={cn('text-lg font-semibold leading-none tracking-tight', className)}>\n {children}\n </Heading>\n );\n}\n\nModalTitle.displayName = 'ModalTitle';\n\n/**\n * Modal.Description Component\n *\n * Renders description text that automatically links to modal via aria-describedby.\n * Uses muted text color for visual hierarchy.\n *\n * @see PRD.md FR-007 (Description Requirements)\n * @see PRD.md AR-002 (ARIA Attributes - auto-describedby)\n */\nfunction ModalDescription({ children, className }: ModalDescriptionProps): ReactElement {\n return (\n <p slot=\"description\" className={cn('text-sm text-[var(--menu-muted)]', className)}>\n {children}\n </p>\n );\n}\n\nModalDescription.displayName = 'ModalDescription';\n\n/**\n * Modal.Tabs Component\n *\n * Optional full-width tab strip that renders below the header. Wrap a Themis\n * `<TabList>` inside it; the matching `<TabPanel>`s go in `Modal.Body`. Both\n * must sit inside a single `<Tabs>` provider — React Aria's Tabs uses a\n * collection, so the tablist and panels can live in separate Modal regions:\n *\n * ```tsx\n * <Tabs defaultSelectedKey=\"a\" className=\"flex min-h-0 flex-1 flex-col\">\n * <Modal.Tabs>\n * <TabList aria-label=\"Sections\"><Tab id=\"a\">A</Tab></TabList>\n * </Modal.Tabs>\n * <Modal.Body>\n * <TabPanel id=\"a\">…</TabPanel>\n * </Modal.Body>\n * </Tabs>\n * ```\n *\n * The `<Tabs>` element needs `flex min-h-0 flex-1 flex-col` so the body grows\n * and scrolls while this strip stays pinned.\n */\nfunction ModalTabs({ children, className }: ModalTabsProps): ReactElement {\n return <div className={cn(modalTabsVariants(), className)}>{children}</div>;\n}\n\nModalTabs.displayName = 'ModalTabs';\n\n/**\n * Modal.Body Component\n *\n * Scrollable content region. Owns the horizontal padding the box used to\n * provide and grows to fill the available height so long content scrolls here\n * while the header/tabs/footer stay pinned. Holds `TabPanel`s in the tabbed\n * case, arbitrary content otherwise. Body content MUST be wrapped in this\n * component — `Modal.Content` no longer pads its children.\n */\nfunction ModalBody({ children, className }: ModalBodyProps): ReactElement {\n return <div className={cn(modalBodyVariants(), className)}>{children}</div>;\n}\n\nModalBody.displayName = 'ModalBody';\n\n/**\n * Modal.Footer Component\n *\n * Container for modal action buttons (Cancel, Confirm, etc.).\n * Aligns buttons to the right with consistent spacing.\n * Responsive: stacks vertically on mobile, horizontal on desktop.\n *\n * @see PRD.md FR-008 (Footer Requirements)\n * @see PRD.md DS-003 (Spacing - footer button gap)\n */\nfunction ModalFooter({ children, className }: ModalFooterProps): ReactElement {\n // px-6 pb-6 pt-4 replaces the padding the box used to supply.\n return <div className={cn('flex flex-col-reverse sm:flex-row sm:justify-end gap-2 px-6 pb-6 pt-4', className)}>{children}</div>;\n}\n\nModalFooter.displayName = 'ModalFooter';\n\n/**\n * Modal.Close Component\n *\n * Wraps a child element (typically Button) with modal close behavior.\n * Uses OverlayTriggerStateContext from React Aria to access close function.\n *\n * @see PRD.md FR-009 (Close Requirements)\n */\nfunction ModalClose({ children }: ModalCloseProps): ReactElement {\n // Access the overlay trigger state from React Aria context\n const state = useContext(OverlayTriggerStateContext);\n\n if (!state) {\n throw new Error('Modal.Close must be used inside Modal.Content');\n }\n\n // Clone the child element and add/merge the onPress handler to close the modal\n if (!isValidElement(children)) {\n throw new Error('Modal.Close requires a valid React element as a child');\n }\n\n // Cast to ReactElement after validation\n const childElement = children as ReactElement;\n\n const handlePress = (): void => {\n state.close();\n };\n\n // Merge with existing onPress if present\n const existingOnPress = (childElement.props as unknown as { onPress?: () => void })?.onPress;\n const mergedOnPress = existingOnPress\n ? (): void => {\n existingOnPress();\n handlePress();\n }\n : handlePress;\n\n // cloneElement with onPress override - use unknown for flexible typing\n return cloneElement(childElement, { onPress: mergedOnPress } as unknown as Partial<typeof childElement.props>);\n}\n\nModalClose.displayName = 'ModalClose';\n\n/**\n * Re-export CVA variants from Modal.styles.ts for backwards compatibility.\n * Consumers importing { modalContentVariants, modalOverlayVariants } from './Modal'\n * will continue to work.\n */\nexport {\n modalContentVariants,\n modalOverlayVariants,\n modalPanelVariants,\n modalBodyVariants,\n modalTabsVariants,\n} from './Modal.styles';\n\n/**\n * Compound Component Export\n *\n * Follows Themis library pattern using Object.assign() for compound components.\n * Enables usage like Modal.Trigger, Modal.Content, etc.\n *\n * @deprecated The Object.assign compound pattern will be removed in v2.\n * Use the direct named exports (ModalTrigger, ModalContent, etc.) instead.\n * This pattern is kept for backwards compatibility only.\n *\n * @see RESEARCH.md Section 1 (Compound Component Pattern)\n */\nexport const Modal = Object.assign(ModalRoot, {\n Trigger: ModalTrigger,\n Content: ModalContent,\n Overlay: ModalOverlay,\n Header: ModalHeader,\n Title: ModalTitle,\n Description: ModalDescription,\n Tabs: ModalTabs,\n Body: ModalBody,\n Footer: ModalFooter,\n Close: ModalClose,\n});\n\n// Named exports for individual components\nexport {\n ModalRoot,\n ModalTrigger,\n ModalContent,\n ModalOverlay,\n ModalHeader,\n ModalTitle,\n ModalDescription,\n ModalTabs,\n ModalBody,\n ModalFooter,\n ModalClose,\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/utils/cn.ts","../../../src/utils/browserExtensionOverlay.ts","../../../src/hooks/useMediaQuery.ts","../../../src/styles/shared-variants.ts","../../../src/elements/Modal/Modal.styles.ts","../../../src/elements/Modal/Modal.tsx"],"names":["cn","inputs","twMerge","clsx","isExtensionInjectedElement","element","node","depth","tag","root","custom","parent","createInteractOutsideGuard","consumerShouldClose","useMediaQuery","query","subscribe","useCallback","callback","mql","getSnapshot","getServerSnapshot","useSyncExternalStore","MODAL_ANIMATION_IN","MODAL_ANIMATION_OUT","MODAL_SLIDE_IN","MODAL_SLIDE_OUT","MODAL_SLIDE_IN_LEFT","MODAL_SLIDE_OUT_LEFT","MODAL_SLIDE_IN_RIGHT","MODAL_SLIDE_OUT_RIGHT","MODAL_SLIDE_IN_TOP","MODAL_SLIDE_OUT_TOP","MODAL_SLIDE_IN_BOTTOM","MODAL_SLIDE_OUT_BOTTOM","MODAL_OVERLAY_IN","MODAL_OVERLAY_OUT","REDUCED_MOTION","modalContentVariants","cva","modalOverlayVariants","modalPanelVariants","MODAL_PANEL_SLIDE","modalBodyVariants","modalTabsVariants","ModalContext","createContext","ModalRoot","children","defaultOpen","isOpen","onOpenChange","role","childArray","Children","child","isValidElement","ModalContent","triggerChild","ModalTrigger","unwrappedContent","jsx","AriaDialogTrigger","triggerElement","unwrappedTrigger","jsxs","size","animation","position","mobileBreakpoint","animationDuration","isDismissable","isKeyboardDismissDisabled","shouldCloseOnInteractOutside","showClose","className","useContext","isPanel","isMobile","effectivePosition","overlayClasses","mergedModalClasses","edge","box","slide","AriaModalOverlay","AriaModal","AriaDialog","close","Fragment","AriaButton","X","ModalOverlay","_props","ModalHeader","ModalTitle","as","Heading","ModalDescription","ModalTabs","ModalBody","ModalFooter","ModalClose","state","OverlayTriggerStateContext","childElement","handlePress","existingOnPress","cloneElement","Modal"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Class Name Utility\n * Merges Tailwind CSS classes with conflict resolution\n *\n * Combines clsx for conditional classes and tailwind-merge for deduplication\n *\n * @example\n * cn('px-2 py-1', 'px-4') // => 'py-1 px-4' (px-4 overrides px-2)\n * cn('text-red-500', condition && 'text-blue-500') // => conditional application\n */\n\nimport { clsx, type ClassValue } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * Detection + dismissal guard for in-page writing-assistant browser extensions\n * (Grammarly, LanguageTool, Wordtune, …).\n *\n * These extensions inject their suggestion UI as elements appended to `<body>`,\n * outside any open overlay's DOM subtree. React Aria's `isDismissable` behaviour\n * treats a click on that injected UI as an \"outside click\" and dismisses the\n * overlay — so clicking a spelling suggestion silently closes the form pop-up\n * and discards the user's in-progress correction.\n *\n * The guard below lets overlays keep open when the interaction lands on an\n * extension-injected node, while every other outside click, Escape, and the\n * close button behave exactly as before.\n */\n\nconst MAX_ANCESTOR_DEPTH = 100;\n\n/**\n * Returns `true` when `element` belongs to an injected in-page writing\n * assistant (Grammarly, LanguageTool, Wordtune, …).\n *\n * UX-ONLY heuristic. This is a deliberately spoofable DOM sniff used solely to\n * keep a form pop-up open when a writing-assistant suggestion is clicked. It is\n * NOT a security/authorization control and must never gate access to data or\n * actions (the frontend is not a security boundary). Pure function — never log\n * the element (it can reference arbitrary DOM / user content).\n */\nexport function isExtensionInjectedElement(element: Element): boolean {\n if (!(element instanceof Element)) return false;\n\n // 1. Known-vendor markers (authoritative). Walk up via parentElement, and\n // climb shadow boundaries via getRootNode().host so detection still finds\n // the <grammarly-extension> host if React Aria's shadowDOM flag is ever\n // enabled (it defaults to false today → the document-level listener\n // already receives the retargeted host, so the parentElement walk alone\n // suffices currently). Depth-bounded as a defensive guard against a\n // crafted/overridden getRootNode/host returning a cycle (which would\n // otherwise hang the tab — a client-side DoS).\n let node: Element | null = element;\n for (let depth = 0; node && depth < MAX_ANCESTOR_DEPTH; depth++) {\n const tag = node.tagName?.toLowerCase() ?? '';\n if (tag.startsWith('grammarly-')) return true; // Grammarly\n if (tag.startsWith('lt-')) return true; // LanguageTool\n if (node.hasAttribute('data-grammarly-shadow-root')) return true;\n\n if (node.parentElement) {\n node = node.parentElement;\n } else {\n const root = node.getRootNode();\n node = root instanceof ShadowRoot ? root.host : null;\n }\n }\n\n // 2. Structural fallback (best-effort, future-proofs the long tail).\n // Extensions inject their UI as a custom element (hyphenated tag name)\n // mounted as a top-level sibling of the React root — i.e. a direct child\n // of <body>/<html>. Our app never renders body-level custom elements, and\n // React Aria portals render plain <div>s, so this stays safe for any\n // future first-party web component nested inside the app tree.\n let custom: Element | null = element;\n while (custom && !custom.tagName.includes('-')) {\n custom = custom.parentElement;\n }\n if (!custom) return false;\n\n const parent = custom.parentElement;\n return parent === document.body || parent === document.documentElement;\n}\n\n/**\n * Builds a `shouldCloseOnInteractOutside` predicate for React Aria overlays\n * that keeps the overlay open when the interaction targets an extension-injected\n * element, while preserving any consumer-supplied predicate.\n *\n * React Aria semantics: the predicate returns `true` ⇒ the overlay should\n * close. So we close only when the element is NOT extension-injected AND the\n * consumer (if any) also agrees it should close.\n */\nexport function createInteractOutsideGuard(\n consumerShouldClose?: (element: Element) => boolean,\n): (element: Element) => boolean {\n return (element: Element): boolean =>\n !isExtensionInjectedElement(element) && (consumerShouldClose?.(element) ?? true);\n}\n","'use client';\n\nimport { useCallback, useSyncExternalStore } from 'react';\n\n/**\n * SSR-safe media query hook using useSyncExternalStore.\n *\n * - No hydration mismatch flicker (getServerSnapshot returns false)\n * - Listener registered once per query value (no dependency bugs)\n * - Reacts to changes in real time\n *\n * @param query - CSS media query string, e.g. '(max-width: 767px)'\n * @returns Whether the media query currently matches\n */\nexport function useMediaQuery(query: string): boolean {\n const subscribe = useCallback(\n (callback: () => void) => {\n const mql = window.matchMedia(query);\n mql.addEventListener('change', callback);\n return () => mql.removeEventListener('change', callback);\n },\n [query],\n );\n\n const getSnapshot = useCallback(\n () => window.matchMedia(query).matches,\n [query],\n );\n\n const getServerSnapshot = useCallback(() => false, []);\n\n return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n","/**\n * Shared CVA Variant Utilities\n *\n * Common patterns extracted from component variants for consistency and reduced bundle size.\n * Use these constants in CVA definitions to ensure consistent styling across Themis.\n *\n * @see interaction-states.ts for interaction-specific styles (focus, hover, pressed)\n */\n\n// =============================================================================\n// Focus Ring Patterns\n// =============================================================================\n\n/**\n * Focus-within ring (for container elements with focusable children)\n * Use when the container should show focus when any child is focused\n */\nexport const FOCUS_WITHIN_RING = [\n 'focus-within:outline-none',\n 'focus-within:ring-2',\n 'focus-within:ring-[var(--ring)]',\n 'focus-within:ring-offset-2',\n] as const;\n\n/**\n * Focus-visible ring (for directly focusable elements)\n * Use for buttons, inputs, and other interactive elements\n */\nexport const FOCUS_VISIBLE_RING = [\n 'focus-visible:outline-none',\n 'focus-visible:ring-2',\n 'focus-visible:ring-[var(--ring)]',\n 'focus-visible:ring-offset-2',\n] as const;\n\n/**\n * Standard focus ring (for elements using :focus pseudo-class)\n * Prefer focus-visible when possible for better UX\n */\nexport const FOCUS_RING = [\n 'focus:outline-none',\n 'focus:ring-2',\n 'focus:ring-[var(--ring)]',\n] as const;\n\n/**\n * Focus with background change (for segments, cells, menu items)\n */\nexport const FOCUS_HIGHLIGHT = [\n 'focus:outline-none',\n 'focus:bg-[var(--accent)]',\n 'focus:text-[var(--accent-foreground)]',\n] as const;\n\n// =============================================================================\n// Disabled State Patterns\n// =============================================================================\n\n/**\n * Standard disabled state using disabled attribute\n */\nexport const DISABLED_STANDARD = [\n 'disabled:pointer-events-none',\n 'disabled:opacity-50',\n] as const;\n\n/**\n * Disabled state using data attribute (React Aria pattern)\n */\nexport const DISABLED_DATA_ATTR = [\n 'data-[disabled]:pointer-events-none',\n 'data-[disabled]:opacity-50',\n 'data-[disabled]:cursor-not-allowed',\n] as const;\n\n// =============================================================================\n// Size-Based Text Variants\n// =============================================================================\n\n/**\n * Small text size scale (xs -> sm -> base)\n */\nexport const TEXT_SIZE_SMALL_SCALE = {\n sm: 'text-xs',\n default: 'text-sm',\n lg: 'text-base',\n} as const;\n\n/**\n * Medium text size scale (sm -> base -> lg)\n */\nexport const TEXT_SIZE_MEDIUM_SCALE = {\n sm: 'text-sm',\n default: 'text-base',\n lg: 'text-lg',\n} as const;\n\n// =============================================================================\n// Touch Target Utilities\n// =============================================================================\n\n/**\n * WCAG 2.2 AAA minimum touch target (44x44px)\n */\nexport const TOUCH_TARGET_MIN = [\n 'min-h-[44px]',\n 'min-w-[44px]',\n] as const;\n\n/**\n * Common button/cell sizes with touch target compliance\n */\nexport const INTERACTIVE_SIZES = {\n sm: 'h-9 w-9', // 36px - desktop only, NOT AAA compliant\n default: 'h-11 w-11', // 44px - AAA compliant\n lg: 'h-14 w-14', // 56px - AAA compliant, enhanced\n} as const;\n\n/**\n * Height-only sizes for fields and inputs\n */\nexport const FIELD_HEIGHTS = {\n sm: 'h-9', // 36px\n default: 'h-10', // 40px\n lg: 'h-11', // 44px\n} as const;\n\n/**\n * Layer 2: Visual sizing for form field inputs (height + padding + font).\n * Use on the <input>, <select>, or trigger element itself.\n *\n * Components may append additional classes (e.g. gap-* for segmented fields, py-* for text inputs).\n */\nexport const FIELD_INPUT_SIZES = {\n sm: `${FIELD_HEIGHTS.sm} px-2 text-xs`,\n default: `${FIELD_HEIGHTS.default} px-3 text-sm`,\n lg: `${FIELD_HEIGHTS.lg} px-4 text-base`,\n} as const;\n\n/**\n * Layer 1: Invisible touch target wrapper for form fields.\n * Apply to a <label> (or wrapper div) around the input element.\n * The <label> natively routes clicks/taps to the contained input\n * without JavaScript — achieving a 44px touch target while the\n * input stays at its visual height (h-9/h-10/h-11).\n */\nexport const FIELD_TOUCH_TARGET = 'min-h-[44px] flex items-center' as const;\n\n// =============================================================================\n// Message/Feedback Patterns\n// =============================================================================\n\n/**\n * Error message styling\n */\nexport const ERROR_MESSAGE_BASE = [\n 'flex',\n 'items-center',\n 'gap-1.5',\n 'text-[var(--destructive-background)]',\n] as const;\n\n/**\n * Success message styling\n */\nexport const SUCCESS_MESSAGE_BASE = [\n 'flex',\n 'items-center',\n 'gap-1.5',\n 'text-[var(--success-background)]',\n] as const;\n\n/**\n * Description/helper text styling\n */\nexport const DESCRIPTION_BASE = [\n 'text-[var(--menu-muted)]',\n] as const;\n\n/**\n * Label base styling\n */\nexport const LABEL_BASE = [\n 'font-medium',\n 'text-[var(--content-foreground)]',\n] as const;\n\n/**\n * Required indicator pattern\n */\nexport const REQUIRED_INDICATOR = \"after:content-['*'] after:ml-0.5 after:text-[var(--destructive-background)]\";\n\n// =============================================================================\n// Animation Patterns\n// =============================================================================\n\n/**\n * Popover/dropdown entry animation\n */\nexport const POPOVER_ANIMATION_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:zoom-in-95',\n] as const;\n\n/**\n * Popover/dropdown exit animation\n */\nexport const POPOVER_ANIMATION_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:zoom-out-95',\n] as const;\n\n/**\n * Modal content enter animation (fade + zoom)\n * Uses React Aria data-[entering]/data-[exiting] attributes\n */\nexport const MODAL_ANIMATION_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:zoom-in-95',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\n/**\n * Modal content exit animation (fade + zoom)\n * Uses React Aria data-[entering]/data-[exiting] attributes\n */\nexport const MODAL_ANIMATION_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:zoom-out-95',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\n/**\n * Modal slide enter animation (fade + slide up)\n */\nexport const MODAL_SLIDE_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:slide-in-from-bottom-4',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\n/**\n * Modal slide exit animation (fade + slide down)\n */\nexport const MODAL_SLIDE_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:slide-out-to-bottom-4',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\n/**\n * Directional slide animations for edge-anchored drawers (Modal `position` variant).\n *\n * Unitless `slide-in-from-*` / `slide-out-to-*` translate the full extent of the\n * element (100%), so the drawer slides all the way in from / out to its anchored\n * edge — distinct from `MODAL_SLIDE_*` above, which is the small 1rem nudge used by\n * the centred `slide` animation. No `fade` is paired here: a drawer slides, it\n * does not fade. Honour `prefers-reduced-motion` via REDUCED_MOTION on the element.\n */\nexport const MODAL_SLIDE_IN_LEFT = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-left',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_LEFT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-left',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\nexport const MODAL_SLIDE_IN_RIGHT = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-right',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_RIGHT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-right',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\nexport const MODAL_SLIDE_IN_TOP = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-top',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_TOP = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-top',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\nexport const MODAL_SLIDE_IN_BOTTOM = [\n 'data-[entering]:animate-in',\n 'data-[entering]:slide-in-from-bottom',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n] as const;\n\nexport const MODAL_SLIDE_OUT_BOTTOM = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:slide-out-to-bottom',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n] as const;\n\n/**\n * Modal overlay enter/exit animation (fade only)\n */\nexport const MODAL_OVERLAY_IN = [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:duration-200',\n] as const;\n\nexport const MODAL_OVERLAY_OUT = [\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:duration-150',\n] as const;\n\n/**\n * Reduced motion support (WCAG 2.2)\n */\nexport const REDUCED_MOTION = [\n 'motion-reduce:transition-none',\n 'motion-reduce:animate-none',\n] as const;\n\n/**\n * Standard transition for colors\n */\nexport const TRANSITION_COLORS = [\n 'transition-colors',\n 'duration-200',\n] as const;\n\n/**\n * Fast transition for interactions\n */\nexport const TRANSITION_FAST = [\n 'transition-colors',\n 'duration-150',\n] as const;\n\n// =============================================================================\n// Hover State Patterns\n// =============================================================================\n\n/**\n * Accent background on hover (for interactive items)\n */\nexport const HOVER_ACCENT = [\n 'hover:bg-[var(--accent)]',\n 'hover:text-[var(--accent-foreground)]',\n] as const;\n\n// =============================================================================\n// Helper Functions\n// =============================================================================\n\n/**\n * Combines multiple style arrays into a flat array for CVA base styles\n */\nexport function combineStyles(...styles: (readonly string[] | string)[]): string[] {\n return styles.flatMap(s => Array.isArray(s) ? [...s] : [s]);\n}\n","import { cva, type VariantProps } from 'class-variance-authority';\nimport {\n MODAL_ANIMATION_IN,\n MODAL_ANIMATION_OUT,\n MODAL_SLIDE_IN,\n MODAL_SLIDE_OUT,\n MODAL_SLIDE_IN_LEFT,\n MODAL_SLIDE_OUT_LEFT,\n MODAL_SLIDE_IN_RIGHT,\n MODAL_SLIDE_OUT_RIGHT,\n MODAL_SLIDE_IN_TOP,\n MODAL_SLIDE_OUT_TOP,\n MODAL_SLIDE_IN_BOTTOM,\n MODAL_SLIDE_OUT_BOTTOM,\n MODAL_OVERLAY_IN,\n MODAL_OVERLAY_OUT,\n REDUCED_MOTION,\n} from '../../styles/shared-variants';\n\n/**\n * CVA Variants for Modal.Content\n *\n * Size and animation variant definitions for the modal content container.\n * Uses React Aria data-[entering]/data-[exiting] attributes for enter+exit animations.\n * Uses CSS custom properties for all colours to support theming.\n *\n * @see Modal.types.ts (ModalSize, ModalAnimation)\n */\nexport const modalContentVariants = cva(\n [\n // Base styles\n 'relative',\n 'bg-[var(--content-background)]',\n 'text-[var(--content-foreground)]',\n 'rounded-lg',\n // No box padding — each region (Header/Tabs/Body/Footer) owns its own\n // padding so the tab strip can sit flush, edge-to-edge. The box is a\n // vertical flex column; `overflow-hidden` clips the full-bleed tab divider\n // and bottom-sheet rounded corners. Body content must be wrapped in\n // `Modal.Body` (which supplies the padding + scroll).\n 'flex',\n 'flex-col',\n 'overflow-hidden',\n 'w-full',\n 'outline-none',\n // Responsive: full-width on mobile with padding, constrained on desktop\n 'mx-4',\n 'sm:mx-0',\n // Reduced motion support\n ...REDUCED_MOTION,\n ],\n {\n variants: {\n // Centred dialogs are capped at 85vh so a long `Modal.Body` scrolls\n // within the box while the header/tabs/footer stay pinned.\n size: {\n sm: 'max-w-sm max-h-[85vh]', // 300px\n md: 'max-w-md max-h-[85vh]', // 425px\n lg: 'max-w-lg max-h-[85vh]', // 600px\n xl: 'max-w-2xl max-h-[85vh]', // 800px\n full: 'max-w-full min-h-screen max-h-none rounded-none', // Full screen\n },\n animation: {\n 'fade-zoom': [\n ...MODAL_ANIMATION_IN,\n ...MODAL_ANIMATION_OUT,\n ],\n fade: [\n 'data-[entering]:animate-in',\n 'data-[entering]:fade-in-0',\n 'data-[entering]:duration-200',\n 'data-[entering]:ease-out',\n 'data-[exiting]:animate-out',\n 'data-[exiting]:fade-out-0',\n 'data-[exiting]:duration-150',\n 'data-[exiting]:ease-in',\n ],\n slide: [\n ...MODAL_SLIDE_IN,\n ...MODAL_SLIDE_OUT,\n ],\n none: '',\n },\n },\n defaultVariants: {\n size: 'md',\n animation: 'fade-zoom',\n },\n }\n);\n\n/**\n * CVA Variants for Modal Overlay\n *\n * Animation variant definitions for the modal backdrop overlay.\n * Uses React Aria data-[entering]/data-[exiting] attributes for enter+exit animations.\n * Uses CSS custom properties for all colours to support theming.\n */\nexport const modalOverlayVariants = cva(\n [\n // Base overlay styles\n 'fixed',\n 'inset-0',\n 'z-50',\n 'flex',\n 'bg-black/50',\n 'backdrop-blur-sm',\n // Reduced motion support\n ...REDUCED_MOTION,\n ],\n {\n variants: {\n animation: {\n 'fade-zoom': [...MODAL_OVERLAY_IN, ...MODAL_OVERLAY_OUT],\n fade: [...MODAL_OVERLAY_IN, ...MODAL_OVERLAY_OUT],\n slide: [...MODAL_OVERLAY_IN, ...MODAL_OVERLAY_OUT],\n none: '',\n },\n // How the dialog is anchored within the full-screen backdrop.\n position: {\n center: 'items-center justify-center',\n left: 'items-stretch justify-start',\n right: 'items-stretch justify-end',\n top: 'items-start justify-center',\n bottom: 'items-end justify-center',\n },\n },\n defaultVariants: {\n animation: 'fade-zoom',\n position: 'center',\n },\n }\n);\n\n/**\n * CVA Variants for an edge-anchored Modal drawer/sheet (Modal.Content `position`)\n *\n * The box styling for `position` ∈ {left, right, top, bottom}. `position` sets the\n * full extent on the anchored axis plus the inner-edge border; `size` sets the\n * cross-axis dimension (width for left/right, height for top/bottom) via compound\n * variants. The directional slide animation is applied separately (see\n * MODAL_PANEL_SLIDE) so it can be suppressed for `animation=\"none\"`.\n */\nexport const modalPanelVariants = cva(\n [\n // Base styles — `relative` anchors the absolute close button (as the centred box does)\n 'relative',\n 'bg-[var(--content-background)]',\n 'text-[var(--content-foreground)]',\n // Like the centred box: no box padding (regions own it) and a flex column\n // so `Modal.Body` scrolls within the panel while header/tabs/footer stay\n // pinned. `overflow-hidden` clips the full-bleed tab divider.\n 'flex',\n 'flex-col',\n 'overflow-hidden',\n 'outline-none',\n // Reduced motion support\n ...REDUCED_MOTION,\n ],\n {\n variants: {\n // Only the two exposed corners are rounded (`rounded-lg`, matching the\n // centred modal); the anchored edge stays square against the viewport.\n position: {\n left: 'h-full border-r border-[var(--border)] rounded-r-lg',\n right: 'h-full border-l border-[var(--border)] rounded-l-lg',\n top: 'w-full border-b border-[var(--border)] rounded-b-lg',\n bottom: 'w-full border-t border-[var(--border)] rounded-t-lg',\n },\n // Cross-axis dimension; meaning depends on `position` (see compoundVariants).\n size: {\n sm: '',\n md: '',\n lg: '',\n xl: '',\n full: '',\n },\n },\n compoundVariants: [\n // left/right → width\n { position: ['left', 'right'], size: 'sm', class: 'w-[300px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'md', class: 'w-[400px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'lg', class: 'w-[500px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'xl', class: 'w-[600px] max-w-[90vw]' },\n { position: ['left', 'right'], size: 'full', class: 'w-screen' },\n // top/bottom → height\n { position: ['top', 'bottom'], size: 'sm', class: 'h-[200px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'md', class: 'h-[320px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'lg', class: 'h-[440px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'xl', class: 'h-[560px] max-h-[90vh]' },\n { position: ['top', 'bottom'], size: 'full', class: 'h-screen' },\n ],\n defaultVariants: {\n position: 'right',\n size: 'md',\n },\n }\n);\n\n/**\n * Directional slide classes per anchored edge, keyed by position.\n * Applied to the drawer box unless `animation=\"none\"`.\n */\nexport const MODAL_PANEL_SLIDE = {\n left: [...MODAL_SLIDE_IN_LEFT, ...MODAL_SLIDE_OUT_LEFT],\n right: [...MODAL_SLIDE_IN_RIGHT, ...MODAL_SLIDE_OUT_RIGHT],\n top: [...MODAL_SLIDE_IN_TOP, ...MODAL_SLIDE_OUT_TOP],\n bottom: [...MODAL_SLIDE_IN_BOTTOM, ...MODAL_SLIDE_OUT_BOTTOM],\n} as const;\n\n/**\n * CVA Variants for Modal.Body\n *\n * The scrollable content region. Owns the horizontal padding the box used to\n * provide (`p-6` → `px-6`) plus vertical padding, and grows to fill the box\n * (`flex-1 min-h-0`) so long content scrolls here while the header/tabs/footer\n * stay pinned. Holds `TabPanel`s in the tabbed case, arbitrary content\n * otherwise.\n */\nexport const modalBodyVariants = cva([\n 'px-6',\n 'py-4',\n 'flex-1',\n 'min-h-0',\n 'overflow-y-auto',\n]);\n\n/**\n * CVA Variants for Modal.Tabs\n *\n * Full-width tab strip that sits below the header. Stretches the inner\n * `[role=tablist]` to the full box width so its own `border-b` becomes the\n * seamless divider (edge-to-edge — the box has no padding to inset it). The\n * tablist gets `px-2` (8px) which, combined with the default Tab's own `px-4`\n * (16px), lands the first tab's text at the 24px inset used by the `px-6`\n * header and body. The border lives on the tablist box, so it stays flush at\n * both edges regardless of that interior padding. Designed for the `default`\n * Tabs variant (matching PageTitle); other variants inset differently.\n */\nexport const modalTabsVariants = cva([\n '[&_[role=tablist]]:flex',\n '[&_[role=tablist]]:w-full',\n '[&_[role=tablist]]:px-2',\n]);\n\n/**\n * Type exports for variant props\n * Allows TypeScript inference of variant combinations\n */\nexport type ModalContentVariantProps = VariantProps<typeof modalContentVariants>;\nexport type ModalOverlayVariantProps = VariantProps<typeof modalOverlayVariants>;\nexport type ModalPanelVariantProps = VariantProps<typeof modalPanelVariants>;\n","'use client';\n\n/**\n * Modal Component - Implementation\n *\n * Accessible modal dialog component combining React Aria primitives with ShadCN styling.\n * Follows Themis library patterns with compound component structure.\n *\n * @see PRD.md (Full requirements)\n * @see Modal.types.ts (Zod schemas)\n * @see RESEARCH.md (React Aria patterns)\n */\n\nimport {\n Children,\n isValidElement,\n cloneElement,\n useContext,\n createContext,\n type ReactElement,\n type ReactNode,\n} from 'react';\nimport {\n DialogTrigger as AriaDialogTrigger,\n Modal as AriaModal,\n ModalOverlay as AriaModalOverlay,\n Dialog as AriaDialog,\n Heading,\n Button as AriaButton,\n OverlayTriggerStateContext,\n} from 'react-aria-components';\nimport { X } from 'lucide-react';\nimport { cn } from '../../utils/cn';\nimport { createInteractOutsideGuard } from '../../utils/browserExtensionOverlay';\nimport { useMediaQuery } from '../../hooks/useMediaQuery';\nimport {\n modalContentVariants,\n modalOverlayVariants,\n modalPanelVariants,\n modalBodyVariants,\n modalTabsVariants,\n MODAL_PANEL_SLIDE,\n} from './Modal.styles';\nimport type {\n ModalProps,\n ModalTriggerProps,\n ModalContentProps,\n ModalOverlayProps,\n ModalHeaderProps,\n ModalTitleProps,\n ModalDescriptionProps,\n ModalBodyProps,\n ModalTabsProps,\n ModalFooterProps,\n ModalCloseProps,\n} from './Modal.types';\n\n/**\n * Modal Context\n * Passes role prop from Modal root to Modal.Content\n */\ninterface ModalContextValue {\n role?: 'dialog' | 'alertdialog';\n}\n\nconst ModalContext = createContext<ModalContextValue | undefined>(undefined);\n\n/**\n * Modal Root Component\n *\n * Manages modal open/close state and validates required children structure.\n * Must contain one Modal.Content. Modal.Trigger is optional — when omitted,\n * use controlled mode (isOpen + onOpenChange) to manage the modal.\n *\n * Unwraps compound component children and passes them to React Aria's DialogTrigger.\n *\n * @see PRD.md FR-001 (Modal Root Requirements)\n * @see PRD.md FR-012 (Controlled Mode)\n * @see PRD.md FR-013 (Uncontrolled Mode)\n */\nfunction ModalRoot({ children, defaultOpen, isOpen, onOpenChange, role = 'dialog' }: ModalProps): ReactElement {\n const childArray = Children.toArray(children);\n\n // Validate children structure in development only\n if (process.env.NODE_ENV !== 'production') {\n const hasContent = childArray.some(\n (child) =>\n isValidElement(child) &&\n (child.type === ModalContent ||\n (child.type as { displayName?: string }).displayName === 'ModalContent')\n );\n\n if (!hasContent) {\n throw new Error(\n 'Modal requires a Modal.Content as a child'\n );\n }\n }\n\n // Find trigger and content children\n const triggerChild = childArray.find(\n (child) =>\n isValidElement(child) &&\n (child.type === ModalTrigger ||\n (child.type as { displayName?: string }).displayName === 'ModalTrigger')\n );\n\n const contentChild = childArray.find(\n (child) =>\n isValidElement(child) &&\n (child.type === ModalContent ||\n (child.type as { displayName?: string }).displayName === 'ModalContent')\n );\n\n const unwrappedContent = contentChild as ReactElement;\n\n // When no trigger is provided, render content directly (controlled mode)\n if (!triggerChild) {\n return (\n <ModalContext.Provider value={{ role }}>\n <AriaDialogTrigger\n defaultOpen={defaultOpen}\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n >\n {unwrappedContent}\n </AriaDialogTrigger>\n </ModalContext.Provider>\n );\n }\n\n // Extract the actual children from Modal.Trigger\n // React Aria's DialogTrigger expects direct Button and Modal children\n const triggerElement = triggerChild as ReactElement;\n const unwrappedTrigger = isValidElement(triggerElement)\n ? ((triggerElement.props as unknown as { children?: ReactNode }).children as ReactNode)\n : null;\n\n return (\n <ModalContext.Provider value={{ role }}>\n <AriaDialogTrigger\n defaultOpen={defaultOpen}\n isOpen={isOpen}\n onOpenChange={onOpenChange}\n >\n {unwrappedTrigger}\n {unwrappedContent}\n </AriaDialogTrigger>\n </ModalContext.Provider>\n );\n}\n\nModalRoot.displayName = 'Modal';\n\n/**\n * Modal.Trigger Component\n *\n * Wraps a single child element (typically Button) with modal trigger behavior.\n * Handles click events to open modal and manages ARIA attributes.\n *\n * React Aria's DialogTrigger (used in Modal root) automatically applies:\n * - aria-haspopup=\"dialog\"\n * - aria-expanded (true/false based on state)\n * - onClick handler to toggle modal\n *\n * @see PRD.md FR-002 (Trigger Requirements)\n * @see RESEARCH.md Section 2 (React Aria Integration)\n */\nfunction ModalTrigger({ children }: ModalTriggerProps): ReactElement {\n // React Aria's DialogTrigger (in Modal root) automatically adds ARIA attributes\n // to the first child component. We pass through the child element directly.\n return children as ReactElement;\n}\n\nModalTrigger.displayName = 'ModalTrigger';\n\n/**\n * Modal.Content Component\n *\n * Renders the modal content with overlay backdrop.\n * Uses React Aria's Modal and ModalOverlay for accessibility.\n * Applies CVA variants for size and animation.\n *\n * @see PRD.md FR-003 (Content Requirements)\n * @see PRD.md TR-001 (CVA Variant Styling)\n * @see RESEARCH.md Section 2 (React Aria Integration)\n */\nfunction ModalContent({\n children,\n size = 'md',\n animation = 'fade-zoom',\n position = 'center',\n mobileBreakpoint = 768,\n animationDuration = 250,\n isDismissable = true,\n isKeyboardDismissDisabled = false,\n shouldCloseOnInteractOutside,\n showClose = true,\n className,\n}: ModalContentProps): ReactElement {\n // Get role from context\n const context = useContext(ModalContext);\n const role = context?.role ?? 'dialog';\n\n // Edge-anchored drawer/sheet when position is not 'center'. Below the\n // breakpoint every drawer collapses to a bottom sheet (matching Panel).\n const isPanel = position !== 'center';\n const isMobile = useMediaQuery(`(max-width: ${mobileBreakpoint - 1}px)`);\n const effectivePosition = isPanel && isMobile ? 'bottom' : position;\n\n // Overlay anchors the dialog to the effective edge (or centres it).\n const overlayClasses = modalOverlayVariants({ animation, position: effectivePosition });\n\n // Box classes: the centred dialog keeps its size→max-width behaviour; a drawer\n // uses the panel variant (full extent on the anchored axis + size→cross-axis),\n // with the directional slide applied unless animation is disabled. On mobile the\n // drawer renders as a content-height bottom sheet with a rounded top edge.\n let mergedModalClasses: string;\n if (isPanel) {\n // Within this branch position is never 'center', so effectivePosition is a\n // concrete edge — narrow it for the edge-keyed CVA + slide map.\n const edge = effectivePosition as 'left' | 'right' | 'top' | 'bottom';\n const box = isMobile\n ? cn(modalPanelVariants({ position: 'bottom', size }), 'h-auto max-h-[85vh]')\n : modalPanelVariants({ position: edge, size });\n const slide = animation === 'none' ? undefined : MODAL_PANEL_SLIDE[edge];\n mergedModalClasses = cn(box, slide, className);\n } else {\n mergedModalClasses = cn(modalContentVariants({ size, animation }), className);\n }\n\n return (\n <AriaModalOverlay\n isDismissable={isDismissable}\n isKeyboardDismissDisabled={isKeyboardDismissDisabled}\n shouldCloseOnInteractOutside={createInteractOutsideGuard(shouldCloseOnInteractOutside)}\n className={overlayClasses}\n >\n <AriaModal\n className={mergedModalClasses}\n style={{\n transitionDuration: `${animationDuration}ms`,\n }}\n >\n {/*\n The flex column + height cap live on AriaModal, but the regions\n (Header/Tabs/Body/Footer) are children of AriaDialog — so AriaDialog\n must fill AriaModal (flex-1 min-h-0) and be a flex column itself, or\n Modal.Body's `flex-1 overflow-y-auto` has no flex parent to scroll\n within and tall content is silently clipped by AriaModal's\n overflow-hidden instead of scrolling.\n */}\n <AriaDialog role={role} className=\"flex flex-1 min-h-0 flex-col outline-none\">\n {({ close }) => (\n <>\n {showClose && (\n <AriaButton\n onPress={close}\n className=\"absolute right-0 top-0 flex items-center justify-center rounded-sm opacity-70 ring-offset-[var(--content-background)] transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-[var(--ring)] focus:ring-offset-2 disabled:pointer-events-none min-h-[44px] min-w-[44px]\"\n aria-label=\"Close modal\"\n >\n <X className=\"h-4 w-4\" aria-hidden=\"true\" />\n </AriaButton>\n )}\n {children}\n </>\n )}\n </AriaDialog>\n </AriaModal>\n </AriaModalOverlay>\n );\n}\n\nModalContent.displayName = 'ModalContent';\n\n/**\n * Modal.Overlay Component (Placeholder - not exposed in public API)\n * Overlay is managed internally by Modal.Content via AriaModalOverlay\n */\nfunction ModalOverlay(_props: ModalOverlayProps): null {\n return null;\n}\n\nModalOverlay.displayName = 'ModalOverlay';\n\n/**\n * Modal.Header Component\n *\n * Container for modal header content (typically Title and Description).\n * Applies consistent spacing and layout.\n *\n * @see PRD.md FR-005 (Header Requirements)\n */\nfunction ModalHeader({ children, className }: ModalHeaderProps): ReactElement {\n // px-6 pt-6 pb-4 replaces the padding the box used to supply (Modal.Content\n // is now padding-less so the tab strip can sit flush). pr keeps the close\n // button (absolute, top-right) clear of long titles.\n return (\n <div\n className={cn(\n 'flex flex-col space-y-1.5 px-6 pt-6 pb-4 pr-12 text-center sm:text-left',\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\nModalHeader.displayName = 'ModalHeader';\n\n/**\n * Modal.Title Component\n *\n * Renders modal title using React Aria's Heading component.\n * Automatically links to modal via aria-labelledby.\n *\n * @see PRD.md FR-006 (Title Requirements)\n * @see PRD.md AR-002 (ARIA Attributes - auto-labelledby)\n */\nfunction ModalTitle({ children, as = 'h2', className }: ModalTitleProps): ReactElement {\n return (\n <Heading slot=\"title\" level={parseInt(as[1] ?? '2')} className={cn('text-lg font-semibold leading-none tracking-tight', className)}>\n {children}\n </Heading>\n );\n}\n\nModalTitle.displayName = 'ModalTitle';\n\n/**\n * Modal.Description Component\n *\n * Renders description text that automatically links to modal via aria-describedby.\n * Uses muted text color for visual hierarchy.\n *\n * @see PRD.md FR-007 (Description Requirements)\n * @see PRD.md AR-002 (ARIA Attributes - auto-describedby)\n */\nfunction ModalDescription({ children, className }: ModalDescriptionProps): ReactElement {\n return (\n <p slot=\"description\" className={cn('text-sm text-[var(--menu-muted)]', className)}>\n {children}\n </p>\n );\n}\n\nModalDescription.displayName = 'ModalDescription';\n\n/**\n * Modal.Tabs Component\n *\n * Optional full-width tab strip that renders below the header. Wrap a Themis\n * `<TabList>` inside it; the matching `<TabPanel>`s go in `Modal.Body`. Both\n * must sit inside a single `<Tabs>` provider — React Aria's Tabs uses a\n * collection, so the tablist and panels can live in separate Modal regions:\n *\n * ```tsx\n * <Tabs defaultSelectedKey=\"a\" className=\"flex min-h-0 flex-1 flex-col\">\n * <Modal.Tabs>\n * <TabList aria-label=\"Sections\"><Tab id=\"a\">A</Tab></TabList>\n * </Modal.Tabs>\n * <Modal.Body>\n * <TabPanel id=\"a\">…</TabPanel>\n * </Modal.Body>\n * </Tabs>\n * ```\n *\n * The `<Tabs>` element needs `flex min-h-0 flex-1 flex-col` so the body grows\n * and scrolls while this strip stays pinned.\n */\nfunction ModalTabs({ children, className }: ModalTabsProps): ReactElement {\n return <div className={cn(modalTabsVariants(), className)}>{children}</div>;\n}\n\nModalTabs.displayName = 'ModalTabs';\n\n/**\n * Modal.Body Component\n *\n * Scrollable content region. Owns the horizontal padding the box used to\n * provide and grows to fill the available height so long content scrolls here\n * while the header/tabs/footer stay pinned. Holds `TabPanel`s in the tabbed\n * case, arbitrary content otherwise. Body content MUST be wrapped in this\n * component — `Modal.Content` no longer pads its children.\n */\nfunction ModalBody({ children, className }: ModalBodyProps): ReactElement {\n return <div className={cn(modalBodyVariants(), className)}>{children}</div>;\n}\n\nModalBody.displayName = 'ModalBody';\n\n/**\n * Modal.Footer Component\n *\n * Container for modal action buttons (Cancel, Confirm, etc.).\n * Aligns buttons to the right with consistent spacing.\n * Responsive: stacks vertically on mobile, horizontal on desktop.\n *\n * @see PRD.md FR-008 (Footer Requirements)\n * @see PRD.md DS-003 (Spacing - footer button gap)\n */\nfunction ModalFooter({ children, className }: ModalFooterProps): ReactElement {\n // px-6 pb-6 pt-4 replaces the padding the box used to supply.\n return <div className={cn('flex flex-col-reverse sm:flex-row sm:justify-end gap-2 px-6 pb-6 pt-4', className)}>{children}</div>;\n}\n\nModalFooter.displayName = 'ModalFooter';\n\n/**\n * Modal.Close Component\n *\n * Wraps a child element (typically Button) with modal close behavior.\n * Uses OverlayTriggerStateContext from React Aria to access close function.\n *\n * @see PRD.md FR-009 (Close Requirements)\n */\nfunction ModalClose({ children }: ModalCloseProps): ReactElement {\n // Access the overlay trigger state from React Aria context\n const state = useContext(OverlayTriggerStateContext);\n\n if (!state) {\n throw new Error('Modal.Close must be used inside Modal.Content');\n }\n\n // Clone the child element and add/merge the onPress handler to close the modal\n if (!isValidElement(children)) {\n throw new Error('Modal.Close requires a valid React element as a child');\n }\n\n // Cast to ReactElement after validation\n const childElement = children as ReactElement;\n\n const handlePress = (): void => {\n state.close();\n };\n\n // Merge with existing onPress if present\n const existingOnPress = (childElement.props as unknown as { onPress?: () => void })?.onPress;\n const mergedOnPress = existingOnPress\n ? (): void => {\n existingOnPress();\n handlePress();\n }\n : handlePress;\n\n // cloneElement with onPress override - use unknown for flexible typing\n return cloneElement(childElement, { onPress: mergedOnPress } as unknown as Partial<typeof childElement.props>);\n}\n\nModalClose.displayName = 'ModalClose';\n\n/**\n * Re-export CVA variants from Modal.styles.ts for backwards compatibility.\n * Consumers importing { modalContentVariants, modalOverlayVariants } from './Modal'\n * will continue to work.\n */\nexport {\n modalContentVariants,\n modalOverlayVariants,\n modalPanelVariants,\n modalBodyVariants,\n modalTabsVariants,\n} from './Modal.styles';\n\n/**\n * Compound Component Export\n *\n * Follows Themis library pattern using Object.assign() for compound components.\n * Enables usage like Modal.Trigger, Modal.Content, etc.\n *\n * @deprecated The Object.assign compound pattern will be removed in v2.\n * Use the direct named exports (ModalTrigger, ModalContent, etc.) instead.\n * This pattern is kept for backwards compatibility only.\n *\n * @see RESEARCH.md Section 1 (Compound Component Pattern)\n */\nexport const Modal = Object.assign(ModalRoot, {\n Trigger: ModalTrigger,\n Content: ModalContent,\n Overlay: ModalOverlay,\n Header: ModalHeader,\n Title: ModalTitle,\n Description: ModalDescription,\n Tabs: ModalTabs,\n Body: ModalBody,\n Footer: ModalFooter,\n Close: ModalClose,\n});\n\n// Named exports for individual components\nexport {\n ModalRoot,\n ModalTrigger,\n ModalContent,\n ModalOverlay,\n ModalHeader,\n ModalTitle,\n ModalDescription,\n ModalTabs,\n ModalBody,\n ModalFooter,\n ModalClose,\n};\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
'use strict';var react=require('react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),classVarianceAuthority=require('class-variance-authority'),jsxRuntime=require('react/jsx-runtime');function N(...d){return tailwindMerge.twMerge(clsx.clsx(d))}function Z(d){let{value:p,length:b,focusedIndex:e,onChange:a,setFocusedIndex:t,disabled:
|
|
2
|
+
'use strict';var react=require('react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge'),classVarianceAuthority=require('class-variance-authority'),jsxRuntime=require('react/jsx-runtime');function N(...d){return tailwindMerge.twMerge(clsx.clsx(d))}function Z(d){let{value:p,length:b,focusedIndex:e,onChange:a,setFocusedIndex:t,disabled:y=false,readOnly:f=false}=d,h=react.useCallback(()=>{e>0&&t(e-1);},[e,t]),x=react.useCallback(()=>{e<b-1&&t(e+1);},[e,b,t]),P=react.useCallback(()=>{if(f)return;let r=p.split("");if(r[e]==="_"){if(e>0){let l=e-1;r[l]="_",a(r.join("")),t(l);}}else r[e]="_",a(r.join("")),e>0&&t(e-1);},[p,e,f,a,t]),E=react.useCallback(()=>{if(f)return;let r=p.split("");r[e]="_",a(r.join(""));},[p,e,f,a]),m=react.useCallback(()=>{t(0);},[t]),_=react.useCallback(()=>{t(b-1);},[b,t]);return {handleKeyDown:react.useCallback(r=>{if(!y)switch(r.key){case "ArrowLeft":r.preventDefault(),h();break;case "ArrowRight":r.preventDefault(),x();break;case "Backspace":r.preventDefault(),P();break;case "Delete":r.preventDefault(),E();break;case "Home":r.preventDefault(),m();break;case "End":r.preventDefault(),_();break;case "a":case "A":if(r.ctrlKey||r.metaKey)return;break;case "Enter":r.preventDefault();break;}},[y,h,x,P,E,m,_])}}function Q(d){let{value:p,defaultValue:b="",length:e=6,type:a="numeric",pattern:t,onChange:y,onComplete:f,validate:h,error:x,disabled:P=false,readOnly:E=false}=d,m=p!==void 0,[_,L]=react.useState(()=>(b||"").padEnd(e,"_")),[r,K]=react.useState(null),[l,g]=react.useState(0),I=react.useRef(null),D=react.useRef(null),T=m?p||"".padEnd(e,"_"):_,F=x!==void 0?x:r,R=react.useCallback(n=>{if(n.replace(/_/g,"").length<e)return null;if(h)return h(n);if(a==="numeric"){let o=/^\d+$/,i=n.replace(/_/g,"");if(!o.test(i))return "This code uses digits only (0\u20139)"}else if(a==="alphanumeric"){let o=/^[A-Za-z0-9]+$/,i=n.replace(/_/g,"");if(!o.test(i))return "This code uses letters and numbers only"}else if(a==="custom"&&t){let o=n.replace(/_/g,"");for(let i of o)if(!t.test(i))return "This code contains a character that isn't allowed"}return null},[e,a,t,h]),v=react.useCallback(n=>{m||L(n),y?.(n);let u=R(n);m||K(u);let i=n.replace(/_/g,"").length===e;i&&!u&&I.current!==n&&(I.current=n,D.current&&clearTimeout(D.current),D.current=setTimeout(()=>{f?.(n.replace(/_/g,""));},200)),!i&&I.current&&(I.current=null);},[m,e,y,f,R]),A=react.useCallback(n=>{if(P)return;let u=n.target.value,o=u[u.length-1]||"",i;if(a==="numeric"?i=/^\d$/.test(o):a==="alphanumeric"?i=/^[A-Za-z0-9]$/.test(o):a==="custom"&&t?i=t.test(o):i=true,!i)return;let s=T.split("");s[l]=o;let c=s.join("");v(c),l<e-1&&g(l+1);},[P,a,t,T,l,e,v]),O=react.useCallback(async n=>{if(P||E)return;n.preventDefault();let u=n.clipboardData.getData("text/plain").trim();if(!u||d.onPaste&&await d.onPaste(u)===false)return;let o="";for(let c of u){if(o.length>=e)break;let V;a==="numeric"?V=/^\d$/.test(c):a==="alphanumeric"?V=/^[A-Za-z0-9]$/.test(c):a==="custom"&&t?V=t.test(c):V=true,V&&(o+=c);}let i=o.padEnd(e,"_");v(i);let s=i.indexOf("_");g(s===-1?e-1:s);},[P,E,d,e,a,t,v]),M=react.useCallback(()=>{let n="".padEnd(e,"_");v(n),g(0),m||K(null),I.current=null;},[e,v,m]),$=react.useCallback(n=>{let u=n.padEnd(e,"_").slice(0,e);v(u);let o=u.indexOf("_");g(o===-1?e-1:o);},[e,v]),B=react.useCallback(()=>R(T),[T,R]),U=T.replace(/_/g,"").length===e,{handleKeyDown:z}=Z({value:T,length:e,focusedIndex:l,onChange:v,setFocusedIndex:g,disabled:P,readOnly:E});return react.useEffect(()=>{if(m&&p!==void 0){let n=p.indexOf("_");n!==-1&&g(n);}},[m,p]),react.useEffect(()=>()=>{D.current&&clearTimeout(D.current);},[]),{value:T,focusedIndex:l,error:F,isComplete:U,handleChange:A,handleKeyDown:z,handlePaste:O,setFocusedIndex:g,clear:M,setValue:$,validate:B}}var W=classVarianceAuthority.cva(["inline-flex","items-center","justify-center","rounded-md","border-2","font-semibold","transition-all","duration-200","ease-in-out","select-none","tabindex-[-1]"],{variants:{size:{sm:["h-10","w-10","text-base","p-1"],md:["h-14","w-10 sm:w-12 md:w-14","text-xl"],lg:["h-16","w-10 sm:w-12 md:w-16","text-2xl"]},state:{default:["border-[var(--input-border)]","bg-[var(--content-background)]","text-[var(--content-foreground)]","hover:border-[var(--input-border)]/80"],focus:["border-[var(--input-border)]","ring-2","ring-[var(--ring)]","ring-offset-2","ring-offset-[var(--content-background)]","outline-none"],filled:["border-[var(--primary)]","bg-[var(--content-background)]","text-[var(--content-foreground)]","font-bold"],error:["border-[var(--destructive-background)]","bg-[var(--content-background)]","text-[var(--destructive-foreground)]","animate-shake"],success:["border-green-600","bg-green-50","text-green-900","dark:border-green-500","dark:bg-green-900/20","dark:text-green-100"],disabled:["border-[var(--accent-background)]","bg-[var(--accent-background)]","text-[var(--menu-muted)]","cursor-not-allowed","opacity-60","hover:border-[var(--accent-background)]"]}},defaultVariants:{size:"md",state:"default"}}),X=classVarianceAuthority.cva(["inline-flex","flex-col","gap-2"],{variants:{size:{sm:["gap-1.5"],md:["gap-2"],lg:["gap-3"]}},defaultVariants:{size:"md"}}),Y=classVarianceAuthority.cva(["flex","items-center"],{variants:{size:{sm:["gap-1.5"],md:["gap-2"],lg:["gap-3"]}},defaultVariants:{size:"md"}}),ee=classVarianceAuthority.cva(["text-sm","font-medium","text-[var(--destructive-background)]","mt-1.5","text-center"]),te=classVarianceAuthority.cva(["text-sm","text-[var(--menu-muted)]","mt-1"]);var le=({value:d,focused:p,size:b="md",state:e="default",masked:a=false,className:t,index:y})=>{let f=d!=="_"&&d!=="",h=p&&!e?"focus":f&&e==="default"?"filled":e,x=a&&f?"\u2022":d==="_"?"":d;return jsxRuntime.jsx("div",{className:N(W({size:b,state:h}),t),role:"presentation","aria-hidden":"true","data-focused":p,"data-filled":f,"data-index":y,children:x||jsxRuntime.jsx("span",{className:"text-[var(--menu-muted)] opacity-40",children:"\u2022"})})},G=react.memo(le);G.displayName="OTPDigit";var ae=react.forwardRef((d,p)=>{let{label:b,description:e,size:a="md",disabled:t=false,readOnly:y=false,mask:f=false,showSuccess:h=false,className:x,digitClassName:P,onFocus:E,onBlur:m,inputMode:_="numeric",length:L=6}=d,{value:r,focusedIndex:K,error:l,isComplete:g,handleChange:I,handleKeyDown:D,handlePaste:T,setFocusedIndex:F,clear:R,setValue:v,validate:A}=Q(d),O=react.useRef(null);react.useImperativeHandle(p,()=>({focus:()=>O.current?.focus(),blur:()=>O.current?.blur(),clear:R,setValue:v,getValue:()=>r.replace(/_/g,""),isComplete:()=>g,validate:A,getElement:()=>O.current}));let M=r.split(""),$=s=>t?"disabled":l?"error":h&&g&&!l?"success":s===K?"focus":M[s]!=="_"?"filled":"default",B=s=>{E?.(s);},U=s=>{m?.(s);},z=s=>{t||y||(F(s),O.current?.focus());},n=(s,c)=>{(s.key==="Enter"||s.key===" ")&&(s.preventDefault(),z(c));},u=react.useId(),o=e?`${u}-description`:void 0,i=l?`${u}-error`:void 0;return jsxRuntime.jsxs("div",{className:N(X({size:a}),x),children:[jsxRuntime.jsx("input",{ref:O,type:"text",inputMode:_,value:r.replace(/_/g,""),onChange:I,onKeyDown:D,onPaste:T,onFocus:B,onBlur:U,disabled:t,readOnly:y,"aria-label":b,"aria-describedby":[o,i].filter(Boolean).join(" "),"aria-invalid":!!l,"aria-errormessage":i,className:"sr-only",autoComplete:"one-time-code",maxLength:L}),jsxRuntime.jsx("div",{className:N(Y({size:a})),role:"presentation","aria-hidden":"true",children:M.map((s,c)=>jsxRuntime.jsx("div",{onClick:()=>z(c),onKeyDown:V=>n(V,c),role:"button",tabIndex:-1,className:"cursor-text",children:jsxRuntime.jsx(G,{value:s,focused:c===K,size:a,state:$(c),masked:f,className:P,index:c})},c))}),e&&jsxRuntime.jsx("p",{id:o,className:te(),children:e}),l&&jsxRuntime.jsx("p",{id:i,className:ee(),role:"alert","aria-live":"assertive",children:l}),jsxRuntime.jsxs("div",{role:"status","aria-live":"polite","aria-atomic":"true",className:"sr-only",children:[!g&&!l&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[r.replace(/_/g,"").length," of ",L," digits entered"]}),g&&!l&&jsxRuntime.jsx(jsxRuntime.Fragment,{children:"Verification code complete"})]})]})});ae.displayName="OTPInput";exports.OTPInput=ae;//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|