eidos-ui 3.2.1 → 3.4.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 (332) hide show
  1. package/README.md +17 -5
  2. package/dist/{Input.types-XrJTI0JC.d.ts → Input.types-CSDMaZNe.d.ts} +10 -0
  3. package/dist/{Input.types-DFZNyJGF.d.cts → Input.types-D7-sWq-q.d.cts} +10 -0
  4. package/dist/{Select.types-CAkT2ACt.d.cts → Select.types-2p-mpaBL.d.cts} +1 -1
  5. package/dist/{Select.types-B4kDpkGB.d.ts → Select.types-BqaD7bmB.d.ts} +1 -1
  6. package/dist/alert/index.cjs +5 -5
  7. package/dist/alert/index.js +4 -4
  8. package/dist/button/index.cjs +4 -4
  9. package/dist/button/index.js +3 -3
  10. package/dist/checkbox/index.cjs +2 -2
  11. package/dist/checkbox/index.js +1 -1
  12. package/dist/chip/index.cjs +4 -4
  13. package/dist/chip/index.js +3 -3
  14. package/dist/{chunk-RYOQYYDO.js → chunk-22EKNTWJ.js} +9 -5
  15. package/dist/chunk-22EKNTWJ.js.map +1 -0
  16. package/dist/{chunk-3E7TKBQI.cjs → chunk-27UANGPW.cjs} +14 -6
  17. package/dist/chunk-27UANGPW.cjs.map +1 -0
  18. package/dist/{chunk-CJGFRPAS.cjs → chunk-3QQKWXMV.cjs} +4 -4
  19. package/dist/chunk-3QQKWXMV.cjs.map +1 -0
  20. package/dist/{chunk-R3WPVTGY.cjs → chunk-3S33BIN2.cjs} +23 -21
  21. package/dist/chunk-3S33BIN2.cjs.map +1 -0
  22. package/dist/{chunk-ZPDETHQ7.js → chunk-3WV6RTXX.js} +3 -3
  23. package/dist/{chunk-ZPDETHQ7.js.map → chunk-3WV6RTXX.js.map} +1 -1
  24. package/dist/chunk-44T4HMFU.js +88 -0
  25. package/dist/chunk-44T4HMFU.js.map +1 -0
  26. package/dist/{chunk-3EPND5M2.cjs → chunk-4LET2PEX.cjs} +12 -12
  27. package/dist/chunk-4LET2PEX.cjs.map +1 -0
  28. package/dist/{chunk-VRQBDTLT.cjs → chunk-5E4DYTZ3.cjs} +48 -37
  29. package/dist/chunk-5E4DYTZ3.cjs.map +1 -0
  30. package/dist/{chunk-EL3YXTOW.cjs → chunk-5V7DXZWT.cjs} +79 -76
  31. package/dist/chunk-5V7DXZWT.cjs.map +1 -0
  32. package/dist/{chunk-2R77XZEP.js → chunk-67EXHO4T.js} +2 -4
  33. package/dist/{chunk-2R77XZEP.js.map → chunk-67EXHO4T.js.map} +1 -1
  34. package/dist/{chunk-HI6WCX4T.cjs → chunk-6LCYO2HG.cjs} +10 -9
  35. package/dist/chunk-6LCYO2HG.cjs.map +1 -0
  36. package/dist/{chunk-ZNWIHUHZ.cjs → chunk-7KVXXHFX.cjs} +9 -3
  37. package/dist/chunk-7KVXXHFX.cjs.map +1 -0
  38. package/dist/{chunk-55J4LEKT.cjs → chunk-AHVDTWVM.cjs} +11 -8
  39. package/dist/chunk-AHVDTWVM.cjs.map +1 -0
  40. package/dist/{chunk-OAPOGYA5.cjs → chunk-AXPEJ5IG.cjs} +7 -4
  41. package/dist/chunk-AXPEJ5IG.cjs.map +1 -0
  42. package/dist/{chunk-BHVHM66Z.js → chunk-BBU7QG7L.js} +4 -4
  43. package/dist/{chunk-T2KB3DJJ.js → chunk-BGBAZMFE.js} +9 -3
  44. package/dist/chunk-BGBAZMFE.js.map +1 -0
  45. package/dist/{chunk-EH42ND5Q.cjs → chunk-BIANFFJL.cjs} +5 -5
  46. package/dist/{chunk-EH42ND5Q.cjs.map → chunk-BIANFFJL.cjs.map} +1 -1
  47. package/dist/{chunk-O2AACVSF.js → chunk-BO4LTIJI.js} +3 -3
  48. package/dist/{chunk-ZGT4LT5Y.cjs → chunk-CBT3TTQR.cjs} +36 -36
  49. package/dist/{chunk-ZGT4LT5Y.cjs.map → chunk-CBT3TTQR.cjs.map} +1 -1
  50. package/dist/{chunk-JRLUZDKA.js → chunk-CEC3GO4R.js} +20 -7
  51. package/dist/chunk-CEC3GO4R.js.map +1 -0
  52. package/dist/{chunk-KNQ6NN6L.js → chunk-CP335H4Q.js} +4 -4
  53. package/dist/{chunk-H6CWTMAH.js → chunk-DGXHR6FV.js} +5 -2
  54. package/dist/chunk-DGXHR6FV.js.map +1 -0
  55. package/dist/{chunk-FALDCW2E.js → chunk-DMOZURHV.js} +2 -2
  56. package/dist/{chunk-NWJ6NETE.js → chunk-EWTMX4DK.js} +8 -8
  57. package/dist/{chunk-3RLGH45H.js → chunk-FDMYDIGI.js} +8 -6
  58. package/dist/chunk-FDMYDIGI.js.map +1 -0
  59. package/dist/{chunk-LVHZBCTA.cjs → chunk-FODRHCRP.cjs} +5 -4
  60. package/dist/chunk-FODRHCRP.cjs.map +1 -0
  61. package/dist/{chunk-QIITXVRG.js → chunk-FPLX5FIK.js} +4 -4
  62. package/dist/{chunk-NEYLNRVG.cjs → chunk-FXMRDNR6.cjs} +21 -16
  63. package/dist/chunk-FXMRDNR6.cjs.map +1 -0
  64. package/dist/{chunk-D2SKXRKP.cjs → chunk-G74DHVHV.cjs} +9 -9
  65. package/dist/{chunk-D2SKXRKP.cjs.map → chunk-G74DHVHV.cjs.map} +1 -1
  66. package/dist/{chunk-O3LG7B2J.cjs → chunk-GMDVW76A.cjs} +13 -5
  67. package/dist/chunk-GMDVW76A.cjs.map +1 -0
  68. package/dist/{chunk-NQ4L6PYW.js → chunk-GOF6A4M4.js} +2 -2
  69. package/dist/{chunk-NQ4L6PYW.js.map → chunk-GOF6A4M4.js.map} +1 -1
  70. package/dist/{chunk-QUDVJUZU.js → chunk-GPK2THIG.js} +7 -6
  71. package/dist/chunk-GPK2THIG.js.map +1 -0
  72. package/dist/{chunk-CJVC3A4O.js → chunk-H2RS6Z6Q.js} +44 -27
  73. package/dist/chunk-H2RS6Z6Q.js.map +1 -0
  74. package/dist/{chunk-UOOGS6N7.cjs → chunk-H5D3ZFBM.cjs} +68 -61
  75. package/dist/chunk-H5D3ZFBM.cjs.map +1 -0
  76. package/dist/{chunk-IYWAZEFI.cjs → chunk-IGFR5TES.cjs} +3 -2
  77. package/dist/chunk-IGFR5TES.cjs.map +1 -0
  78. package/dist/{chunk-PR7CVQ4R.js → chunk-IXVM2XNS.js} +7 -6
  79. package/dist/chunk-IXVM2XNS.js.map +1 -0
  80. package/dist/{chunk-Q6ICDKQZ.cjs → chunk-JKBBHAIC.cjs} +21 -20
  81. package/dist/chunk-JKBBHAIC.cjs.map +1 -0
  82. package/dist/{chunk-QQIALSTX.cjs → chunk-KPMIIGHH.cjs} +27 -17
  83. package/dist/chunk-KPMIIGHH.cjs.map +1 -0
  84. package/dist/{chunk-EBSKRZXI.cjs → chunk-KWDOOMHR.cjs} +7 -7
  85. package/dist/{chunk-EBSKRZXI.cjs.map → chunk-KWDOOMHR.cjs.map} +1 -1
  86. package/dist/{chunk-BC5I2MYB.js → chunk-LH5A2SIX.js} +78 -75
  87. package/dist/{chunk-BC5I2MYB.js.map → chunk-LH5A2SIX.js.map} +1 -1
  88. package/dist/{chunk-2WP27QAN.js → chunk-LIKSLFX6.js} +20 -9
  89. package/dist/chunk-LIKSLFX6.js.map +1 -0
  90. package/dist/{chunk-Z5U4NJTW.cjs → chunk-MLUHM3VG.cjs} +48 -19
  91. package/dist/chunk-MLUHM3VG.cjs.map +1 -0
  92. package/dist/{chunk-M7PF4FKQ.js → chunk-MP7LN5CW.js} +6 -3
  93. package/dist/chunk-MP7LN5CW.js.map +1 -0
  94. package/dist/{chunk-4MZ7HEQC.cjs → chunk-MW4YZVP5.cjs} +96 -97
  95. package/dist/chunk-MW4YZVP5.cjs.map +1 -0
  96. package/dist/{chunk-WFEORBLC.cjs → chunk-N5ZWQJTM.cjs} +11 -10
  97. package/dist/chunk-N5ZWQJTM.cjs.map +1 -0
  98. package/dist/{chunk-OMLXP2CK.cjs → chunk-N77VOYCK.cjs} +7 -7
  99. package/dist/{chunk-OMLXP2CK.cjs.map → chunk-N77VOYCK.cjs.map} +1 -1
  100. package/dist/{chunk-OAK7HFOJ.js → chunk-NACA72XI.js} +21 -16
  101. package/dist/chunk-NACA72XI.js.map +1 -0
  102. package/dist/{chunk-TEVZHN5I.cjs → chunk-NCZUQFKL.cjs} +28 -15
  103. package/dist/chunk-NCZUQFKL.cjs.map +1 -0
  104. package/dist/{chunk-NZO2I5AL.cjs → chunk-NF55JG2V.cjs} +7 -7
  105. package/dist/{chunk-NZO2I5AL.cjs.map → chunk-NF55JG2V.cjs.map} +1 -1
  106. package/dist/{chunk-KBGFDHKD.cjs → chunk-O46UJUZM.cjs} +21 -19
  107. package/dist/chunk-O46UJUZM.cjs.map +1 -0
  108. package/dist/{chunk-25PSIDPC.js → chunk-OIXPYXR6.js} +79 -21
  109. package/dist/chunk-OIXPYXR6.js.map +1 -0
  110. package/dist/chunk-OWWPHXAF.cjs +88 -0
  111. package/dist/chunk-OWWPHXAF.cjs.map +1 -0
  112. package/dist/{chunk-65PC3YDT.cjs → chunk-OZQKWDFH.cjs} +7 -7
  113. package/dist/{chunk-65PC3YDT.cjs.map → chunk-OZQKWDFH.cjs.map} +1 -1
  114. package/dist/{chunk-75VXB3H2.cjs → chunk-P7J6WZTO.cjs} +3 -5
  115. package/dist/chunk-P7J6WZTO.cjs.map +1 -0
  116. package/dist/{chunk-KQJ3IAFT.js → chunk-PG2EII6U.js} +2 -2
  117. package/dist/{chunk-6A2ZKVST.cjs → chunk-RUDXHT76.cjs} +47 -30
  118. package/dist/chunk-RUDXHT76.cjs.map +1 -0
  119. package/dist/{chunk-S4S4LB63.js → chunk-SCLFJHN2.js} +11 -3
  120. package/dist/chunk-SCLFJHN2.js.map +1 -0
  121. package/dist/{chunk-WKDT2XFQ.js → chunk-SFRILDXY.js} +4 -4
  122. package/dist/{chunk-J5RMLZ4P.cjs → chunk-SKBKTNEQ.cjs} +80 -22
  123. package/dist/chunk-SKBKTNEQ.cjs.map +1 -0
  124. package/dist/{chunk-75TDY7XF.cjs → chunk-SKLMBE3Z.cjs} +7 -7
  125. package/dist/{chunk-75TDY7XF.cjs.map → chunk-SKLMBE3Z.cjs.map} +1 -1
  126. package/dist/{chunk-PY2376XI.js → chunk-SVCWSW2F.js} +8 -8
  127. package/dist/chunk-SVCWSW2F.js.map +1 -0
  128. package/dist/{chunk-2RXQMQYW.js → chunk-SXL4DMZ7.js} +27 -17
  129. package/dist/{chunk-2RXQMQYW.js.map → chunk-SXL4DMZ7.js.map} +1 -1
  130. package/dist/{chunk-X3U7SDOZ.js → chunk-TLECMA3B.js} +5 -4
  131. package/dist/{chunk-X3U7SDOZ.js.map → chunk-TLECMA3B.js.map} +1 -1
  132. package/dist/{chunk-FXZJL2AM.cjs → chunk-TWXZKUCG.cjs} +9 -5
  133. package/dist/chunk-TWXZKUCG.cjs.map +1 -0
  134. package/dist/{chunk-YDPEHUMH.js → chunk-TYGVKSL6.js} +4 -3
  135. package/dist/chunk-TYGVKSL6.js.map +1 -0
  136. package/dist/{chunk-I3CSPYHG.cjs → chunk-TYSNOPMQ.cjs} +3 -3
  137. package/dist/{chunk-I3CSPYHG.cjs.map → chunk-TYSNOPMQ.cjs.map} +1 -1
  138. package/dist/{chunk-ZZSPQFMX.js → chunk-U233KQZX.js} +2 -1
  139. package/dist/{chunk-ZZSPQFMX.js.map → chunk-U233KQZX.js.map} +1 -1
  140. package/dist/{chunk-5HUZYY54.js → chunk-UDRAM6JU.js} +13 -5
  141. package/dist/chunk-UDRAM6JU.js.map +1 -0
  142. package/dist/{chunk-TJC7UELT.js → chunk-UGTKZ7HP.js} +30 -23
  143. package/dist/chunk-UGTKZ7HP.js.map +1 -0
  144. package/dist/{chunk-FKXKBJUI.cjs → chunk-VHO6JPRR.cjs} +2 -1
  145. package/dist/chunk-VHO6JPRR.cjs.map +1 -0
  146. package/dist/chunk-VHSTB7GB.cjs +123 -0
  147. package/dist/chunk-VHSTB7GB.cjs.map +1 -0
  148. package/dist/{chunk-ATL7325T.js → chunk-VRNCXYEP.js} +4 -4
  149. package/dist/{chunk-4ZWAJTNH.js → chunk-WGCGSM5P.js} +96 -97
  150. package/dist/chunk-WGCGSM5P.js.map +1 -0
  151. package/dist/chunk-WT4WBHBR.js +123 -0
  152. package/dist/chunk-WT4WBHBR.js.map +1 -0
  153. package/dist/{chunk-L23WWO3D.js → chunk-X6UKU3C3.js} +2 -2
  154. package/dist/{chunk-5WH3JMR5.js → chunk-XK5USBHB.js} +7 -4
  155. package/dist/chunk-XK5USBHB.js.map +1 -0
  156. package/dist/{chunk-ND526PMG.cjs → chunk-XMXL6NVJ.cjs} +11 -11
  157. package/dist/{chunk-ND526PMG.cjs.map → chunk-XMXL6NVJ.cjs.map} +1 -1
  158. package/dist/{chunk-YV57RSPD.js → chunk-XO7WTE6O.js} +47 -18
  159. package/dist/chunk-XO7WTE6O.js.map +1 -0
  160. package/dist/{chunk-3CXULV4G.cjs → chunk-XRVRGRC3.cjs} +2 -2
  161. package/dist/{chunk-3CXULV4G.cjs.map → chunk-XRVRGRC3.cjs.map} +1 -1
  162. package/dist/{chunk-O3I3RBZJ.js → chunk-Y55M34RQ.js} +3 -2
  163. package/dist/chunk-Y55M34RQ.js.map +1 -0
  164. package/dist/{chunk-QZX3A4FX.js → chunk-Y7VSWZWM.js} +7 -5
  165. package/dist/chunk-Y7VSWZWM.js.map +1 -0
  166. package/dist/{chunk-2QEKSRVX.js → chunk-YIBPOLNN.js} +4 -4
  167. package/dist/{chunk-IJ4DXG7D.cjs → chunk-YJNQLH2L.cjs} +5 -2
  168. package/dist/chunk-YJNQLH2L.cjs.map +1 -0
  169. package/dist/{chunk-6CSUUQ5M.cjs → chunk-YUH56CJI.cjs} +6 -6
  170. package/dist/{chunk-6CSUUQ5M.cjs.map → chunk-YUH56CJI.cjs.map} +1 -1
  171. package/dist/color-picker/index.cjs +4 -4
  172. package/dist/color-picker/index.d.cts +8 -0
  173. package/dist/color-picker/index.d.ts +8 -0
  174. package/dist/color-picker/index.js +3 -3
  175. package/dist/combobox/index.cjs +6 -6
  176. package/dist/combobox/index.js +5 -5
  177. package/dist/command-palette/index.cjs +3 -2
  178. package/dist/command-palette/index.cjs.map +1 -1
  179. package/dist/command-palette/index.js +2 -1
  180. package/dist/context-menu/index.cjs +6 -6
  181. package/dist/context-menu/index.js +5 -5
  182. package/dist/conversation/index.cjs +9 -9
  183. package/dist/conversation/index.js +8 -8
  184. package/dist/data-grid/index.cjs +16 -16
  185. package/dist/data-grid/index.d.cts +2 -2
  186. package/dist/data-grid/index.d.ts +2 -2
  187. package/dist/data-grid/index.js +15 -15
  188. package/dist/date-picker/index.cjs +8 -8
  189. package/dist/date-picker/index.d.cts +1 -1
  190. package/dist/date-picker/index.d.ts +1 -1
  191. package/dist/date-picker/index.js +7 -7
  192. package/dist/drawer/index.cjs +5 -5
  193. package/dist/drawer/index.js +4 -4
  194. package/dist/dropdown/index.cjs +3 -3
  195. package/dist/dropdown/index.js +2 -2
  196. package/dist/file-upload/index.cjs +2 -2
  197. package/dist/file-upload/index.js +1 -1
  198. package/dist/header/index.cjs +6 -6
  199. package/dist/header/index.js +5 -5
  200. package/dist/index.cjs +43 -43
  201. package/dist/index.css +205 -148
  202. package/dist/index.d.cts +2 -2
  203. package/dist/index.d.ts +2 -2
  204. package/dist/index.js +42 -42
  205. package/dist/inline-edit/index.cjs +5 -5
  206. package/dist/inline-edit/index.js +4 -4
  207. package/dist/input/index.cjs +4 -4
  208. package/dist/input/index.d.cts +2 -2
  209. package/dist/input/index.d.ts +2 -2
  210. package/dist/input/index.js +3 -3
  211. package/dist/menu/index.cjs +5 -5
  212. package/dist/menu/index.js +4 -4
  213. package/dist/modal/index.cjs +5 -5
  214. package/dist/modal/index.js +4 -4
  215. package/dist/navigation/index.cjs +5 -5
  216. package/dist/navigation/index.js +4 -4
  217. package/dist/number-input/index.cjs +3 -3
  218. package/dist/number-input/index.js +2 -2
  219. package/dist/o-t-p-input/index.cjs +2 -2
  220. package/dist/o-t-p-input/index.js +1 -1
  221. package/dist/page-layout/index.cjs +12 -12
  222. package/dist/page-layout/index.js +11 -11
  223. package/dist/pagination/index.cjs +8 -8
  224. package/dist/pagination/index.js +7 -7
  225. package/dist/popover/index.cjs +3 -2
  226. package/dist/popover/index.cjs.map +1 -1
  227. package/dist/popover/index.js +2 -1
  228. package/dist/radio/index.cjs +2 -2
  229. package/dist/radio/index.js +1 -1
  230. package/dist/segmented-control/index.cjs +4 -4
  231. package/dist/segmented-control/index.js +3 -3
  232. package/dist/select/index.cjs +6 -6
  233. package/dist/select/index.d.cts +3 -3
  234. package/dist/select/index.d.ts +3 -3
  235. package/dist/select/index.js +5 -5
  236. package/dist/skeleton/index.cjs +2 -2
  237. package/dist/skeleton/index.js +1 -1
  238. package/dist/snackbar/index.cjs +5 -5
  239. package/dist/snackbar/index.d.cts +4 -0
  240. package/dist/snackbar/index.d.ts +4 -0
  241. package/dist/snackbar/index.js +4 -4
  242. package/dist/split-button/index.cjs +6 -6
  243. package/dist/split-button/index.js +5 -5
  244. package/dist/table/index.cjs +14 -14
  245. package/dist/table/index.js +13 -13
  246. package/dist/tabs/index.cjs +3 -3
  247. package/dist/tabs/index.js +2 -2
  248. package/dist/tag-input/index.cjs +6 -6
  249. package/dist/tag-input/index.js +5 -5
  250. package/dist/textarea/index.cjs +4 -4
  251. package/dist/textarea/index.js +3 -3
  252. package/dist/theme-editor/index.cjs +11 -11
  253. package/dist/theme-editor/index.js +10 -10
  254. package/dist/theme-provider/index.cjs +3 -3
  255. package/dist/theme-provider/index.js +2 -2
  256. package/dist/toolbar/index.cjs +8 -8
  257. package/dist/toolbar/index.js +7 -7
  258. package/dist/tooltip/index.cjs +3 -2
  259. package/dist/tooltip/index.cjs.map +1 -1
  260. package/dist/tooltip/index.js +2 -1
  261. package/dist/virtual-list/index.cjs +3 -3
  262. package/dist/virtual-list/index.js +2 -2
  263. package/package.json +8 -2
  264. package/dist/chunk-25PSIDPC.js.map +0 -1
  265. package/dist/chunk-2WP27QAN.js.map +0 -1
  266. package/dist/chunk-3E7TKBQI.cjs.map +0 -1
  267. package/dist/chunk-3EPND5M2.cjs.map +0 -1
  268. package/dist/chunk-3RLGH45H.js.map +0 -1
  269. package/dist/chunk-4MZ7HEQC.cjs.map +0 -1
  270. package/dist/chunk-4ZWAJTNH.js.map +0 -1
  271. package/dist/chunk-55J4LEKT.cjs.map +0 -1
  272. package/dist/chunk-5HUZYY54.js.map +0 -1
  273. package/dist/chunk-5WH3JMR5.js.map +0 -1
  274. package/dist/chunk-6A2ZKVST.cjs.map +0 -1
  275. package/dist/chunk-75VXB3H2.cjs.map +0 -1
  276. package/dist/chunk-CJGFRPAS.cjs.map +0 -1
  277. package/dist/chunk-CJVC3A4O.js.map +0 -1
  278. package/dist/chunk-CUJBNRJJ.cjs +0 -38
  279. package/dist/chunk-CUJBNRJJ.cjs.map +0 -1
  280. package/dist/chunk-EAJHEYZM.js +0 -38
  281. package/dist/chunk-EAJHEYZM.js.map +0 -1
  282. package/dist/chunk-EL3YXTOW.cjs.map +0 -1
  283. package/dist/chunk-FKXKBJUI.cjs.map +0 -1
  284. package/dist/chunk-FXZJL2AM.cjs.map +0 -1
  285. package/dist/chunk-H6CWTMAH.js.map +0 -1
  286. package/dist/chunk-HI6WCX4T.cjs.map +0 -1
  287. package/dist/chunk-IJ4DXG7D.cjs.map +0 -1
  288. package/dist/chunk-IYWAZEFI.cjs.map +0 -1
  289. package/dist/chunk-J5RMLZ4P.cjs.map +0 -1
  290. package/dist/chunk-JRLUZDKA.js.map +0 -1
  291. package/dist/chunk-KBGFDHKD.cjs.map +0 -1
  292. package/dist/chunk-L5Q6UDY3.js +0 -69
  293. package/dist/chunk-L5Q6UDY3.js.map +0 -1
  294. package/dist/chunk-LVHZBCTA.cjs.map +0 -1
  295. package/dist/chunk-M7PF4FKQ.js.map +0 -1
  296. package/dist/chunk-NEYLNRVG.cjs.map +0 -1
  297. package/dist/chunk-O3I3RBZJ.js.map +0 -1
  298. package/dist/chunk-O3LG7B2J.cjs.map +0 -1
  299. package/dist/chunk-OAK7HFOJ.js.map +0 -1
  300. package/dist/chunk-OAPOGYA5.cjs.map +0 -1
  301. package/dist/chunk-PR7CVQ4R.js.map +0 -1
  302. package/dist/chunk-PY2376XI.js.map +0 -1
  303. package/dist/chunk-Q6ICDKQZ.cjs.map +0 -1
  304. package/dist/chunk-QQIALSTX.cjs.map +0 -1
  305. package/dist/chunk-QUDVJUZU.js.map +0 -1
  306. package/dist/chunk-QZX3A4FX.js.map +0 -1
  307. package/dist/chunk-R3WPVTGY.cjs.map +0 -1
  308. package/dist/chunk-RYOQYYDO.js.map +0 -1
  309. package/dist/chunk-S4S4LB63.js.map +0 -1
  310. package/dist/chunk-T2KB3DJJ.js.map +0 -1
  311. package/dist/chunk-TEVZHN5I.cjs.map +0 -1
  312. package/dist/chunk-TJC7UELT.js.map +0 -1
  313. package/dist/chunk-UOOGS6N7.cjs.map +0 -1
  314. package/dist/chunk-VRQBDTLT.cjs.map +0 -1
  315. package/dist/chunk-WFEORBLC.cjs.map +0 -1
  316. package/dist/chunk-WWYJNWJY.cjs +0 -69
  317. package/dist/chunk-WWYJNWJY.cjs.map +0 -1
  318. package/dist/chunk-YDPEHUMH.js.map +0 -1
  319. package/dist/chunk-YV57RSPD.js.map +0 -1
  320. package/dist/chunk-Z5U4NJTW.cjs.map +0 -1
  321. package/dist/chunk-ZNWIHUHZ.cjs.map +0 -1
  322. /package/dist/{chunk-BHVHM66Z.js.map → chunk-BBU7QG7L.js.map} +0 -0
  323. /package/dist/{chunk-O2AACVSF.js.map → chunk-BO4LTIJI.js.map} +0 -0
  324. /package/dist/{chunk-KNQ6NN6L.js.map → chunk-CP335H4Q.js.map} +0 -0
  325. /package/dist/{chunk-FALDCW2E.js.map → chunk-DMOZURHV.js.map} +0 -0
  326. /package/dist/{chunk-NWJ6NETE.js.map → chunk-EWTMX4DK.js.map} +0 -0
  327. /package/dist/{chunk-QIITXVRG.js.map → chunk-FPLX5FIK.js.map} +0 -0
  328. /package/dist/{chunk-KQJ3IAFT.js.map → chunk-PG2EII6U.js.map} +0 -0
  329. /package/dist/{chunk-WKDT2XFQ.js.map → chunk-SFRILDXY.js.map} +0 -0
  330. /package/dist/{chunk-ATL7325T.js.map → chunk-VRNCXYEP.js.map} +0 -0
  331. /package/dist/{chunk-L23WWO3D.js.map → chunk-X6UKU3C3.js.map} +0 -0
  332. /package/dist/{chunk-2QEKSRVX.js.map → chunk-YIBPOLNN.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-UOOGS6N7.cjs","../src/components/DataGrid/DataGrid.component.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-UOOGS6N7.cjs","sourcesContent":[null,"import React, { useState, useEffect, useRef, useCallback, useMemo, useLayoutEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport {\n GripVertical,\n ArrowUp,\n ArrowDown,\n ChevronsUpDown,\n ChevronRight,\n Plus,\n AlignJustify,\n Check,\n FolderOpen,\n SearchX,\n MoreVertical,\n} from 'lucide-react';\nimport {\n DndContext,\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n closestCenter,\n} from '@dnd-kit/core';\nimport type { DragEndEvent } from '@dnd-kit/core';\nimport {\n SortableContext,\n verticalListSortingStrategy,\n useSortable,\n arrayMove,\n} from '@dnd-kit/sortable';\nimport { CSS } from '@dnd-kit/utilities';\nimport { Button } from '../Button';\nimport { SplitButton } from '../SplitButton';\nimport { Input } from '../Input/Input.component';\nimport { Select } from '../Select/Select.component';\nimport { Combobox } from '../Combobox';\nimport { SegmentedControl } from '../SegmentedControl';\nimport { Checkbox } from '../Checkbox/Checkbox.component';\nimport { Spinner } from '../Spinner/Spinner.component';\nimport { Pagination } from '../Pagination';\nimport { EmptyState } from '../EmptyState';\nimport { Dropdown } from '../Dropdown';\nimport { Menu } from '../Menu';\nimport type { MenuItemType } from '../Menu';\nimport { TableFiltersDropdown } from '../Table/TableFiltersDropdown.component';\nimport type { FilterValue, TableColumn, TableFilters } from '../Table/Table.types';\nimport type {\n DataGridProps,\n DataGridActionsColumn,\n DataGridCellType,\n DataGridFilterField,\n DataGridQuickFilter,\n DataGridSelectOption,\n EditingCell,\n} from './DataGrid.types';\nimport { renderIcon, devWarn } from '../../utils';\nimport type { RowKey } from '../../utils';\nimport './DataGrid.scss';\n\n// Minimum usable widths (px) per column, summed into the automatic card-view\n// threshold so a grid with many columns doesn't have to overflow horizontally\n// before `cardViewBreakpoint` (a single, hand-tuned number) is reached. See\n// `isCardView` below.\n//\n// The system columns and the actions column are counted at their real, fixed\n// width rather than as a full data column: they never grow, so charging them\n// 100px each overstated the grid's minimum and flipped grids into card view\n// while they still had room to spare. Keep in sync with DataGrid.scss (and\n// with Table's own copy of these, which must agree for the two siblings to\n// switch over at the same point).\nconst MIN_DATA_COLUMN_WIDTH_PX = 100;\nconst ACTIONS_COLUMN_WIDTH_PX = 48;\nconst CHECKBOX_COLUMN_WIDTH_PX = 40;\nconst ROW_NUMBER_COLUMN_WIDTH_PX = 40;\nconst DRAG_HANDLE_COLUMN_WIDTH_PX = 32;\nconst EXPAND_COLUMN_WIDTH_PX = 32;\n\n// Container width (px) below which the toolbar's own buttons drop their labels\n// and render icon-only (with tooltips), and quick filters stretch to one per\n// row. Measured off the container, not the viewport, so a grid in a narrow pane\n// compacts too.\n//\n// Compaction only ever applies to what this component renders itself. A\n// consumer's `bulkActions` are rendered exactly as given and rely on the\n// toolbar wrapping instead - which is the part that actually guarantees\n// nothing overlaps, since arbitrary content can always be wider than any\n// threshold. Same value in Table.\nconst COMPACT_TOOLBAR_WIDTH_PX = 560;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal column + row access\n//\n// The public `DataGridColumn<T>` is a discriminated union, which is what lets\n// a consumer's `key` be checked against `keyof T` and gives `renderCell` /\n// `renderEditor` / `validate` the field's own value type instead of `unknown`.\n//\n// Internally the grid walks `columns` generically: it probes members that\n// exist on only one variant (`validate` on value columns, `actions` on the\n// actions column) and looks row fields up by a runtime string key. Narrowing\n// the union at each of those ~30 sites would add a lot of noise for no added\n// safety - the union is enforced where it matters, at the consumer's call\n// site - so the grid widens it once, here, and the render code below is\n// unchanged from when `DataGridColumn` was a single flat interface.\n// ─────────────────────────────────────────────────────────────────────────────\ntype InternalColumn<T extends object> = Omit<DataGridActionsColumn<T>, 'type' | 'key'> & {\n /**\n * Widened to a required plain string. Only the actions variant may omit\n * `key`, and it never reaches a path that consumes one: it renders in its\n * own dedicated slot with a hard-coded `data-col-key=\"__actions__\"`, and is\n * filtered out of `dataColumns` / `sortedColumns`.\n */\n key: string;\n type?: DataGridCellType;\n editable?: boolean;\n required?: boolean;\n options?: DataGridSelectOption[];\n validate?: (value: unknown) => string | true;\n renderCell?: (value: unknown, row: T, rowIndex: number) => React.ReactNode;\n renderEditor?: (value: unknown, onChange: (v: unknown) => void, row: T) => React.ReactNode;\n};\n\n/** Reads a row field by a runtime string key - see `InternalColumn` above. */\nconst cellOf = <T extends object>(row: T, key: string): unknown =>\n (row as Record<string, unknown>)[key];\n\n// Value of the reset (\"All\") segment a quick filter's SegmentedControl always\n// prepends - a SegmentedControl has no empty state of its own, so it needs an\n// explicit segment standing for \"no filter\".\nconst QUICK_FILTER_RESET_VALUE = '';\n\n// Whether a filter value is meaningful enough to keep in the filter state.\n// Empty values are *deleted* from it rather than stored as '' / [], so the\n// key count stays an accurate \"are any filters active?\" signal - that's what\n// drives the \"No results found\" empty state and the dropdown's active\n// indicator.\nconst hasFilterValue = (value: FilterValue): boolean => {\n if (value === undefined || value === null || value === '') return false;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === 'object' && 'start' in value) return !!(value.start || value.end);\n return true;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// SortableTableRow - wraps a <tr> with dnd-kit sortable behaviour.\n//\n// Render-prop pattern: passes `dragHandleProps` down to whichever child element\n// should be the drag handle (typically the grip icon's <span>), so the <tr>\n// itself owns the DOM ref + layout-transform while the handle owns the listeners.\n// Pass `disabled={true}` when draggableRows is off - useSortable is always\n// called unconditionally to satisfy the Rules of Hooks.\n// ─────────────────────────────────────────────────────────────────────────────\nfunction SortableTableRow({\n id,\n disabled,\n className,\n children,\n}: {\n id: string;\n disabled?: boolean;\n className?: string;\n children: (dragHandleProps: object, isDragging: boolean) => React.ReactNode;\n}): React.ReactElement {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id,\n disabled,\n });\n\n return (\n <tr\n ref={setNodeRef}\n className={[className, isDragging ? 'eidos-datagrid-row--dragging' : '']\n .filter(Boolean)\n .join(' ')}\n style={{\n transform: CSS.Transform.toString(transform),\n transition: transition ?? undefined,\n opacity: isDragging ? 0.4 : 1,\n }}\n {...attributes}\n >\n {children(listeners ?? {}, isDragging)}\n </tr>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// useExpandAnimation - drives a row's expanded-content open/close transition.\n// Mirrors the max-height technique AccordionItem uses, so row expansion\n// animates consistently with Accordion elsewhere in the library - but also\n// keeps tracking the content's height for as long as the panel stays open\n// (via ResizeObserver on the unclipped inner node), not just once at the\n// moment it opens. Content whose height changes after opening - e.g. an\n// async fetch resolving into more/less content than a loading placeholder -\n// would otherwise stay clipped at whatever height was measured on open.\n//\n// Content stays mounted through the close animation (so it can transition\n// shut instead of vanishing instantly), but is never mounted before the row\n// is actually opened for the first time - so `renderExpandedContent` (which\n// may kick off a data fetch) never runs for a row nobody has expanded.\n//\n// Must be called from its own per-row component (not inline in a .map()\n// callback) so the number of hook calls stays constant across renders\n// regardless of how many rows are currently displayed.\n// ─────────────────────────────────────────────────────────────────────────────\nfunction useExpandAnimation(isOpen: boolean): {\n mounted: boolean;\n maxHeight: number;\n outerRef: React.RefObject<HTMLDivElement | null>;\n innerRef: React.RefObject<HTMLDivElement | null>;\n handleTransitionEnd: () => void;\n} {\n // outerRef: the clipped, max-height/overflow-hidden wrapper actually\n // animated - its own rendered height is pinned by `maxHeight` below, so it\n // can't be the thing observed for size changes (see innerRef).\n const outerRef = useRef<HTMLDivElement>(null);\n // innerRef: the unconstrained content itself - always laid out at its\n // natural height regardless of the outer wrapper's clipping, so its size\n // genuinely changes when its content does. That's what ResizeObserver\n // needs to be watching.\n const innerRef = useRef<HTMLDivElement>(null);\n const [maxHeight, setMaxHeight] = useState(0);\n const [mounted, setMounted] = useState(isOpen);\n\n useEffect(() => {\n if (isOpen) setMounted(true);\n }, [isOpen]);\n\n useEffect(() => {\n if (!mounted) return;\n\n if (!isOpen) {\n setMaxHeight(0);\n return;\n }\n\n const node = innerRef.current;\n if (!node) return;\n\n const update = () => setMaxHeight(node.scrollHeight);\n update();\n\n const observer = new ResizeObserver(update);\n observer.observe(node);\n return () => observer.disconnect();\n }, [isOpen, mounted]);\n\n const handleTransitionEnd = useCallback(() => {\n if (!isOpen) setMounted(false);\n }, [isOpen]);\n\n return { mounted, maxHeight, outerRef, innerRef, handleTransitionEnd };\n}\n\n// Table mode: one extra full-width <tr> rendered directly after an expanded\n// row - see `expandable`/`renderExpandedContent` on DataGridProps.\nfunction ExpandedTableRow({\n isOpen,\n colSpan,\n render,\n}: {\n isOpen: boolean;\n colSpan: number;\n render: () => React.ReactNode;\n}): React.ReactElement | null {\n const { mounted, maxHeight, outerRef, innerRef, handleTransitionEnd } =\n useExpandAnimation(isOpen);\n if (!mounted) return null;\n\n return (\n <tr className=\"eidos-datagrid-expanded-row\">\n <td colSpan={colSpan} className=\"eidos-datagrid-expanded-cell\">\n <div\n ref={outerRef}\n className=\"eidos-datagrid-expand-panel\"\n style={{ maxHeight: `${maxHeight}px` }}\n onTransitionEnd={handleTransitionEnd}\n >\n <div ref={innerRef} className=\"eidos-datagrid-expand-panel-inner\">\n {render()}\n </div>\n </div>\n </td>\n </tr>\n );\n}\n\n// Card mode: the same animated panel, appended inside the card itself below\n// its fields.\nfunction ExpandedCardPanel({\n isOpen,\n render,\n}: {\n isOpen: boolean;\n render: () => React.ReactNode;\n}): React.ReactElement | null {\n const { mounted, maxHeight, outerRef, innerRef, handleTransitionEnd } =\n useExpandAnimation(isOpen);\n if (!mounted) return null;\n\n return (\n <div\n ref={outerRef}\n className=\"eidos-datagrid-expand-panel\"\n style={{ maxHeight: `${maxHeight}px` }}\n onTransitionEnd={handleTransitionEnd}\n >\n <div ref={innerRef} className=\"eidos-datagrid-expand-panel-inner\">\n {render()}\n </div>\n </div>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Inner implementation (generic function so we can assign .displayName cleanly)\n// ─────────────────────────────────────────────────────────────────────────────\nfunction DataGridInner<T extends object>({\n // ── Existing props ─────────────────────────────────────────────────────────\n columns: columnsProp,\n data,\n rowKey,\n onChange,\n onRowAdd,\n editable = false,\n loading = false,\n emptyText = 'No data available',\n className,\n stickyHeader = false,\n maxHeight,\n showRowNumbers = false,\n draggableRows = false,\n onRowReorder,\n // ── Sorting ────────────────────────────────────────────────────────────────\n currentSort,\n onSortChange,\n // ── Filtering ──────────────────────────────────────────────────────────────\n showFilters = false,\n filterConfig = [],\n filters,\n onFiltersChange,\n // ── Quick filters ──────────────────────────────────────────────────────────\n quickFilters = [],\n // ── Pagination ─────────────────────────────────────────────────────────────\n showPagination = false,\n pageSize = 10,\n pageSizeOptions,\n totalRows: totalRowsProp,\n onPageChange,\n // ── Row selection + bulk actions ───────────────────────────────────────────\n selectable = false,\n selectedRows: controlledSelectedRows,\n defaultSelectedRows,\n onSelectionChange,\n selectAllScope = 'all',\n onSelectAllMatching,\n bulkActions,\n // ── Row expansion ────────────────────────────────────────────────────────────\n expandable = false,\n renderExpandedContent,\n isRowExpandable,\n expandMultiple = true,\n expandedRows: controlledExpandedRows,\n defaultExpandedRows,\n onExpandedRowsChange,\n // ── Display ────────────────────────────────────────────────────────────────\n density = 'comfortable',\n showDensity = false,\n // ── Card view ────────────────────────────────────────────────────────────────\n hasCardView = true,\n cardViewBreakpoint = 640,\n cardMinWidth = 280,\n}: DataGridProps<T>): React.ReactElement {\n // See `InternalColumn` above - a cast, not a copy, so the identity\n // comparisons below (`c !== actionsColumn`) still hold.\n const columns = columnsProp as InternalColumn<T>[];\n // `rowKey` is `RowKey<T>` publicly, so the `'id'` default can't live in the\n // destructuring: `'id'` isn't a field of every `T`.\n const rowKeyField: string = rowKey ?? 'id';\n // ── State + paired refs (editing - preserved exactly) ─────────────────────\n // We keep a ref alongside each piece of mutable state so that event\n // handlers registered in effects can always read the *latest* value without\n // stale-closure bugs, while the state variables drive re-renders normally.\n\n const [localData, _setLocalData] = useState<T[]>(data);\n const localDataRef = useRef<T[]>(data);\n\n const [editingCell, _setEditingCell] = useState<EditingCell>(null);\n const editingCellRef = useRef<EditingCell>(null);\n\n const [editValue, _setEditValue] = useState<unknown>(null);\n const editValueRef = useRef<unknown>(null);\n\n const [editError, setEditError] = useState<string | null>(null);\n\n // ── Validation error tooltip (portaled) ─────────────────────────────────────\n // Positioned via a portal into document.body rather than `position: absolute`\n // inside the cell - a cell on the last visible row would otherwise place the\n // tooltip past the scroll container's bottom edge, which still counts toward\n // that container's scrollable content size and forces an unwanted scrollbar\n // just to reveal a tooltip nobody asked to scroll to. Portaling escapes the\n // scroll container's box entirely, the same way Dropdown's own content does.\n // HTMLElement (not HTMLTableCellElement) so the same ref can anchor either\n // a <td> in table mode or a card field <div> in card view - only\n // `getBoundingClientRect()` is ever called on it.\n const errorTooltipAnchorRef = useRef<HTMLElement | null>(null);\n const [errorTooltipPosition, setErrorTooltipPosition] = useState<{\n top: number;\n left: number;\n } | null>(null);\n const activeCellError = editingCell && editError ? editError : null;\n\n useLayoutEffect(() => {\n if (!activeCellError) {\n setErrorTooltipPosition(null);\n return;\n }\n\n const updatePosition = () => {\n const node = errorTooltipAnchorRef.current;\n if (!node) return;\n const rect = node.getBoundingClientRect();\n setErrorTooltipPosition({ top: rect.bottom + 4, left: rect.left });\n };\n\n updatePosition();\n\n // Capture phase catches scroll events from any scrollable ancestor\n // (e.g. this grid's own scroll container), not just window/document -\n // `scroll` doesn't bubble, so a plain (bubble-phase) listener on window\n // would miss it entirely.\n window.addEventListener('resize', updatePosition);\n window.addEventListener('scroll', updatePosition, true);\n return () => {\n window.removeEventListener('resize', updatePosition);\n window.removeEventListener('scroll', updatePosition, true);\n };\n }, [activeCellError]);\n\n // ── New state ──────────────────────────────────────────────────────────────\n // Density (internal when showDensity=true, otherwise respects the prop)\n const [internalDensity, setInternalDensity] = useState<'compact' | 'comfortable' | 'spacious'>(\n density,\n );\n const effectiveDensity = showDensity ? internalDensity : density;\n\n // Sorting (client-side internal state)\n const [internalSort, setInternalSort] = useState<{\n key: string;\n direction: 'asc' | 'desc';\n } | null>(null);\n\n // Filtering (client-side internal state), seeded from any quick filter\n // `defaultValue`s. Lazy, so it only reads `quickFilters` on mount - later\n // edits to the array's default values don't yank a filter the user has\n // since changed. In server-side mode this state is unused entirely, which\n // is why `defaultValue` is documented as client-side only.\n const [internalFilters, setInternalFilters] = useState<TableFilters>(() => {\n const seeded: TableFilters = {};\n quickFilters.forEach(({ key, defaultValue }) => {\n if (hasFilterValue(defaultValue)) seeded[key] = defaultValue;\n });\n return seeded;\n });\n\n // Pagination\n const [currentPage, setCurrentPage] = useState(1);\n const [currentPageSize, setCurrentPageSize] = useState(pageSize);\n\n // Selection\n const isControlledSelection = controlledSelectedRows !== undefined;\n const [internalSelectedKeys, setInternalSelectedKeys] = useState<Set<string>>(\n () => new Set(defaultSelectedRows ?? []),\n );\n\n // Row expansion\n const isControlledExpansion = controlledExpandedRows !== undefined;\n const [internalExpandedKeys, setInternalExpandedKeys] = useState<Set<string>>(\n () => new Set(defaultExpandedRows ?? []),\n );\n\n // Card view: measured off the grid's own container width (not the\n // viewport) via ResizeObserver, so it responds correctly even when the\n // grid sits in a narrow sidebar/split-pane on an otherwise wide screen.\n // Starts `null` (unmeasured) rather than 0, so the table is what renders\n // for one frame on mount instead of briefly flashing cards.\n const [containerWidth, setContainerWidth] = useState<number | null>(null);\n const resizeObserverRef = useRef<ResizeObserver | null>(null);\n // A callback ref (not `useRef` + a `useLayoutEffect` keyed on `hasCardView`)\n // so this fires whenever the container DOM node actually becomes\n // available, not just when `hasCardView` changes. A plain effect only\n // reruns on its own dependency changing - it does NOT rerun just because\n // a *different* conditional branch attaches the ref for the first time.\n // The `loading` early-return below renders its own container div; any\n // consumer whose `loading` prop starts `true` on mount (e.g. fetching\n // data asynchronously, so it can never resolve before the first render)\n // would mount that div first, find `containerRef.current` still null, and\n // then never re-run the effect once `loading` flips to `false` and the\n // real container mounts - `hasCardView` itself never changed, so nothing\n // triggered a re-run, leaving `containerWidth` (and therefore card view)\n // permanently stuck. A callback ref sidesteps that entirely: whichever\n // container div actually mounts, this runs for it.\n // Deliberately NOT gated on `hasCardView`: the same measurement also drives\n // `isCompactToolbar` below, which a grid with `hasCardView={false}` still\n // needs - it has a toolbar to fit into a narrow container either way.\n const setContainerRef = useCallback((el: HTMLDivElement | null) => {\n resizeObserverRef.current?.disconnect();\n resizeObserverRef.current = null;\n\n if (!el) return;\n\n const observer = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width;\n if (width != null) setContainerWidth(width);\n });\n observer.observe(el);\n resizeObserverRef.current = observer;\n }, []);\n // Narrowest width the grid can render at without overflowing: the sum of\n // every column's own minimum (see the constants above). Computed directly\n // from props, so it's available here without waiting on the `dataColumns`/\n // `actionsColumn` memos below - `columns` already includes the actions\n // column if any, matching how `totalCols` is derived further down.\n const minTableWidth = useMemo(\n () =>\n columns.reduce(\n (total, column) =>\n total + (column.type === 'actions' ? ACTIONS_COLUMN_WIDTH_PX : MIN_DATA_COLUMN_WIDTH_PX),\n (showRowNumbers ? ROW_NUMBER_COLUMN_WIDTH_PX : 0) +\n (draggableRows ? DRAG_HANDLE_COLUMN_WIDTH_PX : 0) +\n (selectable ? CHECKBOX_COLUMN_WIDTH_PX : 0) +\n (expandable ? EXPAND_COLUMN_WIDTH_PX : 0),\n ),\n [columns, showRowNumbers, draggableRows, selectable, expandable],\n );\n\n // Card view kicks in below the explicit `cardViewBreakpoint` OR once the\n // container is too narrow to fit the grid at that minimum - the latter\n // means a wide/many-column grid auto-switches without the consumer having\n // to hand-calculate a breakpoint for it.\n const isCardView =\n hasCardView &&\n containerWidth !== null &&\n (containerWidth < cardViewBreakpoint || containerWidth < minTableWidth);\n\n // Independent of card view on purpose: card view can also trigger on column\n // count in a container that's still plenty wide for labelled buttons, and a\n // `hasCardView={false}` grid still needs its toolbar to fit.\n const isCompactToolbar = containerWidth !== null && containerWidth < COMPACT_TOOLBAR_WIDTH_PX;\n\n // Card view header/subheader: only the first matching column is honored\n // (see DataGridColumn.cardHeader/cardSubheader).\n const cardHeaderColumn = useMemo(() => columns.find((c) => c.cardHeader), [columns]);\n const cardSubheaderColumn = useMemo(() => columns.find((c) => c.cardSubheader), [columns]);\n // The row-actions column, if any - only the first `type: 'actions'` column\n // is honored (see DataGridColumn.actions). Rendered in its own dedicated\n // slot (far right in table mode, top-right of the card in card view),\n // never as a normal data column.\n const actionsColumn = useMemo(() => columns.find((c) => c.type === 'actions'), [columns]);\n // Every column that actually renders as data - excludes the actions\n // column entirely (it's never a normal cell) from both table columns and\n // pinning/filtering/validation logic.\n const dataColumns = useMemo(\n () => columns.filter((c) => c !== actionsColumn),\n [columns, actionsColumn],\n );\n // A card with no system controls, no heading and no actions column has\n // nothing to put in its top bar - rendering it anyway leaves a dead strip\n // and a divider above the first field. Table applies the same guard.\n const hasCardToolbar =\n selectable ||\n showRowNumbers ||\n expandable ||\n !!cardHeaderColumn ||\n !!cardSubheaderColumn ||\n !!actionsColumn;\n\n // Remaining columns render as label:value field rows, in declaration order.\n const cardFieldColumns = useMemo(\n () => dataColumns.filter((c) => c !== cardHeaderColumn && c !== cardSubheaderColumn),\n [dataColumns, cardHeaderColumn, cardSubheaderColumn],\n );\n\n // Pinned columns offset map (computed in useLayoutEffect)\n const theadRef = useRef<HTMLTableSectionElement>(null);\n const [pinnedOffsets, setPinnedOffsets] = useState<\n Map<string, { side: 'left' | 'right'; offset: number }>\n >(new Map());\n\n // ── Synced setters (preserved exactly) ────────────────────────────────────\n const setLocalData = useCallback((v: T[]) => {\n localDataRef.current = v;\n _setLocalData(v);\n }, []);\n\n const setEditingCell = useCallback((v: EditingCell) => {\n editingCellRef.current = v;\n _setEditingCell(v);\n }, []);\n\n const setEditValue = useCallback((v: unknown) => {\n editValueRef.current = v;\n _setEditValue(v);\n }, []);\n\n // ── Sync when the data prop changes externally (preserved) ─────────────────\n useEffect(() => {\n setLocalData(data);\n }, [data, setLocalData]);\n\n // ── Active sort / filters (derived) ───────────────────────────────────────\n // Server-side sort: caller controls currentSort + onSortChange.\n // Client-side sort: we manage internalSort.\n const activeSort = currentSort ?? internalSort;\n\n // Server-side filter: caller controls filters + onFiltersChange.\n // Client-side filter: we manage internalFilters.\n const activeFilters: TableFilters = useMemo(\n () => (onFiltersChange ? (filters ?? {}) : internalFilters),\n [onFiltersChange, filters, internalFilters],\n );\n\n // ── Selection derived state ────────────────────────────────────────────────\n const selectedSet = useMemo(\n () =>\n new Set(isControlledSelection ? (controlledSelectedRows ?? []) : [...internalSelectedKeys]),\n [isControlledSelection, controlledSelectedRows, internalSelectedKeys],\n );\n\n const hasSelection = selectedSet.size > 0;\n\n // Row objects for selected keys, kept across changes to `data`. Filtering\n // the current rows alone silently handed `bulkActions` and\n // `onSelectionChange` fewer rows than the selection count claimed - and\n // under server-side pagination, none at all once the user paged away from\n // everything they had selected. Always prefers the row currently in `data`\n // over the cached copy, so an edited row is never stale.\n const selectedRowCacheRef = useRef(new Map<string, T>());\n\n const rowsForKeys = useCallback(\n (keys: string[]): T[] => {\n const current = new Map<string, T>();\n localDataRef.current.forEach((row, idx) =>\n current.set(String(cellOf(row, rowKeyField) ?? idx), row),\n );\n const cache = selectedRowCacheRef.current;\n return keys\n .map((key) => current.get(key) ?? cache.get(key))\n .filter((row): row is T => row !== undefined);\n },\n [rowKeyField],\n );\n\n // Prunes as well as fills: an entry only survives while its key is still\n // selected, so the cache can't grow without bound.\n useEffect(() => {\n const cache = selectedRowCacheRef.current;\n const current = new Map<string, T>();\n localData.forEach((row, idx) => current.set(String(cellOf(row, rowKeyField) ?? idx), row));\n const next = new Map<string, T>();\n selectedSet.forEach((key) => {\n const row = current.get(key) ?? cache.get(key);\n if (row) next.set(key, row);\n });\n selectedRowCacheRef.current = next;\n }, [localData, selectedSet, rowKeyField]);\n\n const selectedItems = useMemo(\n () => rowsForKeys([...selectedSet]),\n // `localData` isn't read directly here - `rowsForKeys` reads it through a\n // ref - but it must still retrigger this when the loaded rows change.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [rowsForKeys, selectedSet, localData],\n );\n\n // ── Row expansion derived state ─────────────────────────────────────────────\n const expandedSet = useMemo(\n () =>\n new Set(isControlledExpansion ? (controlledExpandedRows ?? []) : [...internalExpandedKeys]),\n [isControlledExpansion, controlledExpandedRows, internalExpandedKeys],\n );\n\n // ── Quick filters: key ownership ───────────────────────────────────────────\n // Quick filters and the filter dropdown share one TableFilters object, so\n // each side has to own a disjoint set of keys - see `dropdownFilters` below\n // for why the dropdown can't just be handed the whole thing.\n const quickFilterKeys = useMemo(() => new Set(quickFilters.map((f) => f.key)), [quickFilters]);\n\n // ── Filter dropdown adapter: DataGridFilterField → TableColumn ─────────────\n // TableFiltersDropdown expects TableColumn<T>[]; we project our (decoupled,\n // column-independent) filter schema onto that shape. A key already claimed\n // by a quick filter is dropped: the quick filter is the always-visible\n // control, and two controls writing one key would each clobber the other.\n const filterDropdownColumns = useMemo((): TableColumn<T>[] => {\n return filterConfig\n .filter((field) => !quickFilterKeys.has(field.key))\n .map(\n (field: DataGridFilterField<T>) =>\n ({\n key: field.key,\n label: field.label,\n filterable: true,\n filterType: field.filterType,\n filterOptions: field.filterOptions,\n dateFilterMode: field.dateFilterMode,\n }) as TableColumn<T>,\n );\n }, [filterConfig, quickFilterKeys]);\n\n const dropdownFilterKeys = useMemo(\n () => new Set(filterDropdownColumns.map((col) => String(col.key))),\n [filterDropdownColumns],\n );\n\n // The dropdown gets only the keys it owns, never the whole filter object.\n // It mirrors every key it's handed into a filter row, so a key it has no\n // matching column for (a quick filter's, or a server-side caller's filter\n // on an unlisted field) would render as a blank, un-removable \"Select\n // column\" row - and its Apply, which emits its complete set, would drop\n // that value on the floor.\n const dropdownFilters = useMemo(\n () =>\n Object.fromEntries(\n Object.entries(activeFilters).filter(([key]) => dropdownFilterKeys.has(key)),\n ),\n [activeFilters, dropdownFilterKeys],\n );\n\n // ── Client-side filtering ──────────────────────────────────────────────────\n const filteredData = useMemo(() => {\n // Server-side mode: parent is responsible for filtering; use data as-is.\n if (onFiltersChange) return localData;\n\n const filterEntries = Object.entries(activeFilters).filter(([, val]) => {\n if (val === undefined || val === null || val === '') return false;\n if (Array.isArray(val) && val.length === 0) return false;\n return true;\n });\n\n if (filterEntries.length === 0) return localData;\n\n return localData.filter((row) =>\n filterEntries.every(([key, filterValue]) => {\n const cellValue = cellOf(row, key);\n\n if (Array.isArray(filterValue)) {\n return filterValue.includes(String(cellValue ?? ''));\n }\n if (typeof filterValue === 'boolean') {\n return Boolean(cellValue) === filterValue;\n }\n if (typeof filterValue === 'object' && filterValue !== null && 'start' in filterValue) {\n const { start, end } = filterValue as { start: string | null; end: string | null };\n const dateStr = String(cellValue ?? '');\n if (start && dateStr < start) return false;\n if (end && dateStr > end) return false;\n return true;\n }\n // Default: case-insensitive text contains\n return String(cellValue ?? '')\n .toLowerCase()\n .includes(String(filterValue).toLowerCase());\n }),\n );\n }, [localData, activeFilters, onFiltersChange]);\n\n // ── Client-side sorting ────────────────────────────────────────────────────\n const sortedData = useMemo(() => {\n // Server-side mode or no active sort: use data as-is.\n if (onSortChange || !activeSort) return filteredData;\n\n return [...filteredData].sort((a, b) => {\n const aVal = cellOf(a, activeSort.key);\n const bVal = cellOf(b, activeSort.key);\n\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return 1;\n if (bVal == null) return -1;\n\n const compare =\n typeof aVal === 'number' && typeof bVal === 'number'\n ? aVal - bVal\n : String(aVal).localeCompare(String(bVal));\n\n return activeSort.direction === 'asc' ? compare : -compare;\n });\n }, [filteredData, activeSort, onSortChange]);\n\n // ── Client-side pagination ─────────────────────────────────────────────────\n const displayData = useMemo(() => {\n if (!showPagination) return sortedData;\n if (onPageChange) return sortedData; // Server-side: parent slices the data\n const start = (currentPage - 1) * currentPageSize;\n return sortedData.slice(start, start + currentPageSize);\n }, [sortedData, showPagination, currentPage, currentPageSize, onPageChange]);\n\n const totalItemCount = totalRowsProp ?? sortedData.length;\n const totalPages = Math.max(1, Math.ceil(totalItemCount / currentPageSize));\n\n // ── Select-all (scope-aware) ──────────────────────────────────────────────\n // Defined after `displayData` because `'page'` scope is exactly that set.\n const selectAllKeys = useMemo(\n () =>\n (selectAllScope === 'page' ? displayData : localData).map((row, idx) =>\n String(cellOf(row, rowKeyField) ?? idx),\n ),\n [selectAllScope, displayData, localData, rowKeyField],\n );\n\n const allSelected =\n selectAllKeys.length > 0 && selectAllKeys.every((key) => selectedSet.has(key));\n const someSelected = !allSelected && selectAllKeys.some((key) => selectedSet.has(key));\n\n // `'all'` can only ever select the rows the grid holds. With server-side\n // pagination that's one page, and the keys of rows it has never received\n // are unknowable here - warn rather than quietly selecting a page and\n // calling it \"all\".\n useEffect(() => {\n if (!selectable || selectAllScope !== 'all') return;\n if (totalItemCount <= localData.length) return;\n devWarn(\n `datagrid-select-all-scope:${localData.length}/${totalItemCount}`,\n `DataGrid: selectAllScope=\"all\" can only select the ${localData.length} row(s) currently ` +\n `loaded, but there are ${totalItemCount} in total. The keys of rows the grid has never ` +\n `received cannot be known here - use selectAllScope=\"page\" to make the scope explicit, ` +\n 'or resolve the full key set yourself and pass it as `selectedRows`.',\n );\n }, [selectable, selectAllScope, totalItemCount, localData.length]);\n\n // Reset to page 1 when filters change (client-side and server-side).\n // Depend on the serialized *value* of activeFilters, not the object\n // reference: in server-side mode activeFilters is derived straight from\n // the caller's `filters` prop, and callers commonly build that object\n // inline in render (a new reference every render) whether or not its\n // contents actually changed. Comparing by reference made this effect -\n // and the page reset it causes - fire on every unrelated parent\n // re-render (e.g. new page data arriving), not just on real filter\n // changes.\n const activeFiltersKey = useMemo(() => JSON.stringify(activeFilters), [activeFilters]);\n useEffect(() => {\n setCurrentPage(1);\n }, [activeFiltersKey]);\n\n // Reset to page 1 when client-side sort changes\n useEffect(() => {\n setCurrentPage(1);\n }, [internalSort]);\n\n // ── Pinned columns: measure th widths and compute sticky offsets ───────────\n // Mirrors the exact pattern used in Table.component.tsx.\n // System columns (sel, drag, rownum, actions) contribute to the offset\n // accumulator but are not themselves sticky-positioned.\n useLayoutEffect(() => {\n const hasPinned = columns.some((c) => c.pin);\n if (!hasPinned || !theadRef.current) {\n setPinnedOffsets((prev) => (prev.size > 0 ? new Map() : prev));\n return;\n }\n\n const headerRow = theadRef.current.querySelector('tr');\n if (!headerRow) return;\n\n const cells = Array.from(headerRow.querySelectorAll<HTMLTableCellElement>('th[data-col-key]'));\n const newOffsets = new Map<string, { side: 'left' | 'right'; offset: number }>();\n\n // Left-pinned: scan left-to-right\n // When any data column is pin:'left', auto-pin the system columns too so\n // the entire frozen-left group moves as a coherent unit.\n const hasLeftPin = columns.some((c) => c.pin === 'left');\n let leftAccum = 0;\n for (const cell of cells) {\n const key = cell.dataset.colKey!;\n if (key === '__expand__' || key === '__sel__' || key === '__drag__' || key === '__rownum__') {\n if (hasLeftPin) {\n newOffsets.set(key, { side: 'left', offset: leftAccum });\n }\n leftAccum += cell.offsetWidth;\n } else {\n const col = columns.find((c) => c.key === key);\n if (col?.pin === 'left') {\n newOffsets.set(key, { side: 'left', offset: leftAccum });\n leftAccum += cell.offsetWidth;\n }\n }\n }\n\n // Right-pinned: scan right-to-left\n let rightAccum = 0;\n for (let i = cells.length - 1; i >= 0; i--) {\n const cell = cells[i];\n const key = cell.dataset.colKey!;\n if (key === '__actions__') {\n rightAccum += cell.offsetWidth;\n } else {\n const col = columns.find((c) => c.key === key);\n if (col?.pin === 'right') {\n newOffsets.set(key, { side: 'right', offset: rightAccum });\n rightAccum += cell.offsetWidth;\n }\n }\n }\n\n setPinnedOffsets(newOffsets);\n }, [columns, selectable, draggableRows, showRowNumbers, expandable]);\n\n // ── Sticky-column helpers ──────────────────────────────────────────────────\n const getCellPinnedProps = useCallback(\n (colKey: string): { style: React.CSSProperties; className: string } => {\n const info = pinnedOffsets.get(colKey);\n if (!info) return { style: {}, className: '' };\n return {\n style: { position: 'sticky', [info.side]: info.offset, zIndex: 1 },\n className:\n info.side === 'left'\n ? 'eidos-datagrid-cell--pinned-left'\n : 'eidos-datagrid-cell--pinned-right',\n };\n },\n [pinnedOffsets],\n );\n\n const getHeaderPinnedProps = useCallback(\n (colKey: string): { style: React.CSSProperties; className: string } => {\n const info = pinnedOffsets.get(colKey);\n if (!info) return { style: {}, className: '' };\n return {\n // z-index 3: above both body sticky cells (1) and non-sticky header (1 from SCSS)\n style: { position: 'sticky', [info.side]: info.offset, zIndex: 3 },\n className:\n info.side === 'left'\n ? 'eidos-datagrid-header-cell--pinned-left'\n : 'eidos-datagrid-header-cell--pinned-right',\n };\n },\n [pinnedOffsets],\n );\n\n // ── Editing helpers (preserved exactly) ────────────────────────────────────\n const isCellEditable = useCallback(\n (col: InternalColumn<T>): boolean => {\n if (!editable) return false;\n if (col.type === 'readonly' || col.type === 'actions') return false;\n if (col.editable === false) return false;\n return true;\n },\n [editable],\n );\n\n const validateCell = useCallback((col: InternalColumn<T>, value: unknown): string | null => {\n if (col.required && (value === '' || value === null || value === undefined)) {\n return 'This field is required';\n }\n if (col.validate) {\n const result = col.validate(value);\n if (result !== true) return result as string;\n }\n return null;\n }, []);\n\n const cellErrorKey = (rowIndex: number, colKey: string) => `${rowIndex}:${colKey}`;\n\n // Cells with a validation error, keyed by `${rowIndex}:${colKey}` - derived\n // directly from the actual data rather than tracked as its own state, so a\n // cell that starts out invalid (e.g. seeded with an empty required field)\n // is flagged immediately on render instead of only after the user has\n // focused and committed it at least once. Recomputes whenever the data\n // actually changes, which already covers every case that needs it: typing\n // a fix and committing, an external `data` prop update, a row being added,\n // etc. Painted as a background tint on every render (see\n // `eidos-data-grid-cell--has-error` below); the more prominent border +\n // text (`eidos-data-grid-cell--error`) only shows while that specific cell\n // is the one being edited, via `editError` (see `startEdit`).\n const cellErrors = useMemo(() => {\n const errors: Record<string, string> = {};\n localData.forEach((row, rowIndex) => {\n for (const col of columns) {\n if (!isCellEditable(col)) continue;\n const error = validateCell(col, cellOf(row, col.key));\n if (error) errors[cellErrorKey(rowIndex, col.key)] = error;\n }\n });\n return errors;\n }, [localData, columns, isCellEditable, validateCell]);\n\n // ── Commit / discard ────────────────────────────────────────────────────────\n /**\n * Validates a cell's value and always persists it (even if invalid - see\n * `commitEdit` below for why). `cellErrors` above picks the result up\n * automatically once `localData` updates. Returns the validation error, if any.\n */\n const commitValue = useCallback(\n (rowIndex: number, colKey: string, value: unknown): string | null => {\n const col = columns.find((c) => c.key === colKey);\n if (!col) return null;\n\n const error = validateCell(col, value);\n\n const newData = localDataRef.current.map<T>((row, idx) =>\n idx === rowIndex ? ({ ...row, [colKey]: value } as T) : row,\n );\n setLocalData(newData);\n onChange?.(newData);\n\n return error;\n },\n [columns, onChange, setLocalData, validateCell],\n );\n\n /**\n * Commits the currently-editing cell and exits edit mode. Never blocks: an\n * invalid value is still saved and flagged via `cellErrors` (see above)\n * rather than trapping the user in the cell - a required field going empty\n * is exactly the case validation is there to catch and surface, not a\n * reason to freeze every other interaction with the grid until it's fixed.\n */\n const commitEdit = useCallback(() => {\n const cell = editingCellRef.current;\n if (!cell) return;\n\n const col = columns.find((c) => c.key === cell.colKey);\n if (!col) {\n setEditingCell(null);\n return;\n }\n\n commitValue(cell.rowIndex, cell.colKey, editValueRef.current);\n setEditingCell(null);\n setEditError(null);\n }, [columns, commitValue, setEditingCell]);\n\n const discardEdit = useCallback(() => {\n setEditingCell(null);\n setEditError(null);\n }, [setEditingCell]);\n\n // Keep a ref to the latest commit so the global handler never captures\n // a stale version, without having the effect re-run on every re-render.\n const commitEditRef = useRef(commitEdit);\n useEffect(() => {\n commitEditRef.current = commitEdit;\n }, [commitEdit]);\n\n // ── Global mousedown: commit on blur - i.e. any click that isn't on the\n // active editor itself ──────────────────────────────────────────────────\n // Using mousedown (fires before blur/click) lets us finalize the active\n // cell before a click elsewhere starts a new edit or does anything else.\n //\n // Deliberately NOT scoped to \"outside the grid container\" - a click on\n // another cell already commits via that cell's own `handleCellClick`\n // (which commits any in-flight edit before starting its own), but a grid\n // has plenty of space that isn't a cell at all: toolbar buttons, sort\n // headers, pagination, row actions, and - especially in card view - the\n // card's own padding, gaps, and labels. None of those have a handler that\n // commits the edit, so scoping this to \"outside the grid\" left the editor\n // stuck open for any click that landed inside the grid but outside an\n // actual cell. The only clicks that should NOT commit are ones still\n // interacting with the editor being edited (identified by its own\n // `--editing` class, shared by both the <td> and card versions) or its\n // portaled dropdown content (eidos-dropdown-content) - e.g. opening a\n // Select's own options list.\n useEffect(() => {\n if (!editingCell) return;\n\n const handleMouseDown = (e: MouseEvent) => {\n const target = e.target as Element;\n\n if (\n target.closest?.(\n '.eidos-data-grid-cell--editing, .eidos-datagrid-card-value--editing, .eidos-dropdown-content',\n )\n ) {\n return;\n }\n\n commitEditRef.current();\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [editingCell]);\n\n // ── Start editing ────────────────────────────────────────────────────────\n const startEdit = useCallback(\n (rowIndex: number, colKey: string, currentValue: unknown) => {\n const col = columns.find((c) => c.key === colKey);\n if (!col || !isCellEditable(col)) return;\n setEditingCell({ rowIndex, colKey });\n setEditValue(currentValue);\n // Re-focusing a cell that's already flagged shows its error\n // immediately, without needing to retype/re-trigger validation first.\n setEditError(cellErrors[cellErrorKey(rowIndex, colKey)] ?? null);\n },\n [columns, isCellEditable, setEditingCell, setEditValue, cellErrors],\n );\n\n // ── Cell click handler (preserved) ─────────────────────────────────────────\n const handleCellClick = useCallback(\n (rowIndex: number, col: InternalColumn<T>) => {\n const row = localDataRef.current[rowIndex];\n const value = row === undefined ? undefined : cellOf(row, col.key);\n\n // Checkbox cells: toggle immediately - no \"edit mode\" UI needed\n if (col.type === 'checkbox') {\n if (!editable || col.editable === false) return;\n const newData = localDataRef.current.map<T>((row, idx) =>\n idx === rowIndex ? ({ ...row, [col.key]: !value } as T) : row,\n );\n setLocalData(newData);\n onChange?.(newData);\n return;\n }\n\n if (!isCellEditable(col)) return;\n\n // Already editing this exact cell - do nothing\n const cell = editingCellRef.current;\n if (cell?.rowIndex === rowIndex && cell?.colKey === col.key) return;\n\n // Commit any in-flight edit first - never blocks navigation, see commitEdit.\n if (cell) commitEdit();\n\n startEdit(rowIndex, col.key, value);\n },\n [editable, isCellEditable, commitEdit, startEdit, setLocalData, onChange],\n );\n\n // ── Keyboard navigation inside an editing cell (preserved) ─────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent, rowIndex: number, colKey: string) => {\n if (e.key === 'Escape') {\n e.preventDefault();\n discardEdit();\n return;\n }\n\n if (e.key === 'Enter') {\n e.preventDefault();\n commitEdit();\n return;\n }\n\n if (e.key === 'Tab') {\n e.preventDefault();\n\n // Flat list of editable cells in DOM order (over the full dataset)\n const editableCells: Array<{ rowIndex: number; colKey: string }> = [];\n for (let ri = 0; ri < localDataRef.current.length; ri++) {\n for (const c of columns) {\n if (isCellEditable(c)) {\n editableCells.push({ rowIndex: ri, colKey: c.key });\n }\n }\n }\n\n const idx = editableCells.findIndex((c) => c.rowIndex === rowIndex && c.colKey === colKey);\n const next = e.shiftKey ? editableCells[idx - 1] : editableCells[idx + 1];\n\n // Commit the current cell - never blocks navigation, see commitEdit.\n commitValue(rowIndex, colKey, editValueRef.current);\n\n if (next) {\n setEditingCell(next);\n setEditValue(cellOf(localDataRef.current[next.rowIndex], next.colKey));\n setEditError(cellErrors[cellErrorKey(next.rowIndex, next.colKey)] ?? null);\n } else {\n setEditingCell(null);\n setEditError(null);\n }\n }\n },\n [\n columns,\n isCellEditable,\n commitEdit,\n commitValue,\n discardEdit,\n cellErrors,\n setEditingCell,\n setEditValue,\n ],\n );\n\n const handleRowAdd = useCallback(() => {\n if (!onRowAdd) return;\n const newRow = onRowAdd();\n const newData = [newRow, ...localDataRef.current];\n setLocalData(newData);\n onChange?.(newData);\n setCurrentPage(1);\n }, [onRowAdd, onChange, setLocalData]);\n\n // ── Selection handlers ──────────────────────────────────────────────────────\n const commitSelection = useCallback(\n (keys: string[]) => {\n if (!isControlledSelection) setInternalSelectedKeys(new Set(keys));\n onSelectionChange?.(keys, rowsForKeys(keys));\n },\n [isControlledSelection, onSelectionChange, rowsForKeys],\n );\n\n const toggleRow = useCallback(\n (key: string) => {\n const next = selectedSet.has(key)\n ? [...selectedSet].filter((k) => k !== key)\n : [...selectedSet, key];\n commitSelection(next);\n },\n [selectedSet, commitSelection],\n );\n\n // ── \"Select all N matching\" (see `onSelectAllMatching`) ───────────────────\n // The grid can only enumerate the keys it has been given, so selecting\n // everything behind a server-side query has to come from the caller.\n const [isSelectingAllMatching, setIsSelectingAllMatching] = useState(false);\n\n const holdsEveryRow = totalItemCount <= localData.length;\n const hasSelectedAllMatching = !holdsEveryRow && selectedSet.size >= totalItemCount;\n // Offered once everything the grid holds is selected, so the select-all\n // checkbox has nothing left to give - but not once every matching row is\n // already selected, where it would be a no-op sitting next to \"Clear\n // selection\".\n const canSelectAllMatching =\n !!onSelectAllMatching && selectable && !holdsEveryRow && allSelected && !hasSelectedAllMatching;\n\n const selectAllMatching = useCallback(async () => {\n if (!onSelectAllMatching) return;\n setIsSelectingAllMatching(true);\n try {\n // Replaces rather than merges: the caller is returning the full\n // matching set, so anything not in it is by definition not a match.\n commitSelection(await onSelectAllMatching());\n } catch (error) {\n devWarn(\n 'datagrid-select-all-matching-failed',\n `DataGrid: onSelectAllMatching() rejected, so the selection was left untouched. ${String(error)}`,\n );\n } finally {\n setIsSelectingAllMatching(false);\n }\n }, [onSelectAllMatching, commitSelection]);\n\n // Adds or removes only the keys in scope, so a selection made on another\n // page (or outside `'page'` scope) is never silently dropped by toggling\n // the select-all control.\n const toggleAll = useCallback(() => {\n const inScope = new Set(selectAllKeys);\n commitSelection(\n allSelected\n ? [...selectedSet].filter((key) => !inScope.has(key))\n : [...new Set([...selectedSet, ...selectAllKeys])],\n );\n }, [allSelected, selectAllKeys, selectedSet, commitSelection]);\n\n // Only reachable from the \"all N matching\" state below - the select-all\n // checkbox is what clears an ordinary selection.\n const clearSelection = useCallback(() => commitSelection([]), [commitSelection]);\n\n // ── Row expansion handlers ───────────────────────────────────────────────────\n const commitExpanded = useCallback(\n (keys: string[]) => {\n if (!isControlledExpansion) setInternalExpandedKeys(new Set(keys));\n onExpandedRowsChange?.(keys);\n },\n [isControlledExpansion, onExpandedRowsChange],\n );\n\n const toggleExpanded = useCallback(\n (key: string) => {\n const isOpen = expandedSet.has(key);\n const next = expandMultiple\n ? isOpen\n ? [...expandedSet].filter((k) => k !== key)\n : [...expandedSet, key]\n : isOpen\n ? []\n : [key];\n commitExpanded(next);\n },\n [expandedSet, expandMultiple, commitExpanded],\n );\n\n // ── Sort handler ────────────────────────────────────────────────────────────\n const handleSortClick = useCallback(\n (colKey: string) => {\n if (onSortChange) {\n // Server-side: delegate to parent\n const newDir =\n currentSort?.key === colKey && currentSort?.direction === 'asc' ? 'desc' : 'asc';\n // `colKey` came from a rendered column, so it is a field of `T` by\n // construction - the public callback is narrowed for the caller's\n // benefit, which this internal string can't prove on its own.\n onSortChange(colKey as RowKey<T>, newDir);\n } else {\n // Client-side: cycle asc → desc → unsorted (null)\n setInternalSort((prev) => {\n if (prev?.key === colKey) {\n return prev.direction === 'asc' ? { key: colKey, direction: 'desc' } : null;\n }\n return { key: colKey, direction: 'asc' };\n });\n }\n },\n [onSortChange, currentSort],\n );\n\n // ── Filter handlers ──────────────────────────────────────────────────────────\n const commitFilters = useCallback(\n (newFilters: TableFilters) => {\n setCurrentPage(1);\n if (onFiltersChange) {\n onFiltersChange(newFilters);\n } else {\n setInternalFilters(newFilters);\n }\n },\n [onFiltersChange],\n );\n\n // The dropdown's writer. It emits its complete cleaned set, covering only\n // the keys it owns, so everything else in the filter state - quick filters\n // included - has to be merged back in rather than replaced. This is also\n // what makes its \"Clear All\" clear only the dropdown's own filters.\n const handleFiltersChange = useCallback(\n (newFilters: TableFilters) => {\n const preserved = Object.entries(activeFilters).filter(\n ([key]) => !dropdownFilterKeys.has(key),\n );\n commitFilters({ ...Object.fromEntries(preserved), ...newFilters });\n },\n [activeFilters, dropdownFilterKeys, commitFilters],\n );\n\n // A quick filter's writer. Empty values delete the key instead of storing\n // '' / [] - see `hasFilterValue`.\n const handleQuickFilterChange = useCallback(\n (key: string, value: FilterValue) => {\n const next = { ...activeFilters };\n if (hasFilterValue(value)) {\n next[key] = value;\n } else {\n delete next[key];\n }\n commitFilters(next);\n },\n [activeFilters, commitFilters],\n );\n\n // Resets every filter, whichever control owns it - what the \"no results\"\n // empty state offers, unlike the dropdown's own scoped \"Clear All\".\n const clearAllFilters = useCallback(() => commitFilters({}), [commitFilters]);\n\n // ── Page handlers ────────────────────────────────────────────────────────────\n const handlePageChange = useCallback(\n (page: number) => {\n setCurrentPage(page);\n onPageChange?.(page, currentPageSize);\n },\n [currentPageSize, onPageChange],\n );\n\n const handlePageSizeChange = useCallback(\n (newPageSize: number) => {\n setCurrentPageSize(newPageSize);\n setCurrentPage(1);\n onPageChange?.(1, newPageSize);\n },\n [onPageChange],\n );\n\n // ── dnd-kit: sensors + drag-end handler (preserved) ───────────────────────\n // Sensors are always initialised (hook rules) - PointerSensor's distance\n // constraint prevents accidental drags on normal cell clicks.\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),\n useSensor(KeyboardSensor),\n );\n\n const handleRowDragEnd = useCallback(\n (event: DragEndEvent) => {\n if (!draggableRows) return; // belt-and-suspenders\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n\n const oldIndex = localDataRef.current.findIndex(\n (row, i) => String(cellOf(row, rowKeyField) ?? i) === String(active.id),\n );\n const newIndex = localDataRef.current.findIndex(\n (row, i) => String(cellOf(row, rowKeyField) ?? i) === String(over.id),\n );\n\n if (oldIndex === -1 || newIndex === -1) return;\n\n const newData = arrayMove(localDataRef.current, oldIndex, newIndex);\n setLocalData(newData);\n onChange?.(newData);\n onRowReorder?.(newData);\n },\n [draggableRows, rowKeyField, setLocalData, onChange, onRowReorder],\n );\n\n // ── Cell content renderers (preserved exactly) ─────────────────────────────\n const renderViewCell = (\n col: InternalColumn<T>,\n value: unknown,\n row: T,\n rowIndex: number,\n ): React.ReactNode => {\n if (col.renderCell) return col.renderCell(value, row, rowIndex);\n\n switch (col.type) {\n case 'checkbox':\n // The <td> onClick handles toggling; onChange is a no-op here so that\n // the controlled Checkbox doesn't re-toggle via its own handler.\n return (\n <Checkbox\n checked={Boolean(value)}\n onChange={() => {\n /* handled by td onClick */\n }}\n size=\"sm\"\n />\n );\n\n case 'select': {\n const option = col.options?.find((o) => o.value === String(value ?? ''));\n return <span>{option ? option.label : String(value ?? '')}</span>;\n }\n\n case 'readonly':\n case 'text':\n case 'number':\n case 'date':\n default:\n return <span>{String(value ?? '')}</span>;\n }\n };\n\n // `variant`/`size` default to the table cell's look: 'bare' has no border/\n // background/radius of its own so the surrounding <td>'s own inset ring\n // (`.eidos-data-grid-cell--editing`) reads as the field's border, and\n // 'sm' keeps the editor from growing the row's height. A card field isn't\n // sitting inside that ring - it's a standalone field in a stack, so it\n // should just look like any other standalone Input/Select in the design\n // system (their own component defaults), not the table's cell-shaped\n // variant. Passing `undefined` here (rather than omitting the prop) is\n // equivalent to omitting it - falls back to each component's own default.\n const renderEditCell = (\n col: InternalColumn<T>,\n value: unknown,\n row: T,\n rowIndex: number,\n context: 'table' | 'card' = 'table',\n ): React.ReactNode => {\n if (col.renderEditor) {\n return col.renderEditor(value, (v) => setEditValue(v), row);\n }\n\n const variant = context === 'table' ? 'bare' : undefined;\n const size = context === 'table' ? 'sm' : undefined;\n\n switch (col.type) {\n case 'number':\n return (\n <Input\n type=\"number\"\n variant={variant}\n size={size}\n value={String(value ?? '')}\n onChange={(e) => setEditValue(e.target.value)}\n autoFocus\n fullWidth\n clearable={false}\n />\n );\n\n case 'date':\n return (\n <Input\n type=\"date\"\n variant={variant}\n size={size}\n value={String(value ?? '')}\n onChange={(e) => setEditValue(e.target.value)}\n autoFocus\n fullWidth\n clearable={false}\n />\n );\n\n case 'select': {\n // Auto-commit as soon as the user picks an option; the Dropdown's portal\n // means we can't rely on a simple onBlur for this cell type.\n // autoOpen=true so the dropdown opens on the same click that entered\n // edit mode - no second click needed.\n const selectOptions = (col.options ?? []).map((o) => ({\n id: o.value,\n value: o.value,\n label: o.label,\n }));\n\n return (\n <Select\n options={selectOptions}\n value={String(value ?? '')}\n autoOpen\n fullWidth\n inputProps={{ variant, size }}\n onChange={(v) => {\n const selected = Array.isArray(v) ? (v[0] ?? '') : v;\n const newData = localDataRef.current.map<T>((r, i) =>\n i === rowIndex ? ({ ...r, [col.key]: selected } as T) : r,\n );\n setLocalData(newData);\n onChange?.(newData);\n setEditingCell(null);\n }}\n clearable={false}\n />\n );\n }\n\n default:\n return (\n <Input\n type=\"text\"\n variant={variant}\n size={size}\n value={String(value ?? '')}\n onChange={(e) => setEditValue(e.target.value)}\n autoFocus\n fullWidth\n clearable={false}\n />\n );\n }\n };\n\n // ── Card view cell value ─────────────────────────────────────────────────\n // Mirrors the <td> cell's click/edit/error-state wiring in `renderViewCell`/\n // `renderEditCell` above, just wrapped in a plain <div> so it can sit\n // either in a card's label:value field row or its header/subheader - both\n // stay just as editable as any table cell.\n const renderCardValue = (\n col: InternalColumn<T>,\n row: T,\n localIndex: number,\n baseClassName: string,\n ): React.ReactNode => {\n const value = cellOf(row, col.key);\n const isEditing = editingCell?.rowIndex === localIndex && editingCell?.colKey === col.key;\n const canEdit = isCellEditable(col);\n const isFlagged = cellErrorKey(localIndex, col.key) in cellErrors;\n const hasError = isEditing && Boolean(editError);\n\n const valueCls = [\n baseClassName,\n // Deliberately NOT `eidos-data-grid-cell--editing` (unlike the <td>\n // version of this same state) - that class's whole job is forcing the\n // editor into `position: absolute; inset: 0` so it fills a <td>\n // without changing the table row's height. A card field has no row\n // to preserve the height of, so it just needs its own \"is editing\"\n // hook for spacing - the editor itself renders normally, with each\n // component's own default (non-`bare`) look (see `renderEditCell`).\n isEditing && 'eidos-datagrid-card-value--editing',\n hasError && 'eidos-data-grid-cell--error',\n isFlagged && !hasError && 'eidos-data-grid-cell--has-error',\n canEdit && !isEditing && 'eidos-data-grid-cell--editable',\n col.type === 'readonly' && 'eidos-data-grid-cell--readonly',\n // Checkbox glyph is intrinsically narrow - without this the value\n // wrapper stretches to the card's full width (block layout), making\n // the whole row clickable/toggleable instead of just the checkbox.\n col.type === 'checkbox' && 'eidos-datagrid-card-value--checkbox',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div\n ref={\n hasError ? (errorTooltipAnchorRef as React.RefObject<HTMLDivElement | null>) : undefined\n }\n className={valueCls}\n onClick={() => handleCellClick(localIndex, col)}\n onKeyDown={isEditing ? (e) => handleKeyDown(e, localIndex, col.key) : undefined}\n tabIndex={isEditing ? -1 : undefined}\n >\n {isEditing\n ? renderEditCell(col, editValue, row, localIndex, 'card')\n : renderViewCell(col, value, row, localIndex)}\n </div>\n );\n };\n\n // Label-above-value field, stacked - used for every column that isn't the\n // card's header/subheader.\n const renderCardField = (col: InternalColumn<T>, row: T, localIndex: number): React.ReactNode => (\n <div key={col.key} className=\"eidos-datagrid-card-field\">\n <span className=\"eidos-datagrid-card-field-label\">{col.header}</span>\n {renderCardValue(col, row, localIndex, 'eidos-datagrid-card-field-value')}\n </div>\n );\n\n // ── Row-actions menu (type: 'actions' column) ──────────────────────────────\n // Shared between the table's dedicated actions <td> and the card's\n // top-right toolbar slot - see `actionsColumn`/`DataGridColumn.actions`.\n // Renders nothing if there's no actions column, or it has no actions.\n const renderActionsMenu = (row: T, localIndex: number): React.ReactNode => {\n if (!actionsColumn) return null;\n\n const items: MenuItemType[] = actionsColumn.renderActions\n ? actionsColumn.renderActions(row, localIndex)\n : (actionsColumn.actions ?? []).flatMap((action, index) => {\n const id = action.id ?? `${action.label}-${index}`;\n const isDisabled =\n typeof action.disabled === 'function' ? action.disabled(row) : Boolean(action.disabled);\n\n const item: MenuItemType = {\n id,\n type: 'item',\n label: action.label,\n icon: action.icon,\n disabled: isDisabled,\n color: action.danger ? 'danger' : undefined,\n onClick: () => action.onClick(row, localIndex),\n };\n\n return action.divider\n ? [{ id: `${id}-divider`, type: 'separator' as const }, item]\n : [item];\n });\n\n if (!items.length) return null;\n\n return (\n <Menu\n trigger={\n <button type=\"button\" className=\"eidos-datagrid-actions-trigger\" aria-label=\"Row actions\">\n {actionsColumn.actionsIcon ? (\n renderIcon(actionsColumn.actionsIcon, 'eidos-datagrid-actions-trigger-icon')\n ) : (\n <MoreVertical size={16} />\n )}\n </button>\n }\n items={items}\n />\n );\n };\n\n // Columns sorted for rendering: pin:left → unpinned → pin:right. Excludes\n // the actions column (see `dataColumns`) - it's never a normal, sortable/\n // pinnable data column, always rendered in its own dedicated slot instead.\n // This ensures a pinned column defined in the middle of the columns array\n // is always displayed at the correct edge regardless of its original position.\n // The original `columns` prop is still used for key-based lookups above.\n const sortedColumns = useMemo(\n () => [\n ...dataColumns.filter((c) => c.pin === 'left'),\n ...dataColumns.filter((c) => !c.pin),\n ...dataColumns.filter((c) => c.pin === 'right'),\n ],\n [dataColumns],\n );\n\n const totalCols =\n dataColumns.length +\n (showRowNumbers ? 1 : 0) +\n (actionsColumn ? 1 : 0) +\n (draggableRows ? 1 : 0) +\n (selectable ? 1 : 0) +\n (expandable ? 1 : 0);\n\n // Row IDs for SortableContext - must be the displayed rows (not full dataset)\n // so dnd-kit knows which items are currently rendered.\n const rowIds = displayData.map((row, i) => String(cellOf(row, rowKeyField) ?? i));\n\n const containerStyle: React.CSSProperties = {};\n if (stickyHeader && maxHeight) {\n containerStyle.maxHeight = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n }\n\n // Toolbar visibility: show whenever filters, quick filters, density picker,\n // selectable, or add-row are active.\n const showToolbar =\n !!showFilters || quickFilters.length > 0 || !!showDensity || !!selectable || !!onRowAdd;\n\n // Quick filters share the toolbar's left zone with the selection count and\n // bulk actions, which take it over entirely while rows are selected.\n const showQuickFilters = quickFilters.length > 0 && !(selectable && hasSelection);\n\n const renderQuickFilter = (quickFilter: DataGridQuickFilter): React.ReactElement => {\n const { key, label, disabled, width } = quickFilter;\n const value = activeFilters[key];\n // Only set inline when supplied - the default lives in the stylesheet, so\n // the common case emits no style attribute at all.\n const style =\n width === undefined\n ? undefined\n : ({\n '--eidos-datagrid-quick-filter-width': typeof width === 'number' ? `${width}px` : width,\n } as React.CSSProperties);\n\n const control = (() => {\n switch (quickFilter.type) {\n case 'segmented':\n return (\n <SegmentedControl\n size=\"sm\"\n color={quickFilter.color}\n disabled={disabled}\n // Select/Combobox are already `fullWidth` and just fill their\n // box; a SegmentedControl sizes itself to its segments, so it\n // needs telling to fill the row once quick filters stack.\n fullWidth={isCompactToolbar}\n // The reset segment is always prepended: a SegmentedControl\n // always has exactly one segment selected, so without it the\n // filter could never be cleared.\n options={[\n { value: QUICK_FILTER_RESET_VALUE, label: quickFilter.allLabel ?? 'All' },\n ...quickFilter.options,\n ]}\n value={typeof value === 'string' ? value : QUICK_FILTER_RESET_VALUE}\n onChange={(next) => handleQuickFilterChange(key, next)}\n />\n );\n case 'combobox':\n return (\n <Combobox\n size=\"sm\"\n fullWidth\n disabled={disabled}\n placeholder={quickFilter.placeholder ?? label}\n clearable={quickFilter.clearable ?? true}\n emptyText={quickFilter.emptyText}\n loading={quickFilter.loading}\n loadingText={quickFilter.loadingText}\n onSearch={quickFilter.onSearch}\n options={(quickFilter.options ?? []).map((option) => ({\n ...option,\n id: option.value,\n }))}\n value={typeof value === 'string' ? value : ''}\n onChange={(next) => handleQuickFilterChange(key, next)}\n />\n );\n case 'select':\n default:\n return (\n <Select\n fullWidth\n inputProps={{ size: 'sm' }}\n disabled={disabled}\n placeholder={quickFilter.placeholder ?? label}\n clearable={quickFilter.clearable ?? true}\n multiple={quickFilter.multiple}\n options={quickFilter.options.map((option) => ({ ...option, id: option.value }))}\n // Never `undefined` - Select and Combobox both treat that as\n // \"uncontrolled\" and would then keep showing a value the\n // filter state no longer holds after a clear.\n value={\n quickFilter.multiple\n ? Array.isArray(value)\n ? value\n : []\n : typeof value === 'string'\n ? value\n : ''\n }\n onChange={(next) => handleQuickFilterChange(key, next)}\n />\n );\n }\n })();\n\n return (\n <div\n key={key}\n className={`eidos-datagrid-quick-filter eidos-datagrid-quick-filter--${quickFilter.type}`}\n style={style}\n // No visible field label fits in the toolbar, so the group carries the\n // accessible name for whichever control it wraps.\n role=\"group\"\n aria-label={label}\n >\n {control}\n </div>\n );\n };\n\n // Density modifier class (comfortable = default = no extra class)\n const densityClass =\n effectiveDensity !== 'comfortable' ? `eidos-datagrid--${effectiveDensity}` : '';\n\n // Always include the current page size in the options so the Select is never blank.\n const effectivePageSizeOptions = useMemo(() => {\n const opts = pageSizeOptions ?? [10, 25, 50, 100];\n return opts.includes(pageSize) ? opts : [...opts, pageSize].sort((a, b) => a - b);\n }, [pageSizeOptions, pageSize]);\n\n // ── Loading state (preserved) ───────────────────────────────────────────────\n // `ref={setContainerRef}` here too (not just the main return below) - a\n // consumer whose `loading` prop starts `true` on mount renders this\n // branch first, and without it `hasCardView` would never get a container\n // to measure until some unrelated re-render happened to remount it - see\n // `setContainerRef`'s comment above for the full explanation.\n if (loading) {\n return (\n <div\n ref={setContainerRef}\n className={['eidos-data-grid-container', className].filter(Boolean).join(' ')}\n >\n <div className=\"eidos-data-grid-loading\">\n <Spinner size=\"md\" />\n <p>Loading…</p>\n </div>\n </div>\n );\n }\n\n // ── Render ─────────────────────────────────────────────────────────────────\n // Pre-compute sticky props for system columns so we can spread them on\n // header <th>s and body <td>s without calling the helpers multiple times.\n const expandHeaderPin = getHeaderPinnedProps('__expand__');\n const selHeaderPin = getHeaderPinnedProps('__sel__');\n const dragHeaderPin = getHeaderPinnedProps('__drag__');\n const rownumHeaderPin = getHeaderPinnedProps('__rownum__');\n const expandCellPin = getCellPinnedProps('__expand__');\n const selCellPin = getCellPinnedProps('__sel__');\n const dragCellPin = getCellPinnedProps('__drag__');\n const rownumCellPin = getCellPinnedProps('__rownum__');\n\n return (\n <div\n ref={setContainerRef}\n className={[\n 'eidos-data-grid-container',\n stickyHeader && 'eidos-data-grid-container--sticky-header',\n densityClass,\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={containerStyle}\n >\n {/* ── Toolbar ─────────────────────────────────────────────────────── */}\n {showToolbar && (\n <div className=\"eidos-datagrid-toolbar\">\n {/* Left zone: quick filters, or selection count + bulk actions */}\n <div className=\"eidos-datagrid-toolbar-left\">\n {/* Card view's stand-in for the header checkbox, which doesn't\n exist without a header row. Also the way out of selection mode\n there: clicking it while checked clears the selection, exactly\n as the header checkbox does in table mode. */}\n {selectable && isCardView && (\n <Checkbox\n size=\"sm\"\n checked={allSelected}\n indeterminate={someSelected}\n onChange={toggleAll}\n // Labelled even in the compact toolbar, unlike the buttons\n // beside it: those keep a recognisable icon, whereas a bare\n // checkbox floating in a toolbar says nothing about what it\n // selects - and a tooltip is no help on touch.\n label=\"Select all\"\n aria-label=\"Select all rows\"\n />\n )}\n\n {showQuickFilters && (\n <div\n className={[\n 'eidos-datagrid-quick-filters',\n isCompactToolbar && 'eidos-datagrid-quick-filters--compact',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {quickFilters.map(renderQuickFilter)}\n </div>\n )}\n\n {selectable && hasSelection && (\n <>\n <span className=\"eidos-datagrid-selection-count\">{selectedSet.size} selected</span>\n\n {/* The select-all checkbox can only reach the loaded rows, so\n reaching past them is an explicit action - and undoing it\n has to be explicit too: with more rows selected than the\n checkbox's own scope, toggling it would strip the page and\n leave the rest selected, which reads as a no-op. */}\n {canSelectAllMatching && (\n <Button\n variant=\"text\"\n size=\"sm\"\n loading={isSelectingAllMatching}\n onClick={() => void selectAllMatching()}\n >\n {`Select all ${totalItemCount}`}\n </Button>\n )}\n\n {hasSelectedAllMatching && (\n <Button variant=\"text\" size=\"sm\" color=\"secondary\" onClick={clearSelection}>\n Clear selection\n </Button>\n )}\n\n {bulkActions && bulkActions.length > 0 && (\n <div className=\"eidos-datagrid-bulk-actions\">\n {bulkActions.map((action) => {\n const isDisabled =\n typeof action.disabled === 'function'\n ? action.disabled(selectedItems)\n : (action.disabled ?? false);\n\n if (action.type === 'split-button') {\n return (\n <SplitButton\n key={action.id}\n size=\"sm\"\n variant={action.variant ?? 'outlined'}\n color={action.color ?? 'secondary'}\n preIcon={action.icon}\n disabled={isDisabled}\n label={action.label}\n onClick={() => action.onClick(selectedItems)}\n options={action.options.map((option) => ({\n id: option.id,\n label: option.label,\n icon: option.icon,\n disabled: option.disabled,\n onClick: () => option.onClick(selectedItems),\n }))}\n />\n );\n }\n\n return action.label ? (\n <Button\n key={action.id}\n size=\"sm\"\n variant={action.variant ?? 'outlined'}\n color={action.color ?? 'secondary'}\n preIcon={action.icon}\n posIcon={action.posIcon}\n disabled={isDisabled}\n onClick={() => action.onClick(selectedItems)}\n >\n {action.label}\n </Button>\n ) : action.icon ? (\n <Button\n key={action.id}\n size=\"sm\"\n variant={action.variant ?? 'outlined'}\n color={action.color ?? 'secondary'}\n icon={action.icon}\n disabled={isDisabled}\n onClick={() => action.onClick(selectedItems)}\n />\n ) : null;\n })}\n </div>\n )}\n </>\n )}\n </div>\n\n {/* Right zone: density picker + filter button + add-row button */}\n <div className=\"eidos-datagrid-toolbar-right\">\n {/* Icon-only below `COMPACT_TOOLBAR_WIDTH_PX`, with the label moved\n into a tooltip so the control stays identifiable. */}\n {showDensity && (\n <Dropdown\n placement=\"bottom\"\n align=\"end\"\n autoWidth={false}\n trigger={\n isCompactToolbar ? (\n <Button variant=\"text\" size=\"sm\" icon={AlignJustify} tooltip=\"Density\" />\n ) : (\n <Button variant=\"text\" size=\"sm\" preIcon={AlignJustify}>\n Density\n </Button>\n )\n }\n content={\n <div className=\"eidos-table-menu-panel\">\n {(\n [\n { value: 'compact', label: 'Compact' },\n { value: 'comfortable', label: 'Comfortable' },\n { value: 'spacious', label: 'Spacious' },\n ] as const\n ).map((option) => (\n <button\n key={option.value}\n className={[\n 'eidos-table-menu-item',\n effectiveDensity === option.value ? 'eidos-table-menu-item--active' : '',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => setInternalDensity(option.value)}\n >\n <span className=\"eidos-table-menu-item-check\">\n {effectiveDensity === option.value && <Check size={14} />}\n </span>\n <span>{option.label}</span>\n </button>\n ))}\n </div>\n }\n />\n )}\n\n {showFilters && filterDropdownColumns.length > 0 && (\n <TableFiltersDropdown<T>\n columns={filterDropdownColumns}\n filters={dropdownFilters}\n onFiltersChange={handleFiltersChange}\n />\n )}\n\n {onRowAdd &&\n (isCompactToolbar ? (\n <Button\n variant=\"outlined\"\n size=\"sm\"\n icon={Plus}\n tooltip=\"Add row\"\n onClick={handleRowAdd}\n />\n ) : (\n <Button variant=\"outlined\" size=\"sm\" preIcon={Plus} onClick={handleRowAdd}>\n Add row\n </Button>\n ))}\n </div>\n </div>\n )}\n\n {isCardView ? (\n /* ── Card view ────────────────────────────────────────────────── */\n <div\n className=\"eidos-datagrid-cards\"\n style={{ '--eidos-datagrid-card-min-width': `${cardMinWidth}px` } as React.CSSProperties}\n >\n {displayData.length > 0 ? (\n displayData.map((row, displayIndex) => {\n const localIndex = localData.indexOf(row);\n const rowKeyValue = String(cellOf(row, rowKeyField) ?? displayIndex);\n const isRowSelected = selectable && selectedSet.has(rowKeyValue);\n const isRowExpanded = expandable && expandedSet.has(rowKeyValue);\n const canExpandRow = !isRowExpandable || isRowExpandable(row);\n\n return (\n <div\n key={rowKeyValue}\n className={[\n 'eidos-datagrid-card',\n isRowSelected && 'eidos-datagrid-card--selected',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {/* stopPropagation so the checkbox/actions menu never bubble\n into a consumer's own row-click handler (e.g. opening a\n details drawer) - mirrors the selection/drag-handle <td>s'\n own `onClick={(e) => e.stopPropagation()}` in table mode. */}\n {hasCardToolbar && (\n <div\n className=\"eidos-datagrid-card-toolbar\"\n onClick={(e) => e.stopPropagation()}\n >\n <div className=\"eidos-datagrid-card-toolbar-left\">\n {expandable && canExpandRow && (\n <button\n type=\"button\"\n className={[\n 'eidos-datagrid-expand-toggle',\n isRowExpanded && 'eidos-datagrid-expand-toggle--open',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => toggleExpanded(rowKeyValue)}\n aria-expanded={isRowExpanded}\n aria-label={isRowExpanded ? 'Collapse row' : 'Expand row'}\n >\n <ChevronRight size={14} />\n </button>\n )}\n {selectable && (\n <Checkbox\n checked={isRowSelected}\n onChange={() => toggleRow(rowKeyValue)}\n size=\"sm\"\n />\n )}\n {showRowNumbers && (\n <span className=\"eidos-datagrid-card-row-number\">#{localIndex + 1}</span>\n )}\n </div>\n\n {/* Always rendered (even empty) so it occupies the toolbar's\n middle grid column - conditionally omitting the element\n itself would shift `eidos-datagrid-card-toolbar-right`\n into this column instead of the third one. */}\n <div className=\"eidos-datagrid-card-heading\">\n {cardHeaderColumn &&\n renderCardValue(\n cardHeaderColumn,\n row,\n localIndex,\n 'eidos-datagrid-card-heading-title',\n )}\n {cardSubheaderColumn &&\n renderCardValue(\n cardSubheaderColumn,\n row,\n localIndex,\n 'eidos-datagrid-card-heading-subtitle',\n )}\n </div>\n\n <div className=\"eidos-datagrid-card-toolbar-right\">\n {renderActionsMenu(row, localIndex)}\n </div>\n </div>\n )}\n\n <div className=\"eidos-datagrid-card-fields\">\n {cardFieldColumns.map((col) => renderCardField(col, row, localIndex))}\n </div>\n\n {expandable && renderExpandedContent && (\n <ExpandedCardPanel\n isOpen={isRowExpanded}\n render={() => renderExpandedContent(row, localIndex)}\n />\n )}\n </div>\n );\n })\n ) : (\n <div className=\"eidos-datagrid-card-empty\">\n {Object.keys(activeFilters).length > 0 ? (\n <EmptyState\n icon={<SearchX />}\n title=\"No results found\"\n description=\"Try adjusting your filters or search terms.\"\n action={\n <Button size=\"sm\" variant=\"outlined\" color=\"primary\" onClick={clearAllFilters}>\n Clear filters\n </Button>\n }\n size=\"sm\"\n />\n ) : (\n <EmptyState\n icon={<FolderOpen />}\n title={emptyText}\n description={\n onRowAdd ? 'Add a row to get started.' : 'There are no records to display.'\n }\n size=\"sm\"\n />\n )}\n </div>\n )}\n </div>\n ) : (\n <>\n {/* ── Table (wrapped for horizontal scroll) ───────────────────────── */}\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragEnd={handleRowDragEnd}\n >\n <div className=\"eidos-data-grid-scroll\">\n <table className={['eidos-data-grid', densityClass].filter(Boolean).join(' ')}>\n <thead ref={theadRef}>\n <tr>\n {/* Drag-handle column - must come before every other system column */}\n {draggableRows && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-datagrid-drag-handle-cell',\n dragHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={dragHeaderPin.style}\n data-col-key=\"__drag__\"\n />\n )}\n\n {/* Expand-toggle column */}\n {expandable && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-datagrid-expand-cell',\n expandHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={expandHeaderPin.style}\n data-col-key=\"__expand__\"\n />\n )}\n\n {/* Selection checkbox column - must come before row numbers */}\n {selectable && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-datagrid-checkbox-cell',\n selHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={selHeaderPin.style}\n data-col-key=\"__sel__\"\n >\n <Checkbox\n checked={allSelected}\n indeterminate={someSelected}\n onChange={toggleAll}\n size=\"sm\"\n />\n </th>\n )}\n\n {showRowNumbers && (\n <th\n className={[\n 'eidos-data-grid-header-cell',\n 'eidos-data-grid-row-number-col',\n rownumHeaderPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={rownumHeaderPin.style}\n data-col-key=\"__rownum__\"\n >\n #\n </th>\n )}\n\n {sortedColumns.map((col) => {\n const colKey = col.key;\n const isSortable = !!col.sortable;\n const isCurrentlySorted = activeSort?.key === colKey;\n const sortDir = isCurrentlySorted ? activeSort!.direction : null;\n const alignClass = `eidos-datagrid-align-${col.align ?? 'left'}`;\n const { style: pinnedStyle, className: pinnedClass } =\n getHeaderPinnedProps(colKey);\n\n return (\n <th\n key={colKey}\n className={[\n 'eidos-data-grid-header-cell',\n isSortable && 'eidos-datagrid-header-cell--sortable',\n isCurrentlySorted && 'eidos-datagrid-header-cell--sorted',\n alignClass,\n pinnedClass,\n ]\n .filter(Boolean)\n .join(' ')}\n style={{\n width:\n col.width != null\n ? typeof col.width === 'number'\n ? `${col.width}px`\n : col.width\n : undefined,\n minWidth:\n col.minWidth != null\n ? typeof col.minWidth === 'number'\n ? `${col.minWidth}px`\n : col.minWidth\n : undefined,\n ...pinnedStyle,\n }}\n data-col-key={colKey}\n onClick={isSortable ? () => handleSortClick(colKey) : undefined}\n >\n <div className={`eidos-datagrid-header-content ${alignClass}`}>\n <span className=\"eidos-datagrid-header-label\">{col.header}</span>\n {isSortable && (\n <span className=\"eidos-datagrid-sort-icon\">\n {sortDir === 'asc' && <ArrowUp size={14} />}\n {sortDir === 'desc' && <ArrowDown size={14} />}\n {!sortDir && <ChevronsUpDown size={14} />}\n </span>\n )}\n </div>\n </th>\n );\n })}\n\n {actionsColumn && (\n <th\n className=\"eidos-data-grid-header-cell eidos-data-grid-actions-col\"\n data-col-key=\"__actions__\"\n />\n )}\n </tr>\n </thead>\n\n <tbody>\n <SortableContext items={rowIds} strategy={verticalListSortingStrategy}>\n {displayData.length > 0 ? (\n displayData.map((row, displayIndex) => {\n // Find this row's position in the full localData array.\n // displayData rows are the same object references as in localData\n // (filter/sort/slice never clone row objects), so indexOf is O(n)\n // but always accurate - even after edits that produce new row objects.\n const localIndex = localData.indexOf(row);\n const rowKeyValue = String(cellOf(row, rowKeyField) ?? displayIndex);\n const isRowSelected = selectable && selectedSet.has(rowKeyValue);\n const isRowExpanded = expandable && expandedSet.has(rowKeyValue);\n const canExpandRow = !isRowExpandable || isRowExpandable(row);\n\n return (\n // React.Fragment (rather than SortableTableRow taking the\n // `key` directly) so an expanded row's detail <tr> - see\n // ExpandedTableRow below - can sit right underneath it as\n // a sibling, keyed together as one reconciliation unit.\n <React.Fragment key={rowKeyValue}>\n {/* SortableTableRow is always rendered (hooks unconditional);\n disabled={true} when draggableRows is off so dnd-kit is a no-op. */}\n <SortableTableRow\n id={rowKeyValue}\n disabled={!draggableRows}\n className={[\n 'eidos-data-grid-row',\n isRowSelected && 'eidos-datagrid-row--selected',\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {(dragHandleProps, isDragging) => (\n <>\n {draggableRows && (\n <td\n className={[\n 'eidos-datagrid-drag-handle-cell',\n dragCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={dragCellPin.style}\n // Prevent a click on the handle from triggering cell editing\n onClick={(e) => e.stopPropagation()}\n >\n <span\n {...(dragHandleProps as React.HTMLAttributes<HTMLSpanElement>)}\n className={[\n 'eidos-datagrid-drag-handle',\n isDragging && 'eidos-datagrid-drag-handle--dragging',\n ]\n .filter(Boolean)\n .join(' ')}\n title=\"Drag to reorder\"\n >\n <GripVertical size={14} />\n </span>\n </td>\n )}\n\n {expandable && (\n <td\n className={[\n 'eidos-data-grid-cell',\n 'eidos-datagrid-expand-cell',\n expandCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={expandCellPin.style}\n // Prevent a click on the toggle from triggering cell editing\n onClick={(e) => e.stopPropagation()}\n >\n {canExpandRow && (\n <button\n type=\"button\"\n className={[\n 'eidos-datagrid-expand-toggle',\n isRowExpanded && 'eidos-datagrid-expand-toggle--open',\n ]\n .filter(Boolean)\n .join(' ')}\n onClick={() => toggleExpanded(rowKeyValue)}\n aria-expanded={isRowExpanded}\n aria-label={isRowExpanded ? 'Collapse row' : 'Expand row'}\n >\n <ChevronRight size={14} />\n </button>\n )}\n </td>\n )}\n\n {selectable && (\n <td\n className={[\n 'eidos-data-grid-cell',\n 'eidos-datagrid-checkbox-cell',\n selCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={selCellPin.style}\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox\n checked={isRowSelected}\n onChange={() => toggleRow(rowKeyValue)}\n size=\"sm\"\n />\n </td>\n )}\n\n {showRowNumbers && (\n <td\n className={[\n 'eidos-data-grid-cell',\n 'eidos-data-grid-row-number',\n rownumCellPin.className,\n ]\n .filter(Boolean)\n .join(' ')}\n style={rownumCellPin.style}\n >\n {localIndex + 1}\n </td>\n )}\n\n {sortedColumns.map((col) => {\n const value = cellOf(row, col.key);\n const isEditing =\n editingCell?.rowIndex === localIndex &&\n editingCell?.colKey === col.key;\n const canEdit = isCellEditable(col);\n // Persists for a cell regardless of focus (background tint only);\n // the border + message only show once this exact cell is\n // focused again, via `hasError` below.\n const isFlagged =\n cellErrorKey(localIndex, col.key) in cellErrors;\n const hasError = isEditing && Boolean(editError);\n const { style: pinnedStyle, className: pinnedClass } =\n getCellPinnedProps(col.key);\n\n const cellCls = [\n 'eidos-data-grid-cell',\n isEditing && 'eidos-data-grid-cell--editing',\n hasError && 'eidos-data-grid-cell--error',\n isFlagged && !hasError && 'eidos-data-grid-cell--has-error',\n canEdit && !isEditing && 'eidos-data-grid-cell--editable',\n col.type === 'readonly' && 'eidos-data-grid-cell--readonly',\n `eidos-datagrid-align-${col.align ?? 'left'}`,\n pinnedClass,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <td\n key={col.key}\n ref={\n hasError\n ? (errorTooltipAnchorRef as React.RefObject<HTMLTableDataCellElement | null>)\n : undefined\n }\n className={cellCls}\n style={pinnedStyle}\n onClick={() => handleCellClick(localIndex, col)}\n onKeyDown={\n isEditing\n ? (e) => handleKeyDown(e, localIndex, col.key)\n : undefined\n }\n // Make editing cells focusable so keydown events register\n tabIndex={isEditing ? -1 : undefined}\n >\n {isEditing\n ? renderEditCell(col, editValue, row, localIndex)\n : renderViewCell(col, value, row, localIndex)}\n </td>\n );\n })}\n\n {actionsColumn && (\n <td\n className=\"eidos-data-grid-cell eidos-data-grid-actions-col\"\n // Prevent a click on the actions menu from bubbling into a\n // consumer's own row-click handler (e.g. opening a details\n // drawer) - mirrors the selection/drag-handle <td>s above.\n onClick={(e) => e.stopPropagation()}\n >\n {renderActionsMenu(row, localIndex)}\n </td>\n )}\n </>\n )}\n </SortableTableRow>\n\n {expandable && renderExpandedContent && (\n <ExpandedTableRow\n isOpen={isRowExpanded}\n colSpan={totalCols}\n render={() => renderExpandedContent(row, localIndex)}\n />\n )}\n </React.Fragment>\n );\n })\n ) : (\n <tr className=\"eidos-data-grid-empty-row\">\n <td\n colSpan={totalCols}\n className=\"eidos-data-grid-cell eidos-data-grid-empty-cell\"\n >\n {Object.keys(activeFilters).length > 0 ? (\n <EmptyState\n icon={<SearchX />}\n title=\"No results found\"\n description=\"Try adjusting your filters or search terms.\"\n action={\n <Button\n size=\"sm\"\n variant=\"outlined\"\n color=\"primary\"\n onClick={clearAllFilters}\n >\n Clear filters\n </Button>\n }\n size=\"sm\"\n />\n ) : (\n <EmptyState\n icon={<FolderOpen />}\n title={emptyText}\n description={\n onRowAdd\n ? 'Add a row to get started.'\n : 'There are no records to display.'\n }\n size=\"sm\"\n />\n )}\n </td>\n </tr>\n )}\n </SortableContext>\n </tbody>\n </table>\n </div>\n </DndContext>\n </>\n )}\n\n {/* ── Pagination footer ────────────────────────────────────────────── */}\n {showPagination && (\n <div className=\"eidos-datagrid-footer\">\n <Pagination\n page={currentPage}\n totalPages={totalPages}\n onChange={handlePageChange}\n totalItems={totalItemCount}\n pageSize={currentPageSize}\n onPageSizeChange={handlePageSizeChange}\n pageSizeOptions={effectivePageSizeOptions}\n />\n </div>\n )}\n\n {/* Portaled rather than a child of the erroring cell - see errorTooltipPosition above */}\n {activeCellError &&\n errorTooltipPosition &&\n createPortal(\n <div\n className=\"eidos-data-grid-cell-error\"\n style={{ top: errorTooltipPosition.top, left: errorTooltipPosition.left }}\n >\n {activeCellError}\n </div>,\n document.body,\n )}\n </div>\n );\n}\n\n// Assign displayName cleanly without losing the generic signature\nexport const DataGrid = Object.assign(DataGridInner, { displayName: 'DataGrid' });\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-VRQBDTLT.cjs","../src/components/ThemeEditor/ThemeEditor.component.tsx"],"names":["ratio","grade"],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-VRQBDTLT.cjs","sourcesContent":[null,"import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react';\nimport { CircleCheck, CircleX, TriangleAlert } from 'lucide-react';\nimport { Alert } from '../Alert';\nimport { Button, IconButton } from '../Button';\nimport { Card } from '../Card';\nimport { ColorPicker } from '../ColorPicker';\nimport { Select } from '../Select';\nimport { Slider } from '../Slider';\nimport { Tooltip } from '../Tooltip';\nimport { useTheme } from '../ThemeProvider';\nimport type { ThemeColorKey, ThemeFontOption } from '../ThemeProvider';\nimport {\n THEME_COLOR_KEYS,\n FONT_SCALE_MAX,\n FONT_SCALE_MIN,\n defaultTheme,\n} from '../ThemeProvider/ThemeProvider.tokens';\nimport {\n CONTRAST_DARK,\n CONTRAST_LIGHT,\n contrastRatio,\n pickContrast,\n} from '../ThemeProvider/ThemeProvider.color';\nimport {\n FONT_ACCEPT,\n isFontStackAvailable,\n registerFontFile,\n subscribeToFontLoads,\n toFontStack,\n} from '../ThemeProvider/ThemeProvider.fonts';\nimport type { ThemeEditorProps } from './ThemeEditor.types';\n\n/** The page surface every diagnostic is measured against - `--white`. */\nconst PAGE_SURFACE = CONTRAST_LIGHT;\n\n/** Below this, a fill is effectively invisible against the page behind it. */\nconst EDGE_MIN_RATIO = 1.5;\n\nconst COLOR_LABELS: Record<ThemeColorKey, string> = {\n primary: 'Primary',\n secondary: 'Secondary',\n success: 'Success',\n danger: 'Danger',\n warning: 'Warning',\n info: 'Info',\n hyperlink: 'Hyperlink',\n};\n\n/** `hyperlink` is only ever text on a surface, so it is graded differently. */\nconst isFill = (key: ThemeColorKey): boolean => key !== 'hyperlink';\n\n/**\n * Built-in stacks, deliberately limited to fonts that can be relied on.\n *\n * A dropdown implies its entries are available, but a font stack is only a list\n * of *names* - nothing here can install a font. So the list is restricted to\n * generic families (guaranteed by CSS), `system-ui` (whatever the OS provides),\n * and the handful of genuinely web-safe faces. Fonts like Inter, which are not\n * installed anywhere by default, were removed: offering one is offering a\n * choice that silently does nothing on most machines.\n *\n * The preset is included because it is the shipped default and therefore has to\n * be representable - and note that `eidos-ui` does not ship the Plus Jakarta\n * Sans file either, so it renders only where the consumer provides it. That is\n * exactly why unavailable options are labelled as such.\n *\n * Anything beyond this belongs to the consuming app, via `fontOptions`.\n */\nconst FONT_STACKS: ThemeFontOption[] = [\n // Taken from the preset rather than retyped, so the shipped default always\n // matches an option exactly. A hand-copied shorter stack did not match, and\n // the row fell back to showing a synthetic \"custom\" entry instead of the\n // name of the font actually in use.\n { label: 'Plus Jakarta Sans (theme default)', value: defaultTheme.typography.fontFamily! },\n { label: 'System UI', value: \"system-ui, -apple-system, 'Segoe UI', sans-serif\" },\n { label: 'Helvetica / Arial', value: 'Helvetica, Arial, sans-serif' },\n { label: 'Georgia', value: \"Georgia, 'Times New Roman', serif\" },\n { label: 'Sans-serif (generic)', value: 'sans-serif' },\n { label: 'Serif (generic)', value: 'serif' },\n];\n\nconst MONO_STACKS: ThemeFontOption[] = [\n { label: 'JetBrains Mono (theme default)', value: defaultTheme.typography.monoFamily! },\n { label: 'Menlo / Consolas', value: 'Menlo, Consolas, monospace' },\n { label: 'Courier New', value: \"'Courier New', Courier, monospace\" },\n { label: 'Monospace (generic)', value: 'monospace' },\n];\n\ntype Grade = 'aa' | 'aa-large' | 'fail';\n\nconst gradeFor = (ratio: number): Grade => (ratio >= 4.5 ? 'aa' : ratio >= 3 ? 'aa-large' : 'fail');\n\n/** Spelled out for the middle grade, which is the one that misleads. */\nconst GRADE_CAVEAT: Partial<Record<Grade, string>> = {\n 'aa-large':\n ' WCAG AA needs 4.5:1 for normal text and allows 3:1 only at 24px or larger - button and label text here is around 12px, so this does not pass.',\n fail: ' Below 3:1, which fails WCAG AA at every text size.',\n};\n\n/**\n * Severity is carried by an icon and the label text, not by colour.\n *\n * These badges cannot use `Pill`, or any themed `--x-color`: the theme being\n * edited also styles this editor, so a washed-out `success` colour would render\n * the \"passes AA\" badge illegible at precisely the moment it is reporting that\n * a colour is illegible. The badge styling comes from the gray ramp, which is\n * deliberately not themeable.\n *\n * Not relying on colour alone also satisfies WCAG 1.4.1 (Use of Colour).\n */\nconst GRADE_BADGE: Record<\n Grade,\n { label: string; icon: React.ComponentType<{ className?: string }> }\n> = {\n aa: { label: 'AA', icon: CircleCheck },\n // Named for what it means in practice, not just for the threshold it clears.\n // WCAG's relaxed 3:1 tier applies only to text at 24px (or 18.7px bold) and\n // above, and nothing in this library is that large - `--font-size-sm` is\n // ~12.25px at the default root size. A bare \"AA large only\" reads as a pass\n // when for every component here it is a fail.\n 'aa-large': { label: 'Too low for body text', icon: TriangleAlert },\n fail: { label: 'Fails AA', icon: CircleX },\n};\n\ninterface Diagnostic {\n grade: Grade;\n ratio: number;\n /** What the ratio describes, for the tooltip. */\n detail: string;\n /** Set when the fill is too close to the page surface to be seen. */\n edgeWarning?: string;\n}\n\nconst diagnose = (key: ThemeColorKey, base: string, explicitContrast?: string): Diagnostic => {\n if (!isFill(key)) {\n // Link text sits on the page, so the page is what it must contrast with.\n const ratio = contrastRatio(base, PAGE_SURFACE);\n const grade = gradeFor(ratio);\n return {\n grade,\n ratio,\n detail: `Link text on the page background: ${ratio.toFixed(2)}:1 against white.${GRADE_CAVEAT[grade] ?? ''}`,\n };\n }\n\n const foreground = explicitContrast ?? pickContrast(base);\n const ratio = contrastRatio(base, foreground);\n const name = foreground.toLowerCase() === CONTRAST_DARK ? 'dark' : 'white';\n const edge = contrastRatio(base, PAGE_SURFACE);\n\n const grade = gradeFor(ratio);\n\n return {\n grade,\n ratio,\n detail: `${name === 'white' ? 'White' : 'Near-black'} text on this fill is ${ratio.toFixed(2)}:1.${\n explicitContrast ? ' Foreground pinned via the theme.' : ''\n }${GRADE_CAVEAT[grade] ?? ''}`,\n edgeWarning:\n edge < EDGE_MIN_RATIO\n ? `Only ${edge.toFixed(2)}:1 against the page background - this fill is barely distinguishable from the page it sits on.`\n : undefined,\n };\n};\n\ninterface FontRowProps {\n label: string;\n /** Stack currently applied, used to pick the matching option. */\n value: string;\n options: ThemeFontOption[];\n mono: boolean;\n allowUpload: boolean;\n onChange: (stack: string) => void;\n onUploaded: (option: ThemeFontOption, file: File, family: string) => void;\n}\n\n/** Pulls the first family out of a stack, unquoted. */\nconst firstFamily = (stack: string): string =>\n stack\n .split(',')[0]\n ?.trim()\n .replace(/^['\"]|['\"]$/g, '') || 'Custom';\n\n/**\n * One font row: a `Select` over the known stacks, an optional upload, and an\n * availability warning.\n *\n * Uses `Select` rather than `Combobox` deliberately. `Combobox` filters its\n * options against the text in the field, and the field holds a full font stack\n * (`'Plus Jakarta Sans', -apple-system, …`) which matches no option label - so\n * the list read \"No results found\" until the text was cleared by hand. A font\n * is a choice from a known set, not a search.\n */\nconst FontRow: React.FC<FontRowProps> = ({\n label,\n value,\n options,\n mono,\n allowUpload,\n onChange,\n onUploaded,\n}) => {\n const inputRef = useRef<HTMLInputElement>(null);\n const [error, setError] = useState<string | null>(null);\n\n // Web fonts load asynchronously, so the first render always sees them as\n // unavailable. Without re-checking once they arrive, the row would sit on a\n // stale \"not installed\" warning for a font that is rendering perfectly.\n // The reducer exists only to schedule that re-render; its value is unused.\n const [, onFontsLoaded] = useReducer((count: number) => count + 1, 0);\n useEffect(() => subscribeToFontLoads(onFontsLoaded), [onFontsLoaded]);\n\n const baseOptions = useMemo(() => {\n // A stack the theme carries but that isn't in the list (loaded by the host\n // app, or set programmatically) still needs to be selectable, labelled with\n // its own first family so the row names the font actually in use.\n const known = options.some((o) => o.value === value);\n return known ? options : [...options, { label: `${firstFamily(value)} (custom)`, value }];\n }, [options, value]);\n\n // Probed on every render rather than memoised: availability changes over time\n // as fonts load, so a cached answer is a wrong answer. Four canvas\n // measurements are far cheaper than getting this wrong.\n //\n // Availability is annotated per option rather than filtered or disabled. A\n // theme is shared across machines, so a font present here may be absent for\n // the next user - and vice versa, which would mean hiding a font someone\n // legitimately wants. Annotating keeps every choice selectable while making\n // the list stop implying that a name is a guarantee.\n const selectOptions = baseOptions.map((option) => ({\n id: option.value,\n label: isFontStackAvailable(option.value) ? option.label : `${option.label} - not installed`,\n value: option.value,\n }));\n\n const handleFile = useCallback(\n async (file: File | undefined) => {\n if (!file) return;\n setError(null);\n try {\n const family = await registerFontFile(file);\n const stack = toFontStack(family, mono);\n onUploaded({ label: `${family} (this session)`, value: stack }, file, family);\n onChange(stack);\n } catch (cause) {\n setError(\n cause instanceof Error ? cause.message : 'That file could not be loaded as a font.',\n );\n }\n },\n [mono, onChange, onUploaded],\n );\n\n const unavailable = !isFontStackAvailable(value);\n\n return (\n <div className=\"eidos-theme-editor-row eidos-theme-editor-row--wide\">\n <span className=\"eidos-theme-editor-row__label\">{label}</span>\n\n <div className=\"eidos-theme-editor-row__control\">\n <Select\n options={selectOptions}\n value={value}\n onChange={(next) => onChange(Array.isArray(next) ? next[0] : next)}\n fullWidth\n />\n </div>\n\n {/* Occasional - lives before the action so it can never displace it. */}\n <div className=\"eidos-theme-editor-row__diagnostics\">\n {unavailable && (\n <Tooltip\n message={`${firstFamily(value)} isn't available here, so the browser is rendering the next family in the stack instead. Upload the file, or have your app serve it, to use it.`}\n >\n <span className=\"eidos-theme-editor-badge eidos-theme-editor-badge--aa-large\">\n <TriangleAlert className=\"eidos-theme-editor-badge__icon\" />\n Not installed\n </span>\n </Tooltip>\n )}\n {error && (\n <Tooltip message={error}>\n <span className=\"eidos-theme-editor-badge eidos-theme-editor-badge--fail\">\n <CircleX className=\"eidos-theme-editor-badge__icon\" />\n Upload failed\n </span>\n </Tooltip>\n )}\n </div>\n\n {allowUpload && (\n <div className=\"eidos-theme-editor-row__actions\">\n <Button\n variant=\"text\"\n color=\"secondary\"\n size=\"sm\"\n preIcon=\"Upload\"\n onClick={() => inputRef.current?.click()}\n tooltip=\"Load a font file to use it right away. Not saved - see onFontUpload to persist it.\"\n >\n Upload\n </Button>\n {/* Plain input rather than FileUpload: this is a single inline\n action in a dense row, not a drop zone. */}\n <input\n ref={inputRef}\n type=\"file\"\n accept={FONT_ACCEPT}\n className=\"eidos-theme-editor-file\"\n onChange={(event) => {\n void handleFile(event.target.files?.[0]);\n // Allow re-picking the same file after a failure.\n event.target.value = '';\n }}\n />\n </div>\n )}\n </div>\n );\n};\n\n/**\n * Live editor for the active `ThemeProvider` theme.\n *\n * Renders no preview of its own: the provider writes tokens to\n * `document.documentElement`, so the surrounding page is the preview.\n *\n * Contrast readouts are diagnostics only - a colour that fails is still\n * applied. Automatically \"correcting\" a chosen colour would mean silently\n * rendering something other than what was picked.\n */\nexport const ThemeEditor: React.FC<ThemeEditorProps> = ({\n colors = THEME_COLOR_KEYS as ThemeColorKey[],\n fontOptions = FONT_STACKS,\n monoFontOptions = MONO_STACKS,\n allowFontUpload = true,\n onFontUpload,\n hideTypography = false,\n hideReset = false,\n hideCssExport = false,\n className,\n}) => {\n const { theme, resolvedTheme, setTheme, updateTheme, resetTheme, isDefault, toCss } = useTheme();\n const [copied, setCopied] = useState(false);\n\n // Fonts registered through the upload control this session. Kept local\n // because font *data* cannot live in a ThemeConfig - see the .mdx.\n const [uploaded, setUploaded] = useState<ThemeFontOption[]>([]);\n const [monoUploaded, setMonoUploaded] = useState<ThemeFontOption[]>([]);\n\n const setColor = useCallback(\n (key: ThemeColorKey, hex: string) => {\n const current = theme.colors?.[key];\n // Only the base is written here. A `contrast` pinned in the theme is\n // preserved rather than dropped, since the editor never sets one.\n const pinned = typeof current === 'object' ? current.contrast : undefined;\n updateTheme({ colors: { [key]: pinned ? { base: hex, contrast: pinned } : hex } });\n },\n [theme.colors, updateTheme],\n );\n\n const resetColor = useCallback(\n (key: ThemeColorKey) => {\n // Must go through setTheme, not updateTheme: updateTheme merges, so a key\n // left out of the patch keeps its existing value instead of being removed.\n // Removal is what restores the preset, since resolveTheme fills gaps.\n const nextColors = { ...theme.colors };\n delete nextColors[key];\n setTheme({ ...theme, colors: nextColors });\n },\n [theme, setTheme],\n );\n\n const diagnostics = useMemo(\n () =>\n colors.map((key) => {\n const { base, contrast } = resolvedTheme.colors[key];\n return { key, base, ...diagnose(key, base, contrast) };\n }),\n [colors, resolvedTheme],\n );\n\n const failing = diagnostics.filter((d) => d.grade === 'fail');\n const invisible = diagnostics.filter((d) => d.edgeWarning);\n\n const copyCss = useCallback(() => {\n const css = toCss();\n const done = () => {\n setCopied(true);\n setTimeout(() => setCopied(false), 1600);\n };\n if (navigator.clipboard?.writeText) {\n navigator.clipboard.writeText(css).then(done, done);\n return;\n }\n done();\n }, [toCss]);\n\n return (\n <div className={['eidos-theme-editor', className].filter(Boolean).join(' ')}>\n {(failing.length > 0 || invisible.length > 0) && (\n <Alert variant=\"warning\" title=\"Contrast issues\">\n {failing.length > 0 && (\n <p>\n Cannot reach WCAG AA (4.5:1) with either foreground:{' '}\n {failing.map((d) => COLOR_LABELS[d.key]).join(', ')}.\n </p>\n )}\n {invisible.length > 0 && (\n <p>\n Too close to the page background to read as a filled surface:{' '}\n {invisible.map((d) => COLOR_LABELS[d.key]).join(', ')}.\n </p>\n )}\n </Alert>\n )}\n\n <Card variant=\"outlined\" padding=\"lg\" className=\"eidos-theme-editor-section\">\n <div className=\"eidos-theme-editor-section__header\">\n <span className=\"eidos-theme-editor-section__title\">Colours</span>\n <span className=\"eidos-theme-editor-section__hint\">\n Shades, tints and foregrounds are derived from each base colour.\n </span>\n </div>\n\n <div className=\"eidos-theme-editor-rows\">\n {diagnostics.map(({ key, base, grade, ratio, detail, edgeWarning }) => {\n const badge = GRADE_BADGE[grade];\n const overridden = theme.colors?.[key] !== undefined;\n return (\n <div className=\"eidos-theme-editor-row\" key={key}>\n <span className=\"eidos-theme-editor-row__label\">{COLOR_LABELS[key]}</span>\n\n {/* `md` rather than `sm`: the size drives the panel's\n saturation/brightness canvas, and 120px is cramped for\n picking a lightness precisely. */}\n <ColorPicker value={base} onChange={(hex) => setColor(key, hex)} size=\"md\" />\n\n <div className=\"eidos-theme-editor-row__diagnostics\">\n <Tooltip message={detail}>\n <span className={`eidos-theme-editor-badge eidos-theme-editor-badge--${grade}`}>\n <badge.icon className=\"eidos-theme-editor-badge__icon\" />\n {`${ratio.toFixed(2)}:1 ${badge.label}`}\n </span>\n </Tooltip>\n {edgeWarning && (\n <Tooltip message={edgeWarning}>\n <span className=\"eidos-theme-editor-badge eidos-theme-editor-badge--fail\">\n <CircleX className=\"eidos-theme-editor-badge__icon\" />\n Invisible on page\n </span>\n </Tooltip>\n )}\n </div>\n\n <div className=\"eidos-theme-editor-row__actions\">\n <IconButton\n icon=\"RotateCcw\"\n variant=\"text\"\n color=\"secondary\"\n size=\"sm\"\n disabled={!overridden}\n onClick={() => resetColor(key)}\n tooltip={overridden ? `Reset ${COLOR_LABELS[key]} to the preset` : 'Unchanged'}\n aria-label={`Reset ${COLOR_LABELS[key]}`}\n />\n </div>\n </div>\n );\n })}\n </div>\n </Card>\n\n {!hideTypography && (\n <Card variant=\"outlined\" padding=\"lg\" className=\"eidos-theme-editor-section\">\n <div className=\"eidos-theme-editor-section__header\">\n <span className=\"eidos-theme-editor-section__title\">Typography</span>\n <span className=\"eidos-theme-editor-section__hint\">\n A stack only names a font - upload the file to use one the browser doesn&apos;t\n already have. Spacing is declared in <code>em</code>, so the scale moves padding with\n the text.\n </span>\n </div>\n\n <div className=\"eidos-theme-editor-rows\">\n <FontRow\n label=\"Body font\"\n value={resolvedTheme.typography.fontFamily}\n options={[...fontOptions, ...uploaded]}\n mono={false}\n allowUpload={allowFontUpload}\n onChange={(fontFamily) => updateTheme({ typography: { fontFamily } })}\n onUploaded={(option, file, family) => {\n setUploaded((prev) => [...prev, option]);\n onFontUpload?.(file, family, false);\n }}\n />\n\n <FontRow\n label=\"Mono font\"\n value={resolvedTheme.typography.monoFamily}\n options={[...monoFontOptions, ...monoUploaded]}\n mono\n allowUpload={allowFontUpload}\n onChange={(monoFamily) => updateTheme({ typography: { monoFamily } })}\n onUploaded={(option, file, family) => {\n setMonoUploaded((prev) => [...prev, option]);\n onFontUpload?.(file, family, true);\n }}\n />\n\n <div className=\"eidos-theme-editor-row eidos-theme-editor-row--wide\">\n <span className=\"eidos-theme-editor-row__label\">Scale</span>\n <div className=\"eidos-theme-editor-row__control\">\n <Slider\n value={resolvedTheme.typography.fontScale}\n min={FONT_SCALE_MIN}\n max={FONT_SCALE_MAX}\n step={0.025}\n showValue\n showMinMax\n unit=\"×\"\n onChange={(value) => updateTheme({ typography: { fontScale: value } })}\n />\n </div>\n </div>\n </div>\n </Card>\n )}\n\n {(!hideReset || !hideCssExport) && (\n <div className=\"eidos-theme-editor-actions\">\n {!hideReset && (\n <Button variant=\"outlined\" color=\"secondary\" disabled={isDefault} onClick={resetTheme}>\n Reset all\n </Button>\n )}\n {!hideCssExport && (\n <Button\n variant=\"outlined\"\n preIcon={copied ? 'Check' : 'Clipboard'}\n onClick={copyCss}\n tooltip=\"Copy the resolved tokens as a :root block you can paste into your own stylesheet\"\n >\n {copied ? 'Copied' : 'Copy as CSS'}\n </Button>\n )}\n </div>\n )}\n </div>\n );\n};\n\nThemeEditor.displayName = 'ThemeEditor';\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-WFEORBLC.cjs","../src/components/Pagination/Pagination.component.tsx"],"names":[],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-WFEORBLC.cjs","sourcesContent":[null,"import React, { useState, useEffect } from 'react';\nimport { ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react';\nimport { Button } from '../Button';\nimport { Select } from '../Select';\nimport type { PaginationProps } from './Pagination.types';\n\n// ── Ellipsis range helper ─────────────────────────────────────────────────────\n\nconst ELLIPSIS = 'ellipsis' as const;\ntype PageItem = number | typeof ELLIPSIS;\n\nfunction usePaginationRange(page: number, totalPages: number, siblingCount: number): PageItem[] {\n const totalShown = siblingCount * 2 + 5;\n\n if (totalPages <= totalShown) {\n return Array.from({ length: totalPages }, (_, i) => i + 1);\n }\n\n const leftSibling = Math.max(page - siblingCount, 1);\n const rightSibling = Math.min(page + siblingCount, totalPages);\n\n const showLeftEllipsis = leftSibling > 2;\n const showRightEllipsis = rightSibling < totalPages - 1;\n\n const items: PageItem[] = [1];\n\n if (showLeftEllipsis) {\n items.push(ELLIPSIS);\n } else {\n for (let i = 2; i < leftSibling; i++) items.push(i);\n }\n\n for (let i = leftSibling; i <= rightSibling; i++) {\n if (i !== 1 && i !== totalPages) items.push(i);\n }\n\n if (showRightEllipsis) {\n items.push(ELLIPSIS);\n } else {\n for (let i = rightSibling + 1; i < totalPages; i++) items.push(i);\n }\n\n items.push(totalPages);\n\n return items;\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const Pagination: React.FC<PaginationProps> = ({\n page,\n totalPages,\n onChange,\n siblingCount = 1,\n showFirstLast = false,\n color = 'primary',\n size = 'sm',\n disabled = false,\n className = '',\n // Table-footer mode\n totalItems,\n pageSize,\n onPageSizeChange,\n pageSizeOptions = [10, 25, 50, 100],\n}) => {\n const items = usePaginationRange(page, totalPages, siblingCount);\n\n // ── Active-page editable input ─────────────────────────────────────────────\n const [inputValue, setInputValue] = useState(String(page));\n useEffect(() => {\n setInputValue(String(page));\n }, [page]);\n\n const commitInput = (raw: string) => {\n const val = parseInt(raw, 10);\n if (!isNaN(val) && val >= 1 && val <= totalPages && val !== page) {\n onChange(val);\n } else {\n setInputValue(String(page)); // revert on invalid or same-page\n }\n };\n\n // ── Layout flags ───────────────────────────────────────────────────────────\n const hasResults = totalItems !== undefined && pageSize !== undefined;\n const hasPageSize = !!onPageSizeChange && pageSize !== undefined;\n const tableMode = hasResults || hasPageSize;\n\n const startItem = hasResults ? (totalItems === 0 ? 0 : (page - 1) * pageSize! + 1) : undefined;\n const endItem = hasResults ? Math.min(page * pageSize!, totalItems!) : undefined;\n\n const rootClasses = [\n 'eidos-pagination',\n `eidos-pagination--${size}`,\n tableMode && 'eidos-pagination--table',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n // ── Navigation block ───────────────────────────────────────────────────────\n const nav = (\n <nav aria-label=\"Pagination\" className=\"eidos-pagination-nav\">\n {showFirstLast && (\n <Button\n variant=\"text\"\n size={size}\n icon={ChevronsLeft}\n disabled={page === 1 || disabled}\n onClick={() => onChange(1)}\n aria-label=\"First page\"\n />\n )}\n\n <Button\n variant=\"text\"\n size={size}\n icon={ChevronLeft}\n disabled={page === 1 || disabled}\n onClick={() => onChange(page - 1)}\n aria-label=\"Previous page\"\n />\n\n {items.map((item, i) =>\n item === ELLIPSIS ? (\n <span key={`ellipsis-${i}`} className=\"eidos-pagination-ellipsis\" aria-hidden=\"true\">\n …\n </span>\n ) : item === page ? (\n // Active page - styled as filled chip, editable via keyboard/click\n <input\n key=\"active-page\"\n type=\"text\"\n inputMode=\"numeric\"\n className={[\n 'eidos-pagination-page',\n 'eidos-pagination-page--active',\n `eidos-pagination-page--${color}`,\n ].join(' ')}\n value={inputValue}\n onChange={(e) => setInputValue(e.target.value)}\n onFocus={(e) => e.target.select()}\n onBlur={() => commitInput(inputValue)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n commitInput(inputValue);\n e.currentTarget.blur();\n } else if (e.key === 'Escape') {\n setInputValue(String(page));\n e.currentTarget.blur();\n }\n }}\n aria-label={`Page ${page} of ${totalPages}, editable`}\n aria-current=\"page\"\n disabled={disabled}\n />\n ) : (\n <button\n key={item}\n type=\"button\"\n className=\"eidos-pagination-page\"\n onClick={() => onChange(item)}\n disabled={disabled}\n aria-label={`Page ${item}`}\n >\n {item}\n </button>\n ),\n )}\n\n <Button\n variant=\"text\"\n size={size}\n icon={ChevronRight}\n disabled={page === totalPages || disabled}\n onClick={() => onChange(page + 1)}\n aria-label=\"Next page\"\n />\n\n {showFirstLast && (\n <Button\n variant=\"text\"\n size={size}\n icon={ChevronsRight}\n disabled={page === totalPages || disabled}\n onClick={() => onChange(totalPages)}\n aria-label=\"Last page\"\n />\n )}\n </nav>\n );\n\n // ── Standalone mode ────────────────────────────────────────────────────────\n if (!tableMode) {\n return <div className={rootClasses}>{nav}</div>;\n }\n\n // ── Table-footer mode ──────────────────────────────────────────────────────\n return (\n <div className={rootClasses}>\n {hasResults && (\n <span className=\"eidos-pagination-info\">\n Showing {startItem}-{endItem} of {totalItems} results\n </span>\n )}\n\n <div className=\"eidos-pagination-controls\">\n {hasPageSize && (\n <div className=\"eidos-pagination-size\">\n <label className=\"eidos-pagination-size-label\">Show:</label>\n <Select\n value={String(pageSize)}\n onChange={(val) => onPageSizeChange(Number(val))}\n options={pageSizeOptions.map((n) => ({\n id: String(n),\n value: String(n),\n label: String(n),\n }))}\n clearable={false}\n disabled={disabled}\n // Sizes the whole field, chevron and padding included - wide\n // enough for a 3-digit page size. `'3ch'` used to work here only\n // because `Input.width` sized the inner <input> and let the\n // chrome overflow it; now that it sizes the field, 3ch left no\n // room for the value at all.\n inputProps={{ size: 'sm', width: 72 }}\n />\n </div>\n )}\n\n {totalPages > 1 && nav}\n </div>\n </div>\n );\n};\n\nPagination.displayName = 'Pagination';\n"]}
@@ -1,69 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
-
3
- var _chunkZ5U4NJTWcjs = require('./chunk-Z5U4NJTW.cjs');
4
-
5
-
6
- var _chunkCUJBNRJJcjs = require('./chunk-CUJBNRJJ.cjs');
7
-
8
- // src/components/Button/Button.component.tsx
9
- var _lucidereact = require('lucide-react');
10
- var _jsxruntime = require('react/jsx-runtime');
11
- var Button = ({
12
- variant = "filled",
13
- color = "primary",
14
- size = "md",
15
- disabled = false,
16
- loading = false,
17
- loadingText = "Loading...",
18
- tooltip,
19
- className = "",
20
- preIcon,
21
- posIcon,
22
- icon,
23
- children,
24
- ...buttonProps
25
- }) => {
26
- const isIconOnly = !!icon;
27
- if (isIconOnly) {
28
- if (children) {
29
- throw new Error("Icon-only buttons cannot have children");
30
- }
31
- if (preIcon) {
32
- throw new Error("Icon-only buttons cannot have preIcon");
33
- }
34
- if (posIcon) {
35
- throw new Error("Icon-only buttons cannot have posIcon");
36
- }
37
- }
38
- const buttonClasses = [
39
- "eidos-button",
40
- `eidos-button--${variant}`,
41
- `eidos-button--${color}`,
42
- `eidos-button--${size}`,
43
- isIconOnly && "eidos-button--icon-only",
44
- disabled && "eidos-button--disabled",
45
- loading && "eidos-button--loading",
46
- className
47
- ].filter(Boolean).join(" ");
48
- const buttonContent = /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "button", { type: "button", ...buttonProps, className: buttonClasses, disabled: disabled || loading, children: loading ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
49
- !isIconOnly && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
50
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "eidos-button--loading-spinner", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.LoaderCircle, { className: "eidos-button--spinner-icon" }) }),
51
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "eidos-button--copy", children: loadingText })
52
- ] }),
53
- isIconOnly && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "eidos-button--loading-spinner", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _lucidereact.LoaderCircle, { className: "eidos-button--spinner-icon" }) })
54
- ] }) : /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
55
- preIcon && _chunkCUJBNRJJcjs.renderIcon.call(void 0, preIcon, "eidos-button--pre-icon"),
56
- isIconOnly && icon ? _chunkCUJBNRJJcjs.renderIcon.call(void 0, icon, "eidos-button--icon") : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "eidos-button--copy", children }),
57
- posIcon && _chunkCUJBNRJJcjs.renderIcon.call(void 0, posIcon, "eidos-button--pos-icon")
58
- ] }) });
59
- return tooltip ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZ5U4NJTWcjs.Tooltip, { message: tooltip, children: buttonContent }) : buttonContent;
60
- };
61
- var IconButton = (props) => {
62
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, Button, { ...props });
63
- };
64
-
65
-
66
-
67
-
68
- exports.Button = Button; exports.IconButton = IconButton;
69
- //# sourceMappingURL=chunk-WWYJNWJY.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-WWYJNWJY.cjs","../src/components/Button/Button.component.tsx"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACE;AACF,wDAA6B;AAC7B;AACA;ACNA,2CAA6B;AAoDjB,+CAAA;AA/CL,IAAM,OAAA,EAAgC,CAAC;AAAA,EAC5C,QAAA,EAAU,QAAA;AAAA,EACV,MAAA,EAAQ,SAAA;AAAA,EACR,KAAA,EAAO,IAAA;AAAA,EACP,SAAA,EAAW,KAAA;AAAA,EACX,QAAA,EAAU,KAAA;AAAA,EACV,YAAA,EAAc,YAAA;AAAA,EACd,OAAA;AAAA,EACA,UAAA,EAAY,EAAA;AAAA,EACZ,OAAA;AAAA,EACA,OAAA;AAAA,EACA,IAAA;AAAA,EACA,QAAA;AAAA,EACA,GAAG;AACL,CAAA,EAAA,GAAM;AACJ,EAAA,MAAM,WAAA,EAAa,CAAC,CAAC,IAAA;AAErB,EAAA,GAAA,CAAI,UAAA,EAAY;AACd,IAAA,GAAA,CAAI,QAAA,EAAU;AACZ,MAAA,MAAM,IAAI,KAAA,CAAM,wCAAwC,CAAA;AAAA,IAC1D;AACA,IAAA,GAAA,CAAI,OAAA,EAAS;AACX,MAAA,MAAM,IAAI,KAAA,CAAM,uCAAuC,CAAA;AAAA,IACzD;AACA,IAAA,GAAA,CAAI,OAAA,EAAS;AACX,MAAA,MAAM,IAAI,KAAA,CAAM,uCAAuC,CAAA;AAAA,IACzD;AAAA,EACF;AAEA,EAAA,MAAM,cAAA,EAAgB;AAAA,IACpB,cAAA;AAAA,IACA,CAAA,cAAA,EAAiB,OAAO,CAAA,CAAA;AACF,IAAA;AACD,IAAA;AACP,IAAA;AACF,IAAA;AACD,IAAA;AACX,IAAA;AAGS,EAAA;AAGT,EAAA;AAIQ,IAAA;AACG,sBAAA;AAGA,sBAAA;AACH,IAAA;AAGA,IAAA;AAMJ,EAAA;AACyB,IAAA;AAGrB,IAAA;AAKqB,IAAA;AAG7B,EAAA;AAGe,EAAA;AACnB;AAagE;AACtD,EAAA;AACV;ADnC6B;AACA;AACA;AACA;AACA","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-WWYJNWJY.cjs","sourcesContent":[null,"import React from 'react';\nimport { LoaderCircle } from 'lucide-react';\nimport { Tooltip } from '../Tooltip';\nimport { ButtonProps, IconButtonProps } from './Button.types';\nimport { renderIcon } from '../../utils';\n\nexport const Button: React.FC<ButtonProps> = ({\n variant = 'filled',\n color = 'primary',\n size = 'md',\n disabled = false,\n loading = false,\n loadingText = 'Loading...',\n tooltip,\n className = '',\n preIcon,\n posIcon,\n icon,\n children,\n ...buttonProps\n}) => {\n const isIconOnly = !!icon;\n\n if (isIconOnly) {\n if (children) {\n throw new Error('Icon-only buttons cannot have children');\n }\n if (preIcon) {\n throw new Error('Icon-only buttons cannot have preIcon');\n }\n if (posIcon) {\n throw new Error('Icon-only buttons cannot have posIcon');\n }\n }\n\n const buttonClasses = [\n 'eidos-button',\n `eidos-button--${variant}`,\n `eidos-button--${color}`,\n `eidos-button--${size}`,\n isIconOnly && 'eidos-button--icon-only',\n disabled && 'eidos-button--disabled',\n loading && 'eidos-button--loading',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const buttonContent = (\n <button type=\"button\" {...buttonProps} className={buttonClasses} disabled={disabled || loading}>\n {loading ? (\n <>\n {!isIconOnly && (\n <>\n <span className=\"eidos-button--loading-spinner\">\n <LoaderCircle className=\"eidos-button--spinner-icon\" />\n </span>\n <span className=\"eidos-button--copy\">{loadingText}</span>\n </>\n )}\n {isIconOnly && (\n <span className=\"eidos-button--loading-spinner\">\n <LoaderCircle className=\"eidos-button--spinner-icon\" />\n </span>\n )}\n </>\n ) : (\n <>\n {preIcon && renderIcon(preIcon, 'eidos-button--pre-icon')}\n\n {isIconOnly && icon ? (\n renderIcon(icon, 'eidos-button--icon')\n ) : (\n <span className=\"eidos-button--copy\">{children}</span>\n )}\n\n {posIcon && renderIcon(posIcon, 'eidos-button--pos-icon')}\n </>\n )}\n </button>\n );\n\n return tooltip ? <Tooltip message={tooltip}>{buttonContent}</Tooltip> : buttonContent;\n};\n\n/**\n * IconButton - Convenience wrapper for icon-only buttons\n *\n * @example\n * ```tsx\n * import { IconButton } from 'eidos-ui';\n * import { Plus } from 'lucide-react';\n *\n * <IconButton icon={Plus} tooltip=\"Add item\" />\n * ```\n */\nexport const IconButton: React.FC<IconButtonProps> = (props) => {\n return <Button {...props} />;\n};\n"]}