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-R3WPVTGY.cjs","../src/components/Conversation/Chat.component.tsx","../src/components/Conversation/MessageComposer.component.tsx","../src/components/Conversation/Conversation.hooks.ts","../src/components/Conversation/Conversation.utils.ts","../src/components/Conversation/MessageItem.component.tsx","../src/components/Conversation/CommentThread.component.tsx"],"names":["useRef","useCallback","Fragment","Paperclip","jsx","jsxs"],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-R3WPVTGY.cjs","sourcesContent":[null,"import React, { useCallback, useMemo } from 'react';\nimport { ArrowDown, MessagesSquare } from 'lucide-react';\nimport { Button } from '../Button';\nimport { EmptyState } from '../EmptyState';\nimport { Skeleton } from '../Skeleton';\nimport { Spinner } from '../Spinner';\nimport { MessageComposer } from './MessageComposer.component';\nimport { MessageItem } from './MessageItem.component';\nimport type { ChatProps, ConversationMessage } from './Conversation.types';\nimport {\n useLoadMoreOnScrollTop,\n useNewMessageIds,\n useScrollAnchor,\n useStickToBottom,\n} from './Conversation.hooks';\nimport { classNames, continuesRun, formatDateSeparator, isSameDay } from './Conversation.utils';\n\nconst ChatSkeleton: React.FC = () => (\n <div className=\"eidos-chat-skeleton\" aria-hidden=\"true\">\n {[0, 1, 2, 3].map((row) => (\n <div\n key={row}\n className={classNames(\n 'eidos-chat-skeleton-row',\n row % 2 === 1 && 'eidos-chat-skeleton-row--own',\n )}\n >\n <Skeleton variant=\"circular\" width={28} height={28} />\n <Skeleton variant=\"rounded\" height={48} />\n </div>\n ))}\n </div>\n);\n\n/**\n * A bottom-anchored message thread with a composer.\n *\n * Controlled and transport-agnostic: it renders the `messages` it is given and\n * reports intent through `onSend` / `onLoadMore`. It never fetches, so the\n * same array can be persisted and replayed verbatim.\n */\nexport const Chat: React.FC<ChatProps> = ({\n messages,\n currentUserId,\n onSend,\n onRetry,\n readOnly = false,\n readOnlyMessage,\n renderBody,\n messageActions,\n renderMessageFooter,\n loading = false,\n emptyContent,\n hasMore = false,\n loadingMore = false,\n onLoadMore,\n groupConsecutive = true,\n groupWindowMinutes = 5,\n showDateSeparators = true,\n formatTimestamp,\n animateNewMessages = true,\n allowAttachments = false,\n attachmentAccept,\n typing,\n unreadFromId,\n placeholder = 'Write a message...',\n sendOnEnter = true,\n ariaLabel = 'Conversation',\n className = '',\n}) => {\n const { scrollRef, atBottom, scrollToBottom, handleScroll } = useStickToBottom(messages);\n const newIds = useNewMessageIds(messages, animateNewMessages);\n useScrollAnchor(scrollRef, messages);\n\n const loadMoreOnScroll = useLoadMoreOnScrollTop(scrollRef, { hasMore, loadingMore, onLoadMore });\n\n const onScroll = useCallback(() => {\n handleScroll();\n loadMoreOnScroll();\n }, [handleScroll, loadMoreOnScroll]);\n\n const byId = useMemo(() => new Map(messages.map((message) => [message.id, message])), [messages]);\n\n // An id that isn't in the current page must not draw a divider above\n // nothing - it would float at the top of whatever happens to be loaded.\n const unreadIndex = useMemo(() => {\n if (!unreadFromId) return -1;\n return messages.findIndex((message) => message.id === unreadFromId);\n }, [messages, unreadFromId]);\n\n const showComposer = !readOnly && Boolean(onSend);\n const isEmpty = !loading && messages.length === 0;\n\n const renderMessage = (message: ConversationMessage, index: number) => {\n const previous = messages[index - 1];\n const actions = readOnly ? undefined : messageActions?.(message);\n const quoted = message.parentId ? byId.get(message.parentId) : undefined;\n\n const nodes: React.ReactNode[] = [];\n\n if (showDateSeparators && (!previous || !isSameDay(previous.sentAt, message.sentAt))) {\n nodes.push(\n <li key={`date-${message.id}`} className=\"eidos-conversation-separator\">\n <span>{formatDateSeparator(message.sentAt)}</span>\n </li>,\n );\n }\n\n if (index === unreadIndex) {\n nodes.push(\n <li\n key={`unread-${message.id}`}\n className=\"eidos-conversation-separator eidos-conversation-separator--unread\"\n >\n <span>Unread messages</span>\n </li>,\n );\n }\n\n // A separator breaks a run: the message under it needs its own header.\n const grouped =\n groupConsecutive &&\n index !== unreadIndex &&\n nodes.length === 0 &&\n continuesRun(previous, message, groupWindowMinutes);\n\n nodes.push(\n <MessageItem\n key={message.id}\n message={message}\n layout=\"chat\"\n isOwn={Boolean(currentUserId) && message.author.id === currentUserId}\n grouped={grouped}\n isNew={newIds.has(message.id)}\n actions={actions}\n footer={renderMessageFooter?.(message)}\n body={renderBody?.(message)}\n onRetry={!readOnly && onRetry ? () => onRetry(message) : undefined}\n quoted={quoted}\n formatTimestamp={formatTimestamp}\n />,\n );\n\n return nodes;\n };\n\n return (\n <div className={classNames('eidos-chat', className)}>\n <div\n ref={scrollRef}\n className=\"eidos-chat-scroll\"\n onScroll={onScroll}\n role=\"log\"\n aria-live=\"polite\"\n aria-relevant=\"additions\"\n aria-busy={loading}\n aria-label={ariaLabel}\n tabIndex={0}\n >\n {hasMore && (\n <div className=\"eidos-chat-load-more\">\n {loadingMore ? (\n <span className=\"eidos-chat-loading-more\">\n <Spinner size=\"sm\" color=\"secondary\" />\n Loading earlier messages…\n </span>\n ) : (\n <Button variant=\"text\" color=\"secondary\" size=\"sm\" onClick={onLoadMore}>\n Load earlier messages\n </Button>\n )}\n </div>\n )}\n\n {loading && <ChatSkeleton />}\n\n {isEmpty &&\n (emptyContent ?? (\n <EmptyState\n icon={<MessagesSquare />}\n title=\"No messages yet\"\n description={\n showComposer ? 'Send the first message to start the conversation.' : undefined\n }\n size=\"sm\"\n />\n ))}\n\n {!loading && messages.length > 0 && (\n <ol className=\"eidos-conversation-list\">{messages.map(renderMessage)}</ol>\n )}\n\n {typing && typing.length > 0 && (\n <div className=\"eidos-chat-typing\" aria-live=\"polite\">\n <span className=\"eidos-chat-typing-dots\" aria-hidden=\"true\">\n <i />\n <i />\n <i />\n </span>\n {typing.length === 1\n ? `${typing[0].name} is typing…`\n : `${typing.length} people are typing…`}\n </div>\n )}\n </div>\n\n {!atBottom && messages.length > 0 && (\n <div className=\"eidos-chat-jump\">\n <Button\n size=\"sm\"\n variant=\"filled\"\n color=\"secondary\"\n preIcon={ArrowDown}\n onClick={() => scrollToBottom('smooth')}\n >\n Latest\n </Button>\n </div>\n )}\n\n {showComposer && (\n <div className=\"eidos-chat-composer\">\n <MessageComposer\n onSubmit={onSend as NonNullable<typeof onSend>}\n placeholder={placeholder}\n sendOnEnter={sendOnEnter}\n allowAttachments={allowAttachments}\n attachmentAccept={attachmentAccept}\n />\n </div>\n )}\n\n {readOnly && readOnlyMessage && (\n <p className=\"eidos-conversation-readonly\">{readOnlyMessage}</p>\n )}\n </div>\n );\n};\n\nChat.displayName = 'Chat';\n","import React, { useCallback, useId, useRef, useState } from 'react';\nimport { Paperclip, Send, X } from 'lucide-react';\nimport { Avatar } from '../Avatar';\nimport { Button } from '../Button';\nimport { Chip } from '../Chip';\nimport type { MessageComposerProps } from './Conversation.types';\nimport { useAutoGrow } from './Conversation.hooks';\nimport { classNames, formatFileSize } from './Conversation.utils';\n\n/**\n * The authoring half of a conversation - a growing text field, optional\n * attachments, and a submit control.\n *\n * Exported on its own so a consumer building a bespoke list can still reuse\n * the input, and used internally by both `Chat` and `CommentThread`.\n */\nexport const MessageComposer: React.FC<MessageComposerProps> = ({\n value,\n onChange,\n onSubmit,\n onCancel,\n placeholder = 'Write a message...',\n submitLabel = 'Send',\n sendOnEnter = false,\n parentId,\n replyingTo,\n allowAttachments = false,\n attachmentAccept,\n author,\n disabled = false,\n requireBody = true,\n autoFocus = false,\n maxLength,\n className = '',\n}) => {\n const [internalBody, setInternalBody] = useState('');\n const [attachments, setAttachments] = useState<File[]>([]);\n const [pending, setPending] = useState(false);\n\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const fileInputId = useId();\n\n const isControlled = value !== undefined;\n const body = isControlled ? value : internalBody;\n\n useAutoGrow(textareaRef, body);\n\n const setBody = useCallback(\n (next: string) => {\n if (!isControlled) setInternalBody(next);\n onChange?.(next);\n },\n [isControlled, onChange],\n );\n\n const canSubmit = !disabled && !pending && (!requireBody || body.trim().length > 0);\n\n const submit = useCallback(async () => {\n if (!canSubmit) return;\n\n const draft = { body: body.trim(), attachments, parentId };\n const result = onSubmit(draft);\n\n // A synchronous handler clears immediately; an async one keeps the draft\n // until it resolves, so a rejected send never loses what was typed.\n if (!(result instanceof Promise)) {\n setBody('');\n setAttachments([]);\n return;\n }\n\n setPending(true);\n try {\n await result;\n setBody('');\n setAttachments([]);\n } catch {\n // Intentionally swallowed: the draft stays put and the consumer owns\n // error reporting (a Snackbar, an Alert, ...). Rethrowing here would\n // surface as an unhandled rejection from a keystroke.\n } finally {\n setPending(false);\n }\n }, [canSubmit, body, attachments, parentId, onSubmit, setBody]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (!sendOnEnter) return;\n // Shift+Enter always inserts a newline; the modifier check also leaves\n // IME composition (which fires Enter to commit) alone.\n if (event.key !== 'Enter' || event.shiftKey || event.nativeEvent.isComposing) return;\n event.preventDefault();\n void submit();\n };\n\n const addFiles = (list: FileList | null) => {\n if (!list?.length) return;\n setAttachments((current) => [...current, ...Array.from(list)]);\n // Reset so selecting the same file twice in a row still fires `change`.\n if (fileInputRef.current) fileInputRef.current.value = '';\n };\n\n const removeAttachment = (index: number) =>\n setAttachments((current) => current.filter((_, i) => i !== index));\n\n return (\n <form\n className={classNames('eidos-message-composer', className)}\n onSubmit={(event) => {\n event.preventDefault();\n void submit();\n }}\n >\n {replyingTo && (\n <div className=\"eidos-composer-reply-hint\">\n <span>\n Replying to <strong>{replyingTo}</strong>\n </span>\n {onCancel && (\n <Button\n variant=\"text\"\n color=\"secondary\"\n size=\"sm\"\n icon={X}\n onClick={onCancel}\n tooltip=\"Cancel reply\"\n aria-label=\"Cancel reply\"\n />\n )}\n </div>\n )}\n\n <div className=\"eidos-composer-row\">\n {author && (\n <div className=\"eidos-composer-avatar\">\n <Avatar\n src={author.avatarSrc}\n name={author.name}\n size=\"sm\"\n color={author.avatarColor ?? 'gray'}\n />\n </div>\n )}\n\n <div className=\"eidos-composer-field\">\n <textarea\n ref={textareaRef}\n className=\"eidos-composer-input\"\n value={body}\n onChange={(event) => setBody(event.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled || pending}\n rows={1}\n maxLength={maxLength}\n // Opt-in and off by default. Only the reply composer sets it, where\n // the field appears in direct response to the user pressing Reply -\n // moving focus there is the expected outcome, not a hijack.\n autoFocus={autoFocus}\n aria-label={placeholder}\n />\n\n {attachments.length > 0 && (\n <ul className=\"eidos-composer-attachments\">\n {attachments.map((file, index) => (\n <li key={`${file.name}-${file.size}-${index}`}>\n <Chip\n size=\"sm\"\n variant=\"outlined\"\n color=\"secondary\"\n preIcon={Paperclip}\n onRemove={() => removeAttachment(index)}\n >\n {file.name}\n {file.size > 0 && ` (${formatFileSize(file.size)})`}\n </Chip>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n\n <div className=\"eidos-composer-actions\">\n <div className=\"eidos-composer-actions-start\">\n {allowAttachments && (\n <>\n <input\n ref={fileInputRef}\n id={fileInputId}\n type=\"file\"\n multiple\n accept={attachmentAccept}\n className=\"eidos-composer-file-input\"\n onChange={(event) => addFiles(event.target.files)}\n disabled={disabled || pending}\n />\n <Button\n variant=\"text\"\n color=\"secondary\"\n size=\"sm\"\n icon={Paperclip}\n tooltip=\"Attach files\"\n aria-label=\"Attach files\"\n disabled={disabled || pending}\n onClick={() => fileInputRef.current?.click()}\n />\n </>\n )}\n {sendOnEnter && (\n <span className=\"eidos-composer-hint\">\n <kbd>Enter</kbd> to send · <kbd>Shift</kbd>+<kbd>Enter</kbd> for a new line\n </span>\n )}\n </div>\n\n <div className=\"eidos-composer-actions-end\">\n {onCancel && !replyingTo && (\n <Button\n variant=\"text\"\n color=\"secondary\"\n size=\"sm\"\n onClick={onCancel}\n disabled={pending}\n >\n Cancel\n </Button>\n )}\n <Button\n type=\"submit\"\n size=\"sm\"\n color=\"primary\"\n preIcon={Send}\n loading={pending}\n disabled={!canSubmit}\n >\n {submitLabel}\n </Button>\n </div>\n </div>\n </form>\n );\n};\n\nMessageComposer.displayName = 'MessageComposer';\n","import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport type { ConversationMessage } from './Conversation.types';\n\n/**\n * Ids that arrived *after* the first render.\n *\n * Seeded from the first commit so an initial page of history is never\n * animated - otherwise opening a thread fades forty messages in at once,\n * which reads as a glitch rather than as feedback. Only genuinely new ids get\n * the entrance class, and each one only once.\n */\nexport const useNewMessageIds = (\n messages: ConversationMessage[],\n enabled: boolean,\n): Set<string> => {\n const seen = useRef<Set<string> | null>(null);\n const [fresh, setFresh] = useState<Set<string>>(() => new Set());\n\n useEffect(() => {\n // First commit: everything already on screen counts as pre-existing.\n if (seen.current === null) {\n seen.current = new Set(messages.map((message) => message.id));\n return;\n }\n\n if (!enabled) {\n for (const message of messages) seen.current.add(message.id);\n return;\n }\n\n const added = messages.filter((message) => !seen.current?.has(message.id));\n if (!added.length) return;\n\n for (const message of added) seen.current.add(message.id);\n setFresh(new Set(added.map((message) => message.id)));\n }, [messages, enabled]);\n\n return fresh;\n};\n\ninterface StickToBottom {\n scrollRef: React.RefObject<HTMLDivElement | null>;\n /** The viewport is at (or near) the newest message. */\n atBottom: boolean;\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n handleScroll: () => void;\n}\n\n/** Distance from the bottom still counted as \"reading the newest message\". */\nconst BOTTOM_THRESHOLD_PX = 48;\n\n/**\n * Keeps a bottom-anchored list pinned to the newest message - but only while\n * the reader is already there.\n *\n * Scrolling someone back down while they are reading scrollback is the single\n * most common defect in chat UIs, so arrival of a new message only forces a\n * scroll when `atBottom` was already true. Otherwise the caller surfaces a\n * jump-to-latest affordance and leaves the viewport alone.\n */\nexport const useStickToBottom = (messages: ConversationMessage[]): StickToBottom => {\n const scrollRef = useRef<HTMLDivElement>(null);\n const [atBottom, setAtBottom] = useState(true);\n // Read during a layout effect, so it must not be state - it has to be\n // correct for the commit that is happening right now, not the next render.\n const atBottomRef = useRef(true);\n const lastCount = useRef(messages.length);\n\n const scrollToBottom = useCallback((behavior: ScrollBehavior = 'smooth') => {\n const node = scrollRef.current;\n if (!node) return;\n node.scrollTo({ top: node.scrollHeight, behavior });\n }, []);\n\n const handleScroll = useCallback(() => {\n const node = scrollRef.current;\n if (!node) return;\n const distance = node.scrollHeight - node.scrollTop - node.clientHeight;\n const next = distance <= BOTTOM_THRESHOLD_PX;\n atBottomRef.current = next;\n setAtBottom((current) => (current === next ? current : next));\n }, []);\n\n // Jump (not animate) to the bottom on first paint so a thread opens on its\n // newest message instead of visibly scrolling there.\n useLayoutEffect(() => {\n scrollToBottom('auto');\n // eslint-disable-next-line react-hooks/exhaustive-deps -- mount only\n }, []);\n\n useLayoutEffect(() => {\n if (messages.length > lastCount.current && atBottomRef.current) {\n scrollToBottom('smooth');\n }\n lastCount.current = messages.length;\n }, [messages.length, scrollToBottom]);\n\n return { scrollRef, atBottom, scrollToBottom, handleScroll };\n};\n\n/**\n * Preserves the reader's position when older messages are prepended.\n *\n * Without this, inserting a page above the viewport pushes the content the\n * user was reading down by the height of everything added, which reads as the\n * list jumping to a random place. Capturing `scrollHeight` before the DOM\n * mutation and restoring the delta in a *layout* effect corrects it before the\n * browser paints, so there is no visible shift.\n */\nexport const useScrollAnchor = (\n scrollRef: React.RefObject<HTMLDivElement | null>,\n messages: ConversationMessage[],\n): void => {\n const previous = useRef({ height: 0, firstId: '', count: 0 });\n\n useLayoutEffect(() => {\n const node = scrollRef.current;\n if (!node) return;\n\n const firstId = messages[0]?.id ?? '';\n const before = previous.current;\n // A prepend is the only case where the first id changes while the list\n // grows - an append leaves it untouched, and a replacement shrinks it.\n const prepended =\n before.firstId !== '' && before.firstId !== firstId && messages.length > before.count;\n\n if (prepended) {\n node.scrollTop += node.scrollHeight - before.height;\n }\n\n previous.current = { height: node.scrollHeight, firstId, count: messages.length };\n }, [messages, scrollRef]);\n};\n\n/**\n * Fires `onLoadMore` once the reader approaches the top of the history.\n *\n * Latched on the way in and released only after they scroll back out of the\n * trigger zone, so a single pause near the top can't queue a dozen fetches.\n */\nexport const useLoadMoreOnScrollTop = (\n scrollRef: React.RefObject<HTMLDivElement | null>,\n {\n hasMore,\n loadingMore,\n onLoadMore,\n }: {\n hasMore?: boolean;\n loadingMore?: boolean;\n onLoadMore?: () => void;\n },\n thresholdPx = 80,\n): (() => void) => {\n const armed = useRef(true);\n\n return useCallback(() => {\n const node = scrollRef.current;\n if (!node) return;\n\n if (node.scrollTop > thresholdPx) {\n armed.current = true;\n return;\n }\n\n if (!armed.current || !hasMore || loadingMore || !onLoadMore) return;\n armed.current = false;\n onLoadMore();\n }, [scrollRef, hasMore, loadingMore, onLoadMore, thresholdPx]);\n};\n\n/**\n * Grows a textarea to fit its content, up to `maxHeight`.\n *\n * Height is written to `style.height` directly rather than through a React\n * inline style: it is measured from the live DOM after every keystroke, so it\n * cannot be expressed as a class, and routing it through state would add a\n * render per character. Assigning the property imperatively also keeps it out\n * of the server-rendered `style` attribute that CSP's `style-src-attr` gates.\n */\nexport const useAutoGrow = (\n ref: React.RefObject<HTMLTextAreaElement | null>,\n value: string,\n maxHeight = 200,\n): void => {\n useLayoutEffect(() => {\n const node = ref.current;\n if (!node) return;\n\n node.style.height = 'auto';\n const next = Math.min(node.scrollHeight, maxHeight);\n node.style.height = `${next}px`;\n node.style.overflowY = node.scrollHeight > maxHeight ? 'auto' : 'hidden';\n }, [ref, value, maxHeight]);\n};\n","import type { ConversationMessage } from './Conversation.types';\n\n/**\n * Timestamp formatting goes through `Intl` rather than a date library on\n * purpose: `dayjs` is currently bundled only into the `DatePicker` chunk, and\n * pulling it in here would add it to every consumer importing a conversation.\n * `Intl` is built in and already locale-aware.\n */\n\nconst timeFormat = new Intl.DateTimeFormat(undefined, {\n hour: '2-digit',\n minute: '2-digit',\n});\n\nconst dateFormat = new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'long',\n year: 'numeric',\n});\n\nconst dateFormatSameYear = new Intl.DateTimeFormat(undefined, {\n day: 'numeric',\n month: 'long',\n});\n\n/** Parses an ISO string, returning null rather than an `Invalid Date`. */\nexport const parseDate = (iso: string): Date | null => {\n const date = new Date(iso);\n return Number.isNaN(date.getTime()) ? null : date;\n};\n\nexport const formatMessageTime = (iso: string): string => {\n const date = parseDate(iso);\n return date ? timeFormat.format(date) : iso;\n};\n\nconst startOfDay = (date: Date): number =>\n new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();\n\nexport const isSameDay = (a: string, b: string): boolean => {\n const first = parseDate(a);\n const second = parseDate(b);\n if (!first || !second) return false;\n return startOfDay(first) === startOfDay(second);\n};\n\n/** `Today` / `Yesterday` / a full date, for the divider between days. */\nexport const formatDateSeparator = (iso: string): string => {\n const date = parseDate(iso);\n if (!date) return iso;\n\n const now = new Date();\n const dayDelta = Math.round((startOfDay(now) - startOfDay(date)) / 86_400_000);\n\n if (dayDelta === 0) return 'Today';\n if (dayDelta === 1) return 'Yesterday';\n return date.getFullYear() === now.getFullYear()\n ? dateFormatSameYear.format(date)\n : dateFormat.format(date);\n};\n\nconst FILE_UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];\n\nexport const formatFileSize = (bytes: number): string => {\n if (!Number.isFinite(bytes) || bytes < 0) return '';\n if (bytes < 1024) return `${bytes} B`;\n\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < FILE_UNITS.length - 1) {\n value /= 1024;\n unit += 1;\n }\n // One decimal below 10 (9.4 MB), none above it (24 MB) - the extra digit\n // stops carrying information once the integer part is two digits.\n return `${value < 10 ? value.toFixed(1) : Math.round(value)} ${FILE_UNITS[unit]}`;\n};\n\n/**\n * Whether `message` continues a run started by `previous` - same author, same\n * day, and within the grouping window. A grouped message hides its avatar and\n * author line.\n */\nexport const continuesRun = (\n previous: ConversationMessage | undefined,\n message: ConversationMessage,\n windowMinutes: number,\n): boolean => {\n if (!previous) return false;\n if (previous.author.id !== message.author.id) return false;\n if (!isSameDay(previous.sentAt, message.sentAt)) return false;\n\n const before = parseDate(previous.sentAt);\n const after = parseDate(message.sentAt);\n if (!before || !after) return false;\n\n return Math.abs(after.getTime() - before.getTime()) <= windowMinutes * 60_000;\n};\n\nexport const sortBySentAt = (messages: ConversationMessage[]): ConversationMessage[] =>\n [...messages].sort((a, b) => {\n const first = parseDate(a.sentAt)?.getTime() ?? 0;\n const second = parseDate(b.sentAt)?.getTime() ?? 0;\n return first - second;\n });\n\nexport interface ThreadNode {\n message: ConversationMessage;\n replies: ConversationMessage[];\n}\n\n/**\n * Collapses a flat list into one level of nesting.\n *\n * Deeper chains are flattened onto the nearest **top-level** ancestor rather\n * than being dropped or nested further: indentation past one level is\n * unreadable on a phone, and silently discarding a reply-to-a-reply would lose\n * data the consumer stored. `replyingToName` on the rendered reply is what\n * preserves who was actually answered.\n *\n * A `parentId` pointing at a message that isn't present (paged out, deleted)\n * is treated as top-level, so nothing ever disappears from the list.\n */\nexport const buildThread = (messages: ConversationMessage[]): ThreadNode[] => {\n const byId = new Map(messages.map((message) => [message.id, message]));\n\n /** Walks up to the outermost ancestor, guarding against a parentId cycle. */\n const rootIdOf = (message: ConversationMessage): string => {\n const seen = new Set<string>([message.id]);\n let current = message;\n\n while (current.parentId) {\n const parent = byId.get(current.parentId);\n if (!parent || seen.has(parent.id)) break;\n seen.add(parent.id);\n current = parent;\n }\n\n return current.id;\n };\n\n const nodes = new Map<string, ThreadNode>();\n const roots: ThreadNode[] = [];\n\n for (const message of messages) {\n if (message.parentId && byId.has(message.parentId)) continue;\n const node: ThreadNode = { message, replies: [] };\n nodes.set(message.id, node);\n roots.push(node);\n }\n\n for (const message of messages) {\n if (!message.parentId || !byId.has(message.parentId)) continue;\n nodes.get(rootIdOf(message))?.replies.push(message);\n }\n\n for (const node of nodes.values()) {\n node.replies = sortBySentAt(node.replies);\n }\n\n return roots;\n};\n\n/** Name of the author a reply was addressed to, when it isn't its own parent. */\nexport const replyTargetName = (\n message: ConversationMessage,\n byId: Map<string, ConversationMessage>,\n rootId: string,\n): string | undefined => {\n if (!message.parentId || message.parentId === rootId) return undefined;\n return byId.get(message.parentId)?.author.name;\n};\n\n/** Trims a quoted excerpt so a long parent message can't dominate the reply. */\nexport const excerpt = (body: string, maxLength = 120): string => {\n const flat = body.replace(/\\s+/g, ' ').trim();\n return flat.length > maxLength ? `${flat.slice(0, maxLength - 1)}…` : flat;\n};\n\nexport const classNames = (...values: Array<string | false | null | undefined>): string =>\n values.filter(Boolean).join(' ');\n","import React from 'react';\nimport { AlertCircle, MoreHorizontal, Paperclip, RotateCw } from 'lucide-react';\nimport { Avatar } from '../Avatar';\nimport { Button } from '../Button';\nimport { Menu } from '../Menu';\nimport { Pill } from '../Pill';\nimport { Spinner } from '../Spinner';\nimport type { ConversationMessage, MessageAttachment } from './Conversation.types';\nimport type { MenuItemType } from '../Menu';\nimport { classNames, excerpt, formatFileSize, formatMessageTime } from './Conversation.utils';\n\nexport interface MessageItemProps {\n message: ConversationMessage;\n /** 'chat' right-aligns own messages into bubbles; 'comment' renders a flat card. */\n layout: 'chat' | 'comment';\n isOwn: boolean;\n /** Continues a run from the same author - hides the avatar and author line. */\n grouped: boolean;\n /** Plays the entrance animation. */\n isNew: boolean;\n actions?: MenuItemType[];\n footer?: React.ReactNode;\n body?: React.ReactNode;\n onRetry?: () => void;\n /** Author of the message being answered, for a flattened deep reply. */\n replyingToName?: string;\n /** Quoted parent, rendered above the body in chat layout. */\n quoted?: ConversationMessage;\n /** Reply affordance; omitted entirely when the viewer may not reply. */\n onReply?: () => void;\n formatTimestamp?: (iso: string) => string;\n}\n\nconst AttachmentList: React.FC<{ attachments: MessageAttachment[] }> = ({ attachments }) => (\n <ul className=\"eidos-message-attachments\">\n {attachments.map((attachment) => {\n const label = (\n <>\n <Paperclip aria-hidden=\"true\" />\n <span className=\"eidos-message-attachment-name\">{attachment.name}</span>\n {attachment.size !== undefined && (\n <span className=\"eidos-message-attachment-size\">{formatFileSize(attachment.size)}</span>\n )}\n </>\n );\n\n return (\n <li key={attachment.id} className=\"eidos-message-attachment\">\n {attachment.url ? (\n <a\n href={attachment.url}\n className=\"eidos-message-attachment-link\"\n target=\"_blank\"\n rel=\"noreferrer\"\n download={attachment.name}\n >\n {label}\n </a>\n ) : (\n <span className=\"eidos-message-attachment-link\">{label}</span>\n )}\n </li>\n );\n })}\n </ul>\n);\n\nexport const MessageItem: React.FC<MessageItemProps> = ({\n message,\n layout,\n isOwn,\n grouped,\n isNew,\n actions,\n footer,\n body,\n onRetry,\n replyingToName,\n quoted,\n onReply,\n formatTimestamp,\n}) => {\n const { author, status = 'sent' } = message;\n const timestamp = formatTimestamp\n ? formatTimestamp(message.sentAt)\n : formatMessageTime(message.sentAt);\n // An empty action list must not leave a dead trigger behind - see the\n // permissions note in Conversation.types.ts.\n const hasActions = Boolean(actions && actions.length > 0);\n\n return (\n <li\n className={classNames(\n 'eidos-message',\n `eidos-message--${layout}`,\n isOwn && 'eidos-message--own',\n grouped && 'eidos-message--grouped',\n status !== 'sent' && `eidos-message--${status}`,\n isNew && 'eidos-message--enter',\n )}\n data-message-id={message.id}\n >\n <div className=\"eidos-message-avatar\">\n {!grouped && (\n <Avatar\n src={author.avatarSrc}\n name={author.name}\n size=\"sm\"\n color={author.avatarColor ?? 'gray'}\n />\n )}\n </div>\n\n <div className=\"eidos-message-main\">\n {!grouped && (\n <div className=\"eidos-message-header\">\n <span className=\"eidos-message-author\">{author.name}</span>\n {author.role && (\n <Pill size=\"sm\" variant=\"outlined\" color=\"secondary\">\n {author.role}\n </Pill>\n )}\n <time className=\"eidos-message-time\" dateTime={message.sentAt}>\n {timestamp}\n </time>\n {message.edited && <span className=\"eidos-message-edited\">edited</span>}\n </div>\n )}\n\n <div className=\"eidos-message-bubble\">\n {quoted && (\n <blockquote className=\"eidos-message-quote\">\n <span className=\"eidos-message-quote-author\">{quoted.author.name}</span>\n <span className=\"eidos-message-quote-body\">{excerpt(quoted.body)}</span>\n </blockquote>\n )}\n\n {replyingToName && (\n <span className=\"eidos-message-reply-target\">Replying to {replyingToName}</span>\n )}\n\n <div className=\"eidos-message-body\">{body ?? message.body}</div>\n\n {message.attachments && message.attachments.length > 0 && (\n <AttachmentList attachments={message.attachments} />\n )}\n\n {grouped && (\n <time\n className=\"eidos-message-time eidos-message-time--inline\"\n dateTime={message.sentAt}\n >\n {timestamp}\n </time>\n )}\n </div>\n\n {status === 'sending' && (\n <span className=\"eidos-message-status\">\n <Spinner size=\"sm\" color=\"secondary\" />\n Sending…\n </span>\n )}\n\n {status === 'failed' && (\n <span className=\"eidos-message-status eidos-message-status--failed\">\n <AlertCircle aria-hidden=\"true\" />\n Not sent\n {onRetry && (\n <Button variant=\"text\" color=\"danger\" size=\"sm\" preIcon={RotateCw} onClick={onRetry}>\n Retry\n </Button>\n )}\n </span>\n )}\n\n {footer && <div className=\"eidos-message-footer\">{footer}</div>}\n\n {onReply && (\n <div className=\"eidos-message-reply\">\n <Button variant=\"text\" color=\"secondary\" size=\"sm\" onClick={onReply}>\n Reply\n </Button>\n </div>\n )}\n </div>\n\n {hasActions && (\n <div className=\"eidos-message-actions\">\n <Menu\n items={actions ?? []}\n trigger={\n <Button\n variant=\"text\"\n color=\"secondary\"\n size=\"sm\"\n icon={MoreHorizontal}\n aria-label={`Actions for ${author.name}'s message`}\n />\n }\n />\n </div>\n )}\n </li>\n );\n};\n\nMessageItem.displayName = 'MessageItem';\n","import React, { useCallback, useMemo, useState } from 'react';\nimport { MessageSquare } from 'lucide-react';\nimport { Button } from '../Button';\nimport { EmptyState } from '../EmptyState';\nimport { Skeleton } from '../Skeleton';\nimport { Spinner } from '../Spinner';\nimport { MessageComposer } from './MessageComposer.component';\nimport { MessageItem } from './MessageItem.component';\nimport type { CommentThreadProps, ConversationMessage, MessageDraft } from './Conversation.types';\nimport { useNewMessageIds } from './Conversation.hooks';\nimport {\n buildThread,\n classNames,\n continuesRun,\n replyTargetName,\n sortBySentAt,\n} from './Conversation.utils';\n\nconst CommentSkeleton: React.FC = () => (\n <div className=\"eidos-comment-thread-skeleton\" aria-hidden=\"true\">\n {[0, 1, 2].map((row) => (\n <div key={row} className=\"eidos-comment-thread-skeleton-row\">\n <Skeleton variant=\"circular\" width={28} height={28} />\n <Skeleton variant=\"text\" lines={2} />\n </div>\n ))}\n </div>\n);\n\n/**\n * A comment section: document flow, no internal scroll container, one level of\n * nesting.\n *\n * Shares its data model and message rendering with `Chat`, so history captured\n * in one can be displayed in the other. Unlike `Chat` it has no scroll region\n * of its own - it grows with the page and pages *downward* via `onLoadMore`.\n */\nexport const CommentThread: React.FC<CommentThreadProps> = ({\n messages,\n currentUserId,\n onSend,\n onReply,\n onRetry,\n readOnly = false,\n readOnlyMessage,\n renderBody,\n messageActions,\n renderMessageFooter,\n loading = false,\n emptyContent,\n hasMore = false,\n loadingMore = false,\n onLoadMore,\n groupConsecutive = true,\n groupWindowMinutes = 5,\n formatTimestamp,\n animateNewMessages = true,\n allowAttachments = false,\n attachmentAccept,\n order = 'oldest-first',\n composerPosition = 'bottom',\n placeholder = 'Add a comment...',\n sendOnEnter = false,\n title,\n ariaLabel = 'Comments',\n className = '',\n}) => {\n const [replyingToId, setReplyingToId] = useState<string | null>(null);\n const newIds = useNewMessageIds(messages, animateNewMessages);\n\n const byId = useMemo(() => new Map(messages.map((message) => [message.id, message])), [messages]);\n\n const roots = useMemo(() => {\n const thread = buildThread(messages);\n const sorted = sortBySentAt(thread.map((node) => node.message));\n const ordered = order === 'newest-first' ? [...sorted].reverse() : sorted;\n const nodeById = new Map(thread.map((node) => [node.message.id, node]));\n // Replies always read oldest-first, even when roots are reversed - a\n // conversation under a single comment is chronological by nature.\n return ordered.map((message) => nodeById.get(message.id)!);\n }, [messages, order]);\n\n const canReply = !readOnly && Boolean(onReply);\n const showComposer = !readOnly && Boolean(onSend);\n const isEmpty = !loading && messages.length === 0;\n const total = messages.length;\n\n const handleReply = useCallback(\n async (draft: MessageDraft) => {\n await onReply?.(draft);\n setReplyingToId(null);\n },\n [onReply],\n );\n\n const renderComment = (\n message: ConversationMessage,\n options: { rootId: string; previous?: ConversationMessage; isReply: boolean },\n ) => {\n const actions = readOnly ? undefined : messageActions?.(message);\n\n return (\n <MessageItem\n key={message.id}\n message={message}\n layout=\"comment\"\n isOwn={Boolean(currentUserId) && message.author.id === currentUserId}\n grouped={groupConsecutive && continuesRun(options.previous, message, groupWindowMinutes)}\n isNew={newIds.has(message.id)}\n actions={actions}\n footer={renderMessageFooter?.(message)}\n body={renderBody?.(message)}\n onRetry={!readOnly && onRetry ? () => onRetry(message) : undefined}\n replyingToName={\n options.isReply ? replyTargetName(message, byId, options.rootId) : undefined\n }\n onReply={canReply ? () => setReplyingToId(message.id) : undefined}\n formatTimestamp={formatTimestamp}\n />\n );\n };\n\n const composer = showComposer && (\n <MessageComposer\n onSubmit={onSend as NonNullable<typeof onSend>}\n placeholder={placeholder}\n submitLabel=\"Comment\"\n sendOnEnter={sendOnEnter}\n allowAttachments={allowAttachments}\n attachmentAccept={attachmentAccept}\n className=\"eidos-comment-thread-composer\"\n />\n );\n\n return (\n <section className={classNames('eidos-comment-thread', className)} aria-label={ariaLabel}>\n {(title || total > 0) && (\n <header className=\"eidos-comment-thread-header\">\n {title && <h3 className=\"eidos-comment-thread-title\">{title}</h3>}\n {total > 0 && (\n <span className=\"eidos-comment-thread-count\">\n {total} {total === 1 ? 'comment' : 'comments'}\n </span>\n )}\n </header>\n )}\n\n {composerPosition === 'top' && composer}\n\n {loading && <CommentSkeleton />}\n\n {isEmpty &&\n (emptyContent ?? (\n <EmptyState\n icon={<MessageSquare />}\n title=\"No comments yet\"\n description={showComposer ? 'Start the discussion by leaving a comment.' : undefined}\n size=\"sm\"\n />\n ))}\n\n {!loading && roots.length > 0 && (\n <ol className=\"eidos-conversation-list eidos-comment-thread-list\" aria-busy={loadingMore}>\n {roots.map((node) => (\n <li key={node.message.id} className=\"eidos-comment-thread-node\">\n <ol className=\"eidos-conversation-list\">\n {renderComment(node.message, { rootId: node.message.id, isReply: false })}\n </ol>\n\n {node.replies.length > 0 && (\n <ol className=\"eidos-conversation-list eidos-comment-thread-replies\">\n {node.replies.map((reply, index) =>\n renderComment(reply, {\n rootId: node.message.id,\n previous: node.replies[index - 1],\n isReply: true,\n }),\n )}\n </ol>\n )}\n\n {replyingToId &&\n (replyingToId === node.message.id ||\n node.replies.some((reply) => reply.id === replyingToId)) && (\n <div className=\"eidos-comment-thread-reply-composer\">\n <MessageComposer\n onSubmit={handleReply}\n onCancel={() => setReplyingToId(null)}\n parentId={replyingToId}\n replyingTo={byId.get(replyingToId)?.author.name}\n placeholder=\"Write a reply...\"\n submitLabel=\"Reply\"\n sendOnEnter={sendOnEnter}\n allowAttachments={allowAttachments}\n attachmentAccept={attachmentAccept}\n autoFocus\n />\n </div>\n )}\n </li>\n ))}\n </ol>\n )}\n\n {hasMore && (\n <div className=\"eidos-comment-thread-load-more\">\n {loadingMore ? (\n <span className=\"eidos-comment-thread-loading-more\">\n <Spinner size=\"sm\" color=\"secondary\" />\n Loading more comments…\n </span>\n ) : (\n <Button variant=\"outlined\" color=\"secondary\" size=\"sm\" onClick={onLoadMore}>\n Load more comments\n </Button>\n )}\n </div>\n )}\n\n {composerPosition === 'bottom' && composer}\n\n {readOnly && readOnlyMessage && (\n <p className=\"eidos-conversation-readonly\">{readOnlyMessage}</p>\n )}\n </section>\n );\n};\n\nCommentThread.displayName = 'CommentThread';\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/Modal/Modal.component.tsx"],"sourcesContent":["import React, { useEffect, useCallback, useId, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { Button } from '../Button/Button.component';\nimport type { ModalProps } from './Modal.types';\nimport { renderIcon } from '../../utils';\n\nexport const Modal: React.FC<ModalProps> = ({\n isOpen,\n onClose,\n title,\n icon,\n type,\n children,\n actions = [],\n closeOnBackdropClick = true,\n closeOnEscape = true,\n size = 'md',\n className = '',\n}) => {\n const titleId = useId();\n const bodyId = useId();\n\n // ── Animation state ────────────────────────────────────────────────────────\n // isMounted: whether the portal DOM node exists at all.\n // isVisible: whether the '--is-open' CSS class is applied (drives the transition).\n //\n // On open: mount first → one rAF → apply --is-open (browser sees the\n // initial faded/scaled-down state before transitioning, so the\n // animation fires instead of just painting straight into \"open\").\n // On close: remove --is-open (CSS transition plays) → after 200 ms unmount.\n // 200 ms matches the CSS `transition: 0.2s` in Modal.scss.\n const TRANSITION_MS = 200;\n const [isMounted, setIsMounted] = useState(isOpen);\n const [isVisible, setIsVisible] = useState(isOpen);\n\n useEffect(() => {\n let raf1 = 0;\n let raf2 = 0;\n let timerId: ReturnType<typeof setTimeout>;\n\n if (isOpen) {\n setIsMounted(true);\n // Double rAF: React 18 automatic batching means setIsMounted(true) may\n // not produce a committed browser paint before the first rAF fires.\n // The second frame guarantees the modal is rendered in its closed\n // (faded/scaled-down) state before the CSS transition begins.\n raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(() => setIsVisible(true));\n });\n } else {\n setIsVisible(false);\n timerId = setTimeout(() => setIsMounted(false), TRANSITION_MS);\n }\n\n return () => {\n cancelAnimationFrame(raf1);\n cancelAnimationFrame(raf2);\n clearTimeout(timerId);\n };\n }, [isOpen]);\n\n // Handle escape key\n const handleEscapeKey = useCallback(\n (event: KeyboardEvent) => {\n if (closeOnEscape && event.key === 'Escape' && isOpen) {\n onClose();\n }\n },\n [closeOnEscape, isOpen, onClose],\n );\n\n // Handle backdrop click\n const handleBackdropClick = useCallback(\n (event: React.MouseEvent) => {\n if (closeOnBackdropClick && event.target === event.currentTarget) {\n onClose();\n }\n },\n [closeOnBackdropClick, onClose],\n );\n\n // Add/remove escape key listener (tied to isOpen, not isMounted)\n useEffect(() => {\n if (isOpen) {\n document.addEventListener('keydown', handleEscapeKey);\n // Prevent body scroll when modal is open\n document.body.style.overflow = 'hidden';\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscapeKey);\n document.body.style.overflow = '';\n };\n }, [isOpen, handleEscapeKey]);\n\n if (!isMounted) return null;\n\n const modalContent = (\n <div className={`eidos-modal ${isVisible ? 'eidos-modal--is-open' : ''} ${className}`}>\n <div className=\"eidos-modal-backdrop\" onClick={handleBackdropClick} aria-hidden=\"true\" />\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={bodyId}\n className={`eidos-modal-content eidos-modal-content--${size}`}\n >\n {title && (\n <div className={`eidos-modal-header ${type ? `eidos-modal-header--${type}` : ''}`}>\n <h4 id={titleId}>\n {icon && renderIcon(icon, 'eidos-modal-icon')} {title}\n </h4>\n </div>\n )}\n\n <div id={bodyId} className=\"eidos-modal-body\">\n {children}\n </div>\n\n {actions.length > 0 && (\n <div className={`eidos-modal-footer`}>\n {actions.map((action) => (\n <Button\n key={action.id}\n variant={action.variant || 'outlined'}\n size={action.size || 'md'}\n color={action.color}\n disabled={action.disabled}\n loading={action.loading}\n onClick={action.onClick}\n >\n {action.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n </div>\n );\n\n return createPortal(modalContent, document.body);\n};\n\nModal.displayName = 'Modal';\n"],"mappings":"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","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/SegmentedControl/SegmentedControl.component.tsx"],"sourcesContent":["import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport { ChevronLeft, ChevronRight } from 'lucide-react';\nimport { Tooltip } from '../Tooltip';\nimport { renderIcon } from '../../utils';\nimport type { SegmentedControlProps } from './SegmentedControl.types';\n\n/**\n * SegmentedControl - a compact, single-select toggle group rendered as a pill.\n *\n * Semantics: `role=\"radiogroup\"` on the container, `role=\"radio\"` on each\n * segment. Supports controlled and uncontrolled usage.\n *\n * @example\n * ```tsx\n * // Controlled\n * const [view, setView] = useState('list');\n * <SegmentedControl\n * options={[\n * { value: 'list', icon: List, tooltip: 'List view' },\n * { value: 'grid', icon: LayoutGrid, tooltip: 'Grid view' },\n * { value: 'table', icon: Table2, tooltip: 'Table view' },\n * ]}\n * value={view}\n * onChange={setView}\n * />\n *\n * // With labels\n * <SegmentedControl\n * options={[\n * { value: 'day', label: 'Day' },\n * { value: 'week', label: 'Week' },\n * { value: 'month', label: 'Month' },\n * ]}\n * defaultValue=\"week\"\n * onChange={console.log}\n * />\n * ```\n */\nexport const SegmentedControl: React.FC<SegmentedControlProps> = ({\n options,\n value,\n defaultValue,\n onChange,\n size = 'md',\n color = 'primary',\n disabled = false,\n fullWidth = false,\n scrollButtons = 'auto',\n className = '',\n}) => {\n const isControlled = value !== undefined;\n const [internalValue, setInternalValue] = useState<string>(\n defaultValue ?? options[0]?.value ?? '',\n );\n\n const activeValue = isControlled ? value : internalValue;\n\n const handleSelect = (optValue: string) => {\n if (disabled) return;\n if (optValue === activeValue) return; // already selected\n if (!isControlled) setInternalValue(optValue);\n onChange?.(optValue);\n };\n\n const trackRef = useRef<HTMLDivElement>(null);\n\n // Roving tabindex: the group is a single tab stop, and arrow keys move\n // within it - the WAI-ARIA radiogroup pattern, which this had none of.\n // Every segment used to be its own tab stop, so a six-option control cost\n // six presses to skip past.\n //\n // The tabbable segment is the selected one; if nothing is selected (a\n // controlled `value` matching no option), the first enabled segment takes\n // the stop so the group can still be reached at all.\n const selectableValues = options.filter((opt) => !opt.disabled).map((opt) => opt.value);\n const hasSelection = selectableValues.includes(activeValue);\n const fallbackTabbableValue = selectableValues[0];\n\n const isTabbable = (optValue: string) =>\n hasSelection ? optValue === activeValue : optValue === fallbackTabbableValue;\n\n // Selection follows focus, which is what the radio pattern specifies -\n // unlike `Tabs`, where arrow keys only move focus. Disabled segments are\n // skipped rather than selected-and-ignored, and both ends wrap.\n const handleKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {\n if (disabled || selectableValues.length === 0) return;\n\n const currentIndex = selectableValues.indexOf(activeValue);\n const from = currentIndex === -1 ? 0 : currentIndex;\n let targetIndex = -1;\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n targetIndex = (from + 1) % selectableValues.length;\n } else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n targetIndex = (from - 1 + selectableValues.length) % selectableValues.length;\n } else if (event.key === 'Home') {\n targetIndex = 0;\n } else if (event.key === 'End') {\n targetIndex = selectableValues.length - 1;\n }\n\n if (targetIndex === -1) return;\n\n event.preventDefault();\n const targetValue = selectableValues[targetIndex];\n handleSelect(targetValue);\n\n // Focus has to follow the selection explicitly: the segment that was\n // focused is about to lose its `tabIndex={0}` to the newly selected one,\n // and focus does not move on its own.\n const target = trackRef.current?.querySelector<HTMLElement>(\n `[data-value=\"${CSS.escape(targetValue)}\"]`,\n );\n target?.focus();\n };\n\n // The selected segment can sit outside the scrollport - after a controlled\n // `value` change, or once focus moves to a segment off screen. `scrollLeft`\n // is set directly rather than through `scrollIntoView`, which walks the\n // whole ancestor chain and would scroll the page vertically when the\n // control is below the fold.\n useLayoutEffect(() => {\n const track = trackRef.current;\n if (!track) return;\n\n const activeItem = track.querySelector('[aria-checked=\"true\"]') as HTMLElement | null;\n if (!activeItem) return;\n\n const left = activeItem.offsetLeft;\n const right = left + activeItem.offsetWidth;\n\n if (left < track.scrollLeft) {\n track.scrollLeft = left;\n } else if (right > track.scrollLeft + track.clientWidth) {\n track.scrollLeft = right - track.clientWidth;\n }\n }, [activeValue]);\n\n // Whether either end has segments hidden past it. Drives the presence of\n // each button: none at all when the track fits, and none at the end already\n // reached, since a button there would point at nothing.\n const [scrollState, setScrollState] = useState({ canScrollPrev: false, canScrollNext: false });\n const hasScrollButtons = scrollButtons !== 'none';\n\n useEffect(() => {\n if (!hasScrollButtons) return;\n const track = trackRef.current;\n if (!track) return;\n\n const update = () => {\n const maxScroll = track.scrollWidth - track.clientWidth;\n // 1px of tolerance: fractional layout widths leave a sub-pixel\n // remainder at either end that would otherwise keep a button enabled\n // for a scroll that can no longer move anything.\n const canScrollPrev = track.scrollLeft > 1;\n const canScrollNext = track.scrollLeft < maxScroll - 1;\n setScrollState((previous) =>\n previous.canScrollPrev === canScrollPrev && previous.canScrollNext === canScrollNext\n ? previous\n : { canScrollPrev, canScrollNext },\n );\n };\n\n update();\n track.addEventListener('scroll', update, { passive: true });\n\n // The track's own box stays the same width when a label reflows or a font\n // finishes loading, so the segments have to be observed as well as the\n // container - only their sizes tell us the track started overflowing.\n const observer = new ResizeObserver(update);\n observer.observe(track);\n Array.from(track.children).forEach((child) => observer.observe(child));\n\n return () => {\n track.removeEventListener('scroll', update);\n observer.disconnect();\n };\n }, [hasScrollButtons, options.length, size, fullWidth]);\n\n const scrollByStep = (direction: -1 | 1) => {\n const track = trackRef.current;\n if (!track) return;\n\n // A step just short of a full page keeps a segment of context on screen,\n // the same way a paged scrollbar click does.\n const step = track.clientWidth * 0.8;\n const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n track.scrollBy({ left: direction * step, behavior: reducedMotion ? 'auto' : 'smooth' });\n };\n\n const barClasses = [\n 'eidos-segmented-bar',\n fullWidth && 'eidos-segmented-bar--full-width',\n disabled && 'eidos-segmented-bar--disabled',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const containerClasses = [\n 'eidos-segmented',\n `eidos-segmented--${size}`,\n `eidos-segmented--${color}`,\n disabled && 'eidos-segmented--disabled',\n fullWidth && 'eidos-segmented--full-width',\n // With buttons, they are the affordance - a native scrollbar under a\n // compact pill control is redundant and would break its shape.\n hasScrollButtons && 'eidos-segmented--hide-scrollbar',\n ]\n .filter(Boolean)\n .join(' ');\n\n const showPrevButton = hasScrollButtons && scrollState.canScrollPrev;\n const showNextButton = hasScrollButtons && scrollState.canScrollNext;\n\n const scrollButtonClasses = (direction: 'prev' | 'next') =>\n [\n 'eidos-segmented-scroll-button',\n `eidos-segmented-scroll-button--${direction}`,\n `eidos-segmented-scroll-button--${size}`,\n ].join(' ');\n\n const track = (\n <div ref={trackRef} className={containerClasses} role=\"radiogroup\">\n {options.map((opt) => {\n const isActive = activeValue === opt.value;\n const isDisabled = disabled || !!opt.disabled;\n\n const segment = (\n <button\n key={opt.value}\n type=\"button\"\n role=\"radio\"\n aria-checked={isActive}\n disabled={isDisabled}\n // Read back by the keyboard handler to move focus onto the newly\n // selected segment, which `value` alone can't identify in the DOM.\n data-value={opt.value}\n tabIndex={isTabbable(opt.value) ? 0 : -1}\n className={['eidos-segmented-item', isActive && 'eidos-segmented-item--active']\n .filter(Boolean)\n .join(' ')}\n onClick={() => !isDisabled && handleSelect(opt.value)}\n onKeyDown={handleKeyDown}\n >\n {opt.icon && renderIcon(opt.icon, 'eidos-segmented-icon')}\n {opt.label && <span className=\"eidos-segmented-label\">{opt.label}</span>}\n </button>\n );\n\n return opt.tooltip ? (\n <Tooltip key={opt.value} message={opt.tooltip}>\n {segment}\n </Tooltip>\n ) : (\n React.cloneElement(segment, { key: opt.value })\n );\n })}\n </div>\n );\n\n // The buttons are siblings of the track, never children of it:\n // `role=\"radiogroup\"` only accepts radios. They are also hidden from\n // assistive tech and skipped by Tab, because they are a pointer affordance\n // for something the keyboard already does - tabbing to a segment scrolls it\n // into view. Same arrangement as `Tabs`.\n return (\n <div className={barClasses}>\n {showPrevButton && (\n <button\n type=\"button\"\n className={scrollButtonClasses('prev')}\n onClick={() => scrollByStep(-1)}\n // `tabIndex={-1}` keeps these out of the tab order, but it does not\n // stop a *click* from focusing them - and focus landing inside an\n // `aria-hidden` subtree is an error the browser reports (\"Blocked\n // aria-hidden on an element because its descendant retained\n // focus\"). Suppressing the default mousedown behaviour keeps the\n // button unfocusable by pointer as well, while the click still\n // fires. `inert` would also prevent the click, so it is not an\n // option here.\n onMouseDown={(event) => event.preventDefault()}\n tabIndex={-1}\n aria-hidden=\"true\"\n >\n <ChevronLeft className=\"eidos-segmented-scroll-button-icon\" />\n </button>\n )}\n\n {track}\n\n {showNextButton && (\n <button\n type=\"button\"\n className={scrollButtonClasses('next')}\n onClick={() => scrollByStep(1)}\n onMouseDown={(event) => event.preventDefault()} // see the prev button\n tabIndex={-1}\n aria-hidden=\"true\"\n >\n <ChevronRight className=\"eidos-segmented-scroll-button-icon\" />\n </button>\n )}\n </div>\n );\n};\n"],"mappings":"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","names":["track"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/Drawer/Drawer.component.tsx"],"sourcesContent":["import React, { useEffect, useCallback, useId, useState } from 'react';\nimport { createPortal } from 'react-dom';\nimport { X } from 'lucide-react';\nimport { Button } from '../Button';\nimport type { DrawerProps } from './Drawer.types';\n\nexport const Drawer: React.FC<DrawerProps> = ({\n isOpen,\n onClose,\n placement = 'right',\n title,\n children,\n actions,\n size = 'md',\n closeOnBackdropClick = true,\n closeOnEscape = true,\n className = '',\n}) => {\n const titleId = useId();\n const bodyId = useId();\n\n // ── Animation state ────────────────────────────────────────────────────────\n // isMounted: whether the portal DOM node exists at all.\n // isVisible: whether the '--open' CSS class is applied (drives the transition).\n //\n // On open: mount first → one rAF → apply --open (browser sees initial\n // closed position before transitioning, so the animation fires).\n // On close: remove --open (CSS transition plays) → after 300 ms unmount.\n // 300 ms matches the CSS `transition: 0.3s` in Drawer.scss.\n const TRANSITION_MS = 300;\n const [isMounted, setIsMounted] = useState(isOpen);\n const [isVisible, setIsVisible] = useState(isOpen);\n\n useEffect(() => {\n let raf1 = 0;\n let raf2 = 0;\n let timerId: ReturnType<typeof setTimeout>;\n\n if (isOpen) {\n setIsMounted(true);\n // Double rAF: React 18 automatic batching means setIsMounted(true) may\n // not produce a committed browser paint before the first rAF fires.\n // The second frame guarantees the panel is rendered in its off-screen\n // (closed) position before the CSS transition begins.\n raf1 = requestAnimationFrame(() => {\n raf2 = requestAnimationFrame(() => setIsVisible(true));\n });\n } else {\n setIsVisible(false);\n timerId = setTimeout(() => setIsMounted(false), TRANSITION_MS);\n }\n\n return () => {\n cancelAnimationFrame(raf1);\n cancelAnimationFrame(raf2);\n clearTimeout(timerId);\n };\n }, [isOpen]);\n\n // ── Escape key + scroll lock (tied to isOpen, not isMounted) ───────────────\n const handleEscapeKey = useCallback(\n (event: KeyboardEvent) => {\n if (closeOnEscape && event.key === 'Escape' && isOpen) {\n onClose();\n }\n },\n [closeOnEscape, isOpen, onClose],\n );\n\n useEffect(() => {\n if (isOpen) {\n document.addEventListener('keydown', handleEscapeKey);\n document.body.style.overflow = 'hidden';\n }\n return () => {\n document.removeEventListener('keydown', handleEscapeKey);\n document.body.style.overflow = '';\n };\n }, [isOpen, handleEscapeKey]);\n\n // ── Backdrop click ─────────────────────────────────────────────────────────\n const handleBackdropClick = useCallback(\n (event: React.MouseEvent) => {\n if (closeOnBackdropClick && event.target === event.currentTarget) {\n onClose();\n }\n },\n [closeOnBackdropClick, onClose],\n );\n\n if (!isMounted) return null;\n\n return createPortal(\n <div\n className={[\n 'eidos-drawer',\n `eidos-drawer--${placement}`,\n isVisible && 'eidos-drawer--open',\n className,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {/* Backdrop */}\n <div className=\"eidos-drawer-backdrop\" onClick={handleBackdropClick} aria-hidden=\"true\" />\n\n {/* Panel */}\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={title ? titleId : undefined}\n aria-describedby={bodyId}\n className={[\n 'eidos-drawer-panel',\n `eidos-drawer-panel--${size}`,\n `eidos-drawer-panel--${placement}`,\n ]\n .filter(Boolean)\n .join(' ')}\n >\n {/* Header */}\n <div className=\"eidos-drawer-header\">\n {title && (\n <h3 id={titleId} className=\"eidos-drawer-title\">\n {title}\n </h3>\n )}\n <button\n type=\"button\"\n className=\"eidos-drawer-close\"\n onClick={onClose}\n aria-label=\"Close drawer\"\n >\n <X aria-hidden=\"true\" />\n </button>\n </div>\n\n {/* Body */}\n <div id={bodyId} className=\"eidos-drawer-body\">\n {children}\n </div>\n\n {/* Footer - only rendered when actions are provided */}\n {actions && actions.length > 0 && (\n <div className=\"eidos-drawer-footer\">\n {actions.map((action) => (\n <Button\n key={action.id}\n variant={action.variant ?? 'outlined'}\n color={action.color}\n disabled={action.disabled}\n loading={action.loading}\n onClick={action.onClick}\n >\n {action.label}\n </Button>\n ))}\n </div>\n )}\n </div>\n </div>,\n document.body,\n );\n};\n\nDrawer.displayName = 'Drawer';\n"],"mappings":";;;;;AAAA,SAAgB,WAAW,aAAa,OAAO,gBAAgB;AAC/D,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAsGZ,cAiBE,YAjBF;AAlGC,IAAM,SAAgC,CAAC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA,OAAO;AAAA,EACP,uBAAuB;AAAA,EACvB,gBAAgB;AAAA,EAChB,YAAY;AACd,MAAM;AACJ,QAAM,UAAU,MAAM;AACtB,QAAM,SAAS,MAAM;AAUrB,QAAM,gBAAgB;AACtB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,MAAM;AACjD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,MAAM;AAEjD,YAAU,MAAM;AACd,QAAI,OAAO;AACX,QAAI,OAAO;AACX,QAAI;AAEJ,QAAI,QAAQ;AACV,mBAAa,IAAI;AAKjB,aAAO,sBAAsB,MAAM;AACjC,eAAO,sBAAsB,MAAM,aAAa,IAAI,CAAC;AAAA,MACvD,CAAC;AAAA,IACH,OAAO;AACL,mBAAa,KAAK;AAClB,gBAAU,WAAW,MAAM,aAAa,KAAK,GAAG,aAAa;AAAA,IAC/D;AAEA,WAAO,MAAM;AACX,2BAAqB,IAAI;AACzB,2BAAqB,IAAI;AACzB,mBAAa,OAAO;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,QAAM,kBAAkB;AAAA,IACtB,CAAC,UAAyB;AACxB,UAAI,iBAAiB,MAAM,QAAQ,YAAY,QAAQ;AACrD,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,CAAC,eAAe,QAAQ,OAAO;AAAA,EACjC;AAEA,YAAU,MAAM;AACd,QAAI,QAAQ;AACV,eAAS,iBAAiB,WAAW,eAAe;AACpD,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AACA,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,eAAe;AACvD,eAAS,KAAK,MAAM,WAAW;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,QAAQ,eAAe,CAAC;AAG5B,QAAM,sBAAsB;AAAA,IAC1B,CAAC,UAA4B;AAC3B,UAAI,wBAAwB,MAAM,WAAW,MAAM,eAAe;AAChE,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,IACA,CAAC,sBAAsB,OAAO;AAAA,EAChC;AAEA,MAAI,CAAC,UAAW,QAAO;AAEvB,SAAO;AAAA,IACL;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,iBAAiB,SAAS;AAAA,UAC1B,aAAa;AAAA,UACb;AAAA,QACF,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,QAGX;AAAA,8BAAC,SAAI,WAAU,yBAAwB,SAAS,qBAAqB,eAAY,QAAO;AAAA,UAGxF;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,cAAW;AAAA,cACX,mBAAiB,QAAQ,UAAU;AAAA,cACnC,oBAAkB;AAAA,cAClB,WAAW;AAAA,gBACT;AAAA,gBACA,uBAAuB,IAAI;AAAA,gBAC3B,uBAAuB,SAAS;AAAA,cAClC,EACG,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,cAGX;AAAA,qCAAC,SAAI,WAAU,uBACZ;AAAA,2BACC,oBAAC,QAAG,IAAI,SAAS,WAAU,sBACxB,iBACH;AAAA,kBAEF;AAAA,oBAAC;AAAA;AAAA,sBACC,MAAK;AAAA,sBACL,WAAU;AAAA,sBACV,SAAS;AAAA,sBACT,cAAW;AAAA,sBAEX,8BAAC,KAAE,eAAY,QAAO;AAAA;AAAA,kBACxB;AAAA,mBACF;AAAA,gBAGA,oBAAC,SAAI,IAAI,QAAQ,WAAU,qBACxB,UACH;AAAA,gBAGC,WAAW,QAAQ,SAAS,KAC3B,oBAAC,SAAI,WAAU,uBACZ,kBAAQ,IAAI,CAAC,WACZ;AAAA,kBAAC;AAAA;AAAA,oBAEC,SAAS,OAAO,WAAW;AAAA,oBAC3B,OAAO,OAAO;AAAA,oBACd,UAAU,OAAO;AAAA,oBACjB,SAAS,OAAO;AAAA,oBAChB,SAAS,OAAO;AAAA,oBAEf,iBAAO;AAAA;AAAA,kBAPH,OAAO;AAAA,gBAQd,CACD,GACH;AAAA;AAAA;AAAA,UAEJ;AAAA;AAAA;AAAA,IACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;AAEA,OAAO,cAAc;","names":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-TEVZHN5I.cjs","../src/components/Input/Input.component.tsx"],"names":["currentValue"],"mappings":"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","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-TEVZHN5I.cjs","sourcesContent":[null,"import React, { forwardRef, useState, useId } from 'react';\nimport { Eye, EyeOff, CircleAlert, X, ChevronDown } from 'lucide-react';\nimport { renderIcon, fullWidthModifier } from '../../utils';\nimport type { InputProps } from './Input.types';\nimport { Tooltip } from '../Tooltip/Tooltip.component';\n\nexport const Input = forwardRef<HTMLInputElement, InputProps>(\n (\n {\n variant = 'filled',\n color = 'primary',\n size = 'md',\n disabled = false,\n loading = false,\n className = '',\n width,\n label,\n error,\n disclaimerIcon,\n disclaimerContent,\n preIcon,\n posIcon,\n posIconButton = false,\n onPosIconClick,\n type = 'text',\n id,\n name,\n required,\n isSelect = false,\n clearable = true,\n fullWidth = false,\n ...inputProps\n },\n ref,\n ) => {\n // Extract defaultValue so it never reaches the native <input> alongside `value`.\n // Passing both causes React's \"controlled/uncontrolled\" warning because we always\n // set value={currentValue} below - defaultValue is only needed to seed local state.\n const { defaultValue, ...restInputProps } = inputProps;\n\n const [showPassword, setShowPassword] = useState(false);\n const [localValue, setLocalValue] = useState(defaultValue || '');\n const [isFocused, setIsFocused] = useState(false);\n\n // Auto-generate id and name if not provided\n const generatedId = useId();\n const inputId = id || `input-${generatedId}`;\n const inputName = name || inputId;\n\n // Determine if using controlled or uncontrolled mode\n const isControlled = restInputProps.value !== undefined;\n const currentValue = isControlled ? restInputProps.value : localValue;\n\n // For select inputs, don't manage localValue - let the select component handle it\n // This prevents the Input's clear button from showing\n\n // Determine if label should float (only when there's content - static behavior)\n // For select inputs, keep label completely static (never float)\n const shouldFloatLabel = isSelect ? false : currentValue || defaultValue;\n\n // Check if field is required (from props or enhanced register function)\n const isRequired = required || Boolean('required' in restInputProps && restInputProps.required);\n\n // Handle number input to allow decimals and negative numbers\n const handleNumberInput = (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (type === 'number') {\n const allowedKeys = [\n 'Backspace',\n 'Delete',\n 'Tab',\n 'Escape',\n 'Enter',\n 'ArrowLeft',\n 'ArrowRight',\n 'ArrowUp',\n 'ArrowDown',\n ];\n const isNumber = /[0-9]/.test(e.key);\n const isDecimal = /[.,]/.test(e.key);\n const isMinus = e.key === '-';\n const isAllowedKey = allowedKeys.includes(e.key);\n const isControlKey = e.ctrlKey || e.metaKey;\n\n // Allow minus only at the beginning or when no text is selected\n if (isMinus) {\n const selectionStart = e.currentTarget.selectionStart || 0;\n const selectionEnd = e.currentTarget.selectionEnd || 0;\n const hasSelection = selectionStart !== selectionEnd;\n\n // Allow if at beginning or if replacing selected text\n if (selectionStart !== 0 && !hasSelection) {\n e.preventDefault();\n return;\n }\n }\n\n // Allow decimal only once\n if (isDecimal) {\n const currentValue = e.currentTarget.value;\n const hasDecimal = /[.,]/.test(currentValue);\n if (hasDecimal) {\n e.preventDefault();\n return;\n }\n }\n\n if (!isNumber && !isDecimal && !isMinus && !isAllowedKey && !isControlKey) {\n e.preventDefault();\n }\n }\n };\n\n // Handle input change for number inputs\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n // Only update localValue if uncontrolled and not a select input\n if (!isControlled && !isSelect) {\n setLocalValue(value);\n }\n restInputProps.onChange?.(e);\n };\n\n // Handle clear button\n const handleClear = () => {\n // Update localValue if uncontrolled\n if (!isControlled) {\n setLocalValue('');\n }\n\n // Create a proper synthetic event for React Hook Form\n const syntheticEvent = {\n target: {\n value: '',\n name: inputName,\n id: inputId,\n type: actualType,\n },\n } as React.ChangeEvent<HTMLInputElement>;\n\n // Call the form's onChange handler\n restInputProps.onChange?.(syntheticEvent);\n\n // Also try to update the input element directly\n if (ref && typeof ref === 'object' && ref.current) {\n ref.current.value = '';\n }\n };\n\n // Handle focus and blur\n const handleFocus = () => {\n setIsFocused(true);\n };\n\n const handleBlur = () => {\n setIsFocused(false);\n };\n\n // Determine actual input type (handle password visibility)\n const actualType = type === 'password' ? (showPassword ? 'text' : 'password') : type;\n\n // Generate CSS classes\n const inputClasses = [\n 'eidos-input',\n `eidos-input--${variant}`,\n `eidos-input--${color}`,\n `eidos-input--${size}`,\n fullWidth && fullWidthModifier('eidos-input'),\n loading && `eidos-input--loading`,\n error && `eidos-input--error`,\n ]\n .filter(Boolean)\n .join(' ');\n\n const wrapperClasses = [\n 'eidos-input-wrapper',\n `eidos-input-wrapper--${variant}`,\n `eidos-input-wrapper--${color}`,\n `eidos-input-wrapper--${size}`,\n fullWidth && fullWidthModifier('eidos-input-wrapper'),\n loading && `eidos-input-wrapper--loading`,\n isFocused && `eidos-input-wrapper--focused`,\n error && `eidos-input-wrapper--error`,\n ]\n .filter(Boolean)\n .join(' ');\n\n const labelClasses = [\n 'eidos-input-label',\n shouldFloatLabel && `eidos-input-label--floating`,\n disclaimerIcon && `eidos-input-label--with-icon`,\n error && `eidos-input-label--error`,\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div\n className={`eidos-input-container ${fullWidth ? fullWidthModifier('eidos-input-container') : ''} ${className}`}\n // `width` sizes the whole field, so it belongs on the container rather\n // than on the inner <input> (which is a `flex: 1 1 0` child of the\n // wrapper and only ever fills whatever the wrapper gives it). Applying\n // it here also keeps it a *preferred* width: `max-width: 100%` lets the\n // field shrink inside a narrower parent instead of overflowing it.\n style={\n width\n ? { width: typeof width === 'number' ? `${width}px` : width, maxWidth: '100%' }\n : undefined\n }\n >\n {label && (\n <label htmlFor={inputId} className={labelClasses}>\n <span className={`eidos-input-label-text`}>{label}</span>\n {isRequired && <span className={`eidos-input-required-asterisk`}>*</span>}\n {disclaimerIcon &&\n (disclaimerContent ? (\n <Tooltip message={disclaimerContent}>\n <span className={`eidos-input-disclaimer-icon`}>\n {renderIcon(disclaimerIcon, `eidos-input-disclaimer-icon-svg`)}\n </span>\n </Tooltip>\n ) : (\n <span className={`eidos-input-disclaimer-icon`}>\n {renderIcon(disclaimerIcon, `eidos-input-disclaimer-icon-svg`)}\n </span>\n ))}\n </label>\n )}\n\n <div className={wrapperClasses}>\n {preIcon && (\n <span className={`eidos-input-pre-icon`}>\n {renderIcon(preIcon, `eidos-input-pre-icon-svg`)}\n </span>\n )}\n\n <input\n ref={ref}\n id={inputId}\n name={inputName}\n type={actualType}\n className={inputClasses}\n disabled={disabled || loading}\n onKeyDown={handleNumberInput}\n {...restInputProps}\n value={currentValue}\n onFocus={(e) => {\n handleFocus();\n restInputProps.onFocus?.(e);\n }}\n onBlur={(e) => {\n handleBlur();\n restInputProps.onBlur?.(e);\n }}\n onChange={handleInputChange}\n />\n\n {type === 'password' && (\n <button\n type=\"button\"\n className={`eidos-input-password-toggle`}\n onClick={() => setShowPassword(!showPassword)}\n disabled={disabled || loading}\n tabIndex={-1}\n >\n {showPassword ? (\n <EyeOff className={`eidos-input-password-toggle-icon`} />\n ) : (\n <Eye className={`eidos-input-password-toggle-icon`} />\n )}\n </button>\n )}\n\n {/* Clear button - show when there's content and clearable is enabled (default: true) */}\n {currentValue &&\n String(currentValue).trim().length > 0 &&\n type !== 'password' &&\n !isSelect &&\n clearable !== false && (\n <button\n type=\"button\"\n className={`eidos-input-clear-button`}\n onClick={handleClear}\n disabled={disabled || loading}\n tabIndex={-1}\n >\n <X className={`eidos-input-clear-button-icon`} />\n </button>\n )}\n\n {posIcon && !posIconButton && (\n <span className={`eidos-input-pos-icon`}>\n {renderIcon(posIcon, `eidos-input-pos-icon-svg`)}\n </span>\n )}\n\n {posIcon && posIconButton && onPosIconClick && (\n <button\n type=\"button\"\n className={`eidos-input-pos-icon-button`}\n onClick={onPosIconClick}\n disabled={disabled || loading}\n tabIndex={-1}\n >\n {renderIcon(posIcon, `eidos-input-pos-icon-svg`)}\n </button>\n )}\n\n {/* Chevron for select inputs */}\n {isSelect && (\n <span className={`eidos-input-select-chevron`}>\n <ChevronDown className={`eidos-input-pos-icon-svg`} />\n </span>\n )}\n </div>\n\n {error && (\n <div className={`eidos-input-error-message`}>\n <CircleAlert className={`eidos-input-error-icon`} />\n <span>{error}</span>\n </div>\n )}\n </div>\n );\n },\n);\n\nInput.displayName = 'Input';\n"]}