eidos-ui 3.2.1 → 3.3.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 (121) hide show
  1. package/dist/{chunk-2WP27QAN.js → chunk-27ZWUEPE.js} +3 -3
  2. package/dist/{chunk-TJC7UELT.js → chunk-3EUTXQDB.js} +8 -8
  3. package/dist/{chunk-2R77XZEP.js → chunk-6CPCNQKV.js} +1 -3
  4. package/dist/{chunk-2R77XZEP.js.map → chunk-6CPCNQKV.js.map} +1 -1
  5. package/dist/{chunk-X3U7SDOZ.js → chunk-7SVDYEKM.js} +2 -2
  6. package/dist/{chunk-4MZ7HEQC.cjs → chunk-ACNTH2OQ.cjs} +3 -3
  7. package/dist/{chunk-4MZ7HEQC.cjs.map → chunk-ACNTH2OQ.cjs.map} +1 -1
  8. package/dist/{chunk-LVHZBCTA.cjs → chunk-AOGNTG3W.cjs} +3 -3
  9. package/dist/{chunk-LVHZBCTA.cjs.map → chunk-AOGNTG3W.cjs.map} +1 -1
  10. package/dist/{chunk-KNQ6NN6L.js → chunk-BNVQ34FD.js} +2 -2
  11. package/dist/{chunk-6A2ZKVST.cjs → chunk-E6SE552P.cjs} +7 -6
  12. package/dist/chunk-E6SE552P.cjs.map +1 -0
  13. package/dist/{chunk-QIITXVRG.js → chunk-E7FGY7BJ.js} +3 -3
  14. package/dist/{chunk-ZGT4LT5Y.cjs → chunk-G5N3C6AU.cjs} +18 -18
  15. package/dist/{chunk-ZGT4LT5Y.cjs.map → chunk-G5N3C6AU.cjs.map} +1 -1
  16. package/dist/{chunk-BHVHM66Z.js → chunk-GDQWSCJQ.js} +2 -2
  17. package/dist/{chunk-YDPEHUMH.js → chunk-GG5YGESX.js} +2 -2
  18. package/dist/{chunk-QZX3A4FX.js → chunk-GPRJYPIG.js} +3 -3
  19. package/dist/{chunk-BC5I2MYB.js → chunk-HALVTKW7.js} +2 -2
  20. package/dist/{chunk-CJVC3A4O.js → chunk-HYDNZXPU.js} +4 -3
  21. package/dist/chunk-HYDNZXPU.js.map +1 -0
  22. package/dist/{chunk-EL3YXTOW.cjs → chunk-I5XR2V4A.cjs} +3 -3
  23. package/dist/{chunk-EL3YXTOW.cjs.map → chunk-I5XR2V4A.cjs.map} +1 -1
  24. package/dist/{chunk-IYWAZEFI.cjs → chunk-IGFR5TES.cjs} +3 -2
  25. package/dist/chunk-IGFR5TES.cjs.map +1 -0
  26. package/dist/{chunk-4ZWAJTNH.js → chunk-ITXDSQT2.js} +2 -2
  27. package/dist/{chunk-HI6WCX4T.cjs → chunk-K7T25Y3A.cjs} +3 -3
  28. package/dist/{chunk-HI6WCX4T.cjs.map → chunk-K7T25Y3A.cjs.map} +1 -1
  29. package/dist/{chunk-NWJ6NETE.js → chunk-L5W2XC6M.js} +5 -5
  30. package/dist/{chunk-Q6ICDKQZ.cjs → chunk-MCWTQT3M.cjs} +7 -7
  31. package/dist/{chunk-Q6ICDKQZ.cjs.map → chunk-MCWTQT3M.cjs.map} +1 -1
  32. package/dist/{chunk-PR7CVQ4R.js → chunk-MHZIBV2C.js} +3 -3
  33. package/dist/{chunk-OMLXP2CK.cjs → chunk-OGC44JFZ.cjs} +5 -5
  34. package/dist/{chunk-OMLXP2CK.cjs.map → chunk-OGC44JFZ.cjs.map} +1 -1
  35. package/dist/{chunk-UOOGS6N7.cjs → chunk-OJDYTCMO.cjs} +25 -25
  36. package/dist/{chunk-UOOGS6N7.cjs.map → chunk-OJDYTCMO.cjs.map} +1 -1
  37. package/dist/{chunk-FALDCW2E.js → chunk-OU7DCYIA.js} +2 -2
  38. package/dist/{chunk-75VXB3H2.cjs → chunk-P2JM22BF.cjs} +1 -3
  39. package/dist/chunk-P2JM22BF.cjs.map +1 -0
  40. package/dist/{chunk-R3WPVTGY.cjs → chunk-Q5KQVBQP.cjs} +8 -8
  41. package/dist/{chunk-R3WPVTGY.cjs.map → chunk-Q5KQVBQP.cjs.map} +1 -1
  42. package/dist/{chunk-EBSKRZXI.cjs → chunk-QJRKXT54.cjs} +3 -3
  43. package/dist/{chunk-EBSKRZXI.cjs.map → chunk-QJRKXT54.cjs.map} +1 -1
  44. package/dist/{chunk-VRQBDTLT.cjs → chunk-R4M6SNUU.cjs} +5 -5
  45. package/dist/{chunk-VRQBDTLT.cjs.map → chunk-R4M6SNUU.cjs.map} +1 -1
  46. package/dist/{chunk-3RLGH45H.js → chunk-SXGCRHBZ.js} +4 -4
  47. package/dist/{chunk-QUDVJUZU.js → chunk-TQGO5WK6.js} +2 -2
  48. package/dist/{chunk-I3CSPYHG.cjs → chunk-TTO7JXDB.cjs} +3 -3
  49. package/dist/{chunk-I3CSPYHG.cjs.map → chunk-TTO7JXDB.cjs.map} +1 -1
  50. package/dist/{chunk-ATL7325T.js → chunk-VPRUHE35.js} +2 -2
  51. package/dist/{chunk-D2SKXRKP.cjs → chunk-VXO2MYYH.cjs} +4 -4
  52. package/dist/{chunk-D2SKXRKP.cjs.map → chunk-VXO2MYYH.cjs.map} +1 -1
  53. package/dist/{chunk-O3I3RBZJ.js → chunk-Y55M34RQ.js} +3 -2
  54. package/dist/chunk-Y55M34RQ.js.map +1 -0
  55. package/dist/{chunk-75TDY7XF.cjs → chunk-YW65NAKH.cjs} +3 -3
  56. package/dist/{chunk-75TDY7XF.cjs.map → chunk-YW65NAKH.cjs.map} +1 -1
  57. package/dist/{chunk-KBGFDHKD.cjs → chunk-ZWAFL2B3.cjs} +9 -9
  58. package/dist/{chunk-KBGFDHKD.cjs.map → chunk-ZWAFL2B3.cjs.map} +1 -1
  59. package/dist/{chunk-WFEORBLC.cjs → chunk-ZXJU3TQD.cjs} +3 -3
  60. package/dist/{chunk-WFEORBLC.cjs.map → chunk-ZXJU3TQD.cjs.map} +1 -1
  61. package/dist/color-picker/index.cjs +3 -3
  62. package/dist/color-picker/index.js +2 -2
  63. package/dist/combobox/index.cjs +3 -3
  64. package/dist/combobox/index.js +2 -2
  65. package/dist/context-menu/index.cjs +4 -4
  66. package/dist/context-menu/index.js +3 -3
  67. package/dist/conversation/index.cjs +5 -5
  68. package/dist/conversation/index.js +4 -4
  69. package/dist/data-grid/index.cjs +10 -10
  70. package/dist/data-grid/index.js +9 -9
  71. package/dist/date-picker/index.cjs +4 -4
  72. package/dist/date-picker/index.js +3 -3
  73. package/dist/dropdown/index.cjs +2 -2
  74. package/dist/dropdown/index.js +1 -1
  75. package/dist/index.cjs +20 -20
  76. package/dist/index.css +112 -110
  77. package/dist/index.js +19 -19
  78. package/dist/menu/index.cjs +3 -3
  79. package/dist/menu/index.js +2 -2
  80. package/dist/page-layout/index.cjs +5 -5
  81. package/dist/page-layout/index.js +4 -4
  82. package/dist/pagination/index.cjs +4 -4
  83. package/dist/pagination/index.js +3 -3
  84. package/dist/select/index.cjs +3 -3
  85. package/dist/select/index.js +2 -2
  86. package/dist/skeleton/index.cjs +2 -2
  87. package/dist/skeleton/index.js +1 -1
  88. package/dist/split-button/index.cjs +4 -4
  89. package/dist/split-button/index.js +3 -3
  90. package/dist/table/index.cjs +9 -9
  91. package/dist/table/index.js +8 -8
  92. package/dist/tag-input/index.cjs +3 -3
  93. package/dist/tag-input/index.js +2 -2
  94. package/dist/theme-editor/index.cjs +5 -5
  95. package/dist/theme-editor/index.js +4 -4
  96. package/dist/toolbar/index.cjs +4 -4
  97. package/dist/toolbar/index.js +3 -3
  98. package/dist/virtual-list/index.cjs +3 -3
  99. package/dist/virtual-list/index.js +2 -2
  100. package/package.json +2 -1
  101. package/dist/chunk-6A2ZKVST.cjs.map +0 -1
  102. package/dist/chunk-75VXB3H2.cjs.map +0 -1
  103. package/dist/chunk-CJVC3A4O.js.map +0 -1
  104. package/dist/chunk-IYWAZEFI.cjs.map +0 -1
  105. package/dist/chunk-O3I3RBZJ.js.map +0 -1
  106. /package/dist/{chunk-2WP27QAN.js.map → chunk-27ZWUEPE.js.map} +0 -0
  107. /package/dist/{chunk-TJC7UELT.js.map → chunk-3EUTXQDB.js.map} +0 -0
  108. /package/dist/{chunk-X3U7SDOZ.js.map → chunk-7SVDYEKM.js.map} +0 -0
  109. /package/dist/{chunk-KNQ6NN6L.js.map → chunk-BNVQ34FD.js.map} +0 -0
  110. /package/dist/{chunk-QIITXVRG.js.map → chunk-E7FGY7BJ.js.map} +0 -0
  111. /package/dist/{chunk-BHVHM66Z.js.map → chunk-GDQWSCJQ.js.map} +0 -0
  112. /package/dist/{chunk-YDPEHUMH.js.map → chunk-GG5YGESX.js.map} +0 -0
  113. /package/dist/{chunk-QZX3A4FX.js.map → chunk-GPRJYPIG.js.map} +0 -0
  114. /package/dist/{chunk-BC5I2MYB.js.map → chunk-HALVTKW7.js.map} +0 -0
  115. /package/dist/{chunk-4ZWAJTNH.js.map → chunk-ITXDSQT2.js.map} +0 -0
  116. /package/dist/{chunk-NWJ6NETE.js.map → chunk-L5W2XC6M.js.map} +0 -0
  117. /package/dist/{chunk-PR7CVQ4R.js.map → chunk-MHZIBV2C.js.map} +0 -0
  118. /package/dist/{chunk-FALDCW2E.js.map → chunk-OU7DCYIA.js.map} +0 -0
  119. /package/dist/{chunk-3RLGH45H.js.map → chunk-SXGCRHBZ.js.map} +0 -0
  120. /package/dist/{chunk-QUDVJUZU.js.map → chunk-TQGO5WK6.js.map} +0 -0
  121. /package/dist/{chunk-ATL7325T.js.map → chunk-VPRUHE35.js.map} +0 -0
@@ -19,13 +19,13 @@ import {
19
19
  } from "./chunk-2BC4M4QZ.js";
20
20
  import {
21
21
  Select
22
- } from "./chunk-X3U7SDOZ.js";
22
+ } from "./chunk-7SVDYEKM.js";
23
23
  import {
24
24
  Card
25
25
  } from "./chunk-EF4Y6Q35.js";
26
26
  import {
27
27
  ColorPicker
28
- } from "./chunk-YDPEHUMH.js";
28
+ } from "./chunk-GG5YGESX.js";
29
29
  import {
30
30
  Alert
31
31
  } from "./chunk-O2AACVSF.js";
@@ -387,4 +387,4 @@ ThemeEditor.displayName = "ThemeEditor";
387
387
  export {
388
388
  ThemeEditor
389
389
  };
390
- //# sourceMappingURL=chunk-2WP27QAN.js.map
390
+ //# sourceMappingURL=chunk-27ZWUEPE.js.map
@@ -11,25 +11,25 @@ import {
11
11
  useSensors,
12
12
  useSortable,
13
13
  verticalListSortingStrategy
14
- } from "./chunk-3RLGH45H.js";
14
+ } from "./chunk-SXGCRHBZ.js";
15
15
  import {
16
16
  SplitButton
17
- } from "./chunk-QIITXVRG.js";
17
+ } from "./chunk-E7FGY7BJ.js";
18
18
  import {
19
19
  Pagination
20
- } from "./chunk-QUDVJUZU.js";
20
+ } from "./chunk-TQGO5WK6.js";
21
21
  import {
22
22
  SegmentedControl
23
23
  } from "./chunk-S4S4LB63.js";
24
24
  import {
25
25
  Select
26
- } from "./chunk-X3U7SDOZ.js";
26
+ } from "./chunk-7SVDYEKM.js";
27
27
  import {
28
28
  Checkbox
29
29
  } from "./chunk-H6CWTMAH.js";
30
30
  import {
31
31
  Combobox
32
- } from "./chunk-BC5I2MYB.js";
32
+ } from "./chunk-HALVTKW7.js";
33
33
  import {
34
34
  Input
35
35
  } from "./chunk-JRLUZDKA.js";
@@ -41,10 +41,10 @@ import {
41
41
  } from "./chunk-3TUCQAYL.js";
42
42
  import {
43
43
  Menu
44
- } from "./chunk-KNQ6NN6L.js";
44
+ } from "./chunk-BNVQ34FD.js";
45
45
  import {
46
46
  Dropdown
47
- } from "./chunk-2R77XZEP.js";
47
+ } from "./chunk-6CPCNQKV.js";
48
48
  import {
49
49
  Button
50
50
  } from "./chunk-L5Q6UDY3.js";
@@ -1718,4 +1718,4 @@ var DataGrid = Object.assign(DataGridInner, { displayName: "DataGrid" });
1718
1718
  export {
1719
1719
  DataGrid
1720
1720
  };
1721
- //# sourceMappingURL=chunk-TJC7UELT.js.map
1721
+ //# sourceMappingURL=chunk-3EUTXQDB.js.map
@@ -361,8 +361,6 @@ var DropdownInternal = ({
361
361
  ref: triggerRef,
362
362
  onClick: handleTriggerClick,
363
363
  className: `eidos-dropdown-trigger ${fullWidth ? fullWidthModifier("eidos-dropdown-trigger") : ""} ${triggerClassName}`,
364
- role: "button",
365
- tabIndex: -1,
366
364
  children: trigger
367
365
  }
368
366
  ),
@@ -402,4 +400,4 @@ export {
402
400
  useDropdownContext,
403
401
  Dropdown
404
402
  };
