@brycks/core-front 0.3.5 → 0.5.0

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.
Files changed (168) hide show
  1. package/dist/components/data/List/List.cjs +1 -1
  2. package/dist/components/data/List/List.cjs.map +1 -1
  3. package/dist/components/data/List/List.js +1 -1
  4. package/dist/components/data/List/List.js.map +1 -1
  5. package/dist/components/data/Table/Table.cjs +1 -1
  6. package/dist/components/data/Table/Table.cjs.map +1 -1
  7. package/dist/components/data/Table/Table.js +196 -166
  8. package/dist/components/data/Table/Table.js.map +1 -1
  9. package/dist/components/data/TreeView/TreeView.cjs +1 -1
  10. package/dist/components/data/TreeView/TreeView.cjs.map +1 -1
  11. package/dist/components/data/TreeView/TreeView.js +1 -1
  12. package/dist/components/data/TreeView/TreeView.js.map +1 -1
  13. package/dist/components/data.cjs +1 -1
  14. package/dist/components/data.js +23 -21
  15. package/dist/components/feedback/Alert/Alert.cjs +1 -1
  16. package/dist/components/feedback/Alert/Alert.cjs.map +1 -1
  17. package/dist/components/feedback/Alert/Alert.js +20 -19
  18. package/dist/components/feedback/Alert/Alert.js.map +1 -1
  19. package/dist/components/feedback/Banner/Banner.cjs +2 -0
  20. package/dist/components/feedback/Banner/Banner.cjs.map +1 -0
  21. package/dist/components/feedback/Banner/Banner.js +77 -0
  22. package/dist/components/feedback/Banner/Banner.js.map +1 -0
  23. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.cjs +2 -0
  24. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.cjs.map +1 -0
  25. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js +68 -0
  26. package/dist/components/feedback/ConfirmDialog/ConfirmDialog.js.map +1 -0
  27. package/dist/components/feedback/Drawer/Drawer.cjs +2 -2
  28. package/dist/components/feedback/Drawer/Drawer.cjs.map +1 -1
  29. package/dist/components/feedback/Drawer/Drawer.js +58 -57
  30. package/dist/components/feedback/Drawer/Drawer.js.map +1 -1
  31. package/dist/components/feedback/Modal/Modal.cjs +1 -1
  32. package/dist/components/feedback/Modal/Modal.cjs.map +1 -1
  33. package/dist/components/feedback/Modal/Modal.js +101 -91
  34. package/dist/components/feedback/Modal/Modal.js.map +1 -1
  35. package/dist/components/feedback/Popover/Popover.cjs +2 -2
  36. package/dist/components/feedback/Popover/Popover.cjs.map +1 -1
  37. package/dist/components/feedback/Popover/Popover.js +93 -85
  38. package/dist/components/feedback/Popover/Popover.js.map +1 -1
  39. package/dist/components/feedback/Toast/Toast.cjs +2 -2
  40. package/dist/components/feedback/Toast/Toast.cjs.map +1 -1
  41. package/dist/components/feedback/Toast/Toast.js +102 -93
  42. package/dist/components/feedback/Toast/Toast.js.map +1 -1
  43. package/dist/components/feedback.cjs +1 -1
  44. package/dist/components/feedback.js +17 -13
  45. package/dist/components/feedback.js.map +1 -1
  46. package/dist/components/form/Combobox/Combobox.cjs +2 -2
  47. package/dist/components/form/Combobox/Combobox.cjs.map +1 -1
  48. package/dist/components/form/Combobox/Combobox.js +187 -167
  49. package/dist/components/form/Combobox/Combobox.js.map +1 -1
  50. package/dist/components/form/DateInput/DateInput.cjs +1 -1
  51. package/dist/components/form/DateInput/DateInput.cjs.map +1 -1
  52. package/dist/components/form/DateInput/DateInput.js +2 -2
  53. package/dist/components/form/DateInput/DateInput.js.map +1 -1
  54. package/dist/components/form/DateRangePicker/DateRangePicker.cjs +1 -1
  55. package/dist/components/form/DateRangePicker/DateRangePicker.cjs.map +1 -1
  56. package/dist/components/form/DateRangePicker/DateRangePicker.js +34 -34
  57. package/dist/components/form/DateRangePicker/DateRangePicker.js.map +1 -1
  58. package/dist/components/form/DropZone/DropZone.cjs +1 -1
  59. package/dist/components/form/DropZone/DropZone.cjs.map +1 -1
  60. package/dist/components/form/DropZone/DropZone.js +10 -10
  61. package/dist/components/form/DropZone/DropZone.js.map +1 -1
  62. package/dist/components/form/FileInput/FileInput.cjs +1 -1
  63. package/dist/components/form/FileInput/FileInput.cjs.map +1 -1
  64. package/dist/components/form/FileInput/FileInput.js +1 -1
  65. package/dist/components/form/FileInput/FileInput.js.map +1 -1
  66. package/dist/components/form/FormCard/FormCard.cjs +1 -1
  67. package/dist/components/form/FormCard/FormCard.cjs.map +1 -1
  68. package/dist/components/form/FormCard/FormCard.js +9 -9
  69. package/dist/components/form/FormCard/FormCard.js.map +1 -1
  70. package/dist/components/form/FormField/FormField.cjs +1 -1
  71. package/dist/components/form/FormField/FormField.cjs.map +1 -1
  72. package/dist/components/form/FormField/FormField.js +67 -55
  73. package/dist/components/form/FormField/FormField.js.map +1 -1
  74. package/dist/components/form/MultiSelect/MultiSelect.cjs +1 -1
  75. package/dist/components/form/MultiSelect/MultiSelect.cjs.map +1 -1
  76. package/dist/components/form/MultiSelect/MultiSelect.js +2 -3
  77. package/dist/components/form/MultiSelect/MultiSelect.js.map +1 -1
  78. package/dist/components/form/Radio/Radio.cjs +1 -1
  79. package/dist/components/form/Radio/Radio.cjs.map +1 -1
  80. package/dist/components/form/Radio/Radio.js +20 -20
  81. package/dist/components/form/Radio/Radio.js.map +1 -1
  82. package/dist/components/form/SearchInput/SearchInput.cjs +2 -0
  83. package/dist/components/form/SearchInput/SearchInput.cjs.map +1 -0
  84. package/dist/components/form/SearchInput/SearchInput.js +68 -0
  85. package/dist/components/form/SearchInput/SearchInput.js.map +1 -0
  86. package/dist/components/form/TagInput/TagInput.cjs +1 -1
  87. package/dist/components/form/TagInput/TagInput.cjs.map +1 -1
  88. package/dist/components/form/TagInput/TagInput.js +104 -102
  89. package/dist/components/form/TagInput/TagInput.js.map +1 -1
  90. package/dist/components/form/TimePicker/TimePicker.cjs +1 -1
  91. package/dist/components/form/TimePicker/TimePicker.cjs.map +1 -1
  92. package/dist/components/form/TimePicker/TimePicker.js +29 -30
  93. package/dist/components/form/TimePicker/TimePicker.js.map +1 -1
  94. package/dist/components/form.cjs +1 -1
  95. package/dist/components/form.js +15 -13
  96. package/dist/components/form.js.map +1 -1
  97. package/dist/components/layout/Card/Card.cjs +1 -1
  98. package/dist/components/layout/Card/Card.cjs.map +1 -1
  99. package/dist/components/layout/Card/Card.js +7 -5
  100. package/dist/components/layout/Card/Card.js.map +1 -1
  101. package/dist/components/layout/Collapse/Collapse.cjs.map +1 -1
  102. package/dist/components/layout/Collapse/Collapse.js.map +1 -1
  103. package/dist/components/media/Image/Image.cjs +1 -1
  104. package/dist/components/media/Image/Image.cjs.map +1 -1
  105. package/dist/components/media/Image/Image.js +9 -9
  106. package/dist/components/media/Image/Image.js.map +1 -1
  107. package/dist/components/media/ImageThumbnail/ImageThumbnail.cjs +1 -1
  108. package/dist/components/media/ImageThumbnail/ImageThumbnail.cjs.map +1 -1
  109. package/dist/components/media/ImageThumbnail/ImageThumbnail.js +6 -6
  110. package/dist/components/media/ImageThumbnail/ImageThumbnail.js.map +1 -1
  111. package/dist/components/navigation/Accordion/Accordion.cjs +1 -1
  112. package/dist/components/navigation/Accordion/Accordion.cjs.map +1 -1
  113. package/dist/components/navigation/Accordion/Accordion.js +0 -1
  114. package/dist/components/navigation/Accordion/Accordion.js.map +1 -1
  115. package/dist/components/navigation/Menu/Menu.cjs +1 -1
  116. package/dist/components/navigation/Menu/Menu.cjs.map +1 -1
  117. package/dist/components/navigation/Menu/Menu.js +87 -86
  118. package/dist/components/navigation/Menu/Menu.js.map +1 -1
  119. package/dist/components/navigation/Pagination/Pagination.cjs +1 -1
  120. package/dist/components/navigation/Pagination/Pagination.cjs.map +1 -1
  121. package/dist/components/navigation/Pagination/Pagination.js +107 -99
  122. package/dist/components/navigation/Pagination/Pagination.js.map +1 -1
  123. package/dist/components/navigation/SegmentedControl/SegmentedControl.cjs +2 -0
  124. package/dist/components/navigation/SegmentedControl/SegmentedControl.cjs.map +1 -0
  125. package/dist/components/navigation/SegmentedControl/SegmentedControl.js +90 -0
  126. package/dist/components/navigation/SegmentedControl/SegmentedControl.js.map +1 -0
  127. package/dist/components/navigation.cjs +1 -1
  128. package/dist/components/navigation.js +25 -23
  129. package/dist/components/navigation.js.map +1 -1
  130. package/dist/components/utility/Avatar/Avatar.cjs +1 -1
  131. package/dist/components/utility/Avatar/Avatar.cjs.map +1 -1
  132. package/dist/components/utility/Avatar/Avatar.js +18 -18
  133. package/dist/components/utility/Avatar/Avatar.js.map +1 -1
  134. package/dist/components/utility/Badge/Badge.cjs +1 -1
  135. package/dist/components/utility/Badge/Badge.cjs.map +1 -1
  136. package/dist/components/utility/Badge/Badge.js +31 -23
  137. package/dist/components/utility/Badge/Badge.js.map +1 -1
  138. package/dist/components/utility/Kbd/Kbd.cjs +2 -0
  139. package/dist/components/utility/Kbd/Kbd.cjs.map +1 -0
  140. package/dist/components/utility/Kbd/Kbd.js +20 -0
  141. package/dist/components/utility/Kbd/Kbd.js.map +1 -0
  142. package/dist/components/utility/Logo/Logo.cjs +1 -1
  143. package/dist/components/utility/Logo/Logo.cjs.map +1 -1
  144. package/dist/components/utility/Logo/Logo.js +21 -21
  145. package/dist/components/utility/Logo/Logo.js.map +1 -1
  146. package/dist/components/utility.cjs +1 -1
  147. package/dist/components/utility.js +9 -7
  148. package/dist/components/utility.js.map +1 -1
  149. package/dist/data.d.ts +34 -1
  150. package/dist/design-system/themes/ThemeProvider.cjs +1 -1
  151. package/dist/design-system/themes/ThemeProvider.cjs.map +1 -1
  152. package/dist/design-system/themes/ThemeProvider.js +46 -46
  153. package/dist/design-system/themes/ThemeProvider.js.map +1 -1
  154. package/dist/design-system/tokens/zIndex.cjs +1 -1
  155. package/dist/design-system/tokens/zIndex.cjs.map +1 -1
  156. package/dist/design-system/tokens/zIndex.js +2 -0
  157. package/dist/design-system/tokens/zIndex.js.map +1 -1
  158. package/dist/feedback.d.ts +75 -2
  159. package/dist/form.d.ts +41 -0
  160. package/dist/index.cjs +1 -1
  161. package/dist/index.d.ts +213 -3
  162. package/dist/index.js +177 -165
  163. package/dist/index.js.map +1 -1
  164. package/dist/navigation.d.ts +58 -0
  165. package/dist/styles.css +1 -1
  166. package/dist/tokens.d.ts +2 -0
  167. package/dist/utility.d.ts +19 -0
  168. package/package.json +2 -1
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.cjs","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\nimport { spacing, fontSizes } from '../../../design-system'\r\nimport { componentGap, iconButtonSizes } from '../../../design-system/primitives'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n '2xl': '1200px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Store onClose in a ref to avoid re-running effects when it changes\r\n const onCloseRef = useRef(onClose)\r\n useEffect(() => {\r\n onCloseRef.current = onClose\r\n }, [onClose])\r\n\r\n // Focus modal when it opens (only once)\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n }, [isOpen])\r\n\r\n // Keyboard handling and body scroll lock\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onCloseRef.current()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: spacing[6],\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: `${spacing[5]}px ${spacing[6]}px`,\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: spacing[1],\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: fontSizes.lg,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: fontSizes.base,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: iconButtonSizes.sm,\r\n height: iconButtonSizes.sm,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: componentGap.xl,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: spacing[6],\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","onCloseRef","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","spacing","modalStyle","headerStyle","titleContainerStyle","titleStyle","fontSizes","descriptionStyle","closeButtonStyle","iconButtonSizes","componentGap","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"Modal.cjs","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\nimport { useId } from '../../../hooks/useId'\r\nimport { spacing, fontSizes } from '../../../design-system'\r\nimport { componentGap, iconButtonSizes } from '../../../design-system/primitives'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Accessible label of the close button */\r\n closeLabel?: string\r\n /** Footer content (e.g. action buttons), pinned below the scrollable body */\r\n footer?: ReactNode\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n '2xl': '1200px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n closeLabel = 'Close modal',\r\n footer,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const titleId = useId('modal-title')\r\n const descriptionId = useId('modal-description')\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Store onClose in a ref to avoid re-running effects when it changes\r\n const onCloseRef = useRef(onClose)\r\n useEffect(() => {\r\n onCloseRef.current = onClose\r\n }, [onClose])\r\n\r\n // Focus modal when it opens (only once)\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n }, [isOpen])\r\n\r\n // Keyboard handling and body scroll lock\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onCloseRef.current()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: spacing[6],\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: `${spacing[5]}px ${spacing[6]}px`,\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: spacing[1],\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: fontSizes.lg,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: fontSizes.base,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: iconButtonSizes.sm,\r\n height: iconButtonSizes.sm,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: componentGap.xl,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: spacing[6],\r\n }\r\n\r\n const footerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'flex-end',\r\n gap: spacing[2],\r\n padding: `${spacing[4]}px ${spacing[6]}px`,\r\n borderTop: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? titleId : undefined}\r\n aria-describedby={description ? descriptionId : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id={titleId} style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id={descriptionId} style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label={closeLabel}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n\r\n {footer && (\r\n <div className=\"brycks-modal-footer\" style={footerStyle}>\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","closeLabel","footer","className","style","testId","children","props","ref","modalRef","useRef","titleId","useId","descriptionId","previousActiveElement","wasOpen","useEffect","onCloseRef","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","spacing","modalStyle","headerStyle","titleContainerStyle","titleStyle","fontSizes","descriptionStyle","closeButtonStyle","iconButtonSizes","componentGap","contentStyle","footerStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
@@ -1,20 +1,21 @@
1
- import { jsx as r, jsxs as v } from "react/jsx-runtime";
2
- import { forwardRef as G, useRef as d, useEffect as u, useCallback as P, useMemo as C, memo as J } from "react";
3
- import { createPortal as Q } from "react-dom";
4
- import { cx as U } from "../../../utils/styles.js";
5
- import { iconButtonSizes as M } from "../../../design-system/primitives/layout.js";
6
- import { spacing as a } from "../../../design-system/tokens/spacing.js";
7
- import { fontSizes as L } from "../../../design-system/tokens/typography.js";
8
- import { componentGap as V } from "../../../design-system/primitives/sizing.js";
9
- const X = {
1
+ import { jsx as t, jsxs as v } from "react/jsx-runtime";
2
+ import { forwardRef as X, useRef as i, useEffect as u, useCallback as Y, useMemo as L, memo as Z } from "react";
3
+ import { createPortal as _ } from "react-dom";
4
+ import { cx as O } from "../../../utils/styles.js";
5
+ import { useId as T } from "../../../hooks/useId.js";
6
+ import { iconButtonSizes as N } from "../../../design-system/primitives/layout.js";
7
+ import { spacing as o } from "../../../design-system/tokens/spacing.js";
8
+ import { fontSizes as j } from "../../../design-system/tokens/typography.js";
9
+ import { componentGap as ee } from "../../../design-system/primitives/sizing.js";
10
+ const te = {
10
11
  sm: "400px",
11
12
  md: "500px",
12
13
  lg: "640px",
13
14
  xl: "800px",
14
15
  "2xl": "1200px",
15
16
  full: "calc(100vw - 48px)"
16
- }, Y = J(function() {
17
- return /* @__PURE__ */ r("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ r(
17
+ }, re = Z(function() {
18
+ return /* @__PURE__ */ t("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ t(
18
19
  "path",
19
20
  {
20
21
  d: "M4 4L12 12M12 4L4 12",
@@ -23,70 +24,71 @@ const X = {
23
24
  strokeLinecap: "round"
24
25
  }
25
26
  ) });
26
- }), Z = G(function({
27
- isOpen: t,
28
- onClose: o,
27
+ }), oe = X(function({
28
+ isOpen: r,
29
+ onClose: n,
29
30
  size: l = "md",
30
31
  title: c,
31
32
  description: m,
32
- closeOnOverlayClick: k = !0,
33
- closeOnEscape: p = !0,
34
- showCloseButton: g = !0,
35
- className: T,
36
- style: x,
37
- testId: N,
38
- children: R,
39
- ...j
40
- }, s) {
41
- const f = d(null), y = d(null), i = d(!1);
33
+ closeOnOverlayClick: p = !0,
34
+ closeOnEscape: k = !0,
35
+ showCloseButton: x = !0,
36
+ closeLabel: D = "Close modal",
37
+ footer: g,
38
+ className: $,
39
+ style: h,
40
+ testId: z,
41
+ children: W,
42
+ ...B
43
+ }, d) {
44
+ const f = i(null), w = T("modal-title"), S = T("modal-description"), y = i(null), s = i(!1);
42
45
  u(() => {
43
- t && !i.current ? (y.current = document.activeElement, i.current = !0) : !t && i.current && (document.activeElement instanceof HTMLElement && document.activeElement.blur(), y.current?.focus(), y.current = null, i.current = !1);
44
- }, [t]);
45
- const h = d(o);
46
+ r && !s.current ? (y.current = document.activeElement, s.current = !0) : !r && s.current && (document.activeElement instanceof HTMLElement && document.activeElement.blur(), y.current?.focus(), y.current = null, s.current = !1);
47
+ }, [r]);
48
+ const E = i(n);
46
49
  u(() => {
47
- h.current = o;
48
- }, [o]), u(() => {
49
- if (!t) return;
50
+ E.current = n;
51
+ }, [n]), u(() => {
52
+ if (!r) return;
50
53
  const e = f.current;
51
54
  e && e.focus();
52
- }, [t]), u(() => {
53
- if (!t) return;
54
- const e = f.current, w = (n) => {
55
- if (n.key === "Escape" && p) {
56
- h.current();
55
+ }, [r]), u(() => {
56
+ if (!r) return;
57
+ const e = f.current, C = (a) => {
58
+ if (a.key === "Escape" && k) {
59
+ E.current();
57
60
  return;
58
61
  }
59
- if (n.key === "Tab" && e) {
62
+ if (a.key === "Tab" && e) {
60
63
  const b = e.querySelectorAll(
61
- 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
62
- ), S = b[0], E = b[b.length - 1];
63
- n.shiftKey && document.activeElement === S ? (n.preventDefault(), E?.focus()) : !n.shiftKey && document.activeElement === E && (n.preventDefault(), S?.focus());
64
+ 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable="true"], [tabindex]:not([tabindex="-1"])'
65
+ ), I = b[0], M = b[b.length - 1];
66
+ a.shiftKey && document.activeElement === I ? (a.preventDefault(), M?.focus()) : !a.shiftKey && document.activeElement === M && (a.preventDefault(), I?.focus());
64
67
  }
65
68
  };
66
- return document.addEventListener("keydown", w), document.body.style.overflow = "hidden", () => {
67
- document.removeEventListener("keydown", w), document.body.style.overflow = "";
69
+ return document.addEventListener("keydown", C), document.body.style.overflow = "hidden", () => {
70
+ document.removeEventListener("keydown", C), document.body.style.overflow = "";
68
71
  };
69
- }, [t, p]);
70
- const D = P(
72
+ }, [r, k]);
73
+ const H = Y(
71
74
  (e) => {
72
- e.target === e.currentTarget && k && o();
75
+ e.target === e.currentTarget && p && n();
73
76
  },
74
- [k, o]
75
- ), z = C(() => ({
77
+ [p, n]
78
+ ), K = L(() => ({
76
79
  position: "fixed",
77
80
  inset: 0,
78
81
  zIndex: "var(--brycks-z-modal)",
79
82
  display: "flex",
80
83
  alignItems: "center",
81
84
  justifyContent: "center",
82
- padding: a[6],
85
+ padding: o[6],
83
86
  backgroundColor: "var(--brycks-background-overlay)",
84
- backdropFilter: "blur(4px)",
85
87
  animation: "brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)"
86
- }), []), W = C(() => ({
88
+ }), []), A = L(() => ({
87
89
  position: "relative",
88
90
  width: "100%",
89
- maxWidth: X[l],
91
+ maxWidth: te[l],
90
92
  maxHeight: l === "full" ? "calc(100vh - 48px)" : "85vh",
91
93
  backgroundColor: "var(--brycks-background-elevated)",
92
94
  borderRadius: "var(--brycks-radius-xl)",
@@ -94,101 +96,109 @@ const X = {
94
96
  display: "flex",
95
97
  flexDirection: "column",
96
98
  overflow: "hidden",
97
- animation: "brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)",
99
+ animation: "brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-out)",
98
100
  outline: "none",
99
- ...x
100
- }), [l, x]);
101
- if (!t) return null;
102
- const B = {
101
+ ...h
102
+ }), [l, h]);
103
+ if (!r) return null;
104
+ const q = {
103
105
  display: "flex",
104
106
  alignItems: "flex-start",
105
107
  justifyContent: "space-between",
106
- padding: `${a[5]}px ${a[6]}px`,
108
+ padding: `${o[5]}px ${o[6]}px`,
107
109
  borderBottom: "1px solid var(--brycks-border-muted)"
108
- }, H = {
110
+ }, G = {
109
111
  display: "flex",
110
112
  flexDirection: "column",
111
- gap: a[1]
112
- }, K = {
113
- fontSize: L.lg,
113
+ gap: o[1]
114
+ }, P = {
115
+ fontSize: j.lg,
114
116
  fontWeight: 600,
115
117
  color: "var(--brycks-foreground-default)",
116
118
  margin: 0,
117
119
  lineHeight: 1.3
118
- }, $ = {
119
- fontSize: L.base,
120
+ }, F = {
121
+ fontSize: j.base,
120
122
  color: "var(--brycks-foreground-muted)",
121
123
  margin: 0
122
- }, A = {
124
+ }, J = {
123
125
  display: "flex",
124
126
  alignItems: "center",
125
127
  justifyContent: "center",
126
- width: M.sm,
127
- height: M.sm,
128
+ width: N.sm,
129
+ height: N.sm,
128
130
  borderRadius: "var(--brycks-radius-md)",
129
131
  color: "var(--brycks-foreground-muted)",
130
132
  transition: "all var(--brycks-duration-fast) var(--brycks-ease-out)",
131
- marginLeft: V.xl,
133
+ marginLeft: ee.xl,
132
134
  flexShrink: 0
133
- }, q = {
135
+ }, Q = {
134
136
  flex: 1,
135
137
  overflow: "auto",
136
- padding: a[6]
137
- }, F = /* @__PURE__ */ r(
138
+ padding: o[6]
139
+ }, U = {
140
+ display: "flex",
141
+ alignItems: "center",
142
+ justifyContent: "flex-end",
143
+ gap: o[2],
144
+ padding: `${o[4]}px ${o[6]}px`,
145
+ borderTop: "1px solid var(--brycks-border-muted)"
146
+ }, V = /* @__PURE__ */ t(
138
147
  "div",
139
148
  {
140
149
  className: "brycks-modal-overlay",
141
- style: z,
142
- onClick: D,
150
+ style: K,
151
+ onClick: H,
143
152
  children: /* @__PURE__ */ v(
144
153
  "div",
145
154
  {
146
155
  ref: (e) => {
147
- f.current = e, typeof s == "function" ? s(e) : s && (s.current = e);
156
+ f.current = e, typeof d == "function" ? d(e) : d && (d.current = e);
148
157
  },
149
158
  role: "dialog",
150
159
  "aria-modal": "true",
151
- "aria-labelledby": c ? "brycks-modal-title" : void 0,
152
- "aria-describedby": m ? "brycks-modal-description" : void 0,
153
- className: U("brycks-modal", `brycks-modal--${l}`, T),
154
- style: W,
160
+ "aria-labelledby": c ? w : void 0,
161
+ "aria-describedby": m ? S : void 0,
162
+ className: O("brycks-modal", `brycks-modal--${l}`, $),
163
+ style: A,
155
164
  tabIndex: -1,
156
- "data-testid": N,
157
- ...j,
165
+ "data-testid": z,
166
+ ...B,
158
167
  children: [
159
- (c || g) && /* @__PURE__ */ v("div", { className: "brycks-modal-header", style: B, children: [
160
- /* @__PURE__ */ v("div", { style: H, children: [
161
- c && /* @__PURE__ */ r("h2", { id: "brycks-modal-title", style: K, children: c }),
162
- m && /* @__PURE__ */ r("p", { id: "brycks-modal-description", style: $, children: m })
168
+ (c || x) && /* @__PURE__ */ v("div", { className: "brycks-modal-header", style: q, children: [
169
+ /* @__PURE__ */ v("div", { style: G, children: [
170
+ c && /* @__PURE__ */ t("h2", { id: w, style: P, children: c }),
171
+ m && /* @__PURE__ */ t("p", { id: S, style: F, children: m })
163
172
  ] }),
164
- g && /* @__PURE__ */ r(
173
+ x && /* @__PURE__ */ t(
165
174
  "button",
166
175
  {
167
176
  type: "button",
168
177
  className: "brycks-modal-close",
169
- style: A,
170
- onClick: o,
171
- "aria-label": "Close modal",
178
+ style: J,
179
+ onClick: n,
180
+ "aria-label": D,
172
181
  onMouseEnter: (e) => {
173
182
  e.currentTarget.style.backgroundColor = "var(--brycks-background-muted)", e.currentTarget.style.color = "var(--brycks-foreground-default)";
174
183
  },
175
184
  onMouseLeave: (e) => {
176
185
  e.currentTarget.style.backgroundColor = "transparent", e.currentTarget.style.color = "var(--brycks-foreground-muted)";
177
186
  },
178
- children: /* @__PURE__ */ r(Y, {})
187
+ children: /* @__PURE__ */ t(re, {})
179
188
  }
180
189
  )
181
190
  ] }),
182
- /* @__PURE__ */ r("div", { className: "brycks-modal-content", style: q, children: R })
191
+ /* @__PURE__ */ t("div", { className: "brycks-modal-content", style: Q, children: W }),
192
+ g && /* @__PURE__ */ t("div", { className: "brycks-modal-footer", style: U, children: g })
183
193
  ]
184
194
  }
185
195
  )
186
196
  }
187
197
  );
188
- return Q(F, document.body);
198
+ return _(V, document.body);
189
199
  });
190
- Z.displayName = "Modal";
200
+ oe.displayName = "Modal";
191
201
  export {
192
- Z as Modal
202
+ oe as Modal
193
203
  };
194
204
  //# sourceMappingURL=Modal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Modal.js","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\nimport { spacing, fontSizes } from '../../../design-system'\r\nimport { componentGap, iconButtonSizes } from '../../../design-system/primitives'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n '2xl': '1200px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Store onClose in a ref to avoid re-running effects when it changes\r\n const onCloseRef = useRef(onClose)\r\n useEffect(() => {\r\n onCloseRef.current = onClose\r\n }, [onClose])\r\n\r\n // Focus modal when it opens (only once)\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n }, [isOpen])\r\n\r\n // Keyboard handling and body scroll lock\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onCloseRef.current()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: spacing[6],\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n backdropFilter: 'blur(4px)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-spring)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: `${spacing[5]}px ${spacing[6]}px`,\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: spacing[1],\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: fontSizes.lg,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: fontSizes.base,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: iconButtonSizes.sm,\r\n height: iconButtonSizes.sm,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: componentGap.xl,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: spacing[6],\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? 'brycks-modal-title' : undefined}\r\n aria-describedby={description ? 'brycks-modal-description' : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id=\"brycks-modal-title\" style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id=\"brycks-modal-description\" style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label=\"Close modal\"\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","className","style","testId","children","props","ref","modalRef","useRef","previousActiveElement","wasOpen","useEffect","onCloseRef","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","spacing","modalStyle","headerStyle","titleContainerStyle","titleStyle","fontSizes","descriptionStyle","closeButtonStyle","iconButtonSizes","componentGap","contentStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"Modal.js","sources":["../../../../src/components/feedback/Modal/Modal.tsx"],"sourcesContent":["/**\r\n * Modal Component\r\n *\r\n * Accessible modal dialog with smooth Apple-inspired animations.\r\n * Supports focus trapping and keyboard navigation.\r\n */\r\n\r\nimport {\r\n forwardRef,\r\n useEffect,\r\n useRef,\r\n useCallback,\r\n useMemo,\r\n memo,\r\n type CSSProperties,\r\n type ReactNode,\r\n type HTMLAttributes,\r\n} from 'react'\r\nimport { createPortal } from 'react-dom'\r\nimport { cx } from '../../../utils/styles'\r\nimport { useId } from '../../../hooks/useId'\r\nimport { spacing, fontSizes } from '../../../design-system'\r\nimport { componentGap, iconButtonSizes } from '../../../design-system/primitives'\r\n\r\nexport type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full'\r\n\r\nexport interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'title'> {\r\n /** Whether the modal is open */\r\n isOpen: boolean\r\n /** Callback when modal should close */\r\n onClose: () => void\r\n /** Modal size */\r\n size?: ModalSize\r\n /** Modal title */\r\n title?: ReactNode\r\n /** Modal description */\r\n description?: ReactNode\r\n /** Whether to close on overlay click */\r\n closeOnOverlayClick?: boolean\r\n /** Whether to close on escape key */\r\n closeOnEscape?: boolean\r\n /** Whether to show close button */\r\n showCloseButton?: boolean\r\n /** Accessible label of the close button */\r\n closeLabel?: string\r\n /** Footer content (e.g. action buttons), pinned below the scrollable body */\r\n footer?: ReactNode\r\n /** Custom class name */\r\n className?: string\r\n /** Test ID */\r\n testId?: string\r\n /** Modal content */\r\n children?: ReactNode\r\n}\r\n\r\nconst sizeWidths: Record<ModalSize, string> = {\r\n sm: '400px',\r\n md: '500px',\r\n lg: '640px',\r\n xl: '800px',\r\n '2xl': '1200px',\r\n full: 'calc(100vw - 48px)',\r\n}\r\n\r\n/** Close icon - memoized to prevent re-renders */\r\nconst CloseIcon = memo(function CloseIcon() {\r\n return (\r\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" aria-hidden=\"true\">\r\n <path\r\n d=\"M4 4L12 12M12 4L4 12\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n </svg>\r\n )\r\n})\r\n\r\nexport const Modal = forwardRef<HTMLDivElement, ModalProps>(function Modal(\r\n {\r\n isOpen,\r\n onClose,\r\n size = 'md',\r\n title,\r\n description,\r\n closeOnOverlayClick = true,\r\n closeOnEscape = true,\r\n showCloseButton = true,\r\n closeLabel = 'Close modal',\r\n footer,\r\n className,\r\n style,\r\n testId,\r\n children,\r\n ...props\r\n },\r\n ref\r\n) {\r\n const modalRef = useRef<HTMLDivElement>(null)\r\n const titleId = useId('modal-title')\r\n const descriptionId = useId('modal-description')\r\n const previousActiveElement = useRef<HTMLElement | null>(null)\r\n const wasOpen = useRef(false)\r\n\r\n // Track previous open state to handle focus restoration before unmount\r\n useEffect(() => {\r\n // Modal just opened\r\n if (isOpen && !wasOpen.current) {\r\n previousActiveElement.current = document.activeElement as HTMLElement\r\n wasOpen.current = true\r\n }\r\n // Modal is about to close - restore focus BEFORE unmount\r\n else if (!isOpen && wasOpen.current) {\r\n // Blur any focused element inside modal to prevent aria-hidden warning\r\n if (document.activeElement instanceof HTMLElement) {\r\n document.activeElement.blur()\r\n }\r\n // Restore focus to previous element\r\n previousActiveElement.current?.focus()\r\n previousActiveElement.current = null\r\n wasOpen.current = false\r\n }\r\n }, [isOpen])\r\n\r\n // Store onClose in a ref to avoid re-running effects when it changes\r\n const onCloseRef = useRef(onClose)\r\n useEffect(() => {\r\n onCloseRef.current = onClose\r\n }, [onClose])\r\n\r\n // Focus modal when it opens (only once)\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n if (modal) {\r\n modal.focus()\r\n }\r\n }, [isOpen])\r\n\r\n // Keyboard handling and body scroll lock\r\n useEffect(() => {\r\n if (!isOpen) return\r\n\r\n const modal = modalRef.current\r\n\r\n const handleKeyDown = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && closeOnEscape) {\r\n onCloseRef.current()\r\n return\r\n }\r\n\r\n if (e.key === 'Tab' && modal) {\r\n const focusableElements = modal.querySelectorAll<HTMLElement>(\r\n 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable=\"true\"], [tabindex]:not([tabindex=\"-1\"])'\r\n )\r\n const firstElement = focusableElements[0]\r\n const lastElement = focusableElements[focusableElements.length - 1]\r\n\r\n if (e.shiftKey && document.activeElement === firstElement) {\r\n e.preventDefault()\r\n lastElement?.focus()\r\n } else if (!e.shiftKey && document.activeElement === lastElement) {\r\n e.preventDefault()\r\n firstElement?.focus()\r\n }\r\n }\r\n }\r\n\r\n document.addEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = 'hidden'\r\n\r\n return () => {\r\n document.removeEventListener('keydown', handleKeyDown)\r\n document.body.style.overflow = ''\r\n }\r\n }, [isOpen, closeOnEscape])\r\n\r\n const handleOverlayClick = useCallback(\r\n (e: React.MouseEvent) => {\r\n if (e.target === e.currentTarget && closeOnOverlayClick) {\r\n onClose()\r\n }\r\n },\r\n [closeOnOverlayClick, onClose]\r\n )\r\n\r\n // Memoize styles to prevent object recreation on every render\r\n const overlayStyle = useMemo<CSSProperties>(() => ({\r\n position: 'fixed',\r\n inset: 0,\r\n zIndex: 'var(--brycks-z-modal)',\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n padding: spacing[6],\r\n backgroundColor: 'var(--brycks-background-overlay)',\r\n animation: 'brycks-fade-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n }), [])\r\n\r\n const modalStyle = useMemo<CSSProperties>(() => ({\r\n position: 'relative',\r\n width: '100%',\r\n maxWidth: sizeWidths[size],\r\n maxHeight: size === 'full' ? 'calc(100vh - 48px)' : '85vh',\r\n backgroundColor: 'var(--brycks-background-elevated)',\r\n borderRadius: 'var(--brycks-radius-xl)',\r\n boxShadow: 'var(--brycks-shadow-2xl)',\r\n display: 'flex',\r\n flexDirection: 'column',\r\n overflow: 'hidden',\r\n animation: 'brycks-scale-in var(--brycks-duration-normal) var(--brycks-ease-out)',\r\n outline: 'none',\r\n ...style,\r\n }), [size, style])\r\n\r\n if (!isOpen) return null\r\n\r\n const headerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'flex-start',\r\n justifyContent: 'space-between',\r\n padding: `${spacing[5]}px ${spacing[6]}px`,\r\n borderBottom: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const titleContainerStyle: CSSProperties = {\r\n display: 'flex',\r\n flexDirection: 'column',\r\n gap: spacing[1],\r\n }\r\n\r\n const titleStyle: CSSProperties = {\r\n fontSize: fontSizes.lg,\r\n fontWeight: 600,\r\n color: 'var(--brycks-foreground-default)',\r\n margin: 0,\r\n lineHeight: 1.3,\r\n }\r\n\r\n const descriptionStyle: CSSProperties = {\r\n fontSize: fontSizes.base,\r\n color: 'var(--brycks-foreground-muted)',\r\n margin: 0,\r\n }\r\n\r\n const closeButtonStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'center',\r\n width: iconButtonSizes.sm,\r\n height: iconButtonSizes.sm,\r\n borderRadius: 'var(--brycks-radius-md)',\r\n color: 'var(--brycks-foreground-muted)',\r\n transition: 'all var(--brycks-duration-fast) var(--brycks-ease-out)',\r\n marginLeft: componentGap.xl,\r\n flexShrink: 0,\r\n }\r\n\r\n const contentStyle: CSSProperties = {\r\n flex: 1,\r\n overflow: 'auto',\r\n padding: spacing[6],\r\n }\r\n\r\n const footerStyle: CSSProperties = {\r\n display: 'flex',\r\n alignItems: 'center',\r\n justifyContent: 'flex-end',\r\n gap: spacing[2],\r\n padding: `${spacing[4]}px ${spacing[6]}px`,\r\n borderTop: '1px solid var(--brycks-border-muted)',\r\n }\r\n\r\n const modalContent = (\r\n <div\r\n className=\"brycks-modal-overlay\"\r\n style={overlayStyle}\r\n onClick={handleOverlayClick}\r\n >\r\n <div\r\n ref={(node) => {\r\n (modalRef as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n if (typeof ref === 'function') ref(node)\r\n else if (ref) (ref as React.MutableRefObject<HTMLDivElement | null>).current = node\r\n }}\r\n role=\"dialog\"\r\n aria-modal=\"true\"\r\n aria-labelledby={title ? titleId : undefined}\r\n aria-describedby={description ? descriptionId : undefined}\r\n className={cx('brycks-modal', `brycks-modal--${size}`, className)}\r\n style={modalStyle}\r\n tabIndex={-1}\r\n data-testid={testId}\r\n {...props}\r\n >\r\n {(title || showCloseButton) && (\r\n <div className=\"brycks-modal-header\" style={headerStyle}>\r\n <div style={titleContainerStyle}>\r\n {title && (\r\n <h2 id={titleId} style={titleStyle}>\r\n {title}\r\n </h2>\r\n )}\r\n {description && (\r\n <p id={descriptionId} style={descriptionStyle}>\r\n {description}\r\n </p>\r\n )}\r\n </div>\r\n {showCloseButton && (\r\n <button\r\n type=\"button\"\r\n className=\"brycks-modal-close\"\r\n style={closeButtonStyle}\r\n onClick={onClose}\r\n aria-label={closeLabel}\r\n onMouseEnter={(e) => {\r\n e.currentTarget.style.backgroundColor = 'var(--brycks-background-muted)'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-default)'\r\n }}\r\n onMouseLeave={(e) => {\r\n e.currentTarget.style.backgroundColor = 'transparent'\r\n e.currentTarget.style.color = 'var(--brycks-foreground-muted)'\r\n }}\r\n >\r\n <CloseIcon />\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n\r\n <div className=\"brycks-modal-content\" style={contentStyle}>\r\n {children}\r\n </div>\r\n\r\n {footer && (\r\n <div className=\"brycks-modal-footer\" style={footerStyle}>\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )\r\n\r\n return createPortal(modalContent, document.body)\r\n})\r\n\r\nModal.displayName = 'Modal'\r\n"],"names":["sizeWidths","CloseIcon","memo","jsx","Modal","forwardRef","isOpen","onClose","size","title","description","closeOnOverlayClick","closeOnEscape","showCloseButton","closeLabel","footer","className","style","testId","children","props","ref","modalRef","useRef","titleId","useId","descriptionId","previousActiveElement","wasOpen","useEffect","onCloseRef","modal","handleKeyDown","e","focusableElements","firstElement","lastElement","handleOverlayClick","useCallback","overlayStyle","useMemo","spacing","modalStyle","headerStyle","titleContainerStyle","titleStyle","fontSizes","descriptionStyle","closeButtonStyle","iconButtonSizes","componentGap","contentStyle","footerStyle","modalContent","jsxs","node","cx","createPortal"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=require("react/jsx-runtime"),t=require("react"),V=require("react-dom"),Z=require("../../../utils/styles.cjs"),$=require("../../../hooks/useClickOutside.cjs"),D=require("../../../hooks/useMediaQuery.cjs"),q=t.forwardRef(function({content:I,children:C,placement:w="bottom",trigger:r="click",isOpen:y,defaultOpen:z=!1,onOpenChange:E,offset:u=8,openDelay:M=200,closeDelay:x=200,closeOnOutsideClick:A=!0,showArrow:te=!0,className:B,style:F,testId:N,...X},h){const[Y,Q]=t.useState(z),[P,W]=t.useState({top:0,left:0}),[v,T]=t.useState(!1),R=t.useRef(null),L=t.useRef(null),d=t.useRef(null),f=t.useRef(null),O=t.useId(),_=D.usePrefersReducedMotion(),s=y??Y,n=t.useCallback(e=>{y===void 0&&Q(e),E?.(e)},[y,E]),G=$.useClickOutside(()=>{A&&r==="click"&&s&&n(!1)},{enabled:s&&r==="click"});t.useEffect(()=>(T(!0),()=>T(!1)),[]),t.useEffect(()=>{if(!s||!R.current||!v)return;const e=()=>{const S=R.current,j=L.current;if(!S||!j)return;const o=S.getBoundingClientRect(),c=j.getBoundingClientRect(),p=window.scrollX,b=window.scrollY;let a=0,i=0;switch(w){case"top":case"top-start":case"top-end":a=o.top+b-c.height-u;break;case"bottom":case"bottom-start":case"bottom-end":a=o.bottom+b+u;break;case"left":case"left-start":case"left-end":i=o.left+p-c.width-u;break;case"right":case"right-start":case"right-end":i=o.right+p+u;break}switch(w){case"top":case"bottom":i=o.left+p+(o.width-c.width)/2;break;case"top-start":case"bottom-start":i=o.left+p;break;case"top-end":case"bottom-end":i=o.right+p-c.width;break;case"left":case"right":a=o.top+b+(o.height-c.height)/2;break;case"left-start":case"right-start":a=o.top+b;break;case"left-end":case"right-end":a=o.bottom+b-c.height;break}W({top:a,left:i})};return e(),window.addEventListener("resize",e),window.addEventListener("scroll",e,!0),()=>{window.removeEventListener("resize",e),window.removeEventListener("scroll",e,!0)}},[s,w,u,v]),t.useEffect(()=>()=>{d.current&&clearTimeout(d.current),f.current&&clearTimeout(f.current)},[]);const m=t.useCallback(()=>{f.current&&clearTimeout(f.current),r==="hover"?d.current=setTimeout(()=>n(!0),M):n(!0)},[r,M,n]),k=t.useCallback(()=>{d.current&&clearTimeout(d.current),r==="hover"?f.current=setTimeout(()=>n(!1),x):n(!1)},[r,x,n]),H=t.useCallback(()=>{s?k():m()},[s,m,k]),g=C.props,J=t.cloneElement(C,{ref:R,"aria-expanded":s,"aria-haspopup":"dialog","aria-controls":s?O:void 0,...r==="click"&&{onClick:e=>{g.onClick?.(e),H()}},...r==="hover"&&{onMouseEnter:e=>{g.onMouseEnter?.(e),m()},onMouseLeave:e=>{g.onMouseLeave?.(e),k()}}}),K={position:"absolute",top:P.top,left:P.left,zIndex:"var(--brycks-z-popover)",backgroundColor:"var(--brycks-background-elevated)",border:"1px solid var(--brycks-border-default)",borderRadius:"var(--brycks-radius-lg)",boxShadow:"var(--brycks-shadow-lg)",padding:"var(--brycks-space-3)",minWidth:120,animation:_?"none":"brycks-popover-in 150ms ease-out",...F},U=s&&v&&l.jsxs(l.Fragment,{children:[l.jsx("style",{children:`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),t=require("react"),Z=require("react-dom"),_=require("../../../utils/styles.cjs"),$=require("../../../hooks/useClickOutside.cjs"),ee=require("../../../hooks/useMediaQuery.cjs"),I=t.forwardRef(function({content:z,children:M,placement:E="bottom",trigger:s="click",isOpen:C,defaultOpen:B=!1,onOpenChange:x,offset:l=8,openDelay:P=200,closeDelay:T=200,closeOnOutsideClick:F=!0,className:N,style:X,testId:Y,...K},v){const[Q,W]=t.useState(B),[L,A]=t.useState({top:0,left:0}),[h,O]=t.useState(!1),m=t.useRef(null),S=t.useRef(null),d=t.useRef(null),f=t.useRef(null),j=t.useId(),D=ee.usePrefersReducedMotion(),r=C??Q,n=t.useCallback(e=>{C===void 0&&W(e),x?.(e)},[C,x]),G=$.useClickOutside(()=>{F&&s==="click"&&r&&n(!1)},{enabled:r&&s==="click"});t.useEffect(()=>(O(!0),()=>O(!1)),[]),t.useEffect(()=>{if(!r||!m.current||!h)return;const e=()=>{const g=m.current,q=S.current;if(!g||!q)return;const o=g.getBoundingClientRect(),c=q.getBoundingClientRect(),p=window.scrollX,b=window.scrollY;let a=0,i=0;switch(E){case"top":case"top-start":case"top-end":a=o.top+b-c.height-l;break;case"bottom":case"bottom-start":case"bottom-end":a=o.bottom+b+l;break;case"left":case"left-start":case"left-end":i=o.left+p-c.width-l;break;case"right":case"right-start":case"right-end":i=o.right+p+l;break}switch(E){case"top":case"bottom":i=o.left+p+(o.width-c.width)/2;break;case"top-start":case"bottom-start":i=o.left+p;break;case"top-end":case"bottom-end":i=o.right+p-c.width;break;case"left":case"right":a=o.top+b+(o.height-c.height)/2;break;case"left-start":case"right-start":a=o.top+b;break;case"left-end":case"right-end":a=o.bottom+b-c.height;break}A({top:a,left:i})};return e(),window.addEventListener("resize",e),window.addEventListener("scroll",e,!0),()=>{window.removeEventListener("resize",e),window.removeEventListener("scroll",e,!0)}},[r,E,l,h]),t.useEffect(()=>()=>{d.current&&clearTimeout(d.current),f.current&&clearTimeout(f.current)},[]),t.useEffect(()=>{if(!r)return;const e=g=>{g.key==="Escape"&&(n(!1),m.current?.focus())};return document.addEventListener("keydown",e),()=>document.removeEventListener("keydown",e)},[r,n]);const k=t.useCallback(()=>{f.current&&clearTimeout(f.current),s==="hover"?d.current=setTimeout(()=>n(!0),P):n(!0)},[s,P,n]),y=t.useCallback(()=>{d.current&&clearTimeout(d.current),s==="hover"?f.current=setTimeout(()=>n(!1),T):n(!1)},[s,T,n]),H=t.useCallback(()=>{r?y():k()},[r,k,y]),w=M.props,R=w.ref,J=t.cloneElement(M,{ref:e=>{m.current=e,typeof R=="function"?R(e):R&&(R.current=e)},"aria-expanded":r,"aria-haspopup":"dialog","aria-controls":r?j:void 0,...s==="click"&&{onClick:e=>{w.onClick?.(e),H()}},...s==="hover"&&{onMouseEnter:e=>{w.onMouseEnter?.(e),k()},onMouseLeave:e=>{w.onMouseLeave?.(e),y()}}}),U={position:"absolute",top:L.top,left:L.left,zIndex:"var(--brycks-z-popover)",backgroundColor:"var(--brycks-background-elevated)",border:"1px solid var(--brycks-border-default)",borderRadius:"var(--brycks-radius-lg)",boxShadow:"var(--brycks-shadow-lg)",padding:"var(--brycks-space-3)",minWidth:120,animation:D?"none":"brycks-popover-in 150ms ease-out",...X},V=r&&h&&u.jsxs(u.Fragment,{children:[u.jsx("style",{children:`
2
2
  @keyframes brycks-popover-in {
3
3
  from { opacity: 0; transform: scale(0.95); }
4
4
  to { opacity: 1; transform: scale(1); }
5
5
  }
6
- `}),l.jsx("div",{ref:e=>{L.current=e,G.current=e,typeof h=="function"?h(e):h&&(h.current=e)},id:O,className:Z.cx("brycks-popover",B),style:K,role:"dialog","aria-modal":"false","data-testid":N,onMouseEnter:r==="hover"?m:void 0,onMouseLeave:r==="hover"?k:void 0,...X,children:I})]});return l.jsxs(l.Fragment,{children:[J,v&&V.createPortal(U,document.body)]})});q.displayName="Popover";exports.Popover=q;
6
+ `}),u.jsx("div",{ref:e=>{S.current=e,G.current=e,typeof v=="function"?v(e):v&&(v.current=e)},id:j,className:_.cx("brycks-popover",N),style:U,role:"dialog","aria-modal":"false","data-testid":Y,onMouseEnter:s==="hover"?k:void 0,onMouseLeave:s==="hover"?y:void 0,...K,children:z})]});return u.jsxs(u.Fragment,{children:[J,h&&Z.createPortal(V,document.body)]})});I.displayName="Popover";exports.Popover=I;
7
7
  //# sourceMappingURL=Popover.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Popover.cjs","sources":["../../../../src/components/feedback/Popover/Popover.tsx"],"sourcesContent":["/**\n * Popover Component\n *\n * A floating content panel that appears relative to a trigger element.\n * Supports different placements and can be triggered by click or hover.\n */\n\nimport React, {\n forwardRef,\n useState,\n useEffect,\n useRef,\n useCallback,\n cloneElement,\n useId,\n type CSSProperties,\n type ReactNode,\n type ReactElement,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\nimport { useClickOutside } from '../../../hooks/useClickOutside'\nimport { usePrefersReducedMotion } from '../../../hooks/useMediaQuery'\n\nexport type PopoverPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n\nexport type PopoverTrigger = 'click' | 'hover'\n\nexport interface PopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'content'> {\n /** Popover content */\n content: ReactNode\n /** Trigger element */\n children: ReactElement\n /** Placement relative to trigger */\n placement?: PopoverPlacement\n /** How to trigger the popover */\n trigger?: PopoverTrigger\n /** Whether the popover is open (controlled) */\n isOpen?: boolean\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (isOpen: boolean) => void\n /** Offset from trigger in pixels */\n offset?: number\n /** Delay before showing (hover trigger) */\n openDelay?: number\n /** Delay before hiding (hover trigger) */\n closeDelay?: number\n /** Whether to close on outside click */\n closeOnOutsideClick?: boolean\n /** Whether to show arrow */\n showArrow?: boolean\n /** Custom class name for content */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(function Popover(\n {\n content,\n children,\n placement = 'bottom',\n trigger = 'click',\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n offset = 8,\n openDelay = 200,\n closeDelay = 200,\n closeOnOutsideClick = true,\n showArrow: _showArrow = true,\n className,\n style,\n testId,\n ...props\n },\n ref\n) {\n const [internalIsOpen, setInternalIsOpen] = useState(defaultOpen)\n const [position, setPosition] = useState({ top: 0, left: 0 })\n const [mounted, setMounted] = useState(false)\n const triggerRef = useRef<HTMLElement>(null)\n const contentRef = useRef<HTMLDivElement>(null)\n const openTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null)\n const closeTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null)\n const popoverId = useId()\n const prefersReducedMotion = usePrefersReducedMotion()\n\n const isOpen = controlledIsOpen ?? internalIsOpen\n\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalIsOpen(open)\n }\n onOpenChange?.(open)\n },\n [controlledIsOpen, onOpenChange]\n )\n\n // Click outside handler\n const clickOutsideRef = useClickOutside<HTMLDivElement>(() => {\n if (closeOnOutsideClick && trigger === 'click' && isOpen) {\n setIsOpen(false)\n }\n }, { enabled: isOpen && trigger === 'click' })\n\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n // Calculate position\n useEffect(() => {\n if (!isOpen || !triggerRef.current || !mounted) return\n\n const updatePosition = () => {\n const trigger = triggerRef.current\n const content = contentRef.current\n if (!trigger || !content) return\n\n const triggerRect = trigger.getBoundingClientRect()\n const contentRect = content.getBoundingClientRect()\n const scrollX = window.scrollX\n const scrollY = window.scrollY\n\n let top = 0\n let left = 0\n\n // Calculate base position\n switch (placement) {\n case 'top':\n case 'top-start':\n case 'top-end':\n top = triggerRect.top + scrollY - contentRect.height - offset\n break\n case 'bottom':\n case 'bottom-start':\n case 'bottom-end':\n top = triggerRect.bottom + scrollY + offset\n break\n case 'left':\n case 'left-start':\n case 'left-end':\n left = triggerRect.left + scrollX - contentRect.width - offset\n break\n case 'right':\n case 'right-start':\n case 'right-end':\n left = triggerRect.right + scrollX + offset\n break\n }\n\n // Calculate alignment\n switch (placement) {\n case 'top':\n case 'bottom':\n left = triggerRect.left + scrollX + (triggerRect.width - contentRect.width) / 2\n break\n case 'top-start':\n case 'bottom-start':\n left = triggerRect.left + scrollX\n break\n case 'top-end':\n case 'bottom-end':\n left = triggerRect.right + scrollX - contentRect.width\n break\n case 'left':\n case 'right':\n top = triggerRect.top + scrollY + (triggerRect.height - contentRect.height) / 2\n break\n case 'left-start':\n case 'right-start':\n top = triggerRect.top + scrollY\n break\n case 'left-end':\n case 'right-end':\n top = triggerRect.bottom + scrollY - contentRect.height\n break\n }\n\n setPosition({ top, left })\n }\n\n updatePosition()\n window.addEventListener('resize', updatePosition)\n window.addEventListener('scroll', updatePosition, true)\n\n return () => {\n window.removeEventListener('resize', updatePosition)\n window.removeEventListener('scroll', updatePosition, true)\n }\n }, [isOpen, placement, offset, mounted])\n\n // Cleanup timeouts\n useEffect(() => {\n return () => {\n if (openTimeoutRef.current) clearTimeout(openTimeoutRef.current)\n if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current)\n }\n }, [])\n\n const handleOpen = useCallback(() => {\n if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current)\n if (trigger === 'hover') {\n openTimeoutRef.current = setTimeout(() => setIsOpen(true), openDelay)\n } else {\n setIsOpen(true)\n }\n }, [trigger, openDelay, setIsOpen])\n\n const handleClose = useCallback(() => {\n if (openTimeoutRef.current) clearTimeout(openTimeoutRef.current)\n if (trigger === 'hover') {\n closeTimeoutRef.current = setTimeout(() => setIsOpen(false), closeDelay)\n } else {\n setIsOpen(false)\n }\n }, [trigger, closeDelay, setIsOpen])\n\n const handleToggle = useCallback(() => {\n if (isOpen) {\n handleClose()\n } else {\n handleOpen()\n }\n }, [isOpen, handleOpen, handleClose])\n\n // Clone trigger with event handlers and ARIA attributes\n const childProps = children.props as Record<string, unknown>\n const triggerElement = cloneElement(children as ReactElement<Record<string, unknown>>, {\n ref: triggerRef,\n 'aria-expanded': isOpen,\n 'aria-haspopup': 'dialog',\n 'aria-controls': isOpen ? popoverId : undefined,\n ...(trigger === 'click' && {\n onClick: (e: React.MouseEvent) => {\n (childProps.onClick as ((e: React.MouseEvent) => void) | undefined)?.(e)\n handleToggle()\n },\n }),\n ...(trigger === 'hover' && {\n onMouseEnter: (e: React.MouseEvent) => {\n (childProps.onMouseEnter as ((e: React.MouseEvent) => void) | undefined)?.(e)\n handleOpen()\n },\n onMouseLeave: (e: React.MouseEvent) => {\n (childProps.onMouseLeave as ((e: React.MouseEvent) => void) | undefined)?.(e)\n handleClose()\n },\n }),\n })\n\n const contentStyle: CSSProperties = {\n position: 'absolute',\n top: position.top,\n left: position.left,\n zIndex: 'var(--brycks-z-popover)',\n backgroundColor: 'var(--brycks-background-elevated)',\n border: '1px solid var(--brycks-border-default)',\n borderRadius: 'var(--brycks-radius-lg)',\n boxShadow: 'var(--brycks-shadow-lg)',\n padding: 'var(--brycks-space-3)',\n minWidth: 120,\n animation: prefersReducedMotion ? 'none' : 'brycks-popover-in 150ms ease-out',\n ...style,\n }\n\n const popoverContent = isOpen && mounted && (\n <>\n <style>\n {`\n @keyframes brycks-popover-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n }\n `}\n </style>\n <div\n ref={(node) => {\n (contentRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n (clickOutsideRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n id={popoverId}\n className={cx('brycks-popover', className)}\n style={contentStyle}\n role=\"dialog\"\n aria-modal=\"false\"\n data-testid={testId}\n onMouseEnter={trigger === 'hover' ? handleOpen : undefined}\n onMouseLeave={trigger === 'hover' ? handleClose : undefined}\n {...props}\n >\n {content}\n </div>\n </>\n )\n\n return (\n <>\n {triggerElement}\n {mounted && createPortal(popoverContent, document.body)}\n </>\n )\n})\n\nPopover.displayName = 'Popover'\n"],"names":["Popover","forwardRef","content","children","placement","trigger","controlledIsOpen","defaultOpen","onOpenChange","offset","openDelay","closeDelay","closeOnOutsideClick","_showArrow","className","style","testId","props","ref","internalIsOpen","setInternalIsOpen","useState","position","setPosition","mounted","setMounted","triggerRef","useRef","contentRef","openTimeoutRef","closeTimeoutRef","popoverId","useId","prefersReducedMotion","usePrefersReducedMotion","isOpen","setIsOpen","useCallback","open","clickOutsideRef","useClickOutside","useEffect","updatePosition","triggerRect","contentRect","scrollX","scrollY","top","left","handleOpen","handleClose","handleToggle","childProps","triggerElement","cloneElement","contentStyle","popoverContent","jsxs","Fragment","jsx","node","cx","createPortal"],"mappings":"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"}
1
+ {"version":3,"file":"Popover.cjs","sources":["../../../../src/components/feedback/Popover/Popover.tsx"],"sourcesContent":["/**\n * Popover Component\n *\n * A floating content panel that appears relative to a trigger element.\n * Supports different placements and can be triggered by click or hover.\n */\n\nimport React, {\n forwardRef,\n useState,\n useEffect,\n useRef,\n useCallback,\n cloneElement,\n useId,\n type CSSProperties,\n type ReactNode,\n type ReactElement,\n type HTMLAttributes,\n} from 'react'\nimport { createPortal } from 'react-dom'\nimport { cx } from '../../../utils/styles'\nimport { useClickOutside } from '../../../hooks/useClickOutside'\nimport { usePrefersReducedMotion } from '../../../hooks/useMediaQuery'\n\nexport type PopoverPlacement =\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n\nexport type PopoverTrigger = 'click' | 'hover'\n\nexport interface PopoverProps extends Omit<HTMLAttributes<HTMLDivElement>, 'content'> {\n /** Popover content */\n content: ReactNode\n /** Trigger element */\n children: ReactElement\n /** Placement relative to trigger */\n placement?: PopoverPlacement\n /** How to trigger the popover */\n trigger?: PopoverTrigger\n /** Whether the popover is open (controlled) */\n isOpen?: boolean\n /** Default open state (uncontrolled) */\n defaultOpen?: boolean\n /** Callback when open state changes */\n onOpenChange?: (isOpen: boolean) => void\n /** Offset from trigger in pixels */\n offset?: number\n /** Delay before showing (hover trigger) */\n openDelay?: number\n /** Delay before hiding (hover trigger) */\n closeDelay?: number\n /** Whether to close on outside click */\n closeOnOutsideClick?: boolean\n /** Custom class name for content */\n className?: string\n /** Test ID */\n testId?: string\n}\n\nexport const Popover = forwardRef<HTMLDivElement, PopoverProps>(function Popover(\n {\n content,\n children,\n placement = 'bottom',\n trigger = 'click',\n isOpen: controlledIsOpen,\n defaultOpen = false,\n onOpenChange,\n offset = 8,\n openDelay = 200,\n closeDelay = 200,\n closeOnOutsideClick = true,\n className,\n style,\n testId,\n ...props\n },\n ref\n) {\n const [internalIsOpen, setInternalIsOpen] = useState(defaultOpen)\n const [position, setPosition] = useState({ top: 0, left: 0 })\n const [mounted, setMounted] = useState(false)\n const triggerRef = useRef<HTMLElement>(null)\n const contentRef = useRef<HTMLDivElement>(null)\n const openTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null)\n const closeTimeoutRef = useRef<ReturnType<typeof setTimeout>>(null)\n const popoverId = useId()\n const prefersReducedMotion = usePrefersReducedMotion()\n\n const isOpen = controlledIsOpen ?? internalIsOpen\n\n const setIsOpen = useCallback(\n (open: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalIsOpen(open)\n }\n onOpenChange?.(open)\n },\n [controlledIsOpen, onOpenChange]\n )\n\n // Click outside handler\n const clickOutsideRef = useClickOutside<HTMLDivElement>(() => {\n if (closeOnOutsideClick && trigger === 'click' && isOpen) {\n setIsOpen(false)\n }\n }, { enabled: isOpen && trigger === 'click' })\n\n useEffect(() => {\n setMounted(true)\n return () => setMounted(false)\n }, [])\n\n // Calculate position\n useEffect(() => {\n if (!isOpen || !triggerRef.current || !mounted) return\n\n const updatePosition = () => {\n const trigger = triggerRef.current\n const content = contentRef.current\n if (!trigger || !content) return\n\n const triggerRect = trigger.getBoundingClientRect()\n const contentRect = content.getBoundingClientRect()\n const scrollX = window.scrollX\n const scrollY = window.scrollY\n\n let top = 0\n let left = 0\n\n // Calculate base position\n switch (placement) {\n case 'top':\n case 'top-start':\n case 'top-end':\n top = triggerRect.top + scrollY - contentRect.height - offset\n break\n case 'bottom':\n case 'bottom-start':\n case 'bottom-end':\n top = triggerRect.bottom + scrollY + offset\n break\n case 'left':\n case 'left-start':\n case 'left-end':\n left = triggerRect.left + scrollX - contentRect.width - offset\n break\n case 'right':\n case 'right-start':\n case 'right-end':\n left = triggerRect.right + scrollX + offset\n break\n }\n\n // Calculate alignment\n switch (placement) {\n case 'top':\n case 'bottom':\n left = triggerRect.left + scrollX + (triggerRect.width - contentRect.width) / 2\n break\n case 'top-start':\n case 'bottom-start':\n left = triggerRect.left + scrollX\n break\n case 'top-end':\n case 'bottom-end':\n left = triggerRect.right + scrollX - contentRect.width\n break\n case 'left':\n case 'right':\n top = triggerRect.top + scrollY + (triggerRect.height - contentRect.height) / 2\n break\n case 'left-start':\n case 'right-start':\n top = triggerRect.top + scrollY\n break\n case 'left-end':\n case 'right-end':\n top = triggerRect.bottom + scrollY - contentRect.height\n break\n }\n\n setPosition({ top, left })\n }\n\n updatePosition()\n window.addEventListener('resize', updatePosition)\n window.addEventListener('scroll', updatePosition, true)\n\n return () => {\n window.removeEventListener('resize', updatePosition)\n window.removeEventListener('scroll', updatePosition, true)\n }\n }, [isOpen, placement, offset, mounted])\n\n // Cleanup timeouts\n useEffect(() => {\n return () => {\n if (openTimeoutRef.current) clearTimeout(openTimeoutRef.current)\n if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current)\n }\n }, [])\n\n // Escape dismisses the popover and returns focus to the trigger (WCAG 1.4.13)\n useEffect(() => {\n if (!isOpen) return\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setIsOpen(false)\n triggerRef.current?.focus()\n }\n }\n\n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [isOpen, setIsOpen])\n\n const handleOpen = useCallback(() => {\n if (closeTimeoutRef.current) clearTimeout(closeTimeoutRef.current)\n if (trigger === 'hover') {\n openTimeoutRef.current = setTimeout(() => setIsOpen(true), openDelay)\n } else {\n setIsOpen(true)\n }\n }, [trigger, openDelay, setIsOpen])\n\n const handleClose = useCallback(() => {\n if (openTimeoutRef.current) clearTimeout(openTimeoutRef.current)\n if (trigger === 'hover') {\n closeTimeoutRef.current = setTimeout(() => setIsOpen(false), closeDelay)\n } else {\n setIsOpen(false)\n }\n }, [trigger, closeDelay, setIsOpen])\n\n const handleToggle = useCallback(() => {\n if (isOpen) {\n handleClose()\n } else {\n handleOpen()\n }\n }, [isOpen, handleOpen, handleClose])\n\n // Clone trigger with event handlers and ARIA attributes\n const childProps = children.props as Record<string, unknown>\n const childRef = childProps.ref as React.Ref<HTMLElement> | undefined\n const triggerElement = cloneElement(children as ReactElement<Record<string, unknown>>, {\n // Merge refs so a ref the consumer set on the trigger keeps working\n ref: (node: HTMLElement | null) => {\n (triggerRef as React.MutableRefObject<HTMLElement | null>).current = node\n if (typeof childRef === 'function') childRef(node)\n else if (childRef) (childRef as React.MutableRefObject<HTMLElement | null>).current = node\n },\n 'aria-expanded': isOpen,\n 'aria-haspopup': 'dialog',\n 'aria-controls': isOpen ? popoverId : undefined,\n ...(trigger === 'click' && {\n onClick: (e: React.MouseEvent) => {\n (childProps.onClick as ((e: React.MouseEvent) => void) | undefined)?.(e)\n handleToggle()\n },\n }),\n ...(trigger === 'hover' && {\n onMouseEnter: (e: React.MouseEvent) => {\n (childProps.onMouseEnter as ((e: React.MouseEvent) => void) | undefined)?.(e)\n handleOpen()\n },\n onMouseLeave: (e: React.MouseEvent) => {\n (childProps.onMouseLeave as ((e: React.MouseEvent) => void) | undefined)?.(e)\n handleClose()\n },\n }),\n })\n\n const contentStyle: CSSProperties = {\n position: 'absolute',\n top: position.top,\n left: position.left,\n zIndex: 'var(--brycks-z-popover)',\n backgroundColor: 'var(--brycks-background-elevated)',\n border: '1px solid var(--brycks-border-default)',\n borderRadius: 'var(--brycks-radius-lg)',\n boxShadow: 'var(--brycks-shadow-lg)',\n padding: 'var(--brycks-space-3)',\n minWidth: 120,\n animation: prefersReducedMotion ? 'none' : 'brycks-popover-in 150ms ease-out',\n ...style,\n }\n\n const popoverContent = isOpen && mounted && (\n <>\n <style>\n {`\n @keyframes brycks-popover-in {\n from { opacity: 0; transform: scale(0.95); }\n to { opacity: 1; transform: scale(1); }\n }\n `}\n </style>\n <div\n ref={(node) => {\n (contentRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n (clickOutsideRef as React.MutableRefObject<HTMLDivElement | null>).current = node\n if (typeof ref === 'function') ref(node)\n else if (ref) ref.current = node\n }}\n id={popoverId}\n className={cx('brycks-popover', className)}\n style={contentStyle}\n role=\"dialog\"\n aria-modal=\"false\"\n data-testid={testId}\n onMouseEnter={trigger === 'hover' ? handleOpen : undefined}\n onMouseLeave={trigger === 'hover' ? handleClose : undefined}\n {...props}\n >\n {content}\n </div>\n </>\n )\n\n return (\n <>\n {triggerElement}\n {mounted && createPortal(popoverContent, document.body)}\n </>\n )\n})\n\nPopover.displayName = 'Popover'\n"],"names":["Popover","forwardRef","content","children","placement","trigger","controlledIsOpen","defaultOpen","onOpenChange","offset","openDelay","closeDelay","closeOnOutsideClick","className","style","testId","props","ref","internalIsOpen","setInternalIsOpen","useState","position","setPosition","mounted","setMounted","triggerRef","useRef","contentRef","openTimeoutRef","closeTimeoutRef","popoverId","useId","prefersReducedMotion","usePrefersReducedMotion","isOpen","setIsOpen","useCallback","open","clickOutsideRef","useClickOutside","useEffect","updatePosition","triggerRect","contentRect","scrollX","scrollY","top","left","handleKeyDown","e","handleOpen","handleClose","handleToggle","childProps","childRef","triggerElement","cloneElement","node","contentStyle","popoverContent","jsxs","Fragment","jsx","cx","createPortal"],"mappings":"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"}