405
- //# sourceMappingURL=chunk-2R77XZEP.js.map
403
+ //# sourceMappingURL=chunk-6CPCNQKV.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/Dropdown/Dropdown.context.types.ts","../src/components/Dropdown/Dropdown.context.tsx","../src/components/Dropdown/Dropdown.hooks.ts","../src/components/Dropdown/Dropdown.component.tsx"],"sourcesContent":["import { createContext } from 'react';\n\nexport interface DropdownContextValue {\n level: number;\n}\n\nexport const DropdownContext = createContext<DropdownContextValue>({ level: 0 });\n","import React from 'react';\nimport { DropdownContext } from './Dropdown.context.types';\n\nexport const DropdownProvider: React.FC<{ children: React.ReactNode; level?: number }> = ({\n children,\n level = 0,\n}) => {\n return <DropdownContext.Provider value={{ level }}>{children}</DropdownContext.Provider>;\n};\n","import { useContext } from 'react';\nimport { DropdownContext } from './Dropdown.context.types';\n\nexport const useDropdownContext = () => {\n return useContext(DropdownContext);\n};\n","import React, { useState, useRef, useCallback, useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { fullWidthModifier } from '../../utils';\nimport type { DropdownProps, DropdownState } from './Dropdown.types';\nimport { DropdownProvider } from './Dropdown.context';\nimport { useDropdownContext } from './Dropdown.hooks';\n\nconst DropdownInternal: React.FC<DropdownProps> = ({\n trigger,\n content,\n placement: preferredPlacement = 'bottom',\n align = 'start',\n delay = 0,\n disabled = false,\n defaultOpen = false,\n triggerClassName = '',\n contentClassName = '',\n closeOnClickOutside = true,\n closeOnEscape = true,\n minWidth,\n maxWidth,\n minHeight,\n maxHeight,\n autoWidth = true,\n fullWidth = false,\n triggerRef: externalTriggerRef,\n isNested = false,\n dropdownLevel,\n dropdownGroup,\n}) => {\n const context = useDropdownContext();\n const actualLevel = dropdownLevel !== undefined ? dropdownLevel : context.level;\n const [dropdownState, setDropdownState] = useState<DropdownState>({\n isVisible: defaultOpen,\n isPositioned: false,\n position: { top: 0, left: 0, placement: preferredPlacement },\n });\n const triggerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const animationFrameRef = useRef<number | undefined>(undefined);\n const isScrollingRef = useRef(false);\n\n // The element to actually measure/anchor against. When a consumer passes\n // `triggerRef`, that's an explicit statement of \"this is what I want the\n // dropdown positioned relative to\" - it must win over the internal wrapper\n // div, not just inform sizing. Consumers occasionally need `trigger` to be\n // a small anchor node (e.g. a 0-height span) sitting inside a larger,\n // variable-content field (chips, wrapped text); anchoring position to the\n // wrapper instead of that field silently drifts as soon as the field's\n // internal layout changes, since the wrapper only ever contains `trigger`\n // itself, never the field around it.\n const getTriggerElement = useCallback(\n (): HTMLElement | null => externalTriggerRef?.current ?? triggerRef.current,\n [externalTriggerRef],\n );\n\n const calculateDynamicSizing = useCallback(\n (triggerRect: DOMRect) => {\n // Shrink the panel to its content by default. This prevents the\n // position:fixed element from expanding to viewport width when children\n // use `width:100%` or `flex:1`. `minWidth` (set below) still wins when\n // the trigger is wider than the content, as CSS min-width overrides width.\n const styles: Record<string, string | number> = { width: 'max-content' };\n\n if (autoWidth && (externalTriggerRef?.current || triggerRef.current)) {\n const triggerWidth = triggerRect.width;\n styles.minWidth = triggerWidth;\n }\n\n if (minWidth !== undefined) {\n styles.minWidth = typeof minWidth === 'number' ? `${minWidth}px` : minWidth;\n }\n if (maxWidth !== undefined) {\n if (maxWidth === 'auto') {\n styles.maxWidth = 'none';\n } else {\n styles.maxWidth = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth;\n }\n }\n if (minHeight !== undefined) {\n styles.minHeight = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n }\n if (maxHeight !== undefined) {\n if (maxHeight === 'auto') {\n styles.maxHeight = 'none';\n } else {\n styles.maxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n }\n }\n\n const hasHeightConstraint = minHeight !== undefined || maxHeight !== undefined;\n if (hasHeightConstraint) {\n styles.overflowY = 'auto';\n } else {\n styles.overflowY = 'visible';\n }\n\n return styles;\n },\n [autoWidth, externalTriggerRef, minWidth, maxWidth, minHeight, maxHeight],\n );\n\n const calculateOptimalPosition = useCallback(\n (triggerRect: DOMRect, contentRect: DOMRect) => {\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const gap = 8;\n\n let placement: 'top' | 'bottom' | 'left' | 'right' = preferredPlacement;\n let top = 0;\n let left = 0;\n\n // Helpers for cross-axis alignment\n const alignedLeft = (() => {\n switch (align) {\n case 'end':\n return triggerRect.right - contentRect.width;\n case 'center':\n return triggerRect.left + (triggerRect.width - contentRect.width) / 2;\n default:\n return triggerRect.left; // \"start\"\n }\n })();\n const alignedTop = (() => {\n switch (align) {\n case 'end':\n return triggerRect.bottom - contentRect.height;\n case 'center':\n return triggerRect.top + (triggerRect.height - contentRect.height) / 2;\n default:\n return triggerRect.top; // \"start\"\n }\n })();\n\n switch (preferredPlacement) {\n case 'top':\n top = triggerRect.top - contentRect.height - gap;\n left = alignedLeft;\n break;\n case 'bottom':\n top = triggerRect.bottom + gap;\n left = alignedLeft;\n break;\n case 'left':\n top = alignedTop;\n left = triggerRect.left - contentRect.width - gap;\n break;\n case 'right':\n top = alignedTop;\n left = triggerRect.right + gap;\n break;\n }\n\n if (placement === 'top' && top < gap) {\n placement = 'bottom';\n top = triggerRect.bottom + gap;\n } else if (placement === 'bottom' && top + contentRect.height > viewportHeight - gap) {\n placement = 'top';\n top = triggerRect.top - contentRect.height - gap;\n } else if (placement === 'left' && left < gap) {\n placement = 'right';\n left = triggerRect.right + gap;\n } else if (placement === 'right' && left + contentRect.width > viewportWidth - gap) {\n placement = 'left';\n left = triggerRect.left - contentRect.width - gap;\n }\n\n // Always clamp to both viewport edges - not just the near one - regardless\n // of whether an explicit min/max size was passed. `autoWidth` consumers\n // (Combobox, Select, TagInput suggestions, Menu, ...) never set\n // minWidth/maxWidth, so gating this on those props left the far edge\n // (right/bottom) completely unclamped and let content overflow the\n // viewport whenever it rendered wider/taller than its trigger.\n top = Math.max(gap, Math.min(top, viewportHeight - contentRect.height - gap));\n\n left = Math.max(gap, Math.min(left, viewportWidth - contentRect.width - gap));\n\n return { top, left, placement };\n },\n [preferredPlacement, align],\n );\n\n const handleScroll = useCallback(() => {\n if (!dropdownState.isVisible || !dropdownState.isPositioned || isScrollingRef.current) return;\n\n isScrollingRef.current = true;\n\n animationFrameRef.current = requestAnimationFrame(() => {\n const triggerElement = getTriggerElement();\n if (triggerElement && contentRef.current) {\n const triggerRect = triggerElement.getBoundingClientRect();\n const contentRect = contentRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, contentRect);\n\n setDropdownState((prev) => ({\n ...prev,\n position: { top, left, placement },\n }));\n }\n\n isScrollingRef.current = false;\n });\n }, [\n dropdownState.isVisible,\n dropdownState.isPositioned,\n calculateOptimalPosition,\n getTriggerElement,\n ]);\n\n const handleTriggerClick = useCallback(() => {\n if (disabled) return;\n\n if (dropdownState.isVisible) {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n } else {\n if (dropdownGroup) {\n const siblingDropdowns = document.querySelectorAll(\n `[data-dropdown-group=\"${dropdownGroup}\"][data-dropdown-content]`,\n );\n siblingDropdowns.forEach((dropdown) => {\n const dropdownElement = dropdown as HTMLElement;\n\n dropdownElement.dispatchEvent(new CustomEvent('closeSibling'));\n });\n }\n\n if (delay > 0) {\n timeoutRef.current = setTimeout(() => {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: true,\n isPositioned: false,\n }));\n }, delay);\n } else {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: true,\n isPositioned: false,\n }));\n }\n }\n }, [disabled, dropdownState.isVisible, delay, dropdownGroup]);\n\n useEffect(() => {\n const handleCloseSibling = () => {\n if (dropdownState.isVisible) {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n }\n };\n\n const currentContentRef = contentRef.current;\n if (currentContentRef) {\n currentContentRef.addEventListener('closeSibling', handleCloseSibling);\n }\n\n return () => {\n if (currentContentRef) {\n currentContentRef.removeEventListener('closeSibling', handleCloseSibling);\n }\n };\n }, [dropdownState.isVisible]);\n\n const handleClickOutside = useCallback(\n (event: MouseEvent) => {\n if (!closeOnClickOutside || !dropdownState.isVisible) return;\n\n const target = event.target as Node;\n\n if (contentRef.current?.contains(target)) {\n return;\n }\n\n if (getTriggerElement()?.contains(target)) {\n return;\n }\n\n const allDropdowns = document.querySelectorAll('[data-dropdown-content]');\n for (const dropdown of allDropdowns) {\n if (dropdown === contentRef.current) continue;\n\n if (dropdown.contains(target)) {\n const clickedDropdownLevel = parseInt(\n dropdown.getAttribute('data-dropdown-level') || '0',\n );\n\n if (clickedDropdownLevel > actualLevel) {\n return;\n }\n }\n }\n\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n },\n [closeOnClickOutside, dropdownState.isVisible, actualLevel, getTriggerElement],\n );\n\n const handleEscapeKey = useCallback(\n (event: KeyboardEvent) => {\n if (closeOnEscape && event.key === 'Escape' && dropdownState.isVisible) {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n }\n },\n [closeOnEscape, dropdownState.isVisible],\n );\n\n useEffect(() => {\n const triggerElement = getTriggerElement();\n if (\n dropdownState.isVisible &&\n !dropdownState.isPositioned &&\n contentRef.current &&\n triggerElement\n ) {\n const triggerRect = triggerElement.getBoundingClientRect();\n const contentRect = contentRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, contentRect);\n\n setDropdownState((prev) => ({\n ...prev,\n position: { top, left, placement },\n isPositioned: true,\n }));\n }\n }, [\n dropdownState.isVisible,\n dropdownState.isPositioned,\n calculateOptimalPosition,\n getTriggerElement,\n ]);\n\n useEffect(() => {\n const handleResize = () => {\n const triggerElement = getTriggerElement();\n if (\n dropdownState.isVisible &&\n dropdownState.isPositioned &&\n contentRef.current &&\n triggerElement\n ) {\n const triggerRect = triggerElement.getBoundingClientRect();\n const contentRect = contentRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, contentRect);\n\n setDropdownState((prev) => ({\n ...prev,\n position: { top, left, placement },\n }));\n }\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [\n dropdownState.isVisible,\n dropdownState.isPositioned,\n calculateOptimalPosition,\n getTriggerElement,\n ]);\n\n useEffect(() => {\n if (dropdownState.isVisible) {\n window.addEventListener('scroll', handleScroll, { passive: true });\n\n document.body.addEventListener('scroll', handleScroll, { passive: true });\n }\n\n return () => {\n window.removeEventListener('scroll', handleScroll);\n document.body.removeEventListener('scroll', handleScroll);\n };\n }, [dropdownState.isVisible, handleScroll]);\n\n useEffect(() => {\n if (dropdownState.isVisible) {\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleEscapeKey);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleEscapeKey);\n };\n }, [dropdownState.isVisible, handleClickOutside, handleEscapeKey]);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, []);\n\n return (\n <>\n <div\n ref={triggerRef}\n onClick={handleTriggerClick}\n className={`eidos-dropdown-trigger ${fullWidth ? fullWidthModifier('eidos-dropdown-trigger') : ''} ${triggerClassName}`}\n role=\"button\"\n tabIndex={-1}\n >\n {trigger}\n </div>\n {dropdownState.isVisible &&\n createPortal(\n <div\n ref={contentRef}\n data-dropdown-content\n data-dropdown-level={actualLevel}\n data-dropdown-group={dropdownGroup || ''}\n className={`eidos-dropdown-content eidos-dropdown-content--${dropdownState.position.placement} ${isNested ? 'eidos-dropdown-content--nested' : ''} ${dropdownState.isPositioned ? 'eidos-dropdown-content--positioned' : ''} ${contentClassName || ''}`}\n style={{\n top: dropdownState.position.top,\n left: dropdownState.position.left,\n ...calculateDynamicSizing(\n getTriggerElement()?.getBoundingClientRect() || new DOMRect(),\n ),\n }}\n role=\"menu\"\n >\n {content}\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nexport const Dropdown: React.FC<DropdownProps> = (props) => {\n const context = useDropdownContext();\n const nextLevel = props.dropdownLevel !== undefined ? props.dropdownLevel : context.level + 1;\n\n return (\n <DropdownProvider level={nextLevel}>\n <DropdownInternal {...props} />\n </DropdownProvider>\n );\n};\n\nDropdown.displayName = 'Dropdown';\n"],"mappings":";;;;;AAAA,SAAS,qBAAqB;AAMvB,IAAM,kBAAkB,cAAoC,EAAE,OAAO,EAAE,CAAC;;;ACCtE;AAJF,IAAM,mBAA4E,CAAC;AAAA,EACxF;AAAA,EACA,QAAQ;AACV,MAAM;AACJ,SAAO,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,MAAM,GAAI,UAAS;AAC/D;;;ACRA,SAAS,kBAAkB;AAGpB,IAAM,qBAAqB,MAAM;AACtC,SAAO,WAAW,eAAe;AACnC;;;ACLA,SAAgB,UAAU,QAAQ,aAAa,iBAAiB;AAChE,SAAS,oBAAoB;AAgazB,mBACE,OAAAA,MADF;AA1ZJ,IAAM,mBAA4C,CAAC;AAAA,EACjD;AAAA,EACA;AAAA,EACA,WAAW,qBAAqB;AAAA,EAChC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU,mBAAmB;AACnC,QAAM,cAAc,kBAAkB,SAAY,gBAAgB,QAAQ;AAC1E,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB;AAAA,IAChE,WAAW;AAAA,IACX,cAAc;AAAA,IACd,UAAU,EAAE,KAAK,GAAG,MAAM,GAAG,WAAW,mBAAmB;AAAA,EAC7D,CAAC;AACD,QAAM,aAAa,OAAuB,IAAI;AAC9C,QAAM,aAAa,OAAuB,IAAI;AAC9C,QAAM,aAAa,OAAkD,MAAS;AAC9E,QAAM,oBAAoB,OAA2B,MAAS;AAC9D,QAAM,iBAAiB,OAAO,KAAK;AAWnC,QAAM,oBAAoB;AAAA,IACxB,MAA0B,oBAAoB,WAAW,WAAW;AAAA,IACpE,CAAC,kBAAkB;AAAA,EACrB;AAEA,QAAM,yBAAyB;AAAA,IAC7B,CAAC,gBAAyB;AAKxB,YAAM,SAA0C,EAAE,OAAO,cAAc;AAEvE,UAAI,cAAc,oBAAoB,WAAW,WAAW,UAAU;AACpE,cAAM,eAAe,YAAY;AACjC,eAAO,WAAW;AAAA,MACpB;AAEA,UAAI,aAAa,QAAW;AAC1B,eAAO,WAAW,OAAO,aAAa,WAAW,GAAG,QAAQ,OAAO;AAAA,MACrE;AACA,UAAI,aAAa,QAAW;AAC1B,YAAI,aAAa,QAAQ;AACvB,iBAAO,WAAW;AAAA,QACpB,OAAO;AACL,iBAAO,WAAW,OAAO,aAAa,WAAW,GAAG,QAAQ,OAAO;AAAA,QACrE;AAAA,MACF;AACA,UAAI,cAAc,QAAW;AAC3B,eAAO,YAAY,OAAO,cAAc,WAAW,GAAG,SAAS,OAAO;AAAA,MACxE;AACA,UAAI,cAAc,QAAW;AAC3B,YAAI,cAAc,QAAQ;AACxB,iBAAO,YAAY;AAAA,QACrB,OAAO;AACL,iBAAO,YAAY,OAAO,cAAc,WAAW,GAAG,SAAS,OAAO;AAAA,QACxE;AAAA,MACF;AAEA,YAAM,sBAAsB,cAAc,UAAa,cAAc;AACrE,UAAI,qBAAqB;AACvB,eAAO,YAAY;AAAA,MACrB,OAAO;AACL,eAAO,YAAY;AAAA,MACrB;AAEA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,WAAW,oBAAoB,UAAU,UAAU,WAAW,SAAS;AAAA,EAC1E;AAEA,QAAM,2BAA2B;AAAA,IAC/B,CAAC,aAAsB,gBAAyB;AAC9C,YAAM,gBAAgB,OAAO;AAC7B,YAAM,iBAAiB,OAAO;AAC9B,YAAM,MAAM;AAEZ,UAAI,YAAiD;AACrD,UAAI,MAAM;AACV,UAAI,OAAO;AAGX,YAAM,eAAe,MAAM;AACzB,gBAAQ,OAAO;AAAA,UACb,KAAK;AACH,mBAAO,YAAY,QAAQ,YAAY;AAAA,UACzC,KAAK;AACH,mBAAO,YAAY,QAAQ,YAAY,QAAQ,YAAY,SAAS;AAAA,UACtE;AACE,mBAAO,YAAY;AAAA,QACvB;AAAA,MACF,GAAG;AACH,YAAM,cAAc,MAAM;AACxB,gBAAQ,OAAO;AAAA,UACb,KAAK;AACH,mBAAO,YAAY,SAAS,YAAY;AAAA,UAC1C,KAAK;AACH,mBAAO,YAAY,OAAO,YAAY,SAAS,YAAY,UAAU;AAAA,UACvE;AACE,mBAAO,YAAY;AAAA,QACvB;AAAA,MACF,GAAG;AAEH,cAAQ,oBAAoB;AAAA,QAC1B,KAAK;AACH,gBAAM,YAAY,MAAM,YAAY,SAAS;AAC7C,iBAAO;AACP;AAAA,QACF,KAAK;AACH,gBAAM,YAAY,SAAS;AAC3B,iBAAO;AACP;AAAA,QACF,KAAK;AACH,gBAAM;AACN,iBAAO,YAAY,OAAO,YAAY,QAAQ;AAC9C;AAAA,QACF,KAAK;AACH,gBAAM;AACN,iBAAO,YAAY,QAAQ;AAC3B;AAAA,MACJ;AAEA,UAAI,cAAc,SAAS,MAAM,KAAK;AACpC,oBAAY;AACZ,cAAM,YAAY,SAAS;AAAA,MAC7B,WAAW,cAAc,YAAY,MAAM,YAAY,SAAS,iBAAiB,KAAK;AACpF,oBAAY;AACZ,cAAM,YAAY,MAAM,YAAY,SAAS;AAAA,MAC/C,WAAW,cAAc,UAAU,OAAO,KAAK;AAC7C,oBAAY;AACZ,eAAO,YAAY,QAAQ;AAAA,MAC7B,WAAW,cAAc,WAAW,OAAO,YAAY,QAAQ,gBAAgB,KAAK;AAClF,oBAAY;AACZ,eAAO,YAAY,OAAO,YAAY,QAAQ;AAAA,MAChD;AAQA,YAAM,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,iBAAiB,YAAY,SAAS,GAAG,CAAC;AAE5E,aAAO,KAAK,IAAI,KAAK,KAAK,IAAI,MAAM,gBAAgB,YAAY,QAAQ,GAAG,CAAC;AAE5E,aAAO,EAAE,KAAK,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,oBAAoB,KAAK;AAAA,EAC5B;AAEA,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,CAAC,cAAc,aAAa,CAAC,cAAc,gBAAgB,eAAe,QAAS;AAEvF,mBAAe,UAAU;AAEzB,sBAAkB,UAAU,sBAAsB,MAAM;AACtD,YAAM,iBAAiB,kBAAkB;AACzC,UAAI,kBAAkB,WAAW,SAAS;AACxC,cAAM,cAAc,eAAe,sBAAsB;AACzD,cAAM,cAAc,WAAW,QAAQ,sBAAsB;AAE7D,cAAM,EAAE,KAAK,MAAM,UAAU,IAAI,yBAAyB,aAAa,WAAW;AAElF,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,UAAU,EAAE,KAAK,MAAM,UAAU;AAAA,QACnC,EAAE;AAAA,MACJ;AAEA,qBAAe,UAAU;AAAA,IAC3B,CAAC;AAAA,EACH,GAAG;AAAA,IACD,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,qBAAqB,YAAY,MAAM;AAC3C,QAAI,SAAU;AAEd,QAAI,cAAc,WAAW;AAC3B,uBAAiB,CAAC,UAAU;AAAA,QAC1B,GAAG;AAAA,QACH,WAAW;AAAA,QACX,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ,OAAO;AACL,UAAI,eAAe;AACjB,cAAM,mBAAmB,SAAS;AAAA,UAChC,yBAAyB,aAAa;AAAA,QACxC;AACA,yBAAiB,QAAQ,CAAC,aAAa;AACrC,gBAAM,kBAAkB;AAExB,0BAAgB,cAAc,IAAI,YAAY,cAAc,CAAC;AAAA,QAC/D,CAAC;AAAA,MACH;AAEA,UAAI,QAAQ,GAAG;AACb,mBAAW,UAAU,WAAW,MAAM;AACpC,2BAAiB,CAAC,UAAU;AAAA,YAC1B,GAAG;AAAA,YACH,WAAW;AAAA,YACX,cAAc;AAAA,UAChB,EAAE;AAAA,QACJ,GAAG,KAAK;AAAA,MACV,OAAO;AACL,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,EACF,GAAG,CAAC,UAAU,cAAc,WAAW,OAAO,aAAa,CAAC;AAE5D,YAAU,MAAM;AACd,UAAM,qBAAqB,MAAM;AAC/B,UAAI,cAAc,WAAW;AAC3B,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAEA,UAAM,oBAAoB,WAAW;AACrC,QAAI,mBAAmB;AACrB,wBAAkB,iBAAiB,gBAAgB,kBAAkB;AAAA,IACvE;AAEA,WAAO,MAAM;AACX,UAAI,mBAAmB;AACrB,0BAAkB,oBAAoB,gBAAgB,kBAAkB;AAAA,MAC1E;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,SAAS,CAAC;AAE5B,QAAM,qBAAqB;AAAA,IACzB,CAAC,UAAsB;AACrB,UAAI,CAAC,uBAAuB,CAAC,cAAc,UAAW;AAEtD,YAAM,SAAS,MAAM;AAErB,UAAI,WAAW,SAAS,SAAS,MAAM,GAAG;AACxC;AAAA,MACF;AAEA,UAAI,kBAAkB,GAAG,SAAS,MAAM,GAAG;AACzC;AAAA,MACF;AAEA,YAAM,eAAe,SAAS,iBAAiB,yBAAyB;AACxE,iBAAW,YAAY,cAAc;AACnC,YAAI,aAAa,WAAW,QAAS;AAErC,YAAI,SAAS,SAAS,MAAM,GAAG;AAC7B,gBAAM,uBAAuB;AAAA,YAC3B,SAAS,aAAa,qBAAqB,KAAK;AAAA,UAClD;AAEA,cAAI,uBAAuB,aAAa;AACtC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,uBAAiB,CAAC,UAAU;AAAA,QAC1B,GAAG;AAAA,QACH,WAAW;AAAA,QACX,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ;AAAA,IACA,CAAC,qBAAqB,cAAc,WAAW,aAAa,iBAAiB;AAAA,EAC/E;AAEA,QAAM,kBAAkB;AAAA,IACtB,CAAC,UAAyB;AACxB,UAAI,iBAAiB,MAAM,QAAQ,YAAY,cAAc,WAAW;AACtE,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,eAAe,cAAc,SAAS;AAAA,EACzC;AAEA,YAAU,MAAM;AACd,UAAM,iBAAiB,kBAAkB;AACzC,QACE,cAAc,aACd,CAAC,cAAc,gBACf,WAAW,WACX,gBACA;AACA,YAAM,cAAc,eAAe,sBAAsB;AACzD,YAAM,cAAc,WAAW,QAAQ,sBAAsB;AAE7D,YAAM,EAAE,KAAK,MAAM,UAAU,IAAI,yBAAyB,aAAa,WAAW;AAElF,uBAAiB,CAAC,UAAU;AAAA,QAC1B,GAAG;AAAA,QACH,UAAU,EAAE,KAAK,MAAM,UAAU;AAAA,QACjC,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ;AAAA,EACF,GAAG;AAAA,IACD,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,YAAU,MAAM;AACd,UAAM,eAAe,MAAM;AACzB,YAAM,iBAAiB,kBAAkB;AACzC,UACE,cAAc,aACd,cAAc,gBACd,WAAW,WACX,gBACA;AACA,cAAM,cAAc,eAAe,sBAAsB;AACzD,cAAM,cAAc,WAAW,QAAQ,sBAAsB;AAE7D,cAAM,EAAE,KAAK,MAAM,UAAU,IAAI,yBAAyB,aAAa,WAAW;AAElF,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,UAAU,EAAE,KAAK,MAAM,UAAU;AAAA,QACnC,EAAE;AAAA,MACJ;AAAA,IACF;AAEA,WAAO,iBAAiB,UAAU,YAAY;AAC9C,WAAO,MAAM,OAAO,oBAAoB,UAAU,YAAY;AAAA,EAChE,GAAG;AAAA,IACD,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,YAAU,MAAM;AACd,QAAI,cAAc,WAAW;AAC3B,aAAO,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAEjE,eAAS,KAAK,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAAA,IAC1E;AAEA,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,YAAY;AACjD,eAAS,KAAK,oBAAoB,UAAU,YAAY;AAAA,IAC1D;AAAA,EACF,GAAG,CAAC,cAAc,WAAW,YAAY,CAAC;AAE1C,YAAU,MAAM;AACd,QAAI,cAAc,WAAW;AAC3B,eAAS,iBAAiB,aAAa,kBAAkB;AACzD,eAAS,iBAAiB,WAAW,eAAe;AAAA,IACtD;AAEA,WAAO,MAAM;AACX,eAAS,oBAAoB,aAAa,kBAAkB;AAC5D,eAAS,oBAAoB,WAAW,eAAe;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,cAAc,WAAW,oBAAoB,eAAe,CAAC;AAEjE,YAAU,MAAM;AACd,WAAO,MAAM;AACX,UAAI,WAAW,SAAS;AACtB,qBAAa,WAAW,OAAO;AAAA,MACjC;AACA,UAAI,kBAAkB,SAAS;AAC7B,6BAAqB,kBAAkB,OAAO;AAAA,MAChD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SACE,iCACE;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAW,0BAA0B,YAAY,kBAAkB,wBAAwB,IAAI,EAAE,IAAI,gBAAgB;AAAA,QACrH,MAAK;AAAA,QACL,UAAU;AAAA,QAET;AAAA;AAAA,IACH;AAAA,IACC,cAAc,aACb;AAAA,MACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,yBAAqB;AAAA,UACrB,uBAAqB;AAAA,UACrB,uBAAqB,iBAAiB;AAAA,UACtC,WAAW,kDAAkD,cAAc,SAAS,SAAS,IAAI,WAAW,mCAAmC,EAAE,IAAI,cAAc,eAAe,uCAAuC,EAAE,IAAI,oBAAoB,EAAE;AAAA,UACrP,OAAO;AAAA,YACL,KAAK,cAAc,SAAS;AAAA,YAC5B,MAAM,cAAc,SAAS;AAAA,YAC7B,GAAG;AAAA,cACD,kBAAkB,GAAG,sBAAsB,KAAK,IAAI,QAAQ;AAAA,YAC9D;AAAA,UACF;AAAA,UACA,MAAK;AAAA,UAEJ;AAAA;AAAA,MACH;AAAA,MACA,SAAS;AAAA,IACX;AAAA,KACJ;AAEJ;AAEO,IAAM,WAAoC,CAAC,UAAU;AAC1D,QAAM,UAAU,mBAAmB;AACnC,QAAM,YAAY,MAAM,kBAAkB,SAAY,MAAM,gBAAgB,QAAQ,QAAQ;AAE5F,SACE,gBAAAA,KAAC,oBAAiB,OAAO,WACvB,0BAAAA,KAAC,oBAAkB,GAAG,OAAO,GAC/B;AAEJ;AAEA,SAAS,cAAc;","names":["jsx"]}
1
+ {"version":3,"sources":["../src/components/Dropdown/Dropdown.context.types.ts","../src/components/Dropdown/Dropdown.context.tsx","../src/components/Dropdown/Dropdown.hooks.ts","../src/components/Dropdown/Dropdown.component.tsx"],"sourcesContent":["import { createContext } from 'react';\n\nexport interface DropdownContextValue {\n level: number;\n}\n\nexport const DropdownContext = createContext<DropdownContextValue>({ level: 0 });\n","import React from 'react';\nimport { DropdownContext } from './Dropdown.context.types';\n\nexport const DropdownProvider: React.FC<{ children: React.ReactNode; level?: number }> = ({\n children,\n level = 0,\n}) => {\n return <DropdownContext.Provider value={{ level }}>{children}</DropdownContext.Provider>;\n};\n","import { useContext } from 'react';\nimport { DropdownContext } from './Dropdown.context.types';\n\nexport const useDropdownContext = () => {\n return useContext(DropdownContext);\n};\n","import React, { useState, useRef, useCallback, useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport { fullWidthModifier } from '../../utils';\nimport type { DropdownProps, DropdownState } from './Dropdown.types';\nimport { DropdownProvider } from './Dropdown.context';\nimport { useDropdownContext } from './Dropdown.hooks';\n\nconst DropdownInternal: React.FC<DropdownProps> = ({\n trigger,\n content,\n placement: preferredPlacement = 'bottom',\n align = 'start',\n delay = 0,\n disabled = false,\n defaultOpen = false,\n triggerClassName = '',\n contentClassName = '',\n closeOnClickOutside = true,\n closeOnEscape = true,\n minWidth,\n maxWidth,\n minHeight,\n maxHeight,\n autoWidth = true,\n fullWidth = false,\n triggerRef: externalTriggerRef,\n isNested = false,\n dropdownLevel,\n dropdownGroup,\n}) => {\n const context = useDropdownContext();\n const actualLevel = dropdownLevel !== undefined ? dropdownLevel : context.level;\n const [dropdownState, setDropdownState] = useState<DropdownState>({\n isVisible: defaultOpen,\n isPositioned: false,\n position: { top: 0, left: 0, placement: preferredPlacement },\n });\n const triggerRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const animationFrameRef = useRef<number | undefined>(undefined);\n const isScrollingRef = useRef(false);\n\n // The element to actually measure/anchor against. When a consumer passes\n // `triggerRef`, that's an explicit statement of \"this is what I want the\n // dropdown positioned relative to\" - it must win over the internal wrapper\n // div, not just inform sizing. Consumers occasionally need `trigger` to be\n // a small anchor node (e.g. a 0-height span) sitting inside a larger,\n // variable-content field (chips, wrapped text); anchoring position to the\n // wrapper instead of that field silently drifts as soon as the field's\n // internal layout changes, since the wrapper only ever contains `trigger`\n // itself, never the field around it.\n const getTriggerElement = useCallback(\n (): HTMLElement | null => externalTriggerRef?.current ?? triggerRef.current,\n [externalTriggerRef],\n );\n\n const calculateDynamicSizing = useCallback(\n (triggerRect: DOMRect) => {\n // Shrink the panel to its content by default. This prevents the\n // position:fixed element from expanding to viewport width when children\n // use `width:100%` or `flex:1`. `minWidth` (set below) still wins when\n // the trigger is wider than the content, as CSS min-width overrides width.\n const styles: Record<string, string | number> = { width: 'max-content' };\n\n if (autoWidth && (externalTriggerRef?.current || triggerRef.current)) {\n const triggerWidth = triggerRect.width;\n styles.minWidth = triggerWidth;\n }\n\n if (minWidth !== undefined) {\n styles.minWidth = typeof minWidth === 'number' ? `${minWidth}px` : minWidth;\n }\n if (maxWidth !== undefined) {\n if (maxWidth === 'auto') {\n styles.maxWidth = 'none';\n } else {\n styles.maxWidth = typeof maxWidth === 'number' ? `${maxWidth}px` : maxWidth;\n }\n }\n if (minHeight !== undefined) {\n styles.minHeight = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;\n }\n if (maxHeight !== undefined) {\n if (maxHeight === 'auto') {\n styles.maxHeight = 'none';\n } else {\n styles.maxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n }\n }\n\n const hasHeightConstraint = minHeight !== undefined || maxHeight !== undefined;\n if (hasHeightConstraint) {\n styles.overflowY = 'auto';\n } else {\n styles.overflowY = 'visible';\n }\n\n return styles;\n },\n [autoWidth, externalTriggerRef, minWidth, maxWidth, minHeight, maxHeight],\n );\n\n const calculateOptimalPosition = useCallback(\n (triggerRect: DOMRect, contentRect: DOMRect) => {\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const gap = 8;\n\n let placement: 'top' | 'bottom' | 'left' | 'right' = preferredPlacement;\n let top = 0;\n let left = 0;\n\n // Helpers for cross-axis alignment\n const alignedLeft = (() => {\n switch (align) {\n case 'end':\n return triggerRect.right - contentRect.width;\n case 'center':\n return triggerRect.left + (triggerRect.width - contentRect.width) / 2;\n default:\n return triggerRect.left; // \"start\"\n }\n })();\n const alignedTop = (() => {\n switch (align) {\n case 'end':\n return triggerRect.bottom - contentRect.height;\n case 'center':\n return triggerRect.top + (triggerRect.height - contentRect.height) / 2;\n default:\n return triggerRect.top; // \"start\"\n }\n })();\n\n switch (preferredPlacement) {\n case 'top':\n top = triggerRect.top - contentRect.height - gap;\n left = alignedLeft;\n break;\n case 'bottom':\n top = triggerRect.bottom + gap;\n left = alignedLeft;\n break;\n case 'left':\n top = alignedTop;\n left = triggerRect.left - contentRect.width - gap;\n break;\n case 'right':\n top = alignedTop;\n left = triggerRect.right + gap;\n break;\n }\n\n if (placement === 'top' && top < gap) {\n placement = 'bottom';\n top = triggerRect.bottom + gap;\n } else if (placement === 'bottom' && top + contentRect.height > viewportHeight - gap) {\n placement = 'top';\n top = triggerRect.top - contentRect.height - gap;\n } else if (placement === 'left' && left < gap) {\n placement = 'right';\n left = triggerRect.right + gap;\n } else if (placement === 'right' && left + contentRect.width > viewportWidth - gap) {\n placement = 'left';\n left = triggerRect.left - contentRect.width - gap;\n }\n\n // Always clamp to both viewport edges - not just the near one - regardless\n // of whether an explicit min/max size was passed. `autoWidth` consumers\n // (Combobox, Select, TagInput suggestions, Menu, ...) never set\n // minWidth/maxWidth, so gating this on those props left the far edge\n // (right/bottom) completely unclamped and let content overflow the\n // viewport whenever it rendered wider/taller than its trigger.\n top = Math.max(gap, Math.min(top, viewportHeight - contentRect.height - gap));\n\n left = Math.max(gap, Math.min(left, viewportWidth - contentRect.width - gap));\n\n return { top, left, placement };\n },\n [preferredPlacement, align],\n );\n\n const handleScroll = useCallback(() => {\n if (!dropdownState.isVisible || !dropdownState.isPositioned || isScrollingRef.current) return;\n\n isScrollingRef.current = true;\n\n animationFrameRef.current = requestAnimationFrame(() => {\n const triggerElement = getTriggerElement();\n if (triggerElement && contentRef.current) {\n const triggerRect = triggerElement.getBoundingClientRect();\n const contentRect = contentRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, contentRect);\n\n setDropdownState((prev) => ({\n ...prev,\n position: { top, left, placement },\n }));\n }\n\n isScrollingRef.current = false;\n });\n }, [\n dropdownState.isVisible,\n dropdownState.isPositioned,\n calculateOptimalPosition,\n getTriggerElement,\n ]);\n\n const handleTriggerClick = useCallback(() => {\n if (disabled) return;\n\n if (dropdownState.isVisible) {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n } else {\n if (dropdownGroup) {\n const siblingDropdowns = document.querySelectorAll(\n `[data-dropdown-group=\"${dropdownGroup}\"][data-dropdown-content]`,\n );\n siblingDropdowns.forEach((dropdown) => {\n const dropdownElement = dropdown as HTMLElement;\n\n dropdownElement.dispatchEvent(new CustomEvent('closeSibling'));\n });\n }\n\n if (delay > 0) {\n timeoutRef.current = setTimeout(() => {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: true,\n isPositioned: false,\n }));\n }, delay);\n } else {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: true,\n isPositioned: false,\n }));\n }\n }\n }, [disabled, dropdownState.isVisible, delay, dropdownGroup]);\n\n useEffect(() => {\n const handleCloseSibling = () => {\n if (dropdownState.isVisible) {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n }\n };\n\n const currentContentRef = contentRef.current;\n if (currentContentRef) {\n currentContentRef.addEventListener('closeSibling', handleCloseSibling);\n }\n\n return () => {\n if (currentContentRef) {\n currentContentRef.removeEventListener('closeSibling', handleCloseSibling);\n }\n };\n }, [dropdownState.isVisible]);\n\n const handleClickOutside = useCallback(\n (event: MouseEvent) => {\n if (!closeOnClickOutside || !dropdownState.isVisible) return;\n\n const target = event.target as Node;\n\n if (contentRef.current?.contains(target)) {\n return;\n }\n\n if (getTriggerElement()?.contains(target)) {\n return;\n }\n\n const allDropdowns = document.querySelectorAll('[data-dropdown-content]');\n for (const dropdown of allDropdowns) {\n if (dropdown === contentRef.current) continue;\n\n if (dropdown.contains(target)) {\n const clickedDropdownLevel = parseInt(\n dropdown.getAttribute('data-dropdown-level') || '0',\n );\n\n if (clickedDropdownLevel > actualLevel) {\n return;\n }\n }\n }\n\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n },\n [closeOnClickOutside, dropdownState.isVisible, actualLevel, getTriggerElement],\n );\n\n const handleEscapeKey = useCallback(\n (event: KeyboardEvent) => {\n if (closeOnEscape && event.key === 'Escape' && dropdownState.isVisible) {\n setDropdownState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n }\n },\n [closeOnEscape, dropdownState.isVisible],\n );\n\n useEffect(() => {\n const triggerElement = getTriggerElement();\n if (\n dropdownState.isVisible &&\n !dropdownState.isPositioned &&\n contentRef.current &&\n triggerElement\n ) {\n const triggerRect = triggerElement.getBoundingClientRect();\n const contentRect = contentRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, contentRect);\n\n setDropdownState((prev) => ({\n ...prev,\n position: { top, left, placement },\n isPositioned: true,\n }));\n }\n }, [\n dropdownState.isVisible,\n dropdownState.isPositioned,\n calculateOptimalPosition,\n getTriggerElement,\n ]);\n\n useEffect(() => {\n const handleResize = () => {\n const triggerElement = getTriggerElement();\n if (\n dropdownState.isVisible &&\n dropdownState.isPositioned &&\n contentRef.current &&\n triggerElement\n ) {\n const triggerRect = triggerElement.getBoundingClientRect();\n const contentRect = contentRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, contentRect);\n\n setDropdownState((prev) => ({\n ...prev,\n position: { top, left, placement },\n }));\n }\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [\n dropdownState.isVisible,\n dropdownState.isPositioned,\n calculateOptimalPosition,\n getTriggerElement,\n ]);\n\n useEffect(() => {\n if (dropdownState.isVisible) {\n window.addEventListener('scroll', handleScroll, { passive: true });\n\n document.body.addEventListener('scroll', handleScroll, { passive: true });\n }\n\n return () => {\n window.removeEventListener('scroll', handleScroll);\n document.body.removeEventListener('scroll', handleScroll);\n };\n }, [dropdownState.isVisible, handleScroll]);\n\n useEffect(() => {\n if (dropdownState.isVisible) {\n document.addEventListener('mousedown', handleClickOutside);\n document.addEventListener('keydown', handleEscapeKey);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n document.removeEventListener('keydown', handleEscapeKey);\n };\n }, [dropdownState.isVisible, handleClickOutside, handleEscapeKey]);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, []);\n\n return (\n <>\n {/*\n A positioning and click-capturing wrapper, and nothing more - it\n deliberately carries no `role` and no `tabIndex`.\n\n It used to be `role=\"button\" tabIndex={-1}`, which was wrong in both\n directions at once. `tabIndex={-1}` with no key handler meant the\n wrapper was never keyboard-operable, so the button role it advertised\n could not be activated by anyone using a keyboard - it promised a\n control that did not exist. Meanwhile, because `trigger` is virtually\n always a real control (Select and Combobox pass an `<input>`, Menu and\n SplitButton a `<button>`, ...), the role wrapped an interactive element\n in another interactive element: invalid ARIA, flagged by axe as\n `nested-interactive`, and announced by screen readers as a button\n inside a button. One attribute did this across every component built\n on Dropdown - Select, Combobox, Menu, Popover, ContextMenu,\n ColorPicker and TagInput - 212 violations in a single axe run.\n\n Semantics belong to whatever is passed as `trigger`, which is the only\n element that can actually own them. A consumer passing a\n non-interactive node should pass a `<button>` instead; it was never\n keyboard-accessible under the old markup either.\n */}\n <div\n ref={triggerRef}\n onClick={handleTriggerClick}\n className={`eidos-dropdown-trigger ${fullWidth ? fullWidthModifier('eidos-dropdown-trigger') : ''} ${triggerClassName}`}\n >\n {trigger}\n </div>\n {dropdownState.isVisible &&\n createPortal(\n <div\n ref={contentRef}\n data-dropdown-content\n data-dropdown-level={actualLevel}\n data-dropdown-group={dropdownGroup || ''}\n className={`eidos-dropdown-content eidos-dropdown-content--${dropdownState.position.placement} ${isNested ? 'eidos-dropdown-content--nested' : ''} ${dropdownState.isPositioned ? 'eidos-dropdown-content--positioned' : ''} ${contentClassName || ''}`}\n style={{\n top: dropdownState.position.top,\n left: dropdownState.position.left,\n ...calculateDynamicSizing(\n getTriggerElement()?.getBoundingClientRect() || new DOMRect(),\n ),\n }}\n role=\"menu\"\n >\n {content}\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nexport const Dropdown: React.FC<DropdownProps> = (props) => {\n const context = useDropdownContext();\n const nextLevel = props.dropdownLevel !== undefined ? props.dropdownLevel : context.level + 1;\n\n return (\n <DropdownProvider level={nextLevel}>\n <DropdownInternal {...props} />\n </DropdownProvider>\n );\n};\n\nDropdown.displayName = 'Dropdown';\n"],"mappings":";;;;;AAAA,SAAS,qBAAqB;AAMvB,IAAM,kBAAkB,cAAoC,EAAE,OAAO,EAAE,CAAC;;;ACCtE;AAJF,IAAM,mBAA4E,CAAC;AAAA,EACxF;AAAA,EACA,QAAQ;AACV,MAAM;AACJ,SAAO,oBAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,MAAM,GAAI,UAAS;AAC/D;;;ACRA,SAAS,kBAAkB;AAGpB,IAAM,qBAAqB,MAAM;AACtC,SAAO,WAAW,eAAe;AACnC;;;ACLA,SAAgB,UAAU,QAAQ,aAAa,iBAAiB;AAChE,SAAS,oBAAoB;AAgazB,mBAuBE,OAAAA,MAvBF;AA1ZJ,IAAM,mBAA4C,CAAC;AAAA,EACjD;AAAA,EACA;AAAA,EACA,WAAW,qBAAqB;AAAA,EAChC,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,sBAAsB;AAAA,EACtB,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,WAAW;AAAA,EACX;AAAA,EACA;AACF,MAAM;AACJ,QAAM,UAAU,mBAAmB;AACnC,QAAM,cAAc,kBAAkB,SAAY,gBAAgB,QAAQ;AAC1E,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAwB;AAAA,IAChE,WAAW;AAAA,IACX,cAAc;AAAA,IACd,UAAU,EAAE,KAAK,GAAG,MAAM,GAAG,WAAW,mBAAmB;AAAA,EAC7D,CAAC;AACD,QAAM,aAAa,OAAuB,IAAI;AAC9C,QAAM,aAAa,OAAuB,IAAI;AAC9C,QAAM,aAAa,OAAkD,MAAS;AAC9E,QAAM,oBAAoB,OAA2B,MAAS;AAC9D,QAAM,iBAAiB,OAAO,KAAK;AAWnC,QAAM,oBAAoB;AAAA,IACxB,MAA0B,oBAAoB,WAAW,WAAW;AAAA,IACpE,CAAC,kBAAkB;AAAA,EACrB;AAEA,QAAM,yBAAyB;AAAA,IAC7B,CAAC,gBAAyB;AAKxB,YAAM,SAA0C,EAAE,OAAO,cAAc;AAEvE,UAAI,cAAc,oBAAoB,WAAW,WAAW,UAAU;AACpE,cAAM,eAAe,YAAY;AACjC,eAAO,WAAW;AAAA,MACpB;AAEA,UAAI,aAAa,QAAW;AAC1B,eAAO,WAAW,OAAO,aAAa,WAAW,GAAG,QAAQ,OAAO;AAAA,MACrE;AACA,UAAI,aAAa,QAAW;AAC1B,YAAI,aAAa,QAAQ;AACvB,iBAAO,WAAW;AAAA,QACpB,OAAO;AACL,iBAAO,WAAW,OAAO,aAAa,WAAW,GAAG,QAAQ,OAAO;AAAA,QACrE;AAAA,MACF;AACA,UAAI,cAAc,QAAW;AAC3B,eAAO,YAAY,OAAO,cAAc,WAAW,GAAG,SAAS,OAAO;AAAA,MACxE;AACA,UAAI,cAAc,QAAW;AAC3B,YAAI,cAAc,QAAQ;AACxB,iBAAO,YAAY;AAAA,QACrB,OAAO;AACL,iBAAO,YAAY,OAAO,cAAc,WAAW,GAAG,SAAS,OAAO;AAAA,QACxE;AAAA,MACF;AAEA,YAAM,sBAAsB,cAAc,UAAa,cAAc;AACrE,UAAI,qBAAqB;AACvB,eAAO,YAAY;AAAA,MACrB,OAAO;AACL,eAAO,YAAY;AAAA,MACrB;AAEA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,WAAW,oBAAoB,UAAU,UAAU,WAAW,SAAS;AAAA,EAC1E;AAEA,QAAM,2BAA2B;AAAA,IAC/B,CAAC,aAAsB,gBAAyB;AAC9C,YAAM,gBAAgB,OAAO;AAC7B,YAAM,iBAAiB,OAAO;AAC9B,YAAM,MAAM;AAEZ,UAAI,YAAiD;AACrD,UAAI,MAAM;AACV,UAAI,OAAO;AAGX,YAAM,eAAe,MAAM;AACzB,gBAAQ,OAAO;AAAA,UACb,KAAK;AACH,mBAAO,YAAY,QAAQ,YAAY;AAAA,UACzC,KAAK;AACH,mBAAO,YAAY,QAAQ,YAAY,QAAQ,YAAY,SAAS;AAAA,UACtE;AACE,mBAAO,YAAY;AAAA,QACvB;AAAA,MACF,GAAG;AACH,YAAM,cAAc,MAAM;AACxB,gBAAQ,OAAO;AAAA,UACb,KAAK;AACH,mBAAO,YAAY,SAAS,YAAY;AAAA,UAC1C,KAAK;AACH,mBAAO,YAAY,OAAO,YAAY,SAAS,YAAY,UAAU;AAAA,UACvE;AACE,mBAAO,YAAY;AAAA,QACvB;AAAA,MACF,GAAG;AAEH,cAAQ,oBAAoB;AAAA,QAC1B,KAAK;AACH,gBAAM,YAAY,MAAM,YAAY,SAAS;AAC7C,iBAAO;AACP;AAAA,QACF,KAAK;AACH,gBAAM,YAAY,SAAS;AAC3B,iBAAO;AACP;AAAA,QACF,KAAK;AACH,gBAAM;AACN,iBAAO,YAAY,OAAO,YAAY,QAAQ;AAC9C;AAAA,QACF,KAAK;AACH,gBAAM;AACN,iBAAO,YAAY,QAAQ;AAC3B;AAAA,MACJ;AAEA,UAAI,cAAc,SAAS,MAAM,KAAK;AACpC,oBAAY;AACZ,cAAM,YAAY,SAAS;AAAA,MAC7B,WAAW,cAAc,YAAY,MAAM,YAAY,SAAS,iBAAiB,KAAK;AACpF,oBAAY;AACZ,cAAM,YAAY,MAAM,YAAY,SAAS;AAAA,MAC/C,WAAW,cAAc,UAAU,OAAO,KAAK;AAC7C,oBAAY;AACZ,eAAO,YAAY,QAAQ;AAAA,MAC7B,WAAW,cAAc,WAAW,OAAO,YAAY,QAAQ,gBAAgB,KAAK;AAClF,oBAAY;AACZ,eAAO,YAAY,OAAO,YAAY,QAAQ;AAAA,MAChD;AAQA,YAAM,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,iBAAiB,YAAY,SAAS,GAAG,CAAC;AAE5E,aAAO,KAAK,IAAI,KAAK,KAAK,IAAI,MAAM,gBAAgB,YAAY,QAAQ,GAAG,CAAC;AAE5E,aAAO,EAAE,KAAK,MAAM,UAAU;AAAA,IAChC;AAAA,IACA,CAAC,oBAAoB,KAAK;AAAA,EAC5B;AAEA,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,CAAC,cAAc,aAAa,CAAC,cAAc,gBAAgB,eAAe,QAAS;AAEvF,mBAAe,UAAU;AAEzB,sBAAkB,UAAU,sBAAsB,MAAM;AACtD,YAAM,iBAAiB,kBAAkB;AACzC,UAAI,kBAAkB,WAAW,SAAS;AACxC,cAAM,cAAc,eAAe,sBAAsB;AACzD,cAAM,cAAc,WAAW,QAAQ,sBAAsB;AAE7D,cAAM,EAAE,KAAK,MAAM,UAAU,IAAI,yBAAyB,aAAa,WAAW;AAElF,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,UAAU,EAAE,KAAK,MAAM,UAAU;AAAA,QACnC,EAAE;AAAA,MACJ;AAEA,qBAAe,UAAU;AAAA,IAC3B,CAAC;AAAA,EACH,GAAG;AAAA,IACD,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,qBAAqB,YAAY,MAAM;AAC3C,QAAI,SAAU;AAEd,QAAI,cAAc,WAAW;AAC3B,uBAAiB,CAAC,UAAU;AAAA,QAC1B,GAAG;AAAA,QACH,WAAW;AAAA,QACX,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ,OAAO;AACL,UAAI,eAAe;AACjB,cAAM,mBAAmB,SAAS;AAAA,UAChC,yBAAyB,aAAa;AAAA,QACxC;AACA,yBAAiB,QAAQ,CAAC,aAAa;AACrC,gBAAM,kBAAkB;AAExB,0BAAgB,cAAc,IAAI,YAAY,cAAc,CAAC;AAAA,QAC/D,CAAC;AAAA,MACH;AAEA,UAAI,QAAQ,GAAG;AACb,mBAAW,UAAU,WAAW,MAAM;AACpC,2BAAiB,CAAC,UAAU;AAAA,YAC1B,GAAG;AAAA,YACH,WAAW;AAAA,YACX,cAAc;AAAA,UAChB,EAAE;AAAA,QACJ,GAAG,KAAK;AAAA,MACV,OAAO;AACL,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,EACF,GAAG,CAAC,UAAU,cAAc,WAAW,OAAO,aAAa,CAAC;AAE5D,YAAU,MAAM;AACd,UAAM,qBAAqB,MAAM;AAC/B,UAAI,cAAc,WAAW;AAC3B,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAEA,UAAM,oBAAoB,WAAW;AACrC,QAAI,mBAAmB;AACrB,wBAAkB,iBAAiB,gBAAgB,kBAAkB;AAAA,IACvE;AAEA,WAAO,MAAM;AACX,UAAI,mBAAmB;AACrB,0BAAkB,oBAAoB,gBAAgB,kBAAkB;AAAA,MAC1E;AAAA,IACF;AAAA,EACF,GAAG,CAAC,cAAc,SAAS,CAAC;AAE5B,QAAM,qBAAqB;AAAA,IACzB,CAAC,UAAsB;AACrB,UAAI,CAAC,uBAAuB,CAAC,cAAc,UAAW;AAEtD,YAAM,SAAS,MAAM;AAErB,UAAI,WAAW,SAAS,SAAS,MAAM,GAAG;AACxC;AAAA,MACF;AAEA,UAAI,kBAAkB,GAAG,SAAS,MAAM,GAAG;AACzC;AAAA,MACF;AAEA,YAAM,eAAe,SAAS,iBAAiB,yBAAyB;AACxE,iBAAW,YAAY,cAAc;AACnC,YAAI,aAAa,WAAW,QAAS;AAErC,YAAI,SAAS,SAAS,MAAM,GAAG;AAC7B,gBAAM,uBAAuB;AAAA,YAC3B,SAAS,aAAa,qBAAqB,KAAK;AAAA,UAClD;AAEA,cAAI,uBAAuB,aAAa;AACtC;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,uBAAiB,CAAC,UAAU;AAAA,QAC1B,GAAG;AAAA,QACH,WAAW;AAAA,QACX,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ;AAAA,IACA,CAAC,qBAAqB,cAAc,WAAW,aAAa,iBAAiB;AAAA,EAC/E;AAEA,QAAM,kBAAkB;AAAA,IACtB,CAAC,UAAyB;AACxB,UAAI,iBAAiB,MAAM,QAAQ,YAAY,cAAc,WAAW;AACtE,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,WAAW;AAAA,UACX,cAAc;AAAA,QAChB,EAAE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,eAAe,cAAc,SAAS;AAAA,EACzC;AAEA,YAAU,MAAM;AACd,UAAM,iBAAiB,kBAAkB;AACzC,QACE,cAAc,aACd,CAAC,cAAc,gBACf,WAAW,WACX,gBACA;AACA,YAAM,cAAc,eAAe,sBAAsB;AACzD,YAAM,cAAc,WAAW,QAAQ,sBAAsB;AAE7D,YAAM,EAAE,KAAK,MAAM,UAAU,IAAI,yBAAyB,aAAa,WAAW;AAElF,uBAAiB,CAAC,UAAU;AAAA,QAC1B,GAAG;AAAA,QACH,UAAU,EAAE,KAAK,MAAM,UAAU;AAAA,QACjC,cAAc;AAAA,MAChB,EAAE;AAAA,IACJ;AAAA,EACF,GAAG;AAAA,IACD,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,YAAU,MAAM;AACd,UAAM,eAAe,MAAM;AACzB,YAAM,iBAAiB,kBAAkB;AACzC,UACE,cAAc,aACd,cAAc,gBACd,WAAW,WACX,gBACA;AACA,cAAM,cAAc,eAAe,sBAAsB;AACzD,cAAM,cAAc,WAAW,QAAQ,sBAAsB;AAE7D,cAAM,EAAE,KAAK,MAAM,UAAU,IAAI,yBAAyB,aAAa,WAAW;AAElF,yBAAiB,CAAC,UAAU;AAAA,UAC1B,GAAG;AAAA,UACH,UAAU,EAAE,KAAK,MAAM,UAAU;AAAA,QACnC,EAAE;AAAA,MACJ;AAAA,IACF;AAEA,WAAO,iBAAiB,UAAU,YAAY;AAC9C,WAAO,MAAM,OAAO,oBAAoB,UAAU,YAAY;AAAA,EAChE,GAAG;AAAA,IACD,cAAc;AAAA,IACd,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,YAAU,MAAM;AACd,QAAI,cAAc,WAAW;AAC3B,aAAO,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAEjE,eAAS,KAAK,iBAAiB,UAAU,cAAc,EAAE,SAAS,KAAK,CAAC;AAAA,IAC1E;AAEA,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,YAAY;AACjD,eAAS,KAAK,oBAAoB,UAAU,YAAY;AAAA,IAC1D;AAAA,EACF,GAAG,CAAC,cAAc,WAAW,YAAY,CAAC;AAE1C,YAAU,MAAM;AACd,QAAI,cAAc,WAAW;AAC3B,eAAS,iBAAiB,aAAa,kBAAkB;AACzD,eAAS,iBAAiB,WAAW,eAAe;AAAA,IACtD;AAEA,WAAO,MAAM;AACX,eAAS,oBAAoB,aAAa,kBAAkB;AAC5D,eAAS,oBAAoB,WAAW,eAAe;AAAA,IACzD;AAAA,EACF,GAAG,CAAC,cAAc,WAAW,oBAAoB,eAAe,CAAC;AAEjE,YAAU,MAAM;AACd,WAAO,MAAM;AACX,UAAI,WAAW,SAAS;AACtB,qBAAa,WAAW,OAAO;AAAA,MACjC;AACA,UAAI,kBAAkB,SAAS;AAC7B,6BAAqB,kBAAkB,OAAO;AAAA,MAChD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SACE,iCAuBE;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAW,0BAA0B,YAAY,kBAAkB,wBAAwB,IAAI,EAAE,IAAI,gBAAgB;AAAA,QAEpH;AAAA;AAAA,IACH;AAAA,IACC,cAAc,aACb;AAAA,MACE,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,yBAAqB;AAAA,UACrB,uBAAqB;AAAA,UACrB,uBAAqB,iBAAiB;AAAA,UACtC,WAAW,kDAAkD,cAAc,SAAS,SAAS,IAAI,WAAW,mCAAmC,EAAE,IAAI,cAAc,eAAe,uCAAuC,EAAE,IAAI,oBAAoB,EAAE;AAAA,UACrP,OAAO;AAAA,YACL,KAAK,cAAc,SAAS;AAAA,YAC5B,MAAM,cAAc,SAAS;AAAA,YAC7B,GAAG;AAAA,cACD,kBAAkB,GAAG,sBAAsB,KAAK,IAAI,QAAQ;AAAA,YAC9D;AAAA,UACF;AAAA,UACA,MAAK;AAAA,UAEJ;AAAA;AAAA,MACH;AAAA,MACA,SAAS;AAAA,IACX;AAAA,KACJ;AAEJ;AAEO,IAAM,WAAoC,CAAC,UAAU;AAC1D,QAAM,UAAU,mBAAmB;AACnC,QAAM,YAAY,MAAM,kBAAkB,SAAY,MAAM,gBAAgB,QAAQ,QAAQ;AAE5F,SACE,gBAAAA,KAAC,oBAAiB,OAAO,WACvB,0BAAAA,KAAC,oBAAkB,GAAG,OAAO,GAC/B;AAEJ;AAEA,SAAS,cAAc;","names":["jsx"]}
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-JRLUZDKA.js";
4
4
  import {
5
5
  Dropdown
6
- } from "./chunk-2R77XZEP.js";
6
+ } from "./chunk-6CPCNQKV.js";
7
7
  import {
8
8
  fullWidthModifier,
9
9
  renderIcon
@@ -340,4 +340,4 @@ Select.displayName = "Select";
340
340
  export {
341
341
  Select
342
342
  };
343
- //# sourceMappingURL=chunk-X3U7SDOZ.js.map
343
+ //# sourceMappingURL=chunk-7SVDYEKM.js.map
@@ -3,7 +3,7 @@
3
3
  var _chunkNEYLNRVGcjs = require('./chunk-NEYLNRVG.cjs');
4
4
 
5
5
 
6
- var _chunk75VXB3H2cjs = require('./chunk-75VXB3H2.cjs');
6
+ var _chunkP2JM22BFcjs = require('./chunk-P2JM22BF.cjs');
7
7
 
8
8
  // src/components/TagInput/TagInput.component.tsx
9
9
  var _react = require('react');
@@ -238,7 +238,7 @@ var TagInput = ({
238
238
  }
239
239
  ),
240
240
  hasSuggestions && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
241
- _chunk75VXB3H2cjs.Dropdown,
241
+ _chunkP2JM22BFcjs.Dropdown,
242
242
  {
243
243
  trigger: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
244
244
  "span",
@@ -296,4 +296,4 @@ TagInput.displayName = "TagInput";
296
296
 
297
297
 
298
298
  exports.TagInput = TagInput;
299
- //# sourceMappingURL=chunk-4MZ7HEQC.cjs.map
299
+ //# sourceMappingURL=chunk-ACNTH2OQ.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-4MZ7HEQC.cjs","../src/components/TagInput/TagInput.component.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-4MZ7HEQC.cjs","sourcesContent":[null,"import React, { useState, useId, useRef, useCallback, useMemo, useEffect } from 'react';\nimport { CircleAlert } from 'lucide-react';\nimport { Chip } from '../Chip/Chip.component';\nimport { Dropdown } from '../Dropdown/Dropdown.component';\nimport type { TagInputProps } from './TagInput.types';\nimport './TagInput.scss';\n\nexport const TagInput: React.FC<TagInputProps> = ({\n value,\n defaultValue = [],\n onChange,\n placeholder = 'Add tag…',\n size = 'md',\n disabled = false,\n label,\n error,\n hint,\n allowDuplicates = false,\n maxTags,\n separators = ['Enter', ',', 'Tab'],\n validate,\n className = '',\n fullWidth = false,\n suggestions,\n onSearch,\n suggestionsEmptyText = 'No matches',\n}) => {\n const generatedId = useId();\n const inputId = `tag-input-${generatedId}`;\n const inputRef = useRef<HTMLInputElement>(null);\n\n /** Entire component wrapper - used for click-outside detection on the suggestions dropdown */\n const containerRef = useRef<HTMLDivElement>(null);\n\n /** The field wrapper - passed to Dropdown as externalTriggerRef for width/anchoring */\n const fieldRef = useRef<HTMLDivElement>(null);\n\n /** The 0-height span that IS the Dropdown trigger element - programmatically clicked to open */\n const dropdownSpanRef = useRef<HTMLSpanElement>(null);\n\n const hasSuggestions = suggestions !== undefined;\n const isSuggestionsOpenRef = useRef(false);\n const [isSuggestionsOpen, setIsSuggestionsOpen] = useState(false);\n const [suggestionsMenuKey, setSuggestionsMenuKey] = useState(0);\n const [focusedSuggestionIndex, setFocusedSuggestionIndex] = useState(-1);\n\n const isControlled = value !== undefined;\n\n const [internalTags, setInternalTags] = useState<string[]>(defaultValue);\n const [inputValue, setInputValue] = useState('');\n const [isFocused, setIsFocused] = useState(false);\n const [validationError, setValidationError] = useState<string | null>(null);\n\n const tags = isControlled ? (value as string[]) : internalTags;\n\n const updateTags = useCallback(\n (newTags: string[]) => {\n if (!isControlled) {\n setInternalTags(newTags);\n }\n onChange?.(newTags);\n },\n [isControlled, onChange],\n );\n\n const addTag = useCallback(\n (rawTag: string) => {\n const tag = rawTag.trim();\n\n if (!tag) return;\n\n if (!allowDuplicates && tags.includes(tag)) return;\n\n if (maxTags !== undefined && tags.length >= maxTags) return;\n\n if (validate) {\n const result = validate(tag);\n if (result === false) return;\n if (typeof result === 'string') {\n setValidationError(result);\n return;\n }\n // result === true → valid, fall through\n }\n\n setValidationError(null);\n updateTags([...tags, tag]);\n setInputValue('');\n },\n [tags, allowDuplicates, maxTags, validate, updateTags],\n );\n\n const removeTag = useCallback(\n (index: number) => {\n updateTags(tags.filter((_, i) => i !== index));\n },\n [tags, updateTags],\n );\n\n // ─── Suggestions dropdown (autocomplete) ───────────────────────────────────\n\n const filteredSuggestions = useMemo<string[]>(() => {\n if (!suggestions) return [];\n const query = inputValue.trim().toLowerCase();\n return suggestions.filter((suggestion) => {\n if (!allowDuplicates && tags.includes(suggestion)) return false;\n if (!query) return true;\n return suggestion.toLowerCase().includes(query);\n });\n }, [suggestions, inputValue, tags, allowDuplicates]);\n\n useEffect(() => {\n setFocusedSuggestionIndex(-1);\n }, [filteredSuggestions]);\n\n const openSuggestions = useCallback(() => {\n if (isSuggestionsOpenRef.current || disabled || !hasSuggestions) return;\n dropdownSpanRef.current?.click(); // opens the Dropdown (bubbles to trigger)\n isSuggestionsOpenRef.current = true;\n setIsSuggestionsOpen(true);\n }, [disabled, hasSuggestions]);\n\n const closeSuggestions = useCallback(() => {\n isSuggestionsOpenRef.current = false;\n setIsSuggestionsOpen(false);\n setFocusedSuggestionIndex(-1);\n setSuggestionsMenuKey((prev) => prev + 1);\n }, []);\n\n const selectSuggestion = useCallback(\n (suggestion: string) => {\n addTag(suggestion);\n closeSuggestions();\n inputRef.current?.focus();\n },\n [addTag, closeSuggestions],\n );\n\n // Click-outside detection for the suggestions dropdown.\n useEffect(() => {\n if (!hasSuggestions) return;\n\n const handleMouseDown = (e: MouseEvent) => {\n if (!isSuggestionsOpenRef.current) return;\n if (e.defaultPrevented) return; // click inside the portal-rendered listbox\n if (containerRef.current?.contains(e.target as Node)) return;\n closeSuggestions();\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [hasSuggestions, closeSuggestions]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (hasSuggestions && isSuggestionsOpenRef.current && filteredSuggestions.length > 0) {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setFocusedSuggestionIndex((prev) => (prev + 1 < filteredSuggestions.length ? prev + 1 : 0));\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n setFocusedSuggestionIndex((prev) => (prev > 0 ? prev - 1 : filteredSuggestions.length - 1));\n return;\n }\n if (e.key === 'Enter' && focusedSuggestionIndex >= 0) {\n e.preventDefault();\n selectSuggestion(filteredSuggestions[focusedSuggestionIndex]);\n return;\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n closeSuggestions();\n return;\n }\n }\n\n if (separators.includes(e.key)) {\n e.preventDefault();\n addTag(inputValue);\n return;\n }\n\n if (e.key === 'Backspace' && inputValue === '' && tags.length > 0) {\n e.preventDefault();\n removeTag(tags.length - 1);\n }\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const query = e.target.value;\n setInputValue(query);\n if (validationError) {\n setValidationError(null);\n }\n onSearch?.(query);\n if (hasSuggestions && !isSuggestionsOpenRef.current) openSuggestions();\n };\n\n const handleInputFocus = () => {\n setIsFocused(true);\n if (hasSuggestions) openSuggestions();\n };\n\n const handleFieldClick = (e: React.MouseEvent<HTMLDivElement>) => {\n // Don't steal focus from interactive child elements\n if ((e.target as HTMLElement).closest('button, input')) return;\n if (!disabled) {\n inputRef.current?.focus();\n }\n };\n\n const displayError = validationError ?? error;\n const hasError = !!displayError;\n const errorId = `${inputId}-error`;\n const hintId = `${inputId}-hint`;\n\n const containerClasses = [\n 'eidos-tag-input',\n hasError && 'eidos-tag-input--error',\n disabled && 'eidos-tag-input--disabled',\n fullWidth && 'eidos-tag-input--full-width',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const fieldClasses = [\n 'eidos-tag-input-field',\n `eidos-tag-input-field--${size}`,\n isFocused && 'eidos-tag-input-field--focused',\n hasError && 'eidos-tag-input-field--error',\n disabled && 'eidos-tag-input-field--disabled',\n ]\n .filter(Boolean)\n .join(' ');\n\n const uid = generatedId;\n\n return (\n <div\n className={containerClasses}\n ref={containerRef}\n aria-expanded={hasSuggestions ? isSuggestionsOpen : undefined}\n aria-haspopup={hasSuggestions ? 'listbox' : undefined}\n >\n {label && (\n <label htmlFor={inputId} className=\"eidos-tag-input-label\">\n {label}\n </label>\n )}\n\n <div className={fieldClasses} onClick={handleFieldClick} ref={fieldRef}>\n {tags.map((tag, index) => (\n <Chip\n key={`${tag}-${index}`}\n variant=\"filled\"\n color=\"primary\"\n size={size}\n onRemove={disabled ? undefined : () => removeTag(index)}\n >\n {tag}\n </Chip>\n ))}\n\n <input\n ref={inputRef}\n id={inputId}\n className=\"eidos-tag-input-input\"\n type=\"text\"\n value={inputValue}\n placeholder={tags.length === 0 ? placeholder : undefined}\n disabled={disabled}\n onKeyDown={handleKeyDown}\n onChange={handleInputChange}\n onFocus={handleInputFocus}\n onBlur={() => setIsFocused(false)}\n aria-label={label ? undefined : 'Tag input'}\n aria-describedby={hasError ? errorId : hint ? hintId : undefined}\n aria-autocomplete={hasSuggestions ? 'list' : undefined}\n aria-controls={hasSuggestions ? `${uid}-tag-suggestions` : undefined}\n aria-activedescendant={\n hasSuggestions && focusedSuggestionIndex >= 0\n ? `${uid}-tag-suggestion-${focusedSuggestionIndex}`\n : undefined\n }\n />\n\n {hasSuggestions && (\n <Dropdown\n key={suggestionsMenuKey}\n trigger={\n <span\n ref={dropdownSpanRef}\n className=\"eidos-tag-input-dropdown-anchor\"\n aria-hidden=\"true\"\n />\n }\n content={\n <div\n id={`${uid}-tag-suggestions`}\n className=\"eidos-tag-input-suggestions\"\n role=\"listbox\"\n aria-label={label ?? 'Suggestions'}\n onMouseDown={(e) => e.preventDefault()}\n >\n {filteredSuggestions.length === 0 ? (\n <div className=\"eidos-tag-input-suggestions-empty\">{suggestionsEmptyText}</div>\n ) : (\n filteredSuggestions.map((suggestion, index) => (\n <div\n key={suggestion}\n id={`${uid}-tag-suggestion-${index}`}\n className={[\n 'eidos-tag-input-suggestion',\n index === focusedSuggestionIndex && 'eidos-tag-input-suggestion--focused',\n ]\n .filter(Boolean)\n .join(' ')}\n role=\"option\"\n aria-selected={index === focusedSuggestionIndex}\n onClick={() => selectSuggestion(suggestion)}\n onMouseEnter={() => setFocusedSuggestionIndex(index)}\n >\n {suggestion}\n </div>\n ))\n )}\n </div>\n }\n triggerRef={fieldRef as React.RefObject<HTMLElement | null>}\n autoWidth\n closeOnClickOutside={false}\n closeOnEscape={false}\n />\n )}\n </div>\n\n {hint && !hasError && (\n <p className=\"eidos-tag-input-hint\" id={hintId}>\n {hint}\n </p>\n )}\n\n {hasError && (\n <p className=\"eidos-tag-input-error-message\" id={errorId} role=\"alert\">\n <CircleAlert />\n {displayError}\n </p>\n )}\n </div>\n );\n};\n\nTagInput.displayName = 'TagInput';\n"]}
1
+ {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-ACNTH2OQ.cjs","../src/components/TagInput/TagInput.component.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-ACNTH2OQ.cjs","sourcesContent":[null,"import React, { useState, useId, useRef, useCallback, useMemo, useEffect } from 'react';\nimport { CircleAlert } from 'lucide-react';\nimport { Chip } from '../Chip/Chip.component';\nimport { Dropdown } from '../Dropdown/Dropdown.component';\nimport type { TagInputProps } from './TagInput.types';\nimport './TagInput.scss';\n\nexport const TagInput: React.FC<TagInputProps> = ({\n value,\n defaultValue = [],\n onChange,\n placeholder = 'Add tag…',\n size = 'md',\n disabled = false,\n label,\n error,\n hint,\n allowDuplicates = false,\n maxTags,\n separators = ['Enter', ',', 'Tab'],\n validate,\n className = '',\n fullWidth = false,\n suggestions,\n onSearch,\n suggestionsEmptyText = 'No matches',\n}) => {\n const generatedId = useId();\n const inputId = `tag-input-${generatedId}`;\n const inputRef = useRef<HTMLInputElement>(null);\n\n /** Entire component wrapper - used for click-outside detection on the suggestions dropdown */\n const containerRef = useRef<HTMLDivElement>(null);\n\n /** The field wrapper - passed to Dropdown as externalTriggerRef for width/anchoring */\n const fieldRef = useRef<HTMLDivElement>(null);\n\n /** The 0-height span that IS the Dropdown trigger element - programmatically clicked to open */\n const dropdownSpanRef = useRef<HTMLSpanElement>(null);\n\n const hasSuggestions = suggestions !== undefined;\n const isSuggestionsOpenRef = useRef(false);\n const [isSuggestionsOpen, setIsSuggestionsOpen] = useState(false);\n const [suggestionsMenuKey, setSuggestionsMenuKey] = useState(0);\n const [focusedSuggestionIndex, setFocusedSuggestionIndex] = useState(-1);\n\n const isControlled = value !== undefined;\n\n const [internalTags, setInternalTags] = useState<string[]>(defaultValue);\n const [inputValue, setInputValue] = useState('');\n const [isFocused, setIsFocused] = useState(false);\n const [validationError, setValidationError] = useState<string | null>(null);\n\n const tags = isControlled ? (value as string[]) : internalTags;\n\n const updateTags = useCallback(\n (newTags: string[]) => {\n if (!isControlled) {\n setInternalTags(newTags);\n }\n onChange?.(newTags);\n },\n [isControlled, onChange],\n );\n\n const addTag = useCallback(\n (rawTag: string) => {\n const tag = rawTag.trim();\n\n if (!tag) return;\n\n if (!allowDuplicates && tags.includes(tag)) return;\n\n if (maxTags !== undefined && tags.length >= maxTags) return;\n\n if (validate) {\n const result = validate(tag);\n if (result === false) return;\n if (typeof result === 'string') {\n setValidationError(result);\n return;\n }\n // result === true → valid, fall through\n }\n\n setValidationError(null);\n updateTags([...tags, tag]);\n setInputValue('');\n },\n [tags, allowDuplicates, maxTags, validate, updateTags],\n );\n\n const removeTag = useCallback(\n (index: number) => {\n updateTags(tags.filter((_, i) => i !== index));\n },\n [tags, updateTags],\n );\n\n // ─── Suggestions dropdown (autocomplete) ───────────────────────────────────\n\n const filteredSuggestions = useMemo<string[]>(() => {\n if (!suggestions) return [];\n const query = inputValue.trim().toLowerCase();\n return suggestions.filter((suggestion) => {\n if (!allowDuplicates && tags.includes(suggestion)) return false;\n if (!query) return true;\n return suggestion.toLowerCase().includes(query);\n });\n }, [suggestions, inputValue, tags, allowDuplicates]);\n\n useEffect(() => {\n setFocusedSuggestionIndex(-1);\n }, [filteredSuggestions]);\n\n const openSuggestions = useCallback(() => {\n if (isSuggestionsOpenRef.current || disabled || !hasSuggestions) return;\n dropdownSpanRef.current?.click(); // opens the Dropdown (bubbles to trigger)\n isSuggestionsOpenRef.current = true;\n setIsSuggestionsOpen(true);\n }, [disabled, hasSuggestions]);\n\n const closeSuggestions = useCallback(() => {\n isSuggestionsOpenRef.current = false;\n setIsSuggestionsOpen(false);\n setFocusedSuggestionIndex(-1);\n setSuggestionsMenuKey((prev) => prev + 1);\n }, []);\n\n const selectSuggestion = useCallback(\n (suggestion: string) => {\n addTag(suggestion);\n closeSuggestions();\n inputRef.current?.focus();\n },\n [addTag, closeSuggestions],\n );\n\n // Click-outside detection for the suggestions dropdown.\n useEffect(() => {\n if (!hasSuggestions) return;\n\n const handleMouseDown = (e: MouseEvent) => {\n if (!isSuggestionsOpenRef.current) return;\n if (e.defaultPrevented) return; // click inside the portal-rendered listbox\n if (containerRef.current?.contains(e.target as Node)) return;\n closeSuggestions();\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [hasSuggestions, closeSuggestions]);\n\n const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (hasSuggestions && isSuggestionsOpenRef.current && filteredSuggestions.length > 0) {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n setFocusedSuggestionIndex((prev) => (prev + 1 < filteredSuggestions.length ? prev + 1 : 0));\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n setFocusedSuggestionIndex((prev) => (prev > 0 ? prev - 1 : filteredSuggestions.length - 1));\n return;\n }\n if (e.key === 'Enter' && focusedSuggestionIndex >= 0) {\n e.preventDefault();\n selectSuggestion(filteredSuggestions[focusedSuggestionIndex]);\n return;\n }\n if (e.key === 'Escape') {\n e.preventDefault();\n closeSuggestions();\n return;\n }\n }\n\n if (separators.includes(e.key)) {\n e.preventDefault();\n addTag(inputValue);\n return;\n }\n\n if (e.key === 'Backspace' && inputValue === '' && tags.length > 0) {\n e.preventDefault();\n removeTag(tags.length - 1);\n }\n };\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const query = e.target.value;\n setInputValue(query);\n if (validationError) {\n setValidationError(null);\n }\n onSearch?.(query);\n if (hasSuggestions && !isSuggestionsOpenRef.current) openSuggestions();\n };\n\n const handleInputFocus = () => {\n setIsFocused(true);\n if (hasSuggestions) openSuggestions();\n };\n\n const handleFieldClick = (e: React.MouseEvent<HTMLDivElement>) => {\n // Don't steal focus from interactive child elements\n if ((e.target as HTMLElement).closest('button, input')) return;\n if (!disabled) {\n inputRef.current?.focus();\n }\n };\n\n const displayError = validationError ?? error;\n const hasError = !!displayError;\n const errorId = `${inputId}-error`;\n const hintId = `${inputId}-hint`;\n\n const containerClasses = [\n 'eidos-tag-input',\n hasError && 'eidos-tag-input--error',\n disabled && 'eidos-tag-input--disabled',\n fullWidth && 'eidos-tag-input--full-width',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const fieldClasses = [\n 'eidos-tag-input-field',\n `eidos-tag-input-field--${size}`,\n isFocused && 'eidos-tag-input-field--focused',\n hasError && 'eidos-tag-input-field--error',\n disabled && 'eidos-tag-input-field--disabled',\n ]\n .filter(Boolean)\n .join(' ');\n\n const uid = generatedId;\n\n return (\n <div\n className={containerClasses}\n ref={containerRef}\n aria-expanded={hasSuggestions ? isSuggestionsOpen : undefined}\n aria-haspopup={hasSuggestions ? 'listbox' : undefined}\n >\n {label && (\n <label htmlFor={inputId} className=\"eidos-tag-input-label\">\n {label}\n </label>\n )}\n\n <div className={fieldClasses} onClick={handleFieldClick} ref={fieldRef}>\n {tags.map((tag, index) => (\n <Chip\n key={`${tag}-${index}`}\n variant=\"filled\"\n color=\"primary\"\n size={size}\n onRemove={disabled ? undefined : () => removeTag(index)}\n >\n {tag}\n </Chip>\n ))}\n\n <input\n ref={inputRef}\n id={inputId}\n className=\"eidos-tag-input-input\"\n type=\"text\"\n value={inputValue}\n placeholder={tags.length === 0 ? placeholder : undefined}\n disabled={disabled}\n onKeyDown={handleKeyDown}\n onChange={handleInputChange}\n onFocus={handleInputFocus}\n onBlur={() => setIsFocused(false)}\n aria-label={label ? undefined : 'Tag input'}\n aria-describedby={hasError ? errorId : hint ? hintId : undefined}\n aria-autocomplete={hasSuggestions ? 'list' : undefined}\n aria-controls={hasSuggestions ? `${uid}-tag-suggestions` : undefined}\n aria-activedescendant={\n hasSuggestions && focusedSuggestionIndex >= 0\n ? `${uid}-tag-suggestion-${focusedSuggestionIndex}`\n : undefined\n }\n />\n\n {hasSuggestions && (\n <Dropdown\n key={suggestionsMenuKey}\n trigger={\n <span\n ref={dropdownSpanRef}\n className=\"eidos-tag-input-dropdown-anchor\"\n aria-hidden=\"true\"\n />\n }\n content={\n <div\n id={`${uid}-tag-suggestions`}\n className=\"eidos-tag-input-suggestions\"\n role=\"listbox\"\n aria-label={label ?? 'Suggestions'}\n onMouseDown={(e) => e.preventDefault()}\n >\n {filteredSuggestions.length === 0 ? (\n <div className=\"eidos-tag-input-suggestions-empty\">{suggestionsEmptyText}</div>\n ) : (\n filteredSuggestions.map((suggestion, index) => (\n <div\n key={suggestion}\n id={`${uid}-tag-suggestion-${index}`}\n className={[\n 'eidos-tag-input-suggestion',\n index === focusedSuggestionIndex && 'eidos-tag-input-suggestion--focused',\n ]\n .filter(Boolean)\n .join(' ')}\n role=\"option\"\n aria-selected={index === focusedSuggestionIndex}\n onClick={() => selectSuggestion(suggestion)}\n onMouseEnter={() => setFocusedSuggestionIndex(index)}\n >\n {suggestion}\n </div>\n ))\n )}\n </div>\n }\n triggerRef={fieldRef as React.RefObject<HTMLElement | null>}\n autoWidth\n closeOnClickOutside={false}\n closeOnEscape={false}\n />\n )}\n </div>\n\n {hint && !hasError && (\n <p className=\"eidos-tag-input-hint\" id={hintId}>\n {hint}\n </p>\n )}\n\n {hasError && (\n <p className=\"eidos-tag-input-error-message\" id={errorId} role=\"alert\">\n <CircleAlert />\n {displayError}\n </p>\n )}\n </div>\n );\n};\n\nTagInput.displayName = 'TagInput';\n"]}
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunk75VXB3H2cjs = require('./chunk-75VXB3H2.cjs');
3
+ var _chunkP2JM22BFcjs = require('./chunk-P2JM22BF.cjs');
4
4
 
5
5
  // src/components/ColorPicker/ColorPicker.component.tsx
6
6
  var _react = require('react');
@@ -496,7 +496,7 @@ var ColorPicker = ({
496
496
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: rootClasses, children: [
497
497
  label && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "eidos-color-picker-label", children: label }),
498
498
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
499
- _chunk75VXB3H2cjs.Dropdown,
499
+ _chunkP2JM22BFcjs.Dropdown,
500
500
  {
501
501
  trigger: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
502
502
  "button",
@@ -531,4 +531,4 @@ ColorPicker.displayName = "ColorPicker";
531
531
 
532
532
 
533
533
  exports.ColorPicker = ColorPicker;
534
- //# sourceMappingURL=chunk-LVHZBCTA.cjs.map
534
+ //# sourceMappingURL=chunk-AOGNTG3W.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-LVHZBCTA.cjs","../src/components/ColorPicker/ColorPicker.component.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-LVHZBCTA.cjs","sourcesContent":[null,"import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport { Copy, Check } from 'lucide-react';\nimport { Dropdown } from '../Dropdown/Dropdown.component';\nimport type {\n ColorPickerProps,\n HSVColor,\n RGBColor,\n HSLColor,\n ColorFormat,\n} from './ColorPicker.types';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_SWATCHES: string[] = [\n '#ef4444',\n '#f97316',\n '#f59e0b',\n '#10b981',\n '#06b6d4',\n '#6366f1',\n '#8b5cf6',\n '#ec4899',\n '#64748b',\n '#1e293b',\n];\n\n// ============================================================================\n// Pure colour-utility functions\n// ============================================================================\n\nfunction hsvToRgb(h: number, s: number, v: number): RGBColor {\n // h: 0–360, s: 0–100, v: 0–100 → r,g,b: 0–255\n const sn = s / 100;\n const vn = v / 100;\n const c = vn * sn;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = vn - c;\n\n let r: number, g: number, b: number;\n if (h < 60) {\n r = c;\n g = x;\n b = 0;\n } else if (h < 120) {\n r = x;\n g = c;\n b = 0;\n } else if (h < 180) {\n r = 0;\n g = c;\n b = x;\n } else if (h < 240) {\n r = 0;\n g = x;\n b = c;\n } else if (h < 300) {\n r = x;\n g = 0;\n b = c;\n } else {\n r = c;\n g = 0;\n b = x;\n }\n\n return {\n r: Math.round((r + m) * 255),\n g: Math.round((g + m) * 255),\n b: Math.round((b + m) * 255),\n };\n}\n\nfunction rgbToHsv(r: number, g: number, b: number): HSVColor {\n // r,g,b: 0–255 → h: 0–360, s: 0–100, v: 0–100\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const delta = max - min;\n\n const v = max * 100;\n const s = max === 0 ? 0 : (delta / max) * 100;\n\n let h = 0;\n if (delta !== 0) {\n if (max === rn) h = ((gn - bn) / delta) % 6;\n else if (max === gn) h = (bn - rn) / delta + 2;\n else h = (rn - gn) / delta + 4;\n h = h * 60;\n if (h < 0) h += 360;\n }\n\n // Deliberately NOT rounded. HSV is this component's internal state, and\n // rounding s/v to whole percent makes the hex -> HSV -> hex round trip lossy:\n // `#5c5de8` came back as `#5d5de8`, so a controlled `value` displayed a\n // different colour than the one passed in, and any interaction committed the\n // drifted value. `hsvToRgb` already rounds to whole channels at the end, so\n // precision is only needed in between. Every consumer of these floats (the\n // hue gradient, the thumb offsets, the hue slider) accepts them.\n return { h, s, v };\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n const ch = (n: number) =>\n Math.round(Math.max(0, Math.min(255, n)))\n .toString(16)\n .padStart(2, '0');\n return `#${ch(r)}${ch(g)}${ch(b)}`;\n}\n\nfunction hexToRgb(hex: string): RGBColor | null {\n const c = hex.trim().replace(/^#/, '');\n let r: number, g: number, b: number;\n\n if (c.length === 3) {\n r = parseInt(c[0] + c[0], 16);\n g = parseInt(c[1] + c[1], 16);\n b = parseInt(c[2] + c[2], 16);\n } else if (c.length === 6) {\n r = parseInt(c.slice(0, 2), 16);\n g = parseInt(c.slice(2, 4), 16);\n b = parseInt(c.slice(4, 6), 16);\n } else {\n return null;\n }\n\n if (isNaN(r) || isNaN(g) || isNaN(b)) return null;\n return { r, g, b };\n}\n\nfunction rgbToHsl(r: number, g: number, b: number): HSLColor {\n // r,g,b: 0–255 → h: 0–360, s: 0–100, l: 0–100\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const delta = max - min;\n const l = (max + min) / 2;\n\n let h = 0;\n let s = 0;\n\n if (delta !== 0) {\n s = delta / (1 - Math.abs(2 * l - 1));\n if (max === rn) h = ((gn - bn) / delta) % 6;\n else if (max === gn) h = (bn - rn) / delta + 2;\n else h = (rn - gn) / delta + 4;\n h = h * 60;\n if (h < 0) h += 360;\n }\n\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nfunction hslToRgb(h: number, s: number, l: number): RGBColor {\n // h: 0–360, s: 0–100, l: 0–100 → r,g,b: 0–255\n const sn = s / 100;\n const ln = l / 100;\n const c = (1 - Math.abs(2 * ln - 1)) * sn;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = ln - c / 2;\n\n let r: number, g: number, b: number;\n if (h < 60) {\n r = c;\n g = x;\n b = 0;\n } else if (h < 120) {\n r = x;\n g = c;\n b = 0;\n } else if (h < 180) {\n r = 0;\n g = c;\n b = x;\n } else if (h < 240) {\n r = 0;\n g = x;\n b = c;\n } else if (h < 300) {\n r = x;\n g = 0;\n b = c;\n } else {\n r = c;\n g = 0;\n b = x;\n }\n\n return {\n r: Math.round((r + m) * 255),\n g: Math.round((g + m) * 255),\n b: Math.round((b + m) * 255),\n };\n}\n\n// ============================================================================\n// Format helpers\n// ============================================================================\n\nfunction colorToText(hsv: HSVColor, alpha: number, fmt: ColorFormat): string {\n const rgb = hsvToRgb(hsv.h, hsv.s, hsv.v);\n\n switch (fmt) {\n case 'hex':\n return rgbToHex(rgb.r, rgb.g, rgb.b);\n case 'rgb':\n return alpha < 1\n ? `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha.toFixed(2)})`\n : `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;\n case 'hsl': {\n const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);\n return alpha < 1\n ? `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${alpha.toFixed(2)})`\n : `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;\n }\n }\n}\n\nfunction parseColorText(text: string): RGBColor | null {\n const t = text.trim();\n\n // Try hex (with or without leading #)\n const hexCandidate = t.startsWith('#') ? t : /^[0-9a-f]{3,6}$/i.test(t) ? `#${t}` : null;\n if (hexCandidate) return hexToRgb(hexCandidate);\n\n // Try rgb() / rgba()\n const rgbMatch = t.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i);\n if (rgbMatch) {\n return {\n r: parseInt(rgbMatch[1], 10),\n g: parseInt(rgbMatch[2], 10),\n b: parseInt(rgbMatch[3], 10),\n };\n }\n\n // Try hsl() / hsla()\n const hslMatch = t.match(/hsla?\\(\\s*(\\d+)\\s*,\\s*(\\d+)%?\\s*,\\s*(\\d+)%?/i);\n if (hslMatch) {\n return hslToRgb(\n parseInt(hslMatch[1], 10),\n parseInt(hslMatch[2], 10),\n parseInt(hslMatch[3], 10),\n );\n }\n\n return null;\n}\n\n/** Parse a hex string to HSVColor, falling back to red on failure. */\nfunction hexToHsv(hex: string): HSVColor {\n const rgb = hexToRgb(hex);\n return rgb ? rgbToHsv(rgb.r, rgb.g, rgb.b) : { h: 0, s: 100, v: 100 };\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ColorPicker: React.FC<ColorPickerProps> = ({\n value,\n defaultValue,\n onChange,\n format = 'hex',\n showAlpha = false,\n showSwatches = true,\n swatches = DEFAULT_SWATCHES,\n disabled = false,\n size = 'md',\n inline = false,\n label,\n className = '',\n}) => {\n // ── Initial hex (only used on first render by useState) ──────────────────\n const initialHex = value ?? defaultValue ?? '#6366f1';\n\n // ── State ─────────────────────────────────────────────────────────────────\n const [hsv, setHsv] = useState<HSVColor>(() => hexToHsv(initialHex));\n const [alpha, setAlpha] = useState<number>(1);\n const [activeFormat, setActiveFormat] = useState<ColorFormat>(format);\n const [inputText, setInputText] = useState<string>(() =>\n colorToText(hexToHsv(initialHex), 1, format),\n );\n const [copied, setCopied] = useState(false);\n\n // ── Refs - give event handlers always-fresh values without stale closures ──\n const hsvRef = useRef<HSVColor>(hsv);\n hsvRef.current = hsv;\n\n const alphaRef = useRef<number>(alpha);\n alphaRef.current = alpha;\n\n const activeFormatRef = useRef<ColorFormat>(activeFormat);\n activeFormatRef.current = activeFormat;\n\n // onChange is stored in a ref so applyColor never becomes stale\n const onChangeRef = useRef<ColorPickerProps['onChange']>(onChange);\n onChangeRef.current = onChange;\n\n const inputFocusedRef = useRef(false);\n const canvasRef = useRef<HTMLDivElement>(null);\n\n // Refs for in-flight drag listeners - cleaned up on mouseup and on unmount\n const dragHandlersRef = useRef<{\n move: (e: MouseEvent) => void;\n up: () => void;\n } | null>(null);\n\n const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // ── Derived ───────────────────────────────────────────────────────────────\n const currentRgb = hsvToRgb(hsv.h, hsv.s, hsv.v);\n const currentHex = rgbToHex(currentRgb.r, currentRgb.g, currentRgb.b);\n\n // ── Sync from controlled `value` prop ─────────────────────────────────────\n // Runs only when `value` changes. We normalize both sides to the same\n // hex format before comparing so that round-trip conversions don't trigger\n // spurious updates (which would overwrite the preserved hue when s/v = 0).\n useEffect(() => {\n if (value === undefined) return;\n const rgb = hexToRgb(value);\n if (!rgb) return;\n const newHsv = rgbToHsv(rgb.r, rgb.g, rgb.b);\n const newNormalised = rgbToHex(\n ...(Object.values(hsvToRgb(newHsv.h, newHsv.s, newHsv.v)) as [number, number, number]),\n );\n // currentHex is captured from this render - guards against echo loops\n // when the parent echoes back our own onChange emission\n if (newNormalised === currentHex) return;\n setHsv(newHsv);\n if (!inputFocusedRef.current) {\n setInputText(colorToText(newHsv, alphaRef.current, activeFormatRef.current));\n }\n }, [value, currentHex]);\n\n // ── Cleanup drag listeners and timers on unmount ──────────────────────────\n useEffect(() => {\n return () => {\n if (dragHandlersRef.current) {\n document.removeEventListener('mousemove', dragHandlersRef.current.move);\n document.removeEventListener('mouseup', dragHandlersRef.current.up);\n }\n if (copiedTimerRef.current) clearTimeout(copiedTimerRef.current);\n };\n }, []);\n\n // ── Core: apply a new HSV + alpha, emit onChange ───────────────────────────\n // Stable (empty dep array) - reads live values exclusively via refs.\n const applyColor = useCallback((newHsv: HSVColor, newAlpha: number) => {\n setHsv(newHsv);\n setAlpha(newAlpha);\n\n if (!inputFocusedRef.current) {\n setInputText(colorToText(newHsv, newAlpha, activeFormatRef.current));\n }\n\n const rgb = hsvToRgb(newHsv.h, newHsv.s, newHsv.v);\n const hex = rgbToHex(rgb.r, rgb.g, rgb.b);\n onChangeRef.current?.(hex);\n }, []);\n\n // ── 2-D saturation / brightness canvas ────────────────────────────────────\n const handleCanvasMouseDown = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n e.preventDefault();\n\n const pick = (clientX: number, clientY: number) => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const rect = canvas.getBoundingClientRect();\n const s = Math.max(0, Math.min(100, ((clientX - rect.left) / rect.width) * 100));\n const v = Math.max(0, Math.min(100, 100 - ((clientY - rect.top) / rect.height) * 100));\n applyColor({ h: hsvRef.current.h, s, v }, alphaRef.current);\n };\n\n pick(e.clientX, e.clientY);\n\n const handleMouseMove = (me: MouseEvent) => pick(me.clientX, me.clientY);\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n dragHandlersRef.current = null;\n };\n\n dragHandlersRef.current = { move: handleMouseMove, up: handleMouseUp };\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n },\n [disabled, applyColor],\n );\n\n // ── Hue slider ────────────────────────────────────────────────────────────\n const handleHueChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n applyColor({ ...hsvRef.current, h: Number(e.target.value) }, alphaRef.current);\n },\n [applyColor],\n );\n\n // ── Alpha slider ──────────────────────────────────────────────────────────\n const handleAlphaChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n applyColor(hsvRef.current, Number(e.target.value) / 100);\n },\n [applyColor],\n );\n\n // ── Format toggle ─────────────────────────────────────────────────────────\n const handleFormatChange = useCallback((fmt: ColorFormat) => {\n setActiveFormat(fmt);\n setInputText(colorToText(hsvRef.current, alphaRef.current, fmt));\n }, []);\n\n // ── Text-input commit (Enter / blur) ──────────────────────────────────────\n const handleInputCommit = useCallback(() => {\n const rgb = parseColorText(inputText);\n if (!rgb) {\n // Invalid - reset to the current valid colour\n setInputText(colorToText(hsvRef.current, alphaRef.current, activeFormatRef.current));\n return;\n }\n const newHsv = rgbToHsv(rgb.r, rgb.g, rgb.b);\n applyColor(newHsv, alphaRef.current);\n // Always normalise the text after a commit (even if the input still has focus)\n setInputText(colorToText(newHsv, alphaRef.current, activeFormatRef.current));\n }, [inputText, applyColor]);\n\n // ── Swatch click ──────────────────────────────────────────────────────────\n const handleSwatchClick = useCallback(\n (hex: string) => {\n const rgb = hexToRgb(hex);\n if (!rgb) return;\n applyColor(rgbToHsv(rgb.r, rgb.g, rgb.b), alphaRef.current);\n },\n [applyColor],\n );\n\n // ── Copy to clipboard ─────────────────────────────────────────────────────\n const handleCopy = useCallback(() => {\n const text = colorToText(hsvRef.current, alphaRef.current, activeFormatRef.current);\n navigator.clipboard\n .writeText(text)\n .then(() => {\n setCopied(true);\n if (copiedTimerRef.current) clearTimeout(copiedTimerRef.current);\n copiedTimerRef.current = setTimeout(() => setCopied(false), 2000);\n })\n .catch(() => {\n // Clipboard access denied - silently ignore\n });\n }, []);\n\n // ── Alpha-slider background gradient ─────────────────────────────────────\n // We use a transparent-to-current-colour gradient layered over a checkerboard\n // so the alpha slider always shows the correct colour transition.\n const transparentColor = `rgba(${currentRgb.r}, ${currentRgb.g}, ${currentRgb.b}, 0)`;\n\n const alphaTrackBg = {\n backgroundImage: [\n `linear-gradient(to right, ${transparentColor}, ${currentHex})`,\n 'linear-gradient(45deg, #e2e8f0 25%, transparent 25%)',\n 'linear-gradient(-45deg, #e2e8f0 25%, transparent 25%)',\n 'linear-gradient(45deg, transparent 75%, #e2e8f0 75%)',\n 'linear-gradient(-45deg, transparent 75%, #e2e8f0 75%)',\n ].join(', '),\n backgroundSize: '100%, 8px 8px, 8px 8px, 8px 8px, 8px 8px',\n backgroundPosition: '0 0, 0 0, 0 4px, 4px -4px, -4px 0',\n };\n\n // ── Picker panel (shared between inline and popover modes) ────────────────\n //\n // The size modifier has to live on the panel itself, not only on the root:\n // in popover mode the panel is portaled to document.body by Dropdown, so\n // anything inside it that sized itself off a `.eidos-color-picker--{size}`\n // ancestor matched nothing and collapsed. That is what removed the\n // saturation/brightness canvas entirely from every non-inline picker.\n const panel = (\n <div\n className={`eidos-color-picker-panel eidos-color-picker-panel--${size}${\n inline ? ' eidos-color-picker-panel--inline' : ''\n }`}\n >\n {/* 2-D sat / brightness canvas */}\n <div\n ref={canvasRef}\n className=\"eidos-color-picker-canvas\"\n style={{\n background: [\n 'linear-gradient(to top, #000, transparent)',\n `linear-gradient(to right, #fff, hsl(${hsv.h}, 100%, 50%))`,\n ].join(', '),\n }}\n onMouseDown={handleCanvasMouseDown}\n role=\"presentation\"\n >\n <div\n className=\"eidos-color-picker-thumb\"\n style={{\n // Rounded for display only - hsv.s/v are unrounded floats so the\n // hex round trip stays lossless (see rgbToHsv).\n left: `${hsv.s.toFixed(2)}%`,\n top: `${(100 - hsv.v).toFixed(2)}%`,\n background: currentHex,\n }}\n />\n </div>\n\n {/* Sliders */}\n <div className=\"eidos-color-picker-sliders\">\n {/* Hue */}\n <input\n type=\"range\"\n className=\"eidos-color-picker-hue\"\n min={0}\n max={360}\n step={1}\n value={hsv.h}\n disabled={disabled}\n onChange={handleHueChange}\n aria-label=\"Hue\"\n />\n\n {/* Alpha */}\n {showAlpha && (\n <div className=\"eidos-color-picker-alpha-wrapper\">\n <div className=\"eidos-color-picker-alpha-bg\" style={alphaTrackBg} />\n <input\n type=\"range\"\n className=\"eidos-color-picker-alpha\"\n min={0}\n max={100}\n step={1}\n value={Math.round(alpha * 100)}\n disabled={disabled}\n onChange={handleAlphaChange}\n aria-label=\"Opacity\"\n />\n </div>\n )}\n </div>\n\n {/* Text input + format toggles + copy */}\n <div className=\"eidos-color-picker-inputs\">\n <div className=\"eidos-color-picker-format-btns\" role=\"group\" aria-label=\"Colour format\">\n {(['hex', 'rgb', 'hsl'] as ColorFormat[]).map((fmt) => (\n <button\n key={fmt}\n type=\"button\"\n className={[\n 'eidos-color-picker-format-btn',\n activeFormat === fmt && 'eidos-color-picker-format-btn--active',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => handleFormatChange(fmt)}\n disabled={disabled}\n aria-pressed={activeFormat === fmt}\n >\n {fmt.toUpperCase()}\n </button>\n ))}\n </div>\n\n <input\n type=\"text\"\n className=\"eidos-color-picker-input\"\n value={inputText}\n disabled={disabled}\n spellCheck={false}\n aria-label=\"Colour value\"\n onChange={(e) => setInputText(e.target.value)}\n onFocus={() => {\n inputFocusedRef.current = true;\n }}\n onBlur={() => {\n inputFocusedRef.current = false;\n handleInputCommit();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleInputCommit();\n if (e.key === 'Escape') {\n setInputText(colorToText(hsvRef.current, alphaRef.current, activeFormatRef.current));\n }\n }}\n />\n\n <button\n type=\"button\"\n className={['eidos-color-picker-copy', copied && 'eidos-color-picker-copy--copied']\n .filter(Boolean)\n .join(' ')}\n onClick={handleCopy}\n disabled={disabled}\n aria-label=\"Copy colour value\"\n title=\"Copy colour value\"\n >\n {copied ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n\n {/* Swatches */}\n {showSwatches && (\n <div className=\"eidos-color-picker-swatches\" role=\"group\" aria-label=\"Colour presets\">\n {swatches.map((swatchHex) => (\n <button\n key={swatchHex}\n type=\"button\"\n className={[\n 'eidos-color-picker-swatch',\n currentHex === swatchHex && 'eidos-color-picker-swatch--active',\n ]\n .filter(Boolean)\n .join(' ')}\n style={{ background: swatchHex }}\n onClick={() => handleSwatchClick(swatchHex)}\n disabled={disabled}\n aria-label={`Select ${swatchHex}`}\n title={swatchHex}\n />\n ))}\n </div>\n )}\n </div>\n );\n\n // ── Root element classes ───────────────────────────────────────────────────\n const rootClasses = [\n 'eidos-color-picker',\n `eidos-color-picker--${size}`,\n disabled && 'eidos-color-picker--disabled',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // ── Inline mode ───────────────────────────────────────────────────────────\n if (inline) {\n return (\n <div className={rootClasses}>\n {label && <span className=\"eidos-color-picker-label\">{label}</span>}\n {panel}\n </div>\n );\n }\n\n // ── Popover (trigger + Dropdown) ──────────────────────────────────────────\n return (\n <div className={rootClasses}>\n {label && <span className=\"eidos-color-picker-label\">{label}</span>}\n <Dropdown\n trigger={\n <button\n type=\"button\"\n className=\"eidos-color-picker-trigger\"\n disabled={disabled}\n aria-label={label ?? 'Open colour picker'}\n >\n <span\n className=\"eidos-color-picker-trigger-swatch\"\n style={{ background: currentHex }}\n />\n <span className=\"eidos-color-picker-trigger-value\">{currentHex}</span>\n </button>\n }\n content={panel}\n placement=\"bottom\"\n autoWidth={false}\n minWidth={260}\n disabled={disabled}\n />\n </div>\n );\n};\n\nColorPicker.displayName = 'ColorPicker';\n"]}
1
+ {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-AOGNTG3W.cjs","../src/components/ColorPicker/ColorPicker.component.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-AOGNTG3W.cjs","sourcesContent":[null,"import React, { useState, useRef, useEffect, useCallback } from 'react';\nimport { Copy, Check } from 'lucide-react';\nimport { Dropdown } from '../Dropdown/Dropdown.component';\nimport type {\n ColorPickerProps,\n HSVColor,\n RGBColor,\n HSLColor,\n ColorFormat,\n} from './ColorPicker.types';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEFAULT_SWATCHES: string[] = [\n '#ef4444',\n '#f97316',\n '#f59e0b',\n '#10b981',\n '#06b6d4',\n '#6366f1',\n '#8b5cf6',\n '#ec4899',\n '#64748b',\n '#1e293b',\n];\n\n// ============================================================================\n// Pure colour-utility functions\n// ============================================================================\n\nfunction hsvToRgb(h: number, s: number, v: number): RGBColor {\n // h: 0–360, s: 0–100, v: 0–100 → r,g,b: 0–255\n const sn = s / 100;\n const vn = v / 100;\n const c = vn * sn;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = vn - c;\n\n let r: number, g: number, b: number;\n if (h < 60) {\n r = c;\n g = x;\n b = 0;\n } else if (h < 120) {\n r = x;\n g = c;\n b = 0;\n } else if (h < 180) {\n r = 0;\n g = c;\n b = x;\n } else if (h < 240) {\n r = 0;\n g = x;\n b = c;\n } else if (h < 300) {\n r = x;\n g = 0;\n b = c;\n } else {\n r = c;\n g = 0;\n b = x;\n }\n\n return {\n r: Math.round((r + m) * 255),\n g: Math.round((g + m) * 255),\n b: Math.round((b + m) * 255),\n };\n}\n\nfunction rgbToHsv(r: number, g: number, b: number): HSVColor {\n // r,g,b: 0–255 → h: 0–360, s: 0–100, v: 0–100\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const delta = max - min;\n\n const v = max * 100;\n const s = max === 0 ? 0 : (delta / max) * 100;\n\n let h = 0;\n if (delta !== 0) {\n if (max === rn) h = ((gn - bn) / delta) % 6;\n else if (max === gn) h = (bn - rn) / delta + 2;\n else h = (rn - gn) / delta + 4;\n h = h * 60;\n if (h < 0) h += 360;\n }\n\n // Deliberately NOT rounded. HSV is this component's internal state, and\n // rounding s/v to whole percent makes the hex -> HSV -> hex round trip lossy:\n // `#5c5de8` came back as `#5d5de8`, so a controlled `value` displayed a\n // different colour than the one passed in, and any interaction committed the\n // drifted value. `hsvToRgb` already rounds to whole channels at the end, so\n // precision is only needed in between. Every consumer of these floats (the\n // hue gradient, the thumb offsets, the hue slider) accepts them.\n return { h, s, v };\n}\n\nfunction rgbToHex(r: number, g: number, b: number): string {\n const ch = (n: number) =>\n Math.round(Math.max(0, Math.min(255, n)))\n .toString(16)\n .padStart(2, '0');\n return `#${ch(r)}${ch(g)}${ch(b)}`;\n}\n\nfunction hexToRgb(hex: string): RGBColor | null {\n const c = hex.trim().replace(/^#/, '');\n let r: number, g: number, b: number;\n\n if (c.length === 3) {\n r = parseInt(c[0] + c[0], 16);\n g = parseInt(c[1] + c[1], 16);\n b = parseInt(c[2] + c[2], 16);\n } else if (c.length === 6) {\n r = parseInt(c.slice(0, 2), 16);\n g = parseInt(c.slice(2, 4), 16);\n b = parseInt(c.slice(4, 6), 16);\n } else {\n return null;\n }\n\n if (isNaN(r) || isNaN(g) || isNaN(b)) return null;\n return { r, g, b };\n}\n\nfunction rgbToHsl(r: number, g: number, b: number): HSLColor {\n // r,g,b: 0–255 → h: 0–360, s: 0–100, l: 0–100\n const rn = r / 255;\n const gn = g / 255;\n const bn = b / 255;\n\n const max = Math.max(rn, gn, bn);\n const min = Math.min(rn, gn, bn);\n const delta = max - min;\n const l = (max + min) / 2;\n\n let h = 0;\n let s = 0;\n\n if (delta !== 0) {\n s = delta / (1 - Math.abs(2 * l - 1));\n if (max === rn) h = ((gn - bn) / delta) % 6;\n else if (max === gn) h = (bn - rn) / delta + 2;\n else h = (rn - gn) / delta + 4;\n h = h * 60;\n if (h < 0) h += 360;\n }\n\n return { h: Math.round(h), s: Math.round(s * 100), l: Math.round(l * 100) };\n}\n\nfunction hslToRgb(h: number, s: number, l: number): RGBColor {\n // h: 0–360, s: 0–100, l: 0–100 → r,g,b: 0–255\n const sn = s / 100;\n const ln = l / 100;\n const c = (1 - Math.abs(2 * ln - 1)) * sn;\n const x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n const m = ln - c / 2;\n\n let r: number, g: number, b: number;\n if (h < 60) {\n r = c;\n g = x;\n b = 0;\n } else if (h < 120) {\n r = x;\n g = c;\n b = 0;\n } else if (h < 180) {\n r = 0;\n g = c;\n b = x;\n } else if (h < 240) {\n r = 0;\n g = x;\n b = c;\n } else if (h < 300) {\n r = x;\n g = 0;\n b = c;\n } else {\n r = c;\n g = 0;\n b = x;\n }\n\n return {\n r: Math.round((r + m) * 255),\n g: Math.round((g + m) * 255),\n b: Math.round((b + m) * 255),\n };\n}\n\n// ============================================================================\n// Format helpers\n// ============================================================================\n\nfunction colorToText(hsv: HSVColor, alpha: number, fmt: ColorFormat): string {\n const rgb = hsvToRgb(hsv.h, hsv.s, hsv.v);\n\n switch (fmt) {\n case 'hex':\n return rgbToHex(rgb.r, rgb.g, rgb.b);\n case 'rgb':\n return alpha < 1\n ? `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha.toFixed(2)})`\n : `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`;\n case 'hsl': {\n const hsl = rgbToHsl(rgb.r, rgb.g, rgb.b);\n return alpha < 1\n ? `hsla(${hsl.h}, ${hsl.s}%, ${hsl.l}%, ${alpha.toFixed(2)})`\n : `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`;\n }\n }\n}\n\nfunction parseColorText(text: string): RGBColor | null {\n const t = text.trim();\n\n // Try hex (with or without leading #)\n const hexCandidate = t.startsWith('#') ? t : /^[0-9a-f]{3,6}$/i.test(t) ? `#${t}` : null;\n if (hexCandidate) return hexToRgb(hexCandidate);\n\n // Try rgb() / rgba()\n const rgbMatch = t.match(/rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i);\n if (rgbMatch) {\n return {\n r: parseInt(rgbMatch[1], 10),\n g: parseInt(rgbMatch[2], 10),\n b: parseInt(rgbMatch[3], 10),\n };\n }\n\n // Try hsl() / hsla()\n const hslMatch = t.match(/hsla?\\(\\s*(\\d+)\\s*,\\s*(\\d+)%?\\s*,\\s*(\\d+)%?/i);\n if (hslMatch) {\n return hslToRgb(\n parseInt(hslMatch[1], 10),\n parseInt(hslMatch[2], 10),\n parseInt(hslMatch[3], 10),\n );\n }\n\n return null;\n}\n\n/** Parse a hex string to HSVColor, falling back to red on failure. */\nfunction hexToHsv(hex: string): HSVColor {\n const rgb = hexToRgb(hex);\n return rgb ? rgbToHsv(rgb.r, rgb.g, rgb.b) : { h: 0, s: 100, v: 100 };\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const ColorPicker: React.FC<ColorPickerProps> = ({\n value,\n defaultValue,\n onChange,\n format = 'hex',\n showAlpha = false,\n showSwatches = true,\n swatches = DEFAULT_SWATCHES,\n disabled = false,\n size = 'md',\n inline = false,\n label,\n className = '',\n}) => {\n // ── Initial hex (only used on first render by useState) ──────────────────\n const initialHex = value ?? defaultValue ?? '#6366f1';\n\n // ── State ─────────────────────────────────────────────────────────────────\n const [hsv, setHsv] = useState<HSVColor>(() => hexToHsv(initialHex));\n const [alpha, setAlpha] = useState<number>(1);\n const [activeFormat, setActiveFormat] = useState<ColorFormat>(format);\n const [inputText, setInputText] = useState<string>(() =>\n colorToText(hexToHsv(initialHex), 1, format),\n );\n const [copied, setCopied] = useState(false);\n\n // ── Refs - give event handlers always-fresh values without stale closures ──\n const hsvRef = useRef<HSVColor>(hsv);\n hsvRef.current = hsv;\n\n const alphaRef = useRef<number>(alpha);\n alphaRef.current = alpha;\n\n const activeFormatRef = useRef<ColorFormat>(activeFormat);\n activeFormatRef.current = activeFormat;\n\n // onChange is stored in a ref so applyColor never becomes stale\n const onChangeRef = useRef<ColorPickerProps['onChange']>(onChange);\n onChangeRef.current = onChange;\n\n const inputFocusedRef = useRef(false);\n const canvasRef = useRef<HTMLDivElement>(null);\n\n // Refs for in-flight drag listeners - cleaned up on mouseup and on unmount\n const dragHandlersRef = useRef<{\n move: (e: MouseEvent) => void;\n up: () => void;\n } | null>(null);\n\n const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // ── Derived ───────────────────────────────────────────────────────────────\n const currentRgb = hsvToRgb(hsv.h, hsv.s, hsv.v);\n const currentHex = rgbToHex(currentRgb.r, currentRgb.g, currentRgb.b);\n\n // ── Sync from controlled `value` prop ─────────────────────────────────────\n // Runs only when `value` changes. We normalize both sides to the same\n // hex format before comparing so that round-trip conversions don't trigger\n // spurious updates (which would overwrite the preserved hue when s/v = 0).\n useEffect(() => {\n if (value === undefined) return;\n const rgb = hexToRgb(value);\n if (!rgb) return;\n const newHsv = rgbToHsv(rgb.r, rgb.g, rgb.b);\n const newNormalised = rgbToHex(\n ...(Object.values(hsvToRgb(newHsv.h, newHsv.s, newHsv.v)) as [number, number, number]),\n );\n // currentHex is captured from this render - guards against echo loops\n // when the parent echoes back our own onChange emission\n if (newNormalised === currentHex) return;\n setHsv(newHsv);\n if (!inputFocusedRef.current) {\n setInputText(colorToText(newHsv, alphaRef.current, activeFormatRef.current));\n }\n }, [value, currentHex]);\n\n // ── Cleanup drag listeners and timers on unmount ──────────────────────────\n useEffect(() => {\n return () => {\n if (dragHandlersRef.current) {\n document.removeEventListener('mousemove', dragHandlersRef.current.move);\n document.removeEventListener('mouseup', dragHandlersRef.current.up);\n }\n if (copiedTimerRef.current) clearTimeout(copiedTimerRef.current);\n };\n }, []);\n\n // ── Core: apply a new HSV + alpha, emit onChange ───────────────────────────\n // Stable (empty dep array) - reads live values exclusively via refs.\n const applyColor = useCallback((newHsv: HSVColor, newAlpha: number) => {\n setHsv(newHsv);\n setAlpha(newAlpha);\n\n if (!inputFocusedRef.current) {\n setInputText(colorToText(newHsv, newAlpha, activeFormatRef.current));\n }\n\n const rgb = hsvToRgb(newHsv.h, newHsv.s, newHsv.v);\n const hex = rgbToHex(rgb.r, rgb.g, rgb.b);\n onChangeRef.current?.(hex);\n }, []);\n\n // ── 2-D saturation / brightness canvas ────────────────────────────────────\n const handleCanvasMouseDown = useCallback(\n (e: React.MouseEvent<HTMLDivElement>) => {\n if (disabled) return;\n e.preventDefault();\n\n const pick = (clientX: number, clientY: number) => {\n const canvas = canvasRef.current;\n if (!canvas) return;\n const rect = canvas.getBoundingClientRect();\n const s = Math.max(0, Math.min(100, ((clientX - rect.left) / rect.width) * 100));\n const v = Math.max(0, Math.min(100, 100 - ((clientY - rect.top) / rect.height) * 100));\n applyColor({ h: hsvRef.current.h, s, v }, alphaRef.current);\n };\n\n pick(e.clientX, e.clientY);\n\n const handleMouseMove = (me: MouseEvent) => pick(me.clientX, me.clientY);\n const handleMouseUp = () => {\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n dragHandlersRef.current = null;\n };\n\n dragHandlersRef.current = { move: handleMouseMove, up: handleMouseUp };\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n },\n [disabled, applyColor],\n );\n\n // ── Hue slider ────────────────────────────────────────────────────────────\n const handleHueChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n applyColor({ ...hsvRef.current, h: Number(e.target.value) }, alphaRef.current);\n },\n [applyColor],\n );\n\n // ── Alpha slider ──────────────────────────────────────────────────────────\n const handleAlphaChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n applyColor(hsvRef.current, Number(e.target.value) / 100);\n },\n [applyColor],\n );\n\n // ── Format toggle ─────────────────────────────────────────────────────────\n const handleFormatChange = useCallback((fmt: ColorFormat) => {\n setActiveFormat(fmt);\n setInputText(colorToText(hsvRef.current, alphaRef.current, fmt));\n }, []);\n\n // ── Text-input commit (Enter / blur) ──────────────────────────────────────\n const handleInputCommit = useCallback(() => {\n const rgb = parseColorText(inputText);\n if (!rgb) {\n // Invalid - reset to the current valid colour\n setInputText(colorToText(hsvRef.current, alphaRef.current, activeFormatRef.current));\n return;\n }\n const newHsv = rgbToHsv(rgb.r, rgb.g, rgb.b);\n applyColor(newHsv, alphaRef.current);\n // Always normalise the text after a commit (even if the input still has focus)\n setInputText(colorToText(newHsv, alphaRef.current, activeFormatRef.current));\n }, [inputText, applyColor]);\n\n // ── Swatch click ──────────────────────────────────────────────────────────\n const handleSwatchClick = useCallback(\n (hex: string) => {\n const rgb = hexToRgb(hex);\n if (!rgb) return;\n applyColor(rgbToHsv(rgb.r, rgb.g, rgb.b), alphaRef.current);\n },\n [applyColor],\n );\n\n // ── Copy to clipboard ─────────────────────────────────────────────────────\n const handleCopy = useCallback(() => {\n const text = colorToText(hsvRef.current, alphaRef.current, activeFormatRef.current);\n navigator.clipboard\n .writeText(text)\n .then(() => {\n setCopied(true);\n if (copiedTimerRef.current) clearTimeout(copiedTimerRef.current);\n copiedTimerRef.current = setTimeout(() => setCopied(false), 2000);\n })\n .catch(() => {\n // Clipboard access denied - silently ignore\n });\n }, []);\n\n // ── Alpha-slider background gradient ─────────────────────────────────────\n // We use a transparent-to-current-colour gradient layered over a checkerboard\n // so the alpha slider always shows the correct colour transition.\n const transparentColor = `rgba(${currentRgb.r}, ${currentRgb.g}, ${currentRgb.b}, 0)`;\n\n const alphaTrackBg = {\n backgroundImage: [\n `linear-gradient(to right, ${transparentColor}, ${currentHex})`,\n 'linear-gradient(45deg, #e2e8f0 25%, transparent 25%)',\n 'linear-gradient(-45deg, #e2e8f0 25%, transparent 25%)',\n 'linear-gradient(45deg, transparent 75%, #e2e8f0 75%)',\n 'linear-gradient(-45deg, transparent 75%, #e2e8f0 75%)',\n ].join(', '),\n backgroundSize: '100%, 8px 8px, 8px 8px, 8px 8px, 8px 8px',\n backgroundPosition: '0 0, 0 0, 0 4px, 4px -4px, -4px 0',\n };\n\n // ── Picker panel (shared between inline and popover modes) ────────────────\n //\n // The size modifier has to live on the panel itself, not only on the root:\n // in popover mode the panel is portaled to document.body by Dropdown, so\n // anything inside it that sized itself off a `.eidos-color-picker--{size}`\n // ancestor matched nothing and collapsed. That is what removed the\n // saturation/brightness canvas entirely from every non-inline picker.\n const panel = (\n <div\n className={`eidos-color-picker-panel eidos-color-picker-panel--${size}${\n inline ? ' eidos-color-picker-panel--inline' : ''\n }`}\n >\n {/* 2-D sat / brightness canvas */}\n <div\n ref={canvasRef}\n className=\"eidos-color-picker-canvas\"\n style={{\n background: [\n 'linear-gradient(to top, #000, transparent)',\n `linear-gradient(to right, #fff, hsl(${hsv.h}, 100%, 50%))`,\n ].join(', '),\n }}\n onMouseDown={handleCanvasMouseDown}\n role=\"presentation\"\n >\n <div\n className=\"eidos-color-picker-thumb\"\n style={{\n // Rounded for display only - hsv.s/v are unrounded floats so the\n // hex round trip stays lossless (see rgbToHsv).\n left: `${hsv.s.toFixed(2)}%`,\n top: `${(100 - hsv.v).toFixed(2)}%`,\n background: currentHex,\n }}\n />\n </div>\n\n {/* Sliders */}\n <div className=\"eidos-color-picker-sliders\">\n {/* Hue */}\n <input\n type=\"range\"\n className=\"eidos-color-picker-hue\"\n min={0}\n max={360}\n step={1}\n value={hsv.h}\n disabled={disabled}\n onChange={handleHueChange}\n aria-label=\"Hue\"\n />\n\n {/* Alpha */}\n {showAlpha && (\n <div className=\"eidos-color-picker-alpha-wrapper\">\n <div className=\"eidos-color-picker-alpha-bg\" style={alphaTrackBg} />\n <input\n type=\"range\"\n className=\"eidos-color-picker-alpha\"\n min={0}\n max={100}\n step={1}\n value={Math.round(alpha * 100)}\n disabled={disabled}\n onChange={handleAlphaChange}\n aria-label=\"Opacity\"\n />\n </div>\n )}\n </div>\n\n {/* Text input + format toggles + copy */}\n <div className=\"eidos-color-picker-inputs\">\n <div className=\"eidos-color-picker-format-btns\" role=\"group\" aria-label=\"Colour format\">\n {(['hex', 'rgb', 'hsl'] as ColorFormat[]).map((fmt) => (\n <button\n key={fmt}\n type=\"button\"\n className={[\n 'eidos-color-picker-format-btn',\n activeFormat === fmt && 'eidos-color-picker-format-btn--active',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => handleFormatChange(fmt)}\n disabled={disabled}\n aria-pressed={activeFormat === fmt}\n >\n {fmt.toUpperCase()}\n </button>\n ))}\n </div>\n\n <input\n type=\"text\"\n className=\"eidos-color-picker-input\"\n value={inputText}\n disabled={disabled}\n spellCheck={false}\n aria-label=\"Colour value\"\n onChange={(e) => setInputText(e.target.value)}\n onFocus={() => {\n inputFocusedRef.current = true;\n }}\n onBlur={() => {\n inputFocusedRef.current = false;\n handleInputCommit();\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') handleInputCommit();\n if (e.key === 'Escape') {\n setInputText(colorToText(hsvRef.current, alphaRef.current, activeFormatRef.current));\n }\n }}\n />\n\n <button\n type=\"button\"\n className={['eidos-color-picker-copy', copied && 'eidos-color-picker-copy--copied']\n .filter(Boolean)\n .join(' ')}\n onClick={handleCopy}\n disabled={disabled}\n aria-label=\"Copy colour value\"\n title=\"Copy colour value\"\n >\n {copied ? <Check size={14} /> : <Copy size={14} />}\n </button>\n </div>\n\n {/* Swatches */}\n {showSwatches && (\n <div className=\"eidos-color-picker-swatches\" role=\"group\" aria-label=\"Colour presets\">\n {swatches.map((swatchHex) => (\n <button\n key={swatchHex}\n type=\"button\"\n className={[\n 'eidos-color-picker-swatch',\n currentHex === swatchHex && 'eidos-color-picker-swatch--active',\n ]\n .filter(Boolean)\n .join(' ')}\n style={{ background: swatchHex }}\n onClick={() => handleSwatchClick(swatchHex)}\n disabled={disabled}\n aria-label={`Select ${swatchHex}`}\n title={swatchHex}\n />\n ))}\n </div>\n )}\n </div>\n );\n\n // ── Root element classes ───────────────────────────────────────────────────\n const rootClasses = [\n 'eidos-color-picker',\n `eidos-color-picker--${size}`,\n disabled && 'eidos-color-picker--disabled',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // ── Inline mode ───────────────────────────────────────────────────────────\n if (inline) {\n return (\n <div className={rootClasses}>\n {label && <span className=\"eidos-color-picker-label\">{label}</span>}\n {panel}\n </div>\n );\n }\n\n // ── Popover (trigger + Dropdown) ──────────────────────────────────────────\n return (\n <div className={rootClasses}>\n {label && <span className=\"eidos-color-picker-label\">{label}</span>}\n <Dropdown\n trigger={\n <button\n type=\"button\"\n className=\"eidos-color-picker-trigger\"\n disabled={disabled}\n aria-label={label ?? 'Open colour picker'}\n >\n <span\n className=\"eidos-color-picker-trigger-swatch\"\n style={{ background: currentHex }}\n />\n <span className=\"eidos-color-picker-trigger-value\">{currentHex}</span>\n </button>\n }\n content={panel}\n placement=\"bottom\"\n autoWidth={false}\n minWidth={260}\n disabled={disabled}\n />\n </div>\n );\n};\n\nColorPicker.displayName = 'ColorPicker';\n"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Dropdown
3
- } from "./chunk-2R77XZEP.js";
3
+ } from "./chunk-6CPCNQKV.js";
4
4
  import {
5
5
  Kbd
6
6
  } from "./chunk-LYH3EKKQ.js";
@@ -122,4 +122,4 @@ export {
122
122
  MenuPanel,
123
123
  Menu
124
124
  };
125
- //# sourceMappingURL=chunk-KNQ6NN6L.js.map
125
+ //# sourceMappingURL=chunk-BNVQ34FD.js.map
@@ -1,6 +1,6 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
- var _chunkIYWAZEFIcjs = require('./chunk-IYWAZEFI.cjs');
3
+ var _chunkIGFR5TEScjs = require('./chunk-IGFR5TES.cjs');
4
4
 
5
5
  // src/components/VirtualList/VirtualList.component.tsx
6
6
  var _react = require('react'); var React = _interopRequireWildcard(_react);
@@ -1502,7 +1502,8 @@ function useVirtualizer(options) {
1502
1502
  // src/components/VirtualList/VirtualList.component.tsx
1503
1503
  var _jsxruntime = require('react/jsx-runtime');
1504
1504
  function toCssSize(value) {
1505
- return typeof value === "number" ? `${value}px` : value;
1505
+ if (typeof value === "number") return `${value}px`;
1506
+ return /^-?\d*\.?\d+$/.test(value.trim()) ? `${value.trim()}px` : value;
1506
1507
  }
1507
1508
  function VirtualListInner({
1508
1509
  data,
@@ -1570,7 +1571,7 @@ function VirtualListInner({
1570
1571
  style: { height: skeletonRowHeight },
1571
1572
  children: [
1572
1573
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1573
- _chunkIYWAZEFIcjs.Skeleton,
1574
+ _chunkIGFR5TEScjs.Skeleton,
1574
1575
  {
1575
1576
  variant: "circular",
1576
1577
  width: skeletonRowHeight - 20,
@@ -1578,8 +1579,8 @@ function VirtualListInner({
1578
1579
  }
1579
1580
  ),
1580
1581
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "eidos-virtual-list-skeleton-row-content", children: [
1581
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIYWAZEFIcjs.Skeleton, { variant: "rounded", width: "55%", height: 12 }),
1582
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIYWAZEFIcjs.Skeleton, { variant: "rounded", width: "38%", height: 12 })
1582
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIGFR5TEScjs.Skeleton, { variant: "rounded", width: "55%", height: 12 }),
1583
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIGFR5TEScjs.Skeleton, { variant: "rounded", width: "38%", height: 12 })
1583
1584
  ] })
1584
1585
  ]
1585
1586
  },
@@ -1619,4 +1620,4 @@ var VirtualList = Object.assign(VirtualListInner, {
1619
1620
 
1620
1621
 
1621
1622
  exports.VirtualList = VirtualList;
1622
- //# sourceMappingURL=chunk-6A2ZKVST.cjs.map
1623
+ //# sourceMappingURL=chunk-E6SE552P.cjs.map