eidos-ui 3.3.0 → 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.
- package/README.md +17 -5
- package/dist/{Input.types-XrJTI0JC.d.ts → Input.types-CSDMaZNe.d.ts} +10 -0
- package/dist/{Input.types-DFZNyJGF.d.cts → Input.types-D7-sWq-q.d.cts} +10 -0
- package/dist/{Select.types-CAkT2ACt.d.cts → Select.types-2p-mpaBL.d.cts} +1 -1
- package/dist/{Select.types-B4kDpkGB.d.ts → Select.types-BqaD7bmB.d.ts} +1 -1
- package/dist/alert/index.cjs +5 -5
- package/dist/alert/index.js +4 -4
- package/dist/button/index.cjs +4 -4
- package/dist/button/index.js +3 -3
- package/dist/checkbox/index.cjs +2 -2
- package/dist/checkbox/index.js +1 -1
- package/dist/chip/index.cjs +4 -4
- package/dist/chip/index.js +3 -3
- package/dist/{chunk-RYOQYYDO.js → chunk-22EKNTWJ.js} +9 -5
- package/dist/chunk-22EKNTWJ.js.map +1 -0
- package/dist/{chunk-3E7TKBQI.cjs → chunk-27UANGPW.cjs} +14 -6
- package/dist/chunk-27UANGPW.cjs.map +1 -0
- package/dist/{chunk-CJGFRPAS.cjs → chunk-3QQKWXMV.cjs} +4 -4
- package/dist/chunk-3QQKWXMV.cjs.map +1 -0
- package/dist/{chunk-Q5KQVBQP.cjs → chunk-3S33BIN2.cjs} +18 -16
- package/dist/chunk-3S33BIN2.cjs.map +1 -0
- package/dist/{chunk-ZPDETHQ7.js → chunk-3WV6RTXX.js} +3 -3
- package/dist/{chunk-ZPDETHQ7.js.map → chunk-3WV6RTXX.js.map} +1 -1
- package/dist/chunk-44T4HMFU.js +88 -0
- package/dist/chunk-44T4HMFU.js.map +1 -0
- package/dist/{chunk-3EPND5M2.cjs → chunk-4LET2PEX.cjs} +12 -12
- package/dist/chunk-4LET2PEX.cjs.map +1 -0
- package/dist/{chunk-R4M6SNUU.cjs → chunk-5E4DYTZ3.cjs} +48 -37
- package/dist/chunk-5E4DYTZ3.cjs.map +1 -0
- package/dist/{chunk-I5XR2V4A.cjs → chunk-5V7DXZWT.cjs} +79 -76
- package/dist/chunk-5V7DXZWT.cjs.map +1 -0
- package/dist/{chunk-6CPCNQKV.js → chunk-67EXHO4T.js} +2 -2
- package/dist/{chunk-K7T25Y3A.cjs → chunk-6LCYO2HG.cjs} +10 -9
- package/dist/chunk-6LCYO2HG.cjs.map +1 -0
- package/dist/{chunk-ZNWIHUHZ.cjs → chunk-7KVXXHFX.cjs} +9 -3
- package/dist/chunk-7KVXXHFX.cjs.map +1 -0
- package/dist/{chunk-55J4LEKT.cjs → chunk-AHVDTWVM.cjs} +11 -8
- package/dist/chunk-AHVDTWVM.cjs.map +1 -0
- package/dist/{chunk-OAPOGYA5.cjs → chunk-AXPEJ5IG.cjs} +7 -4
- package/dist/chunk-AXPEJ5IG.cjs.map +1 -0
- package/dist/{chunk-GDQWSCJQ.js → chunk-BBU7QG7L.js} +4 -4
- package/dist/{chunk-T2KB3DJJ.js → chunk-BGBAZMFE.js} +9 -3
- package/dist/chunk-BGBAZMFE.js.map +1 -0
- package/dist/{chunk-EH42ND5Q.cjs → chunk-BIANFFJL.cjs} +5 -5
- package/dist/{chunk-EH42ND5Q.cjs.map → chunk-BIANFFJL.cjs.map} +1 -1
- package/dist/{chunk-O2AACVSF.js → chunk-BO4LTIJI.js} +3 -3
- package/dist/{chunk-G5N3C6AU.cjs → chunk-CBT3TTQR.cjs} +36 -36
- package/dist/{chunk-G5N3C6AU.cjs.map → chunk-CBT3TTQR.cjs.map} +1 -1
- package/dist/{chunk-JRLUZDKA.js → chunk-CEC3GO4R.js} +20 -7
- package/dist/chunk-CEC3GO4R.js.map +1 -0
- package/dist/{chunk-BNVQ34FD.js → chunk-CP335H4Q.js} +4 -4
- package/dist/{chunk-H6CWTMAH.js → chunk-DGXHR6FV.js} +5 -2
- package/dist/chunk-DGXHR6FV.js.map +1 -0
- package/dist/{chunk-OU7DCYIA.js → chunk-DMOZURHV.js} +2 -2
- package/dist/{chunk-L5W2XC6M.js → chunk-EWTMX4DK.js} +8 -8
- package/dist/{chunk-SXGCRHBZ.js → chunk-FDMYDIGI.js} +8 -6
- package/dist/chunk-FDMYDIGI.js.map +1 -0
- package/dist/{chunk-AOGNTG3W.cjs → chunk-FODRHCRP.cjs} +5 -4
- package/dist/chunk-FODRHCRP.cjs.map +1 -0
- package/dist/{chunk-E7FGY7BJ.js → chunk-FPLX5FIK.js} +4 -4
- package/dist/{chunk-NEYLNRVG.cjs → chunk-FXMRDNR6.cjs} +21 -16
- package/dist/chunk-FXMRDNR6.cjs.map +1 -0
- package/dist/{chunk-VXO2MYYH.cjs → chunk-G74DHVHV.cjs} +9 -9
- package/dist/{chunk-VXO2MYYH.cjs.map → chunk-G74DHVHV.cjs.map} +1 -1
- package/dist/{chunk-O3LG7B2J.cjs → chunk-GMDVW76A.cjs} +13 -5
- package/dist/chunk-GMDVW76A.cjs.map +1 -0
- package/dist/{chunk-NQ4L6PYW.js → chunk-GOF6A4M4.js} +2 -2
- package/dist/{chunk-NQ4L6PYW.js.map → chunk-GOF6A4M4.js.map} +1 -1
- package/dist/{chunk-TQGO5WK6.js → chunk-GPK2THIG.js} +7 -6
- package/dist/chunk-GPK2THIG.js.map +1 -0
- package/dist/{chunk-HYDNZXPU.js → chunk-H2RS6Z6Q.js} +41 -25
- package/dist/{chunk-HYDNZXPU.js.map → chunk-H2RS6Z6Q.js.map} +1 -1
- package/dist/{chunk-OJDYTCMO.cjs → chunk-H5D3ZFBM.cjs} +68 -61
- package/dist/chunk-H5D3ZFBM.cjs.map +1 -0
- package/dist/{chunk-MHZIBV2C.js → chunk-IXVM2XNS.js} +7 -6
- package/dist/chunk-IXVM2XNS.js.map +1 -0
- package/dist/{chunk-MCWTQT3M.cjs → chunk-JKBBHAIC.cjs} +21 -20
- package/dist/chunk-JKBBHAIC.cjs.map +1 -0
- package/dist/{chunk-QQIALSTX.cjs → chunk-KPMIIGHH.cjs} +27 -17
- package/dist/chunk-KPMIIGHH.cjs.map +1 -0
- package/dist/{chunk-QJRKXT54.cjs → chunk-KWDOOMHR.cjs} +7 -7
- package/dist/{chunk-QJRKXT54.cjs.map → chunk-KWDOOMHR.cjs.map} +1 -1
- package/dist/{chunk-HALVTKW7.js → chunk-LH5A2SIX.js} +78 -75
- package/dist/{chunk-HALVTKW7.js.map → chunk-LH5A2SIX.js.map} +1 -1
- package/dist/{chunk-27ZWUEPE.js → chunk-LIKSLFX6.js} +20 -9
- package/dist/chunk-LIKSLFX6.js.map +1 -0
- package/dist/{chunk-Z5U4NJTW.cjs → chunk-MLUHM3VG.cjs} +48 -19
- package/dist/chunk-MLUHM3VG.cjs.map +1 -0
- package/dist/{chunk-M7PF4FKQ.js → chunk-MP7LN5CW.js} +6 -3
- package/dist/chunk-MP7LN5CW.js.map +1 -0
- package/dist/{chunk-ACNTH2OQ.cjs → chunk-MW4YZVP5.cjs} +96 -97
- package/dist/chunk-MW4YZVP5.cjs.map +1 -0
- package/dist/{chunk-ZXJU3TQD.cjs → chunk-N5ZWQJTM.cjs} +11 -10
- package/dist/chunk-N5ZWQJTM.cjs.map +1 -0
- package/dist/{chunk-OGC44JFZ.cjs → chunk-N77VOYCK.cjs} +7 -7
- package/dist/{chunk-OGC44JFZ.cjs.map → chunk-N77VOYCK.cjs.map} +1 -1
- package/dist/{chunk-OAK7HFOJ.js → chunk-NACA72XI.js} +21 -16
- package/dist/chunk-NACA72XI.js.map +1 -0
- package/dist/{chunk-TEVZHN5I.cjs → chunk-NCZUQFKL.cjs} +28 -15
- package/dist/chunk-NCZUQFKL.cjs.map +1 -0
- package/dist/{chunk-NZO2I5AL.cjs → chunk-NF55JG2V.cjs} +7 -7
- package/dist/{chunk-NZO2I5AL.cjs.map → chunk-NF55JG2V.cjs.map} +1 -1
- package/dist/{chunk-ZWAFL2B3.cjs → chunk-O46UJUZM.cjs} +21 -19
- package/dist/chunk-O46UJUZM.cjs.map +1 -0
- package/dist/{chunk-25PSIDPC.js → chunk-OIXPYXR6.js} +79 -21
- package/dist/chunk-OIXPYXR6.js.map +1 -0
- package/dist/chunk-OWWPHXAF.cjs +88 -0
- package/dist/chunk-OWWPHXAF.cjs.map +1 -0
- package/dist/{chunk-65PC3YDT.cjs → chunk-OZQKWDFH.cjs} +7 -7
- package/dist/{chunk-65PC3YDT.cjs.map → chunk-OZQKWDFH.cjs.map} +1 -1
- package/dist/{chunk-P2JM22BF.cjs → chunk-P7J6WZTO.cjs} +3 -3
- package/dist/{chunk-P2JM22BF.cjs.map → chunk-P7J6WZTO.cjs.map} +1 -1
- package/dist/{chunk-KQJ3IAFT.js → chunk-PG2EII6U.js} +2 -2
- package/dist/{chunk-E6SE552P.cjs → chunk-RUDXHT76.cjs} +41 -25
- package/dist/chunk-RUDXHT76.cjs.map +1 -0
- package/dist/{chunk-S4S4LB63.js → chunk-SCLFJHN2.js} +11 -3
- package/dist/chunk-SCLFJHN2.js.map +1 -0
- package/dist/{chunk-WKDT2XFQ.js → chunk-SFRILDXY.js} +4 -4
- package/dist/{chunk-J5RMLZ4P.cjs → chunk-SKBKTNEQ.cjs} +80 -22
- package/dist/chunk-SKBKTNEQ.cjs.map +1 -0
- package/dist/{chunk-YW65NAKH.cjs → chunk-SKLMBE3Z.cjs} +7 -7
- package/dist/{chunk-YW65NAKH.cjs.map → chunk-SKLMBE3Z.cjs.map} +1 -1
- package/dist/{chunk-PY2376XI.js → chunk-SVCWSW2F.js} +8 -8
- package/dist/chunk-SVCWSW2F.js.map +1 -0
- package/dist/{chunk-2RXQMQYW.js → chunk-SXL4DMZ7.js} +27 -17
- package/dist/{chunk-2RXQMQYW.js.map → chunk-SXL4DMZ7.js.map} +1 -1
- package/dist/{chunk-7SVDYEKM.js → chunk-TLECMA3B.js} +5 -4
- package/dist/{chunk-7SVDYEKM.js.map → chunk-TLECMA3B.js.map} +1 -1
- package/dist/{chunk-FXZJL2AM.cjs → chunk-TWXZKUCG.cjs} +9 -5
- package/dist/chunk-TWXZKUCG.cjs.map +1 -0
- package/dist/{chunk-GG5YGESX.js → chunk-TYGVKSL6.js} +4 -3
- package/dist/chunk-TYGVKSL6.js.map +1 -0
- package/dist/{chunk-TTO7JXDB.cjs → chunk-TYSNOPMQ.cjs} +3 -3
- package/dist/{chunk-TTO7JXDB.cjs.map → chunk-TYSNOPMQ.cjs.map} +1 -1
- package/dist/{chunk-ZZSPQFMX.js → chunk-U233KQZX.js} +2 -1
- package/dist/{chunk-ZZSPQFMX.js.map → chunk-U233KQZX.js.map} +1 -1
- package/dist/{chunk-5HUZYY54.js → chunk-UDRAM6JU.js} +13 -5
- package/dist/chunk-UDRAM6JU.js.map +1 -0
- package/dist/{chunk-3EUTXQDB.js → chunk-UGTKZ7HP.js} +30 -23
- package/dist/chunk-UGTKZ7HP.js.map +1 -0
- package/dist/{chunk-FKXKBJUI.cjs → chunk-VHO6JPRR.cjs} +2 -1
- package/dist/chunk-VHO6JPRR.cjs.map +1 -0
- package/dist/chunk-VHSTB7GB.cjs +123 -0
- package/dist/chunk-VHSTB7GB.cjs.map +1 -0
- package/dist/{chunk-VPRUHE35.js → chunk-VRNCXYEP.js} +4 -4
- package/dist/{chunk-ITXDSQT2.js → chunk-WGCGSM5P.js} +96 -97
- package/dist/chunk-WGCGSM5P.js.map +1 -0
- package/dist/chunk-WT4WBHBR.js +123 -0
- package/dist/chunk-WT4WBHBR.js.map +1 -0
- package/dist/{chunk-L23WWO3D.js → chunk-X6UKU3C3.js} +2 -2
- package/dist/{chunk-5WH3JMR5.js → chunk-XK5USBHB.js} +7 -4
- package/dist/chunk-XK5USBHB.js.map +1 -0
- package/dist/{chunk-ND526PMG.cjs → chunk-XMXL6NVJ.cjs} +11 -11
- package/dist/{chunk-ND526PMG.cjs.map → chunk-XMXL6NVJ.cjs.map} +1 -1
- package/dist/{chunk-YV57RSPD.js → chunk-XO7WTE6O.js} +47 -18
- package/dist/chunk-XO7WTE6O.js.map +1 -0
- package/dist/{chunk-3CXULV4G.cjs → chunk-XRVRGRC3.cjs} +2 -2
- package/dist/{chunk-3CXULV4G.cjs.map → chunk-XRVRGRC3.cjs.map} +1 -1
- package/dist/{chunk-GPRJYPIG.js → chunk-Y7VSWZWM.js} +6 -4
- package/dist/chunk-Y7VSWZWM.js.map +1 -0
- package/dist/{chunk-2QEKSRVX.js → chunk-YIBPOLNN.js} +4 -4
- package/dist/{chunk-IJ4DXG7D.cjs → chunk-YJNQLH2L.cjs} +5 -2
- package/dist/chunk-YJNQLH2L.cjs.map +1 -0
- package/dist/{chunk-6CSUUQ5M.cjs → chunk-YUH56CJI.cjs} +6 -6
- package/dist/{chunk-6CSUUQ5M.cjs.map → chunk-YUH56CJI.cjs.map} +1 -1
- package/dist/color-picker/index.cjs +4 -4
- package/dist/color-picker/index.d.cts +8 -0
- package/dist/color-picker/index.d.ts +8 -0
- package/dist/color-picker/index.js +3 -3
- package/dist/combobox/index.cjs +6 -6
- package/dist/combobox/index.js +5 -5
- package/dist/command-palette/index.cjs +3 -2
- package/dist/command-palette/index.cjs.map +1 -1
- package/dist/command-palette/index.js +2 -1
- package/dist/context-menu/index.cjs +6 -6
- package/dist/context-menu/index.js +5 -5
- package/dist/conversation/index.cjs +8 -8
- package/dist/conversation/index.js +7 -7
- package/dist/data-grid/index.cjs +16 -16
- package/dist/data-grid/index.d.cts +2 -2
- package/dist/data-grid/index.d.ts +2 -2
- package/dist/data-grid/index.js +15 -15
- package/dist/date-picker/index.cjs +8 -8
- package/dist/date-picker/index.d.cts +1 -1
- package/dist/date-picker/index.d.ts +1 -1
- package/dist/date-picker/index.js +7 -7
- package/dist/drawer/index.cjs +5 -5
- package/dist/drawer/index.js +4 -4
- package/dist/dropdown/index.cjs +3 -3
- package/dist/dropdown/index.js +2 -2
- package/dist/file-upload/index.cjs +2 -2
- package/dist/file-upload/index.js +1 -1
- package/dist/header/index.cjs +6 -6
- package/dist/header/index.js +5 -5
- package/dist/index.cjs +42 -42
- package/dist/index.css +93 -38
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +41 -41
- package/dist/inline-edit/index.cjs +5 -5
- package/dist/inline-edit/index.js +4 -4
- package/dist/input/index.cjs +4 -4
- package/dist/input/index.d.cts +2 -2
- package/dist/input/index.d.ts +2 -2
- package/dist/input/index.js +3 -3
- package/dist/menu/index.cjs +5 -5
- package/dist/menu/index.js +4 -4
- package/dist/modal/index.cjs +5 -5
- package/dist/modal/index.js +4 -4
- package/dist/navigation/index.cjs +5 -5
- package/dist/navigation/index.js +4 -4
- package/dist/number-input/index.cjs +3 -3
- package/dist/number-input/index.js +2 -2
- package/dist/o-t-p-input/index.cjs +2 -2
- package/dist/o-t-p-input/index.js +1 -1
- package/dist/page-layout/index.cjs +12 -12
- package/dist/page-layout/index.js +11 -11
- package/dist/pagination/index.cjs +8 -8
- package/dist/pagination/index.js +7 -7
- package/dist/popover/index.cjs +3 -2
- package/dist/popover/index.cjs.map +1 -1
- package/dist/popover/index.js +2 -1
- package/dist/radio/index.cjs +2 -2
- package/dist/radio/index.js +1 -1
- package/dist/segmented-control/index.cjs +4 -4
- package/dist/segmented-control/index.js +3 -3
- package/dist/select/index.cjs +6 -6
- package/dist/select/index.d.cts +3 -3
- package/dist/select/index.d.ts +3 -3
- package/dist/select/index.js +5 -5
- package/dist/snackbar/index.cjs +5 -5
- package/dist/snackbar/index.d.cts +4 -0
- package/dist/snackbar/index.d.ts +4 -0
- package/dist/snackbar/index.js +4 -4
- package/dist/split-button/index.cjs +6 -6
- package/dist/split-button/index.js +5 -5
- package/dist/table/index.cjs +14 -14
- package/dist/table/index.js +13 -13
- package/dist/tabs/index.cjs +3 -3
- package/dist/tabs/index.js +2 -2
- package/dist/tag-input/index.cjs +6 -6
- package/dist/tag-input/index.js +5 -5
- package/dist/textarea/index.cjs +4 -4
- package/dist/textarea/index.js +3 -3
- package/dist/theme-editor/index.cjs +11 -11
- package/dist/theme-editor/index.js +10 -10
- package/dist/theme-provider/index.cjs +3 -3
- package/dist/theme-provider/index.js +2 -2
- package/dist/toolbar/index.cjs +8 -8
- package/dist/toolbar/index.js +7 -7
- package/dist/tooltip/index.cjs +3 -2
- package/dist/tooltip/index.cjs.map +1 -1
- package/dist/tooltip/index.js +2 -1
- package/dist/virtual-list/index.cjs +2 -2
- package/dist/virtual-list/index.js +1 -1
- package/package.json +7 -2
- package/dist/chunk-25PSIDPC.js.map +0 -1
- package/dist/chunk-27ZWUEPE.js.map +0 -1
- package/dist/chunk-3E7TKBQI.cjs.map +0 -1
- package/dist/chunk-3EPND5M2.cjs.map +0 -1
- package/dist/chunk-3EUTXQDB.js.map +0 -1
- package/dist/chunk-55J4LEKT.cjs.map +0 -1
- package/dist/chunk-5HUZYY54.js.map +0 -1
- package/dist/chunk-5WH3JMR5.js.map +0 -1
- package/dist/chunk-ACNTH2OQ.cjs.map +0 -1
- package/dist/chunk-AOGNTG3W.cjs.map +0 -1
- package/dist/chunk-CJGFRPAS.cjs.map +0 -1
- package/dist/chunk-CUJBNRJJ.cjs +0 -38
- package/dist/chunk-CUJBNRJJ.cjs.map +0 -1
- package/dist/chunk-E6SE552P.cjs.map +0 -1
- package/dist/chunk-EAJHEYZM.js +0 -38
- package/dist/chunk-EAJHEYZM.js.map +0 -1
- package/dist/chunk-FKXKBJUI.cjs.map +0 -1
- package/dist/chunk-FXZJL2AM.cjs.map +0 -1
- package/dist/chunk-GG5YGESX.js.map +0 -1
- package/dist/chunk-GPRJYPIG.js.map +0 -1
- package/dist/chunk-H6CWTMAH.js.map +0 -1
- package/dist/chunk-I5XR2V4A.cjs.map +0 -1
- package/dist/chunk-IJ4DXG7D.cjs.map +0 -1
- package/dist/chunk-ITXDSQT2.js.map +0 -1
- package/dist/chunk-J5RMLZ4P.cjs.map +0 -1
- package/dist/chunk-JRLUZDKA.js.map +0 -1
- package/dist/chunk-K7T25Y3A.cjs.map +0 -1
- package/dist/chunk-L5Q6UDY3.js +0 -69
- package/dist/chunk-L5Q6UDY3.js.map +0 -1
- package/dist/chunk-M7PF4FKQ.js.map +0 -1
- package/dist/chunk-MCWTQT3M.cjs.map +0 -1
- package/dist/chunk-MHZIBV2C.js.map +0 -1
- package/dist/chunk-NEYLNRVG.cjs.map +0 -1
- package/dist/chunk-O3LG7B2J.cjs.map +0 -1
- package/dist/chunk-OAK7HFOJ.js.map +0 -1
- package/dist/chunk-OAPOGYA5.cjs.map +0 -1
- package/dist/chunk-OJDYTCMO.cjs.map +0 -1
- package/dist/chunk-PY2376XI.js.map +0 -1
- package/dist/chunk-Q5KQVBQP.cjs.map +0 -1
- package/dist/chunk-QQIALSTX.cjs.map +0 -1
- package/dist/chunk-R4M6SNUU.cjs.map +0 -1
- package/dist/chunk-RYOQYYDO.js.map +0 -1
- package/dist/chunk-S4S4LB63.js.map +0 -1
- package/dist/chunk-SXGCRHBZ.js.map +0 -1
- package/dist/chunk-T2KB3DJJ.js.map +0 -1
- package/dist/chunk-TEVZHN5I.cjs.map +0 -1
- package/dist/chunk-TQGO5WK6.js.map +0 -1
- package/dist/chunk-WWYJNWJY.cjs +0 -69
- package/dist/chunk-WWYJNWJY.cjs.map +0 -1
- package/dist/chunk-YV57RSPD.js.map +0 -1
- package/dist/chunk-Z5U4NJTW.cjs.map +0 -1
- package/dist/chunk-ZNWIHUHZ.cjs.map +0 -1
- package/dist/chunk-ZWAFL2B3.cjs.map +0 -1
- package/dist/chunk-ZXJU3TQD.cjs.map +0 -1
- /package/dist/{chunk-6CPCNQKV.js.map → chunk-67EXHO4T.js.map} +0 -0
- /package/dist/{chunk-GDQWSCJQ.js.map → chunk-BBU7QG7L.js.map} +0 -0
- /package/dist/{chunk-O2AACVSF.js.map → chunk-BO4LTIJI.js.map} +0 -0
- /package/dist/{chunk-BNVQ34FD.js.map → chunk-CP335H4Q.js.map} +0 -0
- /package/dist/{chunk-OU7DCYIA.js.map → chunk-DMOZURHV.js.map} +0 -0
- /package/dist/{chunk-L5W2XC6M.js.map → chunk-EWTMX4DK.js.map} +0 -0
- /package/dist/{chunk-E7FGY7BJ.js.map → chunk-FPLX5FIK.js.map} +0 -0
- /package/dist/{chunk-KQJ3IAFT.js.map → chunk-PG2EII6U.js.map} +0 -0
- /package/dist/{chunk-WKDT2XFQ.js.map → chunk-SFRILDXY.js.map} +0 -0
- /package/dist/{chunk-VPRUHE35.js.map → chunk-VRNCXYEP.js.map} +0 -0
- /package/dist/{chunk-L23WWO3D.js.map → chunk-X6UKU3C3.js.map} +0 -0
- /package/dist/{chunk-2QEKSRVX.js.map → chunk-YIBPOLNN.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/Combobox/Combobox.component.tsx"],"sourcesContent":["import React, { useState, useCallback, useRef, useEffect, useMemo, useId } from 'react';\nimport { Check, ChevronDown, X, Loader2, CircleAlert } from 'lucide-react';\nimport { Input } from '../Input/Input.component';\nimport { Dropdown } from '../Dropdown/Dropdown.component';\nimport { fullWidthModifier } from '../../utils';\nimport type { ComboboxProps, ComboboxOption } from './Combobox.types';\nimport './Combobox.scss';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ntype OptionEntry =\n | { type: 'group'; group: string; key: string }\n | { type: 'option'; option: ComboboxOption; index: number; key: string };\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport const Combobox: React.FC<ComboboxProps> = ({\n options = [],\n value,\n defaultValue,\n onChange,\n onSearch,\n placeholder = 'Search or select…',\n loading = false,\n loadingText = 'Searching…',\n emptyText = 'No results found',\n allowFreeText = false,\n clearable = true,\n disabled = false,\n size = 'md',\n label,\n error,\n hint,\n fullWidth = false,\n className = '',\n maxHeight = '300px',\n renderOption,\n}) => {\n const uid = useId();\n\n // ─── Refs ─────────────────────────────────────────────────────────────────\n\n /** Entire combobox wrapper - used for click-outside detection */\n const containerRef = useRef<HTMLDivElement>(null);\n\n /** The trigger wrapper div - passed to Dropdown as externalTriggerRef for width */\n const comboboxTriggerRef = useRef<HTMLDivElement>(null);\n\n /** The 0-height span that IS the Dropdown trigger element - programmatically clicked to open */\n const dropdownSpanRef = useRef<HTMLSpanElement>(null);\n\n /** Native input element */\n const inputRef = useRef<HTMLInputElement>(null);\n\n /** Focusable option divs for scroll-into-view */\n const optionRefs = useRef<(HTMLDivElement | null)[]>([]);\n\n /**\n * Synchronous open-state flag.\n * React state updates are async; reading this ref inside event handlers\n * gives us the true current value without waiting for a re-render.\n */\n const isOpenRef = useRef(false);\n\n // ─── State ────────────────────────────────────────────────────────────────\n\n const isControlled = value !== undefined;\n\n const getInitialInputValue = (): string => {\n const seed = isControlled ? value : defaultValue;\n if (!seed) return '';\n const match = options.find((o) => o.value === seed);\n return match ? match.label : allowFreeText ? seed : '';\n };\n\n const getInitialCommittedValue = (): string => {\n return isControlled ? (value ?? '') : (defaultValue ?? '');\n };\n\n const [inputValue, _setInputValue] = useState<string>(getInitialInputValue);\n const inputValueRef = useRef<string>(inputValue);\n const setInputValue = useCallback((v: string) => {\n inputValueRef.current = v;\n _setInputValue(v);\n }, []);\n\n const [committedValue, _setCommittedValue] = useState<string>(getInitialCommittedValue);\n const committedValueRef = useRef<string>(committedValue);\n const setCommittedValue = useCallback((v: string) => {\n committedValueRef.current = v;\n _setCommittedValue(v);\n }, []);\n\n const [isOpen, setIsOpen] = useState(false);\n const [menuKey, setMenuKey] = useState(0);\n const [focusedIndex, setFocusedIndex] = useState(-1);\n\n // ─── Sync controlled value ────────────────────────────────────────────────\n\n useEffect(() => {\n if (!isControlled) return;\n const v = value ?? '';\n const match = options.find((o) => o.value === v);\n const label = match ? match.label : allowFreeText ? v : '';\n setInputValue(label);\n setCommittedValue(v);\n }, [value, isControlled, options, allowFreeText, setInputValue, setCommittedValue]);\n\n // ─── Derived: filtered options ────────────────────────────────────────────\n\n const filteredOptions = useMemo<ComboboxOption[]>(() => {\n const q = inputValue.toLowerCase();\n if (!q) return options;\n return options.filter((o) => o.label.toLowerCase().includes(q));\n }, [options, inputValue]);\n\n // Reset focused index when the filtered set changes\n useEffect(() => {\n setFocusedIndex(-1);\n }, [filteredOptions]);\n\n // Keep optionRefs array in sync with filteredOptions length\n useEffect(() => {\n optionRefs.current = optionRefs.current.slice(0, filteredOptions.length);\n }, [filteredOptions.length]);\n\n // ─── Derived: option entries (with group headers) ─────────────────────────\n\n const optionEntries = useMemo<OptionEntry[]>(() => {\n const entries: OptionEntry[] = [];\n const hasGroups = filteredOptions.some((o) => o.group);\n\n if (!hasGroups) {\n filteredOptions.forEach((option, index) => {\n entries.push({ type: 'option', option, index, key: option.id });\n });\n return entries;\n }\n\n // Build ordered group map while preserving insertion order\n const groupMap = new Map<string | null, ComboboxOption[]>();\n for (const option of filteredOptions) {\n const g = option.group ?? null;\n if (!groupMap.has(g)) groupMap.set(g, []);\n groupMap.get(g)!.push(option);\n }\n\n let optionIndex = 0;\n for (const [group, opts] of groupMap.entries()) {\n if (group !== null) {\n entries.push({ type: 'group', group, key: `group-${group}` });\n }\n for (const option of opts) {\n entries.push({ type: 'option', option, index: optionIndex++, key: option.id });\n }\n }\n\n return entries;\n }, [filteredOptions]);\n\n // ─── Open / Close helpers ─────────────────────────────────────────────────\n\n /**\n * Open the dropdown by programmatically clicking the 0-height span trigger.\n * The click bubbles to div.eidos-dropdown-trigger → Dropdown.handleTriggerClick → opens.\n *\n * We set isOpenRef AFTER the synthetic click so that the Dropdown's toggle\n * handler fires while isOpenRef is still false (allowing it to open).\n */\n const openDropdown = useCallback(() => {\n if (isOpenRef.current || disabled) return;\n dropdownSpanRef.current?.click(); // opens the Dropdown (bubbles to trigger)\n isOpenRef.current = true;\n setIsOpen(true);\n }, [disabled]);\n\n /**\n * Close the dropdown by remounting it (menuKey increment resets internal state).\n * Optionally revert inputValue to the last committed value.\n */\n const closeDropdown = useCallback(\n (revertInput = false) => {\n isOpenRef.current = false;\n setIsOpen(false);\n setFocusedIndex(-1);\n setMenuKey((prev) => prev + 1);\n\n if (revertInput) {\n const match = options.find((o) => o.value === committedValueRef.current);\n setInputValue(match ? match.label : allowFreeText ? committedValueRef.current : '');\n }\n },\n [options, allowFreeText, setInputValue],\n );\n\n // ─── Click-outside detection ──────────────────────────────────────────────\n\n useEffect(() => {\n const handleMouseDown = (e: MouseEvent) => {\n if (!isOpenRef.current) return;\n // defaultPrevented is set by the listbox's onMouseDown - means the user\n // clicked an option in the portal, not truly outside the combobox.\n if (e.defaultPrevented) return;\n if (containerRef.current?.contains(e.target as Node)) return;\n\n // Focus leaving to a non-focusable target: validate and close\n isOpenRef.current = false;\n setIsOpen(false);\n setFocusedIndex(-1);\n setMenuKey((prev) => prev + 1);\n\n const current = inputValueRef.current;\n const committed = committedValueRef.current;\n\n if (!allowFreeText) {\n const match = options.find((o) => o.label.toLowerCase() === current.toLowerCase());\n if (match) {\n if (match.value !== committed) {\n setCommittedValue(match.value);\n onChange?.(match.value);\n setInputValue(match.label);\n }\n } else {\n const revertMatch = options.find((o) => o.value === committed);\n setInputValue(revertMatch ? revertMatch.label : '');\n }\n } else {\n if (current !== committed) {\n setCommittedValue(current);\n onChange?.(current);\n }\n }\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [allowFreeText, options, onChange, setCommittedValue, setInputValue]);\n\n // ─── Input handlers ───────────────────────────────────────────────────────\n\n const handleFocus = useCallback(() => {\n openDropdown();\n }, [openDropdown]);\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const query = e.target.value;\n setInputValue(query);\n onSearch?.(query);\n if (!isOpenRef.current) openDropdown();\n },\n [setInputValue, onSearch, openDropdown],\n );\n\n /**\n * On blur, validate the typed value against options.\n * A 0ms defer ensures a click on a posIcon button (X / chevron) inside the\n * Input completes its onClick before we run close logic, so we can check\n * document.activeElement reliably.\n */\n const handleBlur = useCallback(() => {\n setTimeout(() => {\n // If focus came back inside the container (e.g., posIcon button clicked),\n // don't close or validate yet.\n if (containerRef.current?.contains(document.activeElement)) return;\n\n // Read both refs *inside* the deferred callback, never captured at blur\n // time - that's the whole reason they're kept in sync by the setters.\n // Anything can run in the gap this 0ms defer opens, including the click\n // that caused the blur: a controlled consumer clearing `value` from\n // another control (e.g. DataGrid's \"Clear filters\", which resets every\n // quick filter at once) updates the refs via the sync effect, and stale\n // captured values would then resurrect the label that was just cleared.\n const current = inputValueRef.current;\n const committed = committedValueRef.current;\n\n // Close\n isOpenRef.current = false;\n setIsOpen(false);\n setFocusedIndex(-1);\n setMenuKey((prev) => prev + 1);\n\n // Validate\n if (!allowFreeText) {\n const match = options.find((o) => o.label.toLowerCase() === current.toLowerCase());\n if (match) {\n if (match.value !== committed) {\n setCommittedValue(match.value);\n onChange?.(match.value);\n setInputValue(match.label);\n } else {\n // Normalize the display label (fix casing)\n setInputValue(match.label);\n }\n } else {\n // No match - revert to last committed value\n const revertMatch = options.find((o) => o.value === committed);\n setInputValue(revertMatch ? revertMatch.label : '');\n }\n } else {\n // allowFreeText: commit whatever the user typed\n if (current !== committed) {\n setCommittedValue(current);\n onChange?.(current);\n }\n }\n }, 0);\n }, [allowFreeText, options, onChange, setCommittedValue, setInputValue]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (!isOpenRef.current) {\n openDropdown();\n setFocusedIndex(0);\n } else {\n setFocusedIndex((prev) => {\n const next = prev < filteredOptions.length - 1 ? prev + 1 : 0;\n setTimeout(\n () =>\n optionRefs.current[next]?.scrollIntoView({\n block: 'nearest',\n behavior: 'smooth',\n }),\n 0,\n );\n return next;\n });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n if (!isOpenRef.current) {\n openDropdown();\n setFocusedIndex(filteredOptions.length - 1);\n } else {\n setFocusedIndex((prev) => {\n const next = prev > 0 ? prev - 1 : filteredOptions.length - 1;\n setTimeout(\n () =>\n optionRefs.current[next]?.scrollIntoView({\n block: 'nearest',\n behavior: 'smooth',\n }),\n 0,\n );\n return next;\n });\n }\n break;\n\n case 'Enter':\n e.preventDefault();\n if (isOpenRef.current && focusedIndex >= 0 && focusedIndex < filteredOptions.length) {\n handleOptionSelect(filteredOptions[focusedIndex]);\n } else if (allowFreeText && inputValue.trim()) {\n const trimmed = inputValue.trim();\n setCommittedValue(trimmed);\n onChange?.(trimmed);\n closeDropdown();\n }\n break;\n\n case 'Escape':\n e.preventDefault();\n if (isOpenRef.current) {\n closeDropdown(true /* revert */);\n }\n break;\n\n default:\n break;\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [\n disabled,\n filteredOptions,\n focusedIndex,\n allowFreeText,\n inputValue,\n onChange,\n openDropdown,\n closeDropdown,\n setCommittedValue,\n ],\n );\n\n // ─── Option selection ─────────────────────────────────────────────────────\n\n const handleOptionSelect = useCallback(\n (option: ComboboxOption) => {\n if (option.disabled) return;\n setInputValue(option.label);\n setCommittedValue(option.value);\n onChange?.(option.value);\n closeDropdown();\n // Return focus to the input so the user can continue interacting\n inputRef.current?.focus();\n },\n [onChange, closeDropdown, setInputValue, setCommittedValue],\n );\n\n // ─── Clear ────────────────────────────────────────────────────────────────\n\n const handleClearClick = useCallback(() => {\n setInputValue('');\n setCommittedValue('');\n onChange?.('');\n // Re-focus input so the user can immediately start typing again\n inputRef.current?.focus();\n }, [onChange, setInputValue, setCommittedValue]);\n\n // ─── Chevron toggle ───────────────────────────────────────────────────────\n\n const handleChevronClick = useCallback(() => {\n if (isOpenRef.current) {\n closeDropdown();\n } else {\n openDropdown();\n }\n }, [closeDropdown, openDropdown]);\n\n // ─── Render: individual option ────────────────────────────────────────────\n\n const renderOptionItem = useCallback(\n (option: ComboboxOption, index: number): React.ReactNode => {\n const isSelected = option.value === committedValue;\n const isFocused = index === focusedIndex;\n\n const classes = [\n 'eidos-combobox-option',\n isSelected && 'eidos-combobox-option--selected',\n isFocused && 'eidos-combobox-option--focused',\n option.disabled && 'eidos-combobox-option--disabled',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div\n key={option.id}\n ref={(el) => {\n optionRefs.current[index] = el;\n }}\n className={classes}\n onClick={() => handleOptionSelect(option)}\n onMouseEnter={() => !option.disabled && setFocusedIndex(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled}\n id={`${uid}-option-${index}`}\n >\n {renderOption ? (\n renderOption(option)\n ) : (\n <>\n <Check\n className={`eidos-combobox-option-icon${\n !isSelected ? ' eidos-combobox-option-icon--hidden' : ''\n }`}\n />\n <span className=\"eidos-combobox-option-label\">{option.label}</span>\n {option.description && (\n <span className=\"eidos-combobox-option-description\">{option.description}</span>\n )}\n </>\n )}\n </div>\n );\n },\n [committedValue, focusedIndex, handleOptionSelect, renderOption, uid],\n );\n\n // ─── Render: dropdown content ─────────────────────────────────────────────\n\n const dropdownContent = useMemo(() => {\n if (loading) {\n return (\n <div className=\"eidos-combobox-loading\">\n <Loader2 className=\"eidos-combobox-loading-icon\" />\n <span>{loadingText}</span>\n </div>\n );\n }\n\n if (filteredOptions.length === 0) {\n return <div className=\"eidos-combobox-empty\">{emptyText}</div>;\n }\n\n return optionEntries.map((entry) => {\n if (entry.type === 'group') {\n return (\n <div key={entry.key} className=\"eidos-combobox-option-group-label\">\n {entry.group}\n </div>\n );\n }\n return renderOptionItem(entry.option, entry.index);\n });\n }, [loading, loadingText, filteredOptions.length, emptyText, optionEntries, renderOptionItem]);\n\n // ─── posIcon / posIconButton wiring ───────────────────────────────────────\n\n const hasClearableValue = clearable && !!inputValue;\n const posIcon = hasClearableValue ? X : ChevronDown;\n const posIconClick = hasClearableValue ? handleClearClick : handleChevronClick;\n\n // ─── CSS classes ──────────────────────────────────────────────────────────\n\n const containerClasses = [\n 'eidos-combobox',\n fullWidth && fullWidthModifier('eidos-combobox'),\n error && 'eidos-combobox--error',\n disabled && 'eidos-combobox--disabled',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const maxHeightValue = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n <div\n ref={containerRef}\n className={containerClasses}\n aria-expanded={isOpen}\n aria-haspopup=\"listbox\"\n >\n <div ref={comboboxTriggerRef} className=\"eidos-combobox-trigger\">\n {/*\n The Input is a direct child of eidos-combobox-trigger.\n It is NOT inside the Dropdown trigger div, so clicking it\n never bubbles to Dropdown.handleTriggerClick and never\n accidentally toggles the dropdown closed mid-edit.\n */}\n <Input\n ref={inputRef}\n value={inputValue}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n size={size}\n fullWidth\n label={label}\n clearable={false} // we render our own clear via posIcon\n isSelect={false}\n posIcon={posIcon}\n posIconButton\n onPosIconClick={posIconClick}\n aria-autocomplete=\"list\"\n aria-controls={`${uid}-listbox`}\n aria-activedescendant={focusedIndex >= 0 ? `${uid}-option-${focusedIndex}` : undefined}\n />\n\n {/*\n The Dropdown trigger is a 0-height span placed AFTER the Input\n in the normal document flow. Its getBoundingClientRect sits at\n exactly the bottom edge of the Input, giving the Dropdown correct\n placement without any click-toggle collision with the Input above.\n closeOnClickOutside and closeOnEscape are disabled because we own\n those interactions in our mousedown listener and onKeyDown handler.\n */}\n <Dropdown\n key={menuKey}\n trigger={\n <span\n ref={dropdownSpanRef}\n className=\"eidos-combobox-dropdown-anchor\"\n aria-hidden=\"true\"\n />\n }\n content={\n <div\n id={`${uid}-listbox`}\n className=\"eidos-combobox-options\"\n role=\"listbox\"\n aria-label={label ?? 'Options'}\n // Prevent the input from blurring when the user clicks an option,\n // and mark this event so the click-outside handler ignores it.\n onMouseDown={(e) => e.preventDefault()}\n >\n {dropdownContent}\n </div>\n }\n triggerRef={comboboxTriggerRef as React.RefObject<HTMLElement | null>}\n maxHeight={maxHeightValue}\n autoWidth\n closeOnClickOutside={false}\n closeOnEscape={false}\n />\n </div>\n\n {hint && !error && <span className=\"eidos-combobox-hint\">{hint}</span>}\n {error && (\n <span className=\"eidos-combobox-error-message\">\n <CircleAlert className=\"eidos-combobox-error-icon\" />\n {error}\n </span>\n )}\n </div>\n );\n};\n\nCombobox.displayName = 'Combobox';\n"],"mappings":"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","names":["label"]}
|
|
1
|
+
{"version":3,"sources":["../src/components/Combobox/Combobox.component.tsx"],"sourcesContent":["import React, { useState, useCallback, useRef, useEffect, useMemo, useId } from 'react';\nimport { Check, ChevronDown, X, Loader2, CircleAlert } from 'lucide-react';\nimport { Input } from '../Input/Input.component';\nimport { Dropdown } from '../Dropdown/Dropdown.component';\nimport { fullWidthModifier } from '../../utils';\nimport type { ComboboxProps, ComboboxOption } from './Combobox.types';\nimport './Combobox.scss';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ntype OptionEntry =\n | { type: 'group'; group: string; key: string }\n | { type: 'option'; option: ComboboxOption; index: number; key: string };\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\nexport const Combobox: React.FC<ComboboxProps> = ({\n options = [],\n value,\n defaultValue,\n onChange,\n onSearch,\n placeholder = 'Search or select…',\n loading = false,\n loadingText = 'Searching…',\n emptyText = 'No results found',\n allowFreeText = false,\n clearable = true,\n disabled = false,\n size = 'md',\n label,\n error,\n hint,\n fullWidth = false,\n className = '',\n maxHeight = '300px',\n renderOption,\n}) => {\n const uid = useId();\n\n // ─── Refs ─────────────────────────────────────────────────────────────────\n\n /** Entire combobox wrapper - used for click-outside detection */\n const containerRef = useRef<HTMLDivElement>(null);\n\n /** The trigger wrapper div - passed to Dropdown as externalTriggerRef for width */\n const comboboxTriggerRef = useRef<HTMLDivElement>(null);\n\n /** The 0-height span that IS the Dropdown trigger element - programmatically clicked to open */\n const dropdownSpanRef = useRef<HTMLSpanElement>(null);\n\n /** Native input element */\n const inputRef = useRef<HTMLInputElement>(null);\n\n /** Focusable option divs for scroll-into-view */\n const optionRefs = useRef<(HTMLDivElement | null)[]>([]);\n\n /**\n * Synchronous open-state flag.\n * React state updates are async; reading this ref inside event handlers\n * gives us the true current value without waiting for a re-render.\n */\n const isOpenRef = useRef(false);\n\n // ─── State ────────────────────────────────────────────────────────────────\n\n const isControlled = value !== undefined;\n\n const getInitialInputValue = (): string => {\n const seed = isControlled ? value : defaultValue;\n if (!seed) return '';\n const match = options.find((o) => o.value === seed);\n return match ? match.label : allowFreeText ? seed : '';\n };\n\n const getInitialCommittedValue = (): string => {\n return isControlled ? (value ?? '') : (defaultValue ?? '');\n };\n\n const [inputValue, _setInputValue] = useState<string>(getInitialInputValue);\n const inputValueRef = useRef<string>(inputValue);\n const setInputValue = useCallback((v: string) => {\n inputValueRef.current = v;\n _setInputValue(v);\n }, []);\n\n const [committedValue, _setCommittedValue] = useState<string>(getInitialCommittedValue);\n const committedValueRef = useRef<string>(committedValue);\n const setCommittedValue = useCallback((v: string) => {\n committedValueRef.current = v;\n _setCommittedValue(v);\n }, []);\n\n const [isOpen, setIsOpen] = useState(false);\n const [menuKey, setMenuKey] = useState(0);\n const [focusedIndex, setFocusedIndex] = useState(-1);\n\n // ─── Sync controlled value ────────────────────────────────────────────────\n\n useEffect(() => {\n if (!isControlled) return;\n const v = value ?? '';\n const match = options.find((o) => o.value === v);\n const label = match ? match.label : allowFreeText ? v : '';\n setInputValue(label);\n setCommittedValue(v);\n }, [value, isControlled, options, allowFreeText, setInputValue, setCommittedValue]);\n\n // ─── Derived: filtered options ────────────────────────────────────────────\n\n const filteredOptions = useMemo<ComboboxOption[]>(() => {\n const q = inputValue.toLowerCase();\n if (!q) return options;\n return options.filter((o) => o.label.toLowerCase().includes(q));\n }, [options, inputValue]);\n\n // Reset focused index when the filtered set changes\n useEffect(() => {\n setFocusedIndex(-1);\n }, [filteredOptions]);\n\n // Keep optionRefs array in sync with filteredOptions length\n useEffect(() => {\n optionRefs.current = optionRefs.current.slice(0, filteredOptions.length);\n }, [filteredOptions.length]);\n\n // ─── Derived: option entries (with group headers) ─────────────────────────\n\n const optionEntries = useMemo<OptionEntry[]>(() => {\n const entries: OptionEntry[] = [];\n const hasGroups = filteredOptions.some((o) => o.group);\n\n if (!hasGroups) {\n filteredOptions.forEach((option, index) => {\n entries.push({ type: 'option', option, index, key: option.id });\n });\n return entries;\n }\n\n // Build ordered group map while preserving insertion order\n const groupMap = new Map<string | null, ComboboxOption[]>();\n for (const option of filteredOptions) {\n const g = option.group ?? null;\n if (!groupMap.has(g)) groupMap.set(g, []);\n groupMap.get(g)!.push(option);\n }\n\n let optionIndex = 0;\n for (const [group, opts] of groupMap.entries()) {\n if (group !== null) {\n entries.push({ type: 'group', group, key: `group-${group}` });\n }\n for (const option of opts) {\n entries.push({ type: 'option', option, index: optionIndex++, key: option.id });\n }\n }\n\n return entries;\n }, [filteredOptions]);\n\n // ─── Open / Close helpers ─────────────────────────────────────────────────\n\n /**\n * Open the dropdown by programmatically clicking the 0-height span trigger.\n * The click bubbles to div.eidos-dropdown-trigger → Dropdown.handleTriggerClick → opens.\n *\n * We set isOpenRef AFTER the synthetic click so that the Dropdown's toggle\n * handler fires while isOpenRef is still false (allowing it to open).\n */\n const openDropdown = useCallback(() => {\n if (isOpenRef.current || disabled) return;\n dropdownSpanRef.current?.click(); // opens the Dropdown (bubbles to trigger)\n isOpenRef.current = true;\n setIsOpen(true);\n }, [disabled]);\n\n /**\n * Close the dropdown by remounting it (menuKey increment resets internal state).\n * Optionally revert inputValue to the last committed value.\n */\n const closeDropdown = useCallback(\n (revertInput = false) => {\n isOpenRef.current = false;\n setIsOpen(false);\n setFocusedIndex(-1);\n setMenuKey((prev) => prev + 1);\n\n if (revertInput) {\n const match = options.find((o) => o.value === committedValueRef.current);\n setInputValue(match ? match.label : allowFreeText ? committedValueRef.current : '');\n }\n },\n [options, allowFreeText, setInputValue],\n );\n\n // ─── Click-outside detection ──────────────────────────────────────────────\n\n useEffect(() => {\n const handleMouseDown = (e: MouseEvent) => {\n if (!isOpenRef.current) return;\n // defaultPrevented is set by the listbox's onMouseDown - means the user\n // clicked an option in the portal, not truly outside the combobox.\n if (e.defaultPrevented) return;\n if (containerRef.current?.contains(e.target as Node)) return;\n\n // Focus leaving to a non-focusable target: validate and close\n isOpenRef.current = false;\n setIsOpen(false);\n setFocusedIndex(-1);\n setMenuKey((prev) => prev + 1);\n\n const current = inputValueRef.current;\n const committed = committedValueRef.current;\n\n if (!allowFreeText) {\n const match = options.find((o) => o.label.toLowerCase() === current.toLowerCase());\n if (match) {\n if (match.value !== committed) {\n setCommittedValue(match.value);\n onChange?.(match.value);\n setInputValue(match.label);\n }\n } else {\n const revertMatch = options.find((o) => o.value === committed);\n setInputValue(revertMatch ? revertMatch.label : '');\n }\n } else {\n if (current !== committed) {\n setCommittedValue(current);\n onChange?.(current);\n }\n }\n };\n\n document.addEventListener('mousedown', handleMouseDown);\n return () => document.removeEventListener('mousedown', handleMouseDown);\n }, [allowFreeText, options, onChange, setCommittedValue, setInputValue]);\n\n // ─── Input handlers ───────────────────────────────────────────────────────\n\n const handleFocus = useCallback(() => {\n openDropdown();\n }, [openDropdown]);\n\n const handleChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const query = e.target.value;\n setInputValue(query);\n onSearch?.(query);\n if (!isOpenRef.current) openDropdown();\n },\n [setInputValue, onSearch, openDropdown],\n );\n\n /**\n * On blur, validate the typed value against options.\n * A 0ms defer ensures a click on a posIcon button (X / chevron) inside the\n * Input completes its onClick before we run close logic, so we can check\n * document.activeElement reliably.\n */\n const handleBlur = useCallback(() => {\n setTimeout(() => {\n // If focus came back inside the container (e.g., posIcon button clicked),\n // don't close or validate yet.\n if (containerRef.current?.contains(document.activeElement)) return;\n\n // Read both refs *inside* the deferred callback, never captured at blur\n // time - that's the whole reason they're kept in sync by the setters.\n // Anything can run in the gap this 0ms defer opens, including the click\n // that caused the blur: a controlled consumer clearing `value` from\n // another control (e.g. DataGrid's \"Clear filters\", which resets every\n // quick filter at once) updates the refs via the sync effect, and stale\n // captured values would then resurrect the label that was just cleared.\n const current = inputValueRef.current;\n const committed = committedValueRef.current;\n\n // Close\n isOpenRef.current = false;\n setIsOpen(false);\n setFocusedIndex(-1);\n setMenuKey((prev) => prev + 1);\n\n // Validate\n if (!allowFreeText) {\n const match = options.find((o) => o.label.toLowerCase() === current.toLowerCase());\n if (match) {\n if (match.value !== committed) {\n setCommittedValue(match.value);\n onChange?.(match.value);\n setInputValue(match.label);\n } else {\n // Normalize the display label (fix casing)\n setInputValue(match.label);\n }\n } else {\n // No match - revert to last committed value\n const revertMatch = options.find((o) => o.value === committed);\n setInputValue(revertMatch ? revertMatch.label : '');\n }\n } else {\n // allowFreeText: commit whatever the user typed\n if (current !== committed) {\n setCommittedValue(current);\n onChange?.(current);\n }\n }\n }, 0);\n }, [allowFreeText, options, onChange, setCommittedValue, setInputValue]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLInputElement>) => {\n if (disabled) return;\n\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n if (!isOpenRef.current) {\n openDropdown();\n setFocusedIndex(0);\n } else {\n setFocusedIndex((prev) => {\n const next = prev < filteredOptions.length - 1 ? prev + 1 : 0;\n setTimeout(\n () =>\n optionRefs.current[next]?.scrollIntoView({\n block: 'nearest',\n behavior: 'smooth',\n }),\n 0,\n );\n return next;\n });\n }\n break;\n\n case 'ArrowUp':\n e.preventDefault();\n if (!isOpenRef.current) {\n openDropdown();\n setFocusedIndex(filteredOptions.length - 1);\n } else {\n setFocusedIndex((prev) => {\n const next = prev > 0 ? prev - 1 : filteredOptions.length - 1;\n setTimeout(\n () =>\n optionRefs.current[next]?.scrollIntoView({\n block: 'nearest',\n behavior: 'smooth',\n }),\n 0,\n );\n return next;\n });\n }\n break;\n\n case 'Enter':\n e.preventDefault();\n if (isOpenRef.current && focusedIndex >= 0 && focusedIndex < filteredOptions.length) {\n handleOptionSelect(filteredOptions[focusedIndex]);\n } else if (allowFreeText && inputValue.trim()) {\n const trimmed = inputValue.trim();\n setCommittedValue(trimmed);\n onChange?.(trimmed);\n closeDropdown();\n }\n break;\n\n case 'Escape':\n e.preventDefault();\n if (isOpenRef.current) {\n closeDropdown(true /* revert */);\n }\n break;\n\n default:\n break;\n }\n },\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [\n disabled,\n filteredOptions,\n focusedIndex,\n allowFreeText,\n inputValue,\n onChange,\n openDropdown,\n closeDropdown,\n setCommittedValue,\n ],\n );\n\n // ─── Option selection ─────────────────────────────────────────────────────\n\n const handleOptionSelect = useCallback(\n (option: ComboboxOption) => {\n if (option.disabled) return;\n setInputValue(option.label);\n setCommittedValue(option.value);\n onChange?.(option.value);\n closeDropdown();\n // Return focus to the input so the user can continue interacting\n inputRef.current?.focus();\n },\n [onChange, closeDropdown, setInputValue, setCommittedValue],\n );\n\n // ─── Clear ────────────────────────────────────────────────────────────────\n\n const handleClearClick = useCallback(() => {\n setInputValue('');\n setCommittedValue('');\n onChange?.('');\n // Re-focus input so the user can immediately start typing again\n inputRef.current?.focus();\n }, [onChange, setInputValue, setCommittedValue]);\n\n // ─── Chevron toggle ───────────────────────────────────────────────────────\n\n const handleChevronClick = useCallback(() => {\n if (isOpenRef.current) {\n closeDropdown();\n } else {\n openDropdown();\n }\n }, [closeDropdown, openDropdown]);\n\n // ─── Render: individual option ────────────────────────────────────────────\n\n const renderOptionItem = useCallback(\n (option: ComboboxOption, index: number): React.ReactNode => {\n const isSelected = option.value === committedValue;\n const isFocused = index === focusedIndex;\n\n const classes = [\n 'eidos-combobox-option',\n isSelected && 'eidos-combobox-option--selected',\n isFocused && 'eidos-combobox-option--focused',\n option.disabled && 'eidos-combobox-option--disabled',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div\n key={option.id}\n ref={(el) => {\n optionRefs.current[index] = el;\n }}\n className={classes}\n onClick={() => handleOptionSelect(option)}\n onMouseEnter={() => !option.disabled && setFocusedIndex(index)}\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled}\n id={`${uid}-option-${index}`}\n >\n {renderOption ? (\n renderOption(option)\n ) : (\n <>\n <Check\n className={`eidos-combobox-option-icon${\n !isSelected ? ' eidos-combobox-option-icon--hidden' : ''\n }`}\n />\n <span className=\"eidos-combobox-option-label\">{option.label}</span>\n {option.description && (\n <span className=\"eidos-combobox-option-description\">{option.description}</span>\n )}\n </>\n )}\n </div>\n );\n },\n [committedValue, focusedIndex, handleOptionSelect, renderOption, uid],\n );\n\n // ─── Render: dropdown content ─────────────────────────────────────────────\n\n const dropdownContent = useMemo(() => {\n if (loading) {\n return (\n <div className=\"eidos-combobox-loading\">\n <Loader2 className=\"eidos-combobox-loading-icon\" />\n <span>{loadingText}</span>\n </div>\n );\n }\n\n if (filteredOptions.length === 0) {\n return <div className=\"eidos-combobox-empty\">{emptyText}</div>;\n }\n\n return optionEntries.map((entry) => {\n if (entry.type === 'group') {\n return (\n <div key={entry.key} className=\"eidos-combobox-option-group-label\">\n {entry.group}\n </div>\n );\n }\n return renderOptionItem(entry.option, entry.index);\n });\n }, [loading, loadingText, filteredOptions.length, emptyText, optionEntries, renderOptionItem]);\n\n // ─── posIcon / posIconButton wiring ───────────────────────────────────────\n\n const hasClearableValue = clearable && !!inputValue;\n const posIcon = hasClearableValue ? X : ChevronDown;\n const posIconClick = hasClearableValue ? handleClearClick : handleChevronClick;\n\n // ─── CSS classes ──────────────────────────────────────────────────────────\n\n const containerClasses = [\n 'eidos-combobox',\n fullWidth && fullWidthModifier('eidos-combobox'),\n error && 'eidos-combobox--error',\n disabled && 'eidos-combobox--disabled',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const maxHeightValue = typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight;\n\n // ─── Render ───────────────────────────────────────────────────────────────\n\n return (\n // No ARIA on this wrapper.\n //\n // It carried `aria-expanded` and `aria-haspopup` while having no role at\n // all, which is invalid - both are only meaningful on an element whose\n // role supports them. They belong on the input, which is the combobox.\n <div ref={containerRef} className={containerClasses}>\n <div ref={comboboxTriggerRef} className=\"eidos-combobox-trigger\">\n {/*\n The Input is a direct child of eidos-combobox-trigger.\n It is NOT inside the Dropdown trigger div, so clicking it\n never bubbles to Dropdown.handleTriggerClick and never\n accidentally toggles the dropdown closed mid-edit.\n */}\n <Input\n ref={inputRef}\n value={inputValue}\n onChange={handleChange}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n size={size}\n fullWidth\n label={label}\n clearable={false} // we render our own clear via posIcon\n isSelect={false}\n posIcon={posIcon}\n posIconButton\n onPosIconClick={posIconClick}\n // The same button does two jobs depending on state, so the name\n // has to follow the icon - \"Clear\" on a chevron would be a lie.\n posIconLabel={hasClearableValue ? 'Clear selection' : 'Show options'}\n // The ARIA 1.2 combobox pattern: the input *is* the combobox.\n role=\"combobox\"\n aria-expanded={isOpen}\n // `Combobox` renders its own error message rather than passing\n // `error` down, so it does not inherit `Input`'s wiring and has to\n // associate the message itself (SC 3.3.1).\n aria-invalid={error ? true : undefined}\n aria-describedby={error ? `${uid}-error` : undefined}\n aria-autocomplete=\"list\"\n // Only while the listbox exists. `aria-controls` pointing at an id\n // that is not in the document is invalid, and that is what shipped\n // - the listbox only renders once the dropdown opens.\n aria-controls={isOpen ? `${uid}-listbox` : undefined}\n aria-activedescendant={focusedIndex >= 0 ? `${uid}-option-${focusedIndex}` : undefined}\n />\n\n {/*\n The Dropdown trigger is a 0-height span placed AFTER the Input\n in the normal document flow. Its getBoundingClientRect sits at\n exactly the bottom edge of the Input, giving the Dropdown correct\n placement without any click-toggle collision with the Input above.\n closeOnClickOutside and closeOnEscape are disabled because we own\n those interactions in our mousedown listener and onKeyDown handler.\n */}\n <Dropdown\n key={menuKey}\n trigger={\n <span\n ref={dropdownSpanRef}\n className=\"eidos-combobox-dropdown-anchor\"\n aria-hidden=\"true\"\n />\n }\n content={\n <div\n id={`${uid}-listbox`}\n className=\"eidos-combobox-options\"\n role=\"listbox\"\n aria-label={label ?? 'Options'}\n // Prevent the input from blurring when the user clicks an option,\n // and mark this event so the click-outside handler ignores it.\n onMouseDown={(e) => e.preventDefault()}\n >\n {dropdownContent}\n </div>\n }\n triggerRef={comboboxTriggerRef as React.RefObject<HTMLElement | null>}\n maxHeight={maxHeightValue}\n autoWidth\n closeOnClickOutside={false}\n closeOnEscape={false}\n />\n </div>\n\n {hint && !error && <span className=\"eidos-combobox-hint\">{hint}</span>}\n {error && (\n <span id={`${uid}-error`} className=\"eidos-combobox-error-message\">\n <CircleAlert className=\"eidos-combobox-error-icon\" />\n {error}\n </span>\n )}\n </div>\n );\n};\n\nCombobox.displayName = 'Combobox';\n"],"mappings":"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","names":["label"]}
|
|
@@ -13,29 +13,29 @@ import {
|
|
|
13
13
|
subscribeToFontLoads,
|
|
14
14
|
toFontStack,
|
|
15
15
|
useTheme
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-PG2EII6U.js";
|
|
17
17
|
import {
|
|
18
18
|
Slider
|
|
19
19
|
} from "./chunk-2BC4M4QZ.js";
|
|
20
20
|
import {
|
|
21
21
|
Select
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-TLECMA3B.js";
|
|
23
23
|
import {
|
|
24
24
|
Card
|
|
25
25
|
} from "./chunk-EF4Y6Q35.js";
|
|
26
26
|
import {
|
|
27
27
|
ColorPicker
|
|
28
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-TYGVKSL6.js";
|
|
29
29
|
import {
|
|
30
30
|
Alert
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-BO4LTIJI.js";
|
|
32
32
|
import {
|
|
33
33
|
Button,
|
|
34
34
|
IconButton
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-44T4HMFU.js";
|
|
36
36
|
import {
|
|
37
37
|
Tooltip
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-XO7WTE6O.js";
|
|
39
39
|
|
|
40
40
|
// src/components/ThemeEditor/ThemeEditor.component.tsx
|
|
41
41
|
import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
@@ -157,7 +157,8 @@ var FontRow = ({
|
|
|
157
157
|
options: selectOptions,
|
|
158
158
|
value,
|
|
159
159
|
onChange: (next) => onChange(Array.isArray(next) ? next[0] : next),
|
|
160
|
-
fullWidth: true
|
|
160
|
+
fullWidth: true,
|
|
161
|
+
inputProps: { "aria-label": label }
|
|
161
162
|
}
|
|
162
163
|
) }),
|
|
163
164
|
/* @__PURE__ */ jsxs("div", { className: "eidos-theme-editor-row__diagnostics", children: [
|
|
@@ -196,6 +197,8 @@ var FontRow = ({
|
|
|
196
197
|
type: "file",
|
|
197
198
|
accept: FONT_ACCEPT,
|
|
198
199
|
className: "eidos-theme-editor-file",
|
|
200
|
+
tabIndex: -1,
|
|
201
|
+
"aria-hidden": "true",
|
|
199
202
|
onChange: (event) => {
|
|
200
203
|
void handleFile(event.target.files?.[0]);
|
|
201
204
|
event.target.value = "";
|
|
@@ -282,7 +285,15 @@ var ThemeEditor = ({
|
|
|
282
285
|
const overridden = theme.colors?.[key] !== void 0;
|
|
283
286
|
return /* @__PURE__ */ jsxs("div", { className: "eidos-theme-editor-row", children: [
|
|
284
287
|
/* @__PURE__ */ jsx("span", { className: "eidos-theme-editor-row__label", children: COLOR_LABELS[key] }),
|
|
285
|
-
/* @__PURE__ */ jsx(
|
|
288
|
+
/* @__PURE__ */ jsx(
|
|
289
|
+
ColorPicker,
|
|
290
|
+
{
|
|
291
|
+
value: base,
|
|
292
|
+
onChange: (hex) => setColor(key, hex),
|
|
293
|
+
size: "md",
|
|
294
|
+
ariaLabel: COLOR_LABELS[key]
|
|
295
|
+
}
|
|
296
|
+
),
|
|
286
297
|
/* @__PURE__ */ jsxs("div", { className: "eidos-theme-editor-row__diagnostics", children: [
|
|
287
298
|
/* @__PURE__ */ jsx(Tooltip, { message: detail, children: /* @__PURE__ */ jsxs("span", { className: `eidos-theme-editor-badge eidos-theme-editor-badge--${grade}`, children: [
|
|
288
299
|
/* @__PURE__ */ jsx(badge.icon, { className: "eidos-theme-editor-badge__icon" }),
|
|
@@ -387,4 +398,4 @@ ThemeEditor.displayName = "ThemeEditor";
|
|
|
387
398
|
export {
|
|
388
399
|
ThemeEditor
|
|
389
400
|
};
|
|
390
|
-
//# sourceMappingURL=chunk-
|
|
401
|
+
//# sourceMappingURL=chunk-LIKSLFX6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ThemeEditor/ThemeEditor.component.tsx"],"sourcesContent":["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 // The row's visible label is a `<span>`, not a `<label htmlFor>`,\n // so nothing associates it with the control. Naming the input\n // directly keeps the two in step without restructuring the row.\n inputProps={{ 'aria-label': label }}\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 // Same proxy pattern as `MessageComposer` - see the note there.\n className=\"eidos-theme-editor-file\"\n tabIndex={-1}\n aria-hidden=\"true\"\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\n value={base}\n onChange={(hex) => setColor(key, hex)}\n size=\"md\"\n // The row draws its own visible label, so the picker must\n // not draw a second one - but without a name of its own\n // every picker here announced as \"Open colour picker\" and\n // they were indistinguishable.\n ariaLabel={COLOR_LABELS[key]}\n />\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'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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAgB,aAAa,WAAW,SAAS,YAAY,QAAQ,gBAAgB;AACrF,SAAS,aAAa,SAAS,qBAAqB;AAgQ9C,cAqBM,YArBN;AAhON,IAAM,eAAe;AAGrB,IAAM,iBAAiB;AAEvB,IAAM,eAA8C;AAAA,EAClD,SAAS;AAAA,EACT,WAAW;AAAA,EACX,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,MAAM;AAAA,EACN,WAAW;AACb;AAGA,IAAM,SAAS,CAAC,QAAgC,QAAQ;AAmBxD,IAAM,cAAiC;AAAA;AAAA;AAAA;AAAA;AAAA,EAKrC,EAAE,OAAO,qCAAqC,OAAO,aAAa,WAAW,WAAY;AAAA,EACzF,EAAE,OAAO,aAAa,OAAO,mDAAmD;AAAA,EAChF,EAAE,OAAO,qBAAqB,OAAO,+BAA+B;AAAA,EACpE,EAAE,OAAO,WAAW,OAAO,oCAAoC;AAAA,EAC/D,EAAE,OAAO,wBAAwB,OAAO,aAAa;AAAA,EACrD,EAAE,OAAO,mBAAmB,OAAO,QAAQ;AAC7C;AAEA,IAAM,cAAiC;AAAA,EACrC,EAAE,OAAO,kCAAkC,OAAO,aAAa,WAAW,WAAY;AAAA,EACtF,EAAE,OAAO,oBAAoB,OAAO,6BAA6B;AAAA,EACjE,EAAE,OAAO,eAAe,OAAO,oCAAoC;AAAA,EACnE,EAAE,OAAO,uBAAuB,OAAO,YAAY;AACrD;AAIA,IAAM,WAAW,CAAC,UAA0B,SAAS,MAAM,OAAO,SAAS,IAAI,aAAa;AAG5F,IAAM,eAA+C;AAAA,EACnD,YACE;AAAA,EACF,MAAM;AACR;AAaA,IAAM,cAGF;AAAA,EACF,IAAI,EAAE,OAAO,MAAM,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrC,YAAY,EAAE,OAAO,yBAAyB,MAAM,cAAc;AAAA,EAClE,MAAM,EAAE,OAAO,YAAY,MAAM,QAAQ;AAC3C;AAWA,IAAM,WAAW,CAAC,KAAoB,MAAc,qBAA0C;AAC5F,MAAI,CAAC,OAAO,GAAG,GAAG;AAEhB,UAAMA,SAAQ,cAAc,MAAM,YAAY;AAC9C,UAAMC,SAAQ,SAASD,MAAK;AAC5B,WAAO;AAAA,MACL,OAAAC;AAAA,MACA,OAAAD;AAAA,MACA,QAAQ,qCAAqCA,OAAM,QAAQ,CAAC,CAAC,oBAAoB,aAAaC,MAAK,KAAK,EAAE;AAAA,IAC5G;AAAA,EACF;AAEA,QAAM,aAAa,oBAAoB,aAAa,IAAI;AACxD,QAAM,QAAQ,cAAc,MAAM,UAAU;AAC5C,QAAM,OAAO,WAAW,YAAY,MAAM,gBAAgB,SAAS;AACnE,QAAM,OAAO,cAAc,MAAM,YAAY;AAE7C,QAAM,QAAQ,SAAS,KAAK;AAE5B,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,QAAQ,GAAG,SAAS,UAAU,UAAU,YAAY,yBAAyB,MAAM,QAAQ,CAAC,CAAC,MAC3F,mBAAmB,sCAAsC,EAC3D,GAAG,aAAa,KAAK,KAAK,EAAE;AAAA,IAC5B,aACE,OAAO,iBACH,QAAQ,KAAK,QAAQ,CAAC,CAAC,mGACvB;AAAA,EACR;AACF;AAcA,IAAM,cAAc,CAAC,UACnB,MACG,MAAM,GAAG,EAAE,CAAC,GACX,KAAK,EACN,QAAQ,gBAAgB,EAAE,KAAK;AAYpC,IAAM,UAAkC,CAAC;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,WAAW,OAAyB,IAAI;AAC9C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,IAAI;AAMtD,QAAM,CAAC,EAAE,aAAa,IAAI,WAAW,CAAC,UAAkB,QAAQ,GAAG,CAAC;AACpE,YAAU,MAAM,qBAAqB,aAAa,GAAG,CAAC,aAAa,CAAC;AAEpE,QAAM,cAAc,QAAQ,MAAM;AAIhC,UAAM,QAAQ,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,KAAK;AACnD,WAAO,QAAQ,UAAU,CAAC,GAAG,SAAS,EAAE,OAAO,GAAG,YAAY,KAAK,CAAC,aAAa,MAAM,CAAC;AAAA,EAC1F,GAAG,CAAC,SAAS,KAAK,CAAC;AAWnB,QAAM,gBAAgB,YAAY,IAAI,CAAC,YAAY;AAAA,IACjD,IAAI,OAAO;AAAA,IACX,OAAO,qBAAqB,OAAO,KAAK,IAAI,OAAO,QAAQ,GAAG,OAAO,KAAK;AAAA,IAC1E,OAAO,OAAO;AAAA,EAChB,EAAE;AAEF,QAAM,aAAa;AAAA,IACjB,OAAO,SAA2B;AAChC,UAAI,CAAC,KAAM;AACX,eAAS,IAAI;AACb,UAAI;AACF,cAAM,SAAS,MAAM,iBAAiB,IAAI;AAC1C,cAAM,QAAQ,YAAY,QAAQ,IAAI;AACtC,mBAAW,EAAE,OAAO,GAAG,MAAM,mBAAmB,OAAO,MAAM,GAAG,MAAM,MAAM;AAC5E,iBAAS,KAAK;AAAA,MAChB,SAAS,OAAO;AACd;AAAA,UACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAC3C;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,MAAM,UAAU,UAAU;AAAA,EAC7B;AAEA,QAAM,cAAc,CAAC,qBAAqB,KAAK;AAE/C,SACE,qBAAC,SAAI,WAAU,uDACb;AAAA,wBAAC,UAAK,WAAU,iCAAiC,iBAAM;AAAA,IAEvD,oBAAC,SAAI,WAAU,mCACb;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT;AAAA,QACA,UAAU,CAAC,SAAS,SAAS,MAAM,QAAQ,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI;AAAA,QACjE,WAAS;AAAA,QAIT,YAAY,EAAE,cAAc,MAAM;AAAA;AAAA,IACpC,GACF;AAAA,IAGA,qBAAC,SAAI,WAAU,uCACZ;AAAA,qBACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,GAAG,YAAY,KAAK,CAAC;AAAA,UAE9B,+BAAC,UAAK,WAAU,+DACd;AAAA,gCAAC,iBAAc,WAAU,kCAAiC;AAAA,YAAE;AAAA,aAE9D;AAAA;AAAA,MACF;AAAA,MAED,SACC,oBAAC,WAAQ,SAAS,OAChB,+BAAC,UAAK,WAAU,2DACd;AAAA,4BAAC,WAAQ,WAAU,kCAAiC;AAAA,QAAE;AAAA,SAExD,GACF;AAAA,OAEJ;AAAA,IAEC,eACC,qBAAC,SAAI,WAAU,mCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,OAAM;AAAA,UACN,MAAK;AAAA,UACL,SAAQ;AAAA,UACR,SAAS,MAAM,SAAS,SAAS,MAAM;AAAA,UACvC,SAAQ;AAAA,UACT;AAAA;AAAA,MAED;AAAA,MAGA;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,MAAK;AAAA,UACL,QAAQ;AAAA,UAER,WAAU;AAAA,UACV,UAAU;AAAA,UACV,eAAY;AAAA,UACZ,UAAU,CAAC,UAAU;AACnB,iBAAK,WAAW,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEvC,kBAAM,OAAO,QAAQ;AAAA,UACvB;AAAA;AAAA,MACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAYO,IAAM,cAA0C,CAAC;AAAA,EACtD,SAAS;AAAA,EACT,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB;AAAA,EACA,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB;AACF,MAAM;AACJ,QAAM,EAAE,OAAO,eAAe,UAAU,aAAa,YAAY,WAAW,MAAM,IAAI,SAAS;AAC/F,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAS,KAAK;AAI1C,QAAM,CAAC,UAAU,WAAW,IAAI,SAA4B,CAAC,CAAC;AAC9D,QAAM,CAAC,cAAc,eAAe,IAAI,SAA4B,CAAC,CAAC;AAEtE,QAAM,WAAW;AAAA,IACf,CAAC,KAAoB,QAAgB;AACnC,YAAM,UAAU,MAAM,SAAS,GAAG;AAGlC,YAAM,SAAS,OAAO,YAAY,WAAW,QAAQ,WAAW;AAChE,kBAAY,EAAE,QAAQ,EAAE,CAAC,GAAG,GAAG,SAAS,EAAE,MAAM,KAAK,UAAU,OAAO,IAAI,IAAI,EAAE,CAAC;AAAA,IACnF;AAAA,IACA,CAAC,MAAM,QAAQ,WAAW;AAAA,EAC5B;AAEA,QAAM,aAAa;AAAA,IACjB,CAAC,QAAuB;AAItB,YAAM,aAAa,EAAE,GAAG,MAAM,OAAO;AACrC,aAAO,WAAW,GAAG;AACrB,eAAS,EAAE,GAAG,OAAO,QAAQ,WAAW,CAAC;AAAA,IAC3C;AAAA,IACA,CAAC,OAAO,QAAQ;AAAA,EAClB;AAEA,QAAM,cAAc;AAAA,IAClB,MACE,OAAO,IAAI,CAAC,QAAQ;AAClB,YAAM,EAAE,MAAM,SAAS,IAAI,cAAc,OAAO,GAAG;AACnD,aAAO,EAAE,KAAK,MAAM,GAAG,SAAS,KAAK,MAAM,QAAQ,EAAE;AAAA,IACvD,CAAC;AAAA,IACH,CAAC,QAAQ,aAAa;AAAA,EACxB;AAEA,QAAM,UAAU,YAAY,OAAO,CAAC,MAAM,EAAE,UAAU,MAAM;AAC5D,QAAM,YAAY,YAAY,OAAO,CAAC,MAAM,EAAE,WAAW;AAEzD,QAAM,UAAU,YAAY,MAAM;AAChC,UAAM,MAAM,MAAM;AAClB,UAAM,OAAO,MAAM;AACjB,gBAAU,IAAI;AACd,iBAAW,MAAM,UAAU,KAAK,GAAG,IAAI;AAAA,IACzC;AACA,QAAI,UAAU,WAAW,WAAW;AAClC,gBAAU,UAAU,UAAU,GAAG,EAAE,KAAK,MAAM,IAAI;AAClD;AAAA,IACF;AACA,SAAK;AAAA,EACP,GAAG,CAAC,KAAK,CAAC;AAEV,SACE,qBAAC,SAAI,WAAW,CAAC,sBAAsB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GACtE;AAAA,aAAQ,SAAS,KAAK,UAAU,SAAS,MACzC,qBAAC,SAAM,SAAQ,WAAU,OAAM,mBAC5B;AAAA,cAAQ,SAAS,KAChB,qBAAC,OAAE;AAAA;AAAA,QACoD;AAAA,QACpD,QAAQ,IAAI,CAAC,MAAM,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,IAAI;AAAA,QAAE;AAAA,SACtD;AAAA,MAED,UAAU,SAAS,KAClB,qBAAC,OAAE;AAAA;AAAA,QAC6D;AAAA,QAC7D,UAAU,IAAI,CAAC,MAAM,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,IAAI;AAAA,QAAE;AAAA,SACxD;AAAA,OAEJ;AAAA,IAGF,qBAAC,QAAK,SAAQ,YAAW,SAAQ,MAAK,WAAU,8BAC9C;AAAA,2BAAC,SAAI,WAAU,sCACb;AAAA,4BAAC,UAAK,WAAU,qCAAoC,qBAAO;AAAA,QAC3D,oBAAC,UAAK,WAAU,oCAAmC,8EAEnD;AAAA,SACF;AAAA,MAEA,oBAAC,SAAI,WAAU,2BACZ,sBAAY,IAAI,CAAC,EAAE,KAAK,MAAM,OAAO,OAAO,QAAQ,YAAY,MAAM;AACrE,cAAM,QAAQ,YAAY,KAAK;AAC/B,cAAM,aAAa,MAAM,SAAS,GAAG,MAAM;AAC3C,eACE,qBAAC,SAAI,WAAU,0BACb;AAAA,8BAAC,UAAK,WAAU,iCAAiC,uBAAa,GAAG,GAAE;AAAA,UAKnE;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,QAAQ,SAAS,KAAK,GAAG;AAAA,cACpC,MAAK;AAAA,cAKL,WAAW,aAAa,GAAG;AAAA;AAAA,UAC7B;AAAA,UAEA,qBAAC,SAAI,WAAU,uCACb;AAAA,gCAAC,WAAQ,SAAS,QAChB,+BAAC,UAAK,WAAW,sDAAsD,KAAK,IAC1E;AAAA,kCAAC,MAAM,MAAN,EAAW,WAAU,kCAAiC;AAAA,cACtD,GAAG,MAAM,QAAQ,CAAC,CAAC,MAAM,MAAM,KAAK;AAAA,eACvC,GACF;AAAA,YACC,eACC,oBAAC,WAAQ,SAAS,aAChB,+BAAC,UAAK,WAAU,2DACd;AAAA,kCAAC,WAAQ,WAAU,kCAAiC;AAAA,cAAE;AAAA,eAExD,GACF;AAAA,aAEJ;AAAA,UAEA,oBAAC,SAAI,WAAU,mCACb;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,OAAM;AAAA,cACN,MAAK;AAAA,cACL,UAAU,CAAC;AAAA,cACX,SAAS,MAAM,WAAW,GAAG;AAAA,cAC7B,SAAS,aAAa,SAAS,aAAa,GAAG,CAAC,mBAAmB;AAAA,cACnE,cAAY,SAAS,aAAa,GAAG,CAAC;AAAA;AAAA,UACxC,GACF;AAAA,aA7C2C,GA8C7C;AAAA,MAEJ,CAAC,GACH;AAAA,OACF;AAAA,IAEC,CAAC,kBACA,qBAAC,QAAK,SAAQ,YAAW,SAAQ,MAAK,WAAU,8BAC9C;AAAA,2BAAC,SAAI,WAAU,sCACb;AAAA,4BAAC,UAAK,WAAU,qCAAoC,wBAAU;AAAA,QAC9D,qBAAC,UAAK,WAAU,oCAAmC;AAAA;AAAA,UAEZ,oBAAC,UAAK,gBAAE;AAAA,UAAO;AAAA,WAEtD;AAAA,SACF;AAAA,MAEA,qBAAC,SAAI,WAAU,2BACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO,cAAc,WAAW;AAAA,YAChC,SAAS,CAAC,GAAG,aAAa,GAAG,QAAQ;AAAA,YACrC,MAAM;AAAA,YACN,aAAa;AAAA,YACb,UAAU,CAAC,eAAe,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAAA,YACpE,YAAY,CAAC,QAAQ,MAAM,WAAW;AACpC,0BAAY,CAAC,SAAS,CAAC,GAAG,MAAM,MAAM,CAAC;AACvC,6BAAe,MAAM,QAAQ,KAAK;AAAA,YACpC;AAAA;AAAA,QACF;AAAA,QAEA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO,cAAc,WAAW;AAAA,YAChC,SAAS,CAAC,GAAG,iBAAiB,GAAG,YAAY;AAAA,YAC7C,MAAI;AAAA,YACJ,aAAa;AAAA,YACb,UAAU,CAAC,eAAe,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,CAAC;AAAA,YACpE,YAAY,CAAC,QAAQ,MAAM,WAAW;AACpC,8BAAgB,CAAC,SAAS,CAAC,GAAG,MAAM,MAAM,CAAC;AAC3C,6BAAe,MAAM,QAAQ,IAAI;AAAA,YACnC;AAAA;AAAA,QACF;AAAA,QAEA,qBAAC,SAAI,WAAU,uDACb;AAAA,8BAAC,UAAK,WAAU,iCAAgC,mBAAK;AAAA,UACrD,oBAAC,SAAI,WAAU,mCACb;AAAA,YAAC;AAAA;AAAA,cACC,OAAO,cAAc,WAAW;AAAA,cAChC,KAAK;AAAA,cACL,KAAK;AAAA,cACL,MAAM;AAAA,cACN,WAAS;AAAA,cACT,YAAU;AAAA,cACV,MAAK;AAAA,cACL,UAAU,CAAC,UAAU,YAAY,EAAE,YAAY,EAAE,WAAW,MAAM,EAAE,CAAC;AAAA;AAAA,UACvE,GACF;AAAA,WACF;AAAA,SACF;AAAA,OACF;AAAA,KAGA,CAAC,aAAa,CAAC,kBACf,qBAAC,SAAI,WAAU,8BACZ;AAAA,OAAC,aACA,oBAAC,UAAO,SAAQ,YAAW,OAAM,aAAY,UAAU,WAAW,SAAS,YAAY,uBAEvF;AAAA,MAED,CAAC,iBACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,SAAS,UAAU;AAAA,UAC5B,SAAS;AAAA,UACT,SAAQ;AAAA,UAEP,mBAAS,WAAW;AAAA;AAAA,MACvB;AAAA,OAEJ;AAAA,KAEJ;AAEJ;AAEA,YAAY,cAAc;","names":["ratio","grade"]}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
var _chunkVHSTB7GBcjs = require('./chunk-VHSTB7GB.cjs');
|
|
4
|
+
|
|
5
|
+
// src/components/Tooltip/Tooltip.component.tsx
|
|
2
6
|
var _react = require('react');
|
|
3
7
|
var _reactdom = require('react-dom');
|
|
4
8
|
var _jsxruntime = require('react/jsx-runtime');
|
|
9
|
+
var HOVER_GRACE_MS = 150;
|
|
5
10
|
var Tooltip = ({
|
|
6
11
|
children,
|
|
7
12
|
message,
|
|
@@ -92,12 +97,13 @@ var Tooltip = ({
|
|
|
92
97
|
}, [tooltipState.isVisible, tooltipState.isPositioned, calculateOptimalPosition]);
|
|
93
98
|
const handleMouseEnter = _react.useCallback.call(void 0, () => {
|
|
94
99
|
if (disabled || triggerType !== "hover") return;
|
|
100
|
+
if (timeoutRef.current) {
|
|
101
|
+
clearTimeout(timeoutRef.current);
|
|
102
|
+
}
|
|
95
103
|
timeoutRef.current = setTimeout(() => {
|
|
96
|
-
setTooltipState(
|
|
97
|
-
...prev,
|
|
98
|
-
|
|
99
|
-
isPositioned: false
|
|
100
|
-
}));
|
|
104
|
+
setTooltipState(
|
|
105
|
+
(prev) => prev.isVisible ? prev : { ...prev, isVisible: true, isPositioned: false }
|
|
106
|
+
);
|
|
101
107
|
}, delay);
|
|
102
108
|
}, [disabled, delay, triggerType]);
|
|
103
109
|
const handleMouseLeave = _react.useCallback.call(void 0, () => {
|
|
@@ -105,11 +111,13 @@ var Tooltip = ({
|
|
|
105
111
|
if (timeoutRef.current) {
|
|
106
112
|
clearTimeout(timeoutRef.current);
|
|
107
113
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
timeoutRef.current = setTimeout(() => {
|
|
115
|
+
setTooltipState((prev) => ({
|
|
116
|
+
...prev,
|
|
117
|
+
isVisible: false,
|
|
118
|
+
isPositioned: false
|
|
119
|
+
}));
|
|
120
|
+
}, HOVER_GRACE_MS);
|
|
113
121
|
}, [triggerType]);
|
|
114
122
|
const handleClick = _react.useCallback.call(void 0, () => {
|
|
115
123
|
if (disabled || triggerType !== "click") return;
|
|
@@ -175,7 +183,7 @@ var Tooltip = ({
|
|
|
175
183
|
);
|
|
176
184
|
const handleEscapeKey = _react.useCallback.call(void 0,
|
|
177
185
|
(event) => {
|
|
178
|
-
if (closeOnEscape && event.key === "Escape" && tooltipState.isVisible
|
|
186
|
+
if (closeOnEscape && event.key === "Escape" && tooltipState.isVisible) {
|
|
179
187
|
setTooltipState((prev) => ({
|
|
180
188
|
...prev,
|
|
181
189
|
isVisible: false,
|
|
@@ -183,7 +191,7 @@ var Tooltip = ({
|
|
|
183
191
|
}));
|
|
184
192
|
}
|
|
185
193
|
},
|
|
186
|
-
[closeOnEscape, tooltipState.isVisible
|
|
194
|
+
[closeOnEscape, tooltipState.isVisible]
|
|
187
195
|
);
|
|
188
196
|
_react.useEffect.call(void 0, () => {
|
|
189
197
|
if (tooltipState.isVisible && !tooltipState.isPositioned && tooltipRef.current && triggerRef.current) {
|
|
@@ -225,13 +233,19 @@ var Tooltip = ({
|
|
|
225
233
|
_react.useEffect.call(void 0, () => {
|
|
226
234
|
if (tooltipState.isVisible && triggerType === "click") {
|
|
227
235
|
document.addEventListener("mousedown", handleClickOutside);
|
|
228
|
-
document.addEventListener("keydown", handleEscapeKey);
|
|
229
236
|
}
|
|
230
237
|
return () => {
|
|
231
238
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
239
|
+
};
|
|
240
|
+
}, [tooltipState.isVisible, triggerType, handleClickOutside]);
|
|
241
|
+
_react.useEffect.call(void 0, () => {
|
|
242
|
+
if (tooltipState.isVisible) {
|
|
243
|
+
document.addEventListener("keydown", handleEscapeKey);
|
|
244
|
+
}
|
|
245
|
+
return () => {
|
|
232
246
|
document.removeEventListener("keydown", handleEscapeKey);
|
|
233
247
|
};
|
|
234
|
-
}, [tooltipState.isVisible,
|
|
248
|
+
}, [tooltipState.isVisible, handleEscapeKey]);
|
|
235
249
|
_react.useEffect.call(void 0, () => {
|
|
236
250
|
return () => {
|
|
237
251
|
if (timeoutRef.current) {
|
|
@@ -251,6 +265,11 @@ var Tooltip = ({
|
|
|
251
265
|
}
|
|
252
266
|
return null;
|
|
253
267
|
};
|
|
268
|
+
const [triggerNeedsFocus, setTriggerNeedsFocus] = _react.useState.call(void 0, false);
|
|
269
|
+
_react.useEffect.call(void 0, () => {
|
|
270
|
+
const el = triggerRef.current;
|
|
271
|
+
setTriggerNeedsFocus(!!el && el.querySelector(_chunkVHSTB7GBcjs.FOCUSABLE_SELECTOR) === null);
|
|
272
|
+
}, [children, triggerType]);
|
|
254
273
|
const getTriggerProps = () => {
|
|
255
274
|
const props = {
|
|
256
275
|
ref: triggerRef,
|
|
@@ -263,13 +282,21 @@ var Tooltip = ({
|
|
|
263
282
|
break;
|
|
264
283
|
case "click":
|
|
265
284
|
props.onClick = handleClick;
|
|
266
|
-
|
|
267
|
-
|
|
285
|
+
if (triggerNeedsFocus) {
|
|
286
|
+
props.role = "button";
|
|
287
|
+
props.tabIndex = 0;
|
|
288
|
+
props.onKeyDown = (event) => {
|
|
289
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
290
|
+
event.preventDefault();
|
|
291
|
+
handleClick();
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
268
295
|
break;
|
|
269
296
|
case "focus":
|
|
270
297
|
props.onFocus = handleFocus;
|
|
271
298
|
props.onBlur = handleBlur;
|
|
272
|
-
props.tabIndex = 0;
|
|
299
|
+
if (triggerNeedsFocus) props.tabIndex = 0;
|
|
273
300
|
break;
|
|
274
301
|
}
|
|
275
302
|
return props;
|
|
@@ -281,6 +308,8 @@ var Tooltip = ({
|
|
|
281
308
|
"div",
|
|
282
309
|
{
|
|
283
310
|
ref: tooltipRef,
|
|
311
|
+
onMouseEnter: handleMouseEnter,
|
|
312
|
+
onMouseLeave: handleMouseLeave,
|
|
284
313
|
className: `eidos-tooltip eidos-tooltip--${tooltipState.position.placement} ${className}`,
|
|
285
314
|
style: {
|
|
286
315
|
top: tooltipState.position.top,
|
|
@@ -300,4 +329,4 @@ Tooltip.displayName = "Tooltip";
|
|
|
300
329
|
|
|
301
330
|
|
|
302
331
|
exports.Tooltip = Tooltip;
|
|
303
|
-
//# sourceMappingURL=chunk-
|
|
332
|
+
//# sourceMappingURL=chunk-MLUHM3VG.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/eidos-ui/eidos-ui/dist/chunk-MLUHM3VG.cjs","../src/components/Tooltip/Tooltip.component.tsx"],"names":[],"mappings":"AAAA;AACE;AACF,wDAA6B;AAC7B;AACA;ACJA,8BAAgE;AAChE,qCAA6B;AAuVhB,+CAAA;AA9Ub,IAAM,eAAA,EAAiB,GAAA;AAEhB,IAAM,QAAA,EAAkC,CAAC;AAAA,EAC9C,QAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA,EAAW,SAAA;AAAA,EACX,cAAA;AAAA,EACA,SAAA,EAAW,mBAAA,EAAqB,KAAA;AAAA,EAChC,MAAA,EAAQ,GAAA;AAAA,EACR,SAAA,EAAW,KAAA;AAAA,EACX,UAAA,EAAY,EAAA;AAAA,EACZ,YAAA,EAAc,OAAA;AAAA,EACd,oBAAA,EAAsB,IAAA;AAAA,EACtB,cAAA,EAAgB;AAClB,CAAA,EAAA,GAAM;AACJ,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,EAAA,EAAI,6BAAA;AAAuB,IAC7D,SAAA,EAAW,KAAA;AAAA,IACX,QAAA,EAAU,EAAE,GAAA,EAAK,CAAA,EAAG,IAAA,EAAM,CAAA,EAAG,SAAA,EAAW,MAAM,CAAA;AAAA,IAC9C,YAAA,EAAc;AAAA,EAChB,CAAC,CAAA;AAED,EAAA,MAAM,WAAA,EAAa,2BAAA,IAA2B,CAAA;AAC9C,EAAA,MAAM,WAAA,EAAa,2BAAA,IAA2B,CAAA;AAC9C,EAAA,MAAM,WAAA,EAAa,2BAAA,KAAkD,CAAS,CAAA;AAC9E,EAAA,MAAM,kBAAA,EAAoB,2BAAA,KAA2B,CAAS,CAAA;AAC9D,EAAA,MAAM,eAAA,EAAiB,2BAAA,KAAY,CAAA;AAEnC,EAAA,MAAM,yBAAA,EAA2B,gCAAA;AAAA,IAC/B,CAAC,WAAA,EAAsB,WAAA,EAAA,GAAyB;AAC9C,MAAA,MAAM,cAAA,EAAgB,MAAA,CAAO,UAAA;AAC7B,MAAA,MAAM,eAAA,EAAiB,MAAA,CAAO,WAAA;AAC9B,MAAA,MAAM,IAAA,EAAM,CAAA;AAEZ,MAAA,IAAI,UAAA,EAAiD,kBAAA;AACrD,MAAA,IAAI,IAAA,EAAM,CAAA;AACV,MAAA,IAAI,KAAA,EAAO,CAAA;AAEX,MAAA,OAAA,CAAQ,kBAAA,EAAoB;AAAA,QAC1B,KAAK,KAAA;AACH,UAAA,IAAA,EAAM,WAAA,CAAY,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,GAAA;AAC7C,UAAA,KAAA,EAAO,WAAA,CAAY,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,EAAA,EAAI,WAAA,CAAY,MAAA,EAAQ,CAAA;AACtE,UAAA,KAAA;AAAA,QACF,KAAK,QAAA;AACH,UAAA,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,GAAA;AAC3B,UAAA,KAAA,EAAO,WAAA,CAAY,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,EAAA,EAAI,WAAA,CAAY,MAAA,EAAQ,CAAA;AACtE,UAAA,KAAA;AAAA,QACF,KAAK,MAAA;AACH,UAAA,IAAA,EAAM,WAAA,CAAY,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,EAAA,EAAI,WAAA,CAAY,OAAA,EAAS,CAAA;AACtE,UAAA,KAAA,EAAO,WAAA,CAAY,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,GAAA;AAC9C,UAAA,KAAA;AAAA,QACF,KAAK,OAAA;AACH,UAAA,IAAA,EAAM,WAAA,CAAY,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,EAAA,EAAI,WAAA,CAAY,OAAA,EAAS,CAAA;AACtE,UAAA,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,GAAA;AAC3B,UAAA,KAAA;AAAA,MACJ;AAEA,MAAA,GAAA,CAAI,UAAA,IAAc,MAAA,GAAS,IAAA,EAAM,GAAA,EAAK;AACpC,QAAA,UAAA,EAAY,QAAA;AACZ,QAAA,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,GAAA;AAC3B,QAAA,KAAA,EAAO,WAAA,CAAY,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,EAAA,EAAI,WAAA,CAAY,MAAA,EAAQ,CAAA;AAAA,MACxE,EAAA,KAAA,GAAA,CAAW,UAAA,IAAc,SAAA,GAAY,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,eAAA,EAAiB,GAAA,EAAK;AACpF,QAAA,UAAA,EAAY,KAAA;AACZ,QAAA,IAAA,EAAM,WAAA,CAAY,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,GAAA;AAC7C,QAAA,KAAA,EAAO,WAAA,CAAY,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,EAAA,EAAI,WAAA,CAAY,MAAA,EAAQ,CAAA;AAAA,MACxE,EAAA,KAAA,GAAA,CAAW,UAAA,IAAc,OAAA,GAAU,KAAA,EAAO,GAAA,EAAK;AAC7C,QAAA,UAAA,EAAY,OAAA;AACZ,QAAA,IAAA,EAAM,WAAA,CAAY,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,EAAA,EAAI,WAAA,CAAY,OAAA,EAAS,CAAA;AACtE,QAAA,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,GAAA;AAAA,MAC7B,EAAA,KAAA,GAAA,CAAW,UAAA,IAAc,QAAA,GAAW,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,cAAA,EAAgB,GAAA,EAAK;AAClF,QAAA,UAAA,EAAY,MAAA;AACZ,QAAA,IAAA,EAAM,WAAA,CAAY,IAAA,EAAM,WAAA,CAAY,OAAA,EAAS,EAAA,EAAI,WAAA,CAAY,OAAA,EAAS,CAAA;AACtE,QAAA,KAAA,EAAO,WAAA,CAAY,KAAA,EAAO,WAAA,CAAY,MAAA,EAAQ,GAAA;AAAA,MAChD;AAEA,MAAA,IAAA,EAAM,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,eAAA,EAAiB,WAAA,CAAY,OAAA,EAAS,GAAG,CAAC,CAAA;AAC5E,MAAA,KAAA,EAAO,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,cAAA,EAAgB,WAAA,CAAY,MAAA,EAAQ,GAAG,CAAC,CAAA;AAE5E,MAAA,OAAO,EAAE,GAAA,EAAK,IAAA,EAAM,UAAU,CAAA;AAAA,IAChC,CAAA;AAAA,IACA,CAAC,kBAAkB;AAAA,EACrB,CAAA;AAEA,EAAA,MAAM,aAAA,EAAe,gCAAA,CAAY,EAAA,GAAM;AACrC,IAAA,GAAA,CAAI,CAAC,YAAA,CAAa,UAAA,GAAa,CAAC,YAAA,CAAa,aAAA,GAAgB,cAAA,CAAe,OAAA,EAAS,MAAA;AAErF,IAAA,cAAA,CAAe,QAAA,EAAU,IAAA;AAEzB,IAAA,iBAAA,CAAkB,QAAA,EAAU,qBAAA,CAAsB,CAAA,EAAA,GAAM;AACtD,MAAA,GAAA,CAAI,UAAA,CAAW,QAAA,GAAW,UAAA,CAAW,OAAA,EAAS;AAC5C,QAAA,MAAM,YAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,qBAAA,CAAsB,CAAA;AAC7D,QAAA,MAAM,YAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,qBAAA,CAAsB,CAAA;AAE7D,QAAA,MAAM,EAAE,GAAA,EAAK,IAAA,EAAM,UAAU,EAAA,EAAI,wBAAA,CAAyB,WAAA,EAAa,WAAW,CAAA;AAElF,QAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,UACzB,GAAG,IAAA;AAAA,UACH,QAAA,EAAU,EAAE,GAAA,EAAK,IAAA,EAAM,UAAU;AAAA,QACnC,CAAA,CAAE,CAAA;AAAA,MACJ;AAEA,MAAA,cAAA,CAAe,QAAA,EAAU,KAAA;AAAA,IAC3B,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,YAAA,CAAa,SAAA,EAAW,YAAA,CAAa,YAAA,EAAc,wBAAwB,CAAC,CAAA;AAEhF,EAAA,MAAM,iBAAA,EAAmB,gCAAA,CAAY,EAAA,GAAM;AACzC,IAAA,GAAA,CAAI,SAAA,GAAY,YAAA,IAAgB,OAAA,EAAS,MAAA;AAKzC,IAAA,GAAA,CAAI,UAAA,CAAW,OAAA,EAAS;AACtB,MAAA,YAAA,CAAa,UAAA,CAAW,OAAO,CAAA;AAAA,IACjC;AAEA,IAAA,UAAA,CAAW,QAAA,EAAU,UAAA,CAAW,CAAA,EAAA,GAAM;AAIpC,MAAA,eAAA;AAAA,QAAgB,CAAC,IAAA,EAAA,GACf,IAAA,CAAK,UAAA,EAAY,KAAA,EAAO,EAAE,GAAG,IAAA,EAAM,SAAA,EAAW,IAAA,EAAM,YAAA,EAAc,MAAM;AAAA,MAC1E,CAAA;AAAA,IACF,CAAA,EAAG,KAAK,CAAA;AAAA,EACV,CAAA,EAAG,CAAC,QAAA,EAAU,KAAA,EAAO,WAAW,CAAC,CAAA;AAEjC,EAAA,MAAM,iBAAA,EAAmB,gCAAA,CAAY,EAAA,GAAM;AACzC,IAAA,GAAA,CAAI,YAAA,IAAgB,OAAA,EAAS,MAAA;AAE7B,IAAA,GAAA,CAAI,UAAA,CAAW,OAAA,EAAS;AACtB,MAAA,YAAA,CAAa,UAAA,CAAW,OAAO,CAAA;AAAA,IACjC;AAUA,IAAA,UAAA,CAAW,QAAA,EAAU,UAAA,CAAW,CAAA,EAAA,GAAM;AACpC,MAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,QACzB,GAAG,IAAA;AAAA,QACH,SAAA,EAAW,KAAA;AAAA,QACX,YAAA,EAAc;AAAA,MAChB,CAAA,CAAE,CAAA;AAAA,IACJ,CAAA,EAAG,cAAc,CAAA;AAAA,EACnB,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAA,MAAM,YAAA,EAAc,gCAAA,CAAY,EAAA,GAAM;AACpC,IAAA,GAAA,CAAI,SAAA,GAAY,YAAA,IAAgB,OAAA,EAAS,MAAA;AAEzC,IAAA,GAAA,CAAI,YAAA,CAAa,SAAA,EAAW;AAC1B,MAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,QACzB,GAAG,IAAA;AAAA,QACH,SAAA,EAAW,KAAA;AAAA,QACX,YAAA,EAAc;AAAA,MAChB,CAAA,CAAE,CAAA;AAAA,IACJ,EAAA,KAAO;AACL,MAAA,GAAA,CAAI,MAAA,EAAQ,CAAA,EAAG;AACb,QAAA,UAAA,CAAW,QAAA,EAAU,UAAA,CAAW,CAAA,EAAA,GAAM;AACpC,UAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,YACzB,GAAG,IAAA;AAAA,YACH,SAAA,EAAW,IAAA;AAAA,YACX,YAAA,EAAc;AAAA,UAChB,CAAA,CAAE,CAAA;AAAA,QACJ,CAAA,EAAG,KAAK,CAAA;AAAA,MACV,EAAA,KAAO;AACL,QAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,UACzB,GAAG,IAAA;AAAA,UACH,SAAA,EAAW,IAAA;AAAA,UACX,YAAA,EAAc;AAAA,QAChB,CAAA,CAAE,CAAA;AAAA,MACJ;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,QAAA,EAAU,WAAA,EAAa,YAAA,CAAa,SAAA,EAAW,KAAK,CAAC,CAAA;AAEzD,EAAA,MAAM,YAAA,EAAc,gCAAA,CAAY,EAAA,GAAM;AACpC,IAAA,GAAA,CAAI,SAAA,GAAY,YAAA,IAAgB,OAAA,EAAS,MAAA;AAEzC,IAAA,UAAA,CAAW,QAAA,EAAU,UAAA,CAAW,CAAA,EAAA,GAAM;AACpC,MAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,QACzB,GAAG,IAAA;AAAA,QACH,SAAA,EAAW,IAAA;AAAA,QACX,YAAA,EAAc;AAAA,MAChB,CAAA,CAAE,CAAA;AAAA,IACJ,CAAA,EAAG,KAAK,CAAA;AAAA,EACV,CAAA,EAAG,CAAC,QAAA,EAAU,WAAA,EAAa,KAAK,CAAC,CAAA;AAEjC,EAAA,MAAM,WAAA,EAAa,gCAAA,CAAY,EAAA,GAAM;AACnC,IAAA,GAAA,CAAI,YAAA,IAAgB,OAAA,EAAS,MAAA;AAE7B,IAAA,GAAA,CAAI,UAAA,CAAW,OAAA,EAAS;AACtB,MAAA,YAAA,CAAa,UAAA,CAAW,OAAO,CAAA;AAAA,IACjC;AACA,IAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,MACzB,GAAG,IAAA;AAAA,MACH,SAAA,EAAW,KAAA;AAAA,MACX,YAAA,EAAc;AAAA,IAChB,CAAA,CAAE,CAAA;AAAA,EACJ,CAAA,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAA,MAAM,mBAAA,EAAqB,gCAAA;AAAA,IACzB,CAAC,KAAA,EAAA,GAAsB;AACrB,MAAA,GAAA,CAAI,CAAC,oBAAA,GAAuB,CAAC,YAAA,CAAa,UAAA,GAAa,YAAA,IAAgB,OAAA,EAAS,MAAA;AAEhF,MAAA,MAAM,OAAA,EAAS,KAAA,CAAM,MAAA;AAErB,MAAA,GAAA,iBAAI,UAAA,mBAAW,OAAA,6BAAS,QAAA,mBAAS,MAAM,IAAA,mBAAK,UAAA,qBAAW,OAAA,6BAAS,QAAA,mBAAS,MAAM,GAAA,EAAG;AAChF,QAAA,MAAA;AAAA,MACF;AAEA,MAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,QACzB,GAAG,IAAA;AAAA,QACH,SAAA,EAAW,KAAA;AAAA,QACX,YAAA,EAAc;AAAA,MAChB,CAAA,CAAE,CAAA;AAAA,IACJ,CAAA;AAAA,IACA,CAAC,mBAAA,EAAqB,YAAA,CAAa,SAAA,EAAW,WAAW;AAAA,EAC3D,CAAA;AAEA,EAAA,MAAM,gBAAA,EAAkB,gCAAA;AAAA,IACtB,CAAC,KAAA,EAAA,GAAyB;AAQxB,MAAA,GAAA,CAAI,cAAA,GAAiB,KAAA,CAAM,IAAA,IAAQ,SAAA,GAAY,YAAA,CAAa,SAAA,EAAW;AACrE,QAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,UACzB,GAAG,IAAA;AAAA,UACH,SAAA,EAAW,KAAA;AAAA,UACX,YAAA,EAAc;AAAA,QAChB,CAAA,CAAE,CAAA;AAAA,MACJ;AAAA,IACF,CAAA;AAAA,IACA,CAAC,aAAA,EAAe,YAAA,CAAa,SAAS;AAAA,EACxC,CAAA;AAEA,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,GAAA,CACE,YAAA,CAAa,UAAA,GACb,CAAC,YAAA,CAAa,aAAA,GACd,UAAA,CAAW,QAAA,GACX,UAAA,CAAW,OAAA,EACX;AACA,MAAA,MAAM,YAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,qBAAA,CAAsB,CAAA;AAC7D,MAAA,MAAM,YAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,qBAAA,CAAsB,CAAA;AAE7D,MAAA,MAAM,EAAE,GAAA,EAAK,IAAA,EAAM,UAAU,EAAA,EAAI,wBAAA,CAAyB,WAAA,EAAa,WAAW,CAAA;AAElF,MAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,QACzB,GAAG,IAAA;AAAA,QACH,QAAA,EAAU,EAAE,GAAA,EAAK,IAAA,EAAM,UAAU,CAAA;AAAA,QACjC,YAAA,EAAc;AAAA,MAChB,CAAA,CAAE,CAAA;AAAA,IACJ;AAAA,EACF,CAAA,EAAG,CAAC,YAAA,CAAa,SAAA,EAAW,YAAA,CAAa,YAAA,EAAc,wBAAwB,CAAC,CAAA;AAEhF,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,MAAM,aAAA,EAAe,CAAA,EAAA,GAAM;AACzB,MAAA,GAAA,CACE,YAAA,CAAa,UAAA,GACb,YAAA,CAAa,aAAA,GACb,UAAA,CAAW,QAAA,GACX,UAAA,CAAW,OAAA,EACX;AACA,QAAA,MAAM,YAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,qBAAA,CAAsB,CAAA;AAC7D,QAAA,MAAM,YAAA,EAAc,UAAA,CAAW,OAAA,CAAQ,qBAAA,CAAsB,CAAA;AAE7D,QAAA,MAAM,EAAE,GAAA,EAAK,IAAA,EAAM,UAAU,EAAA,EAAI,wBAAA,CAAyB,WAAA,EAAa,WAAW,CAAA;AAElF,QAAA,eAAA,CAAgB,CAAC,IAAA,EAAA,GAAA,CAAU;AAAA,UACzB,GAAG,IAAA;AAAA,UACH,QAAA,EAAU,EAAE,GAAA,EAAK,IAAA,EAAM,UAAU;AAAA,QACnC,CAAA,CAAE,CAAA;AAAA,MACJ;AAAA,IACF,CAAA;AAEA,IAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,YAAY,CAAA;AAC9C,IAAA,OAAO,CAAA,EAAA,GAAM,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,YAAY,CAAA;AAAA,EAChE,CAAA,EAAG,CAAC,YAAA,CAAa,SAAA,EAAW,YAAA,CAAa,YAAA,EAAc,wBAAwB,CAAC,CAAA;AAEhF,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,GAAA,CAAI,YAAA,CAAa,SAAA,EAAW;AAC1B,MAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,YAAA,EAAc,EAAE,OAAA,EAAS,KAAK,CAAC,CAAA;AAEjE,MAAA,QAAA,CAAS,IAAA,CAAK,gBAAA,CAAiB,QAAA,EAAU,YAAA,EAAc,EAAE,OAAA,EAAS,KAAK,CAAC,CAAA;AAAA,IAC1E;AAEA,IAAA,OAAO,CAAA,EAAA,GAAM;AACX,MAAA,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,YAAY,CAAA;AACjD,MAAA,QAAA,CAAS,IAAA,CAAK,mBAAA,CAAoB,QAAA,EAAU,YAAY,CAAA;AAAA,IAC1D,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,YAAA,CAAa,SAAA,EAAW,YAAY,CAAC,CAAA;AAMzC,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,GAAA,CAAI,YAAA,CAAa,UAAA,GAAa,YAAA,IAAgB,OAAA,EAAS;AACrD,MAAA,QAAA,CAAS,gBAAA,CAAiB,WAAA,EAAa,kBAAkB,CAAA;AAAA,IAC3D;AAEA,IAAA,OAAO,CAAA,EAAA,GAAM;AACX,MAAA,QAAA,CAAS,mBAAA,CAAoB,WAAA,EAAa,kBAAkB,CAAA;AAAA,IAC9D,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,YAAA,CAAa,SAAA,EAAW,WAAA,EAAa,kBAAkB,CAAC,CAAA;AAE5D,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,GAAA,CAAI,YAAA,CAAa,SAAA,EAAW;AAC1B,MAAA,QAAA,CAAS,gBAAA,CAAiB,SAAA,EAAW,eAAe,CAAA;AAAA,IACtD;AAEA,IAAA,OAAO,CAAA,EAAA,GAAM;AACX,MAAA,QAAA,CAAS,mBAAA,CAAoB,SAAA,EAAW,eAAe,CAAA;AAAA,IACzD,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,YAAA,CAAa,SAAA,EAAW,eAAe,CAAC,CAAA;AAE5C,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,OAAO,CAAA,EAAA,GAAM;AACX,MAAA,GAAA,CAAI,UAAA,CAAW,OAAA,EAAS;AACtB,QAAA,YAAA,CAAa,UAAA,CAAW,OAAO,CAAA;AAAA,MACjC;AACA,MAAA,GAAA,CAAI,iBAAA,CAAkB,OAAA,EAAS;AAC7B,QAAA,oBAAA,CAAqB,iBAAA,CAAkB,OAAO,CAAA;AAAA,MAChD;AAAA,IACF,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,CAAC,CAAA;AAEL,EAAA,MAAM,qBAAA,EAAuB,CAAA,EAAA,GAAM;AACjC,IAAA,GAAA,CAAI,OAAA,EAAS;AACX,MAAA,uBAAO,6BAAA,MAAC,EAAA,EAAK,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAA,QAAA,CAAQ,CAAA;AAAA,IAC1D;AACA,IAAA,GAAA,CAAI,SAAA,EAAW;AACb,MAAA,uBAAO,6BAAA,SAAC,EAAA,EAAW,GAAI,eAAA,GAAkB,CAAC,EAAA,CAAI,CAAA;AAAA,IAChD;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AAMA,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,EAAA,EAAI,6BAAA,KAAc,CAAA;AAEhE,EAAA,8BAAA,CAAU,EAAA,GAAM;AACd,IAAA,MAAM,GAAA,EAAK,UAAA,CAAW,OAAA;AACtB,IAAA,oBAAA,CAAqB,CAAC,CAAC,GAAA,GAAM,EAAA,CAAG,aAAA,CAAc,oCAAkB,EAAA,IAAM,IAAI,CAAA;AAAA,EAC5E,CAAA,EAAG,CAAC,QAAA,EAAU,WAAW,CAAC,CAAA;AAE1B,EAAA,MAAM,gBAAA,EAAkB,CAAA,EAAA,GAAM;AAC5B,IAAA,MAAM,MAAA,EAAiC;AAAA,MACrC,GAAA,EAAK,UAAA;AAAA,MACL,SAAA,EAAW;AAAA,IACb,CAAA;AAEA,IAAA,OAAA,CAAQ,WAAA,EAAa;AAAA,MACnB,KAAK,OAAA;AACH,QAAA,KAAA,CAAM,aAAA,EAAe,gBAAA;AACrB,QAAA,KAAA,CAAM,aAAA,EAAe,gBAAA;AACrB,QAAA,KAAA;AAAA,MACF,KAAK,OAAA;AACH,QAAA,KAAA,CAAM,QAAA,EAAU,WAAA;AAMhB,QAAA,GAAA,CAAI,iBAAA,EAAmB;AACrB,UAAA,KAAA,CAAM,KAAA,EAAO,QAAA;AACb,UAAA,KAAA,CAAM,SAAA,EAAW,CAAA;AACjB,UAAA,KAAA,CAAM,UAAA,EAAY,CAAC,KAAA,EAAA,GAA+B;AAChD,YAAA,GAAA,CAAI,KAAA,CAAM,IAAA,IAAQ,QAAA,GAAW,KAAA,CAAM,IAAA,IAAQ,GAAA,EAAK;AAC9C,cAAA,KAAA,CAAM,cAAA,CAAe,CAAA;AACrB,cAAA,WAAA,CAAY,CAAA;AAAA,YACd;AAAA,UACF,CAAA;AAAA,QACF;AACA,QAAA,KAAA;AAAA,MACF,KAAK,OAAA;AACH,QAAA,KAAA,CAAM,QAAA,EAAU,WAAA;AAChB,QAAA,KAAA,CAAM,OAAA,EAAS,UAAA;AAIf,QAAA,GAAA,CAAI,iBAAA,EAAmB,KAAA,CAAM,SAAA,EAAW,CAAA;AACxC,QAAA,KAAA;AAAA,IACJ;AAEA,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAEA,EAAA,uBACE,8BAAA,oBAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,6BAAA,KAAC,EAAA,EAAK,GAAG,eAAA,CAAgB,CAAA,EAAI,SAAA,CAAS,CAAA;AAAA,IACrC,YAAA,CAAa,UAAA,GACZ,oCAAA;AAAA,sBACE,6BAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,UAAA;AAAA,UAKL,YAAA,EAAc,gBAAA;AAAA,UACd,YAAA,EAAc,gBAAA;AAAA,UACd,SAAA,EAAW,CAAA,6BAAA,EAAgC,YAAA,CAAa,QAAA,CAAS,SAAS,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAChF,UAAA;AACsB,YAAA;AACC,YAAA;AAC9B,UAAA;AACoD,UAAA;AAC/C,UAAA;AAEiB,UAAA;AAAA,QAAA;AACxB,MAAA;AACS,MAAA;AACX,IAAA;AACJ,EAAA;AAEJ;AAEsB;AD3G4E;AACA;AACA;AACA","file":"/home/runner/work/eidos-ui/eidos-ui/dist/chunk-MLUHM3VG.cjs","sourcesContent":[null,"import React, { useState, useRef, useCallback, useEffect } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { TooltipProps, TooltipState } from './Tooltip.types';\nimport { FOCUSABLE_SELECTOR } from '../../utils';\n\n/**\n * How long the tooltip stays open after the pointer leaves, so it can be\n * moved onto the panel itself (SC 1.4.13, \"hoverable\"). Long enough to cross\n * the gap, short enough not to feel stuck.\n */\nconst HOVER_GRACE_MS = 150;\n\nexport const Tooltip: React.FC<TooltipProps> = ({\n children,\n message,\n component: Component,\n componentProps,\n placement: preferredPlacement = 'top',\n delay = 100,\n disabled = false,\n className = '',\n triggerType = 'hover',\n closeOnClickOutside = true,\n closeOnEscape = true,\n}) => {\n const [tooltipState, setTooltipState] = useState<TooltipState>({\n isVisible: false,\n position: { top: 0, left: 0, placement: 'top' },\n isPositioned: false,\n });\n\n const triggerRef = useRef<HTMLDivElement>(null);\n const tooltipRef = useRef<HTMLDivElement>(null);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n const animationFrameRef = useRef<number | undefined>(undefined);\n const isScrollingRef = useRef(false);\n\n const calculateOptimalPosition = useCallback(\n (triggerRect: DOMRect, tooltipRect: DOMRect) => {\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n const gap = 8;\n\n let placement: 'top' | 'bottom' | 'left' | 'right' = preferredPlacement;\n let top = 0;\n let left = 0;\n\n switch (preferredPlacement) {\n case 'top':\n top = triggerRect.top - tooltipRect.height - gap;\n left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;\n break;\n case 'bottom':\n top = triggerRect.bottom + gap;\n left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;\n break;\n case 'left':\n top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;\n left = triggerRect.left - tooltipRect.width - gap;\n break;\n case 'right':\n top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;\n left = triggerRect.right + gap;\n break;\n }\n\n if (placement === 'top' && top < gap) {\n placement = 'bottom';\n top = triggerRect.bottom + gap;\n left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;\n } else if (placement === 'bottom' && top + tooltipRect.height > viewportHeight - gap) {\n placement = 'top';\n top = triggerRect.top - tooltipRect.height - gap;\n left = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;\n } else if (placement === 'left' && left < gap) {\n placement = 'right';\n top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;\n left = triggerRect.right + gap;\n } else if (placement === 'right' && left + tooltipRect.width > viewportWidth - gap) {\n placement = 'left';\n top = triggerRect.top + triggerRect.height / 2 - tooltipRect.height / 2;\n left = triggerRect.left - tooltipRect.width - gap;\n }\n\n top = Math.max(gap, Math.min(top, viewportHeight - tooltipRect.height - gap));\n left = Math.max(gap, Math.min(left, viewportWidth - tooltipRect.width - gap));\n\n return { top, left, placement };\n },\n [preferredPlacement],\n );\n\n const handleScroll = useCallback(() => {\n if (!tooltipState.isVisible || !tooltipState.isPositioned || isScrollingRef.current) return;\n\n isScrollingRef.current = true;\n\n animationFrameRef.current = requestAnimationFrame(() => {\n if (triggerRef.current && tooltipRef.current) {\n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, tooltipRect);\n\n setTooltipState((prev) => ({\n ...prev,\n position: { top, left, placement },\n }));\n }\n\n isScrollingRef.current = false;\n });\n }, [tooltipState.isVisible, tooltipState.isPositioned, calculateOptimalPosition]);\n\n const handleMouseEnter = useCallback(() => {\n if (disabled || triggerType !== 'hover') return;\n\n // Cancels a pending hide as well as scheduling the show. Both handlers\n // are bound to the trigger *and* to the panel, so this is what catches\n // the pointer as it crosses from one to the other.\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n timeoutRef.current = setTimeout(() => {\n // Left untouched when already open. Re-entering from the panel would\n // otherwise re-run the open transition and reposition a tooltip the\n // user is in the middle of reading.\n setTooltipState((prev) =>\n prev.isVisible ? prev : { ...prev, isVisible: true, isPositioned: false },\n );\n }, delay);\n }, [disabled, delay, triggerType]);\n\n const handleMouseLeave = useCallback(() => {\n if (triggerType !== 'hover') return;\n\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n\n // Closing is delayed, not immediate.\n //\n // SC 1.4.13 requires the pointer to be able to reach the tooltip's own\n // content. The panel is portaled and sits a few pixels off the trigger,\n // so an immediate hide made that gap impossible to cross - the tooltip\n // disappeared the moment the pointer left the trigger, however fast the\n // user moved. The grace period is what makes \"hoverable\" reachable in\n // practice.\n timeoutRef.current = setTimeout(() => {\n setTooltipState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n }, HOVER_GRACE_MS);\n }, [triggerType]);\n\n const handleClick = useCallback(() => {\n if (disabled || triggerType !== 'click') return;\n\n if (tooltipState.isVisible) {\n setTooltipState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n } else {\n if (delay > 0) {\n timeoutRef.current = setTimeout(() => {\n setTooltipState((prev) => ({\n ...prev,\n isVisible: true,\n isPositioned: false,\n }));\n }, delay);\n } else {\n setTooltipState((prev) => ({\n ...prev,\n isVisible: true,\n isPositioned: false,\n }));\n }\n }\n }, [disabled, triggerType, tooltipState.isVisible, delay]);\n\n const handleFocus = useCallback(() => {\n if (disabled || triggerType !== 'focus') return;\n\n timeoutRef.current = setTimeout(() => {\n setTooltipState((prev) => ({\n ...prev,\n isVisible: true,\n isPositioned: false,\n }));\n }, delay);\n }, [disabled, triggerType, delay]);\n\n const handleBlur = useCallback(() => {\n if (triggerType !== 'focus') return;\n\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n setTooltipState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n }, [triggerType]);\n\n const handleClickOutside = useCallback(\n (event: MouseEvent) => {\n if (!closeOnClickOutside || !tooltipState.isVisible || triggerType !== 'click') return;\n\n const target = event.target as Node;\n\n if (tooltipRef.current?.contains(target) || triggerRef.current?.contains(target)) {\n return;\n }\n\n setTooltipState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n },\n [closeOnClickOutside, tooltipState.isVisible, triggerType],\n );\n\n const handleEscapeKey = useCallback(\n (event: KeyboardEvent) => {\n // Every trigger type, not just `click`.\n //\n // SC 1.4.13 requires content shown on hover *or focus* to be\n // dismissible without moving the pointer or focus - and `hover` is the\n // default, so the one configuration that could be dismissed was the\n // one least likely to be used. A tooltip covering the text you are\n // trying to read had to be escaped by physically moving the mouse.\n if (closeOnEscape && event.key === 'Escape' && tooltipState.isVisible) {\n setTooltipState((prev) => ({\n ...prev,\n isVisible: false,\n isPositioned: false,\n }));\n }\n },\n [closeOnEscape, tooltipState.isVisible],\n );\n\n useEffect(() => {\n if (\n tooltipState.isVisible &&\n !tooltipState.isPositioned &&\n tooltipRef.current &&\n triggerRef.current\n ) {\n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, tooltipRect);\n\n setTooltipState((prev) => ({\n ...prev,\n position: { top, left, placement },\n isPositioned: true,\n }));\n }\n }, [tooltipState.isVisible, tooltipState.isPositioned, calculateOptimalPosition]);\n\n useEffect(() => {\n const handleResize = () => {\n if (\n tooltipState.isVisible &&\n tooltipState.isPositioned &&\n tooltipRef.current &&\n triggerRef.current\n ) {\n const triggerRect = triggerRef.current.getBoundingClientRect();\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\n\n const { top, left, placement } = calculateOptimalPosition(triggerRect, tooltipRect);\n\n setTooltipState((prev) => ({\n ...prev,\n position: { top, left, placement },\n }));\n }\n };\n\n window.addEventListener('resize', handleResize);\n return () => window.removeEventListener('resize', handleResize);\n }, [tooltipState.isVisible, tooltipState.isPositioned, calculateOptimalPosition]);\n\n useEffect(() => {\n if (tooltipState.isVisible) {\n window.addEventListener('scroll', handleScroll, { passive: true });\n\n document.body.addEventListener('scroll', handleScroll, { passive: true });\n }\n\n return () => {\n window.removeEventListener('scroll', handleScroll);\n document.body.removeEventListener('scroll', handleScroll);\n };\n }, [tooltipState.isVisible, handleScroll]);\n\n // Click-outside is only meaningful for a click-triggered tooltip; Escape\n // is not. Registering them together meant a hover tooltip had no way to be\n // dismissed from the keyboard at all (SC 1.4.13, \"dismissible\") - and\n // `hover` is the default, so the failing case was the common one.\n useEffect(() => {\n if (tooltipState.isVisible && triggerType === 'click') {\n document.addEventListener('mousedown', handleClickOutside);\n }\n\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [tooltipState.isVisible, triggerType, handleClickOutside]);\n\n useEffect(() => {\n if (tooltipState.isVisible) {\n document.addEventListener('keydown', handleEscapeKey);\n }\n\n return () => {\n document.removeEventListener('keydown', handleEscapeKey);\n };\n }, [tooltipState.isVisible, handleEscapeKey]);\n\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n }\n if (animationFrameRef.current) {\n cancelAnimationFrame(animationFrameRef.current);\n }\n };\n }, []);\n\n const renderTooltipContent = () => {\n if (message) {\n return <span className=\"eidos-tooltip-message\">{message}</span>;\n }\n if (Component) {\n return <Component {...(componentProps || {})} />;\n }\n return null;\n };\n\n // Whether the trigger wrapper has to become focusable itself, i.e. whether\n // the consumer's child is not already a control. Measured from the DOM\n // rather than guessed from the node type, because `children` is an\n // arbitrary `ReactNode` and the control may be nested inside it.\n const [triggerNeedsFocus, setTriggerNeedsFocus] = useState(false);\n\n useEffect(() => {\n const el = triggerRef.current;\n setTriggerNeedsFocus(!!el && el.querySelector(FOCUSABLE_SELECTOR) === null);\n }, [children, triggerType]);\n\n const getTriggerProps = () => {\n const props: Record<string, unknown> = {\n ref: triggerRef,\n className: 'eidos-tooltip-trigger',\n };\n\n switch (triggerType) {\n case 'hover':\n props.onMouseEnter = handleMouseEnter;\n props.onMouseLeave = handleMouseLeave;\n break;\n case 'click':\n props.onClick = handleClick;\n // Only when the trigger has no control of its own. Making this\n // wrapper a button unconditionally put a button inside a button\n // whenever the child was one - which is the common case, since\n // `Button` wraps *itself* in a Tooltip when given `tooltip`. That is\n // invalid (`nested-interactive`) and gives one action two tab stops.\n if (triggerNeedsFocus) {\n props.role = 'button';\n props.tabIndex = 0;\n props.onKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleClick();\n }\n };\n }\n break;\n case 'focus':\n props.onFocus = handleFocus;\n props.onBlur = handleBlur;\n // Same reasoning: a focusable child already receives the focus that\n // opens the tooltip, and adding `tabIndex` here would just add a\n // second stop in front of it.\n if (triggerNeedsFocus) props.tabIndex = 0;\n break;\n }\n\n return props;\n };\n\n return (\n <>\n <div {...getTriggerProps()}>{children}</div>\n {tooltipState.isVisible &&\n createPortal(\n <div\n ref={tooltipRef}\n // SC 1.4.13 \"hoverable\": the pointer has to be able to reach the\n // tooltip's own content - to read a long message, or select text\n // from it - without it vanishing. The panel used to be\n // `pointer-events: none`, so moving towards it dismissed it.\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n className={`eidos-tooltip eidos-tooltip--${tooltipState.position.placement} ${className}`}\n style={{\n top: tooltipState.position.top,\n left: tooltipState.position.left,\n }}\n data-state={tooltipState.isPositioned ? 'entered' : 'entering'}\n role=\"tooltip\"\n >\n {renderTooltipContent()}\n </div>,\n document.body,\n )}\n </>\n );\n};\n\nTooltip.displayName = 'Tooltip';\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
fullWidthModifier
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WT4WBHBR.js";
|
|
4
4
|
|
|
5
5
|
// src/components/NumberInput/NumberInput.component.tsx
|
|
6
6
|
import { forwardRef, useState, useEffect, useId } from "react";
|
|
@@ -31,6 +31,7 @@ var NumberInput = forwardRef(
|
|
|
31
31
|
}, ref) => {
|
|
32
32
|
const generatedId = useId();
|
|
33
33
|
const inputId = id || `number-input-${generatedId}`;
|
|
34
|
+
const errorId = `${inputId}-error`;
|
|
34
35
|
const isControlled = value !== void 0;
|
|
35
36
|
const [localValue, setLocalValue] = useState(defaultValue);
|
|
36
37
|
const [inputText, setInputText] = useState(defaultValue.toFixed(precision));
|
|
@@ -150,6 +151,8 @@ var NumberInput = forwardRef(
|
|
|
150
151
|
readOnly,
|
|
151
152
|
inputMode: "decimal",
|
|
152
153
|
role: "spinbutton",
|
|
154
|
+
"aria-invalid": error ? true : void 0,
|
|
155
|
+
"aria-describedby": error && errorMessage ? errorId : void 0,
|
|
153
156
|
"aria-valuemin": min,
|
|
154
157
|
"aria-valuemax": max,
|
|
155
158
|
"aria-valuenow": currentValue,
|
|
@@ -191,7 +194,7 @@ var NumberInput = forwardRef(
|
|
|
191
194
|
}
|
|
192
195
|
),
|
|
193
196
|
helperText && !error && /* @__PURE__ */ jsx("span", { className: "eidos-number-input-helper", children: helperText }),
|
|
194
|
-
error && errorMessage && /* @__PURE__ */ jsx("span", { className: "eidos-number-input-error", role: "alert", children: errorMessage })
|
|
197
|
+
error && errorMessage && /* @__PURE__ */ jsx("span", { id: errorId, className: "eidos-number-input-error", role: "alert", children: errorMessage })
|
|
195
198
|
] });
|
|
196
199
|
}
|
|
197
200
|
);
|
|
@@ -200,4 +203,4 @@ NumberInput.displayName = "NumberInput";
|
|
|
200
203
|
export {
|
|
201
204
|
NumberInput
|
|
202
205
|
};
|
|
203
|
-
//# sourceMappingURL=chunk-
|
|
206
|
+
//# sourceMappingURL=chunk-MP7LN5CW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/NumberInput/NumberInput.component.tsx"],"sourcesContent":["import React, { forwardRef, useState, useEffect, useId } from 'react';\nimport { Minus, Plus } from 'lucide-react';\nimport { fullWidthModifier } from '../../utils';\nimport type { NumberInputProps } from './NumberInput.types';\n\nexport const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(\n (\n {\n value,\n defaultValue = 0,\n onChange,\n min,\n max,\n step = 1,\n precision = 0,\n placeholder,\n disabled = false,\n readOnly = false,\n label,\n helperText,\n error = false,\n errorMessage,\n size = 'md',\n fullWidth = false,\n allowTyping = true,\n className = '',\n id,\n name,\n },\n ref,\n ) => {\n const generatedId = useId();\n const inputId = id || `number-input-${generatedId}`;\n // Associates the visible error with the control. Without it the message\n // is just a red line of text sitting near the field - obvious to look\n // at, absent from the accessibility tree (SC 3.3.1). See the fuller\n // note in `Input`.\n const errorId = `${inputId}-error`;\n\n // Controlled / uncontrolled bridge\n const isControlled = value !== undefined;\n const [localValue, setLocalValue] = useState<number>(defaultValue);\n\n // Intermediate text while the user is actively typing\n const [inputText, setInputText] = useState<string>(defaultValue.toFixed(precision));\n const [isFocused, setIsFocused] = useState(false);\n\n const currentValue = isControlled ? value! : localValue;\n\n // When min/max props change (e.g. via Storybook controls), clamp the\n // uncontrolled localValue so the display never shows an out-of-range value.\n useEffect(() => {\n if (isControlled) return;\n setLocalValue((prev) => {\n let result = prev;\n if (min !== undefined) result = Math.max(min, result);\n if (max !== undefined) result = Math.min(max, result);\n return parseFloat(result.toFixed(precision));\n });\n }, [min, max, precision, isControlled]);\n\n // Clamp to [min, max] and apply precision rounding\n const clampValue = (raw: number): number => {\n let result = raw;\n if (min !== undefined) result = Math.max(min, result);\n if (max !== undefined) result = Math.min(max, result);\n return parseFloat(result.toFixed(precision));\n };\n\n const commitValue = (newVal: number) => {\n const clamped = clampValue(newVal);\n if (!isControlled) setLocalValue(clamped);\n onChange?.(clamped);\n };\n\n // ── Stepper buttons ────────────────────────────────────────────────────\n const handleDecrement = () => {\n if (disabled || readOnly) return;\n commitValue(currentValue - step);\n };\n\n const handleIncrement = () => {\n if (disabled || readOnly) return;\n commitValue(currentValue + step);\n };\n\n // ── Typing mode handlers ───────────────────────────────────────────────\n const handleTextFocus = () => {\n setInputText(currentValue.toFixed(precision));\n setIsFocused(true);\n };\n\n const handleTextChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n setInputText(e.target.value);\n };\n\n const handleTextBlur = () => {\n setIsFocused(false);\n const parsed = parseFloat(inputText);\n if (!isNaN(parsed)) {\n const clamped = clampValue(parsed);\n if (!isControlled) setLocalValue(clamped);\n onChange?.(clamped);\n setInputText(clamped.toFixed(precision));\n } else {\n // Revert to the last known-good value\n setInputText(currentValue.toFixed(precision));\n }\n };\n\n // ArrowUp / ArrowDown keyboard support for both display modes\n const handleDisplayKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleIncrement();\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleDecrement();\n }\n };\n\n // ── Display value ──────────────────────────────────────────────────────\n // Show intermediate inputText while focused in typing mode;\n // otherwise always derive from the real (controlled/uncontrolled) value.\n const formattedValue = currentValue.toFixed(precision);\n const displayedText = allowTyping && isFocused ? inputText : formattedValue;\n\n // ── Disabled states for buttons ────────────────────────────────────────\n const isDecrementDisabled = disabled || readOnly || (min !== undefined && currentValue <= min);\n const isIncrementDisabled = disabled || readOnly || (max !== undefined && currentValue >= max);\n\n // ── CSS classes ────────────────────────────────────────────────────────\n const rootClasses = [\n 'eidos-number-input',\n `eidos-number-input--${size}`,\n fullWidth && fullWidthModifier('eidos-number-input'),\n error && 'eidos-number-input--error',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n const controlClasses = [\n 'eidos-number-input-control',\n disabled && 'eidos-number-input-control--disabled',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div className={rootClasses}>\n {label && (\n <label htmlFor={allowTyping ? inputId : undefined} className=\"eidos-number-input-label\">\n {label}\n </label>\n )}\n\n <div\n className={controlClasses}\n role={allowTyping ? undefined : 'spinbutton'}\n aria-valuemin={allowTyping ? undefined : min}\n aria-valuemax={allowTyping ? undefined : max}\n aria-valuenow={allowTyping ? undefined : currentValue}\n aria-label={allowTyping ? undefined : (label ?? 'Number input')}\n aria-disabled={disabled || undefined}\n tabIndex={!allowTyping && !disabled ? 0 : undefined}\n onKeyDown={!allowTyping ? handleDisplayKeyDown : undefined}\n >\n <button\n type=\"button\"\n className=\"eidos-number-input-btn\"\n onClick={handleDecrement}\n disabled={isDecrementDisabled}\n aria-label=\"Decrease\"\n >\n <Minus />\n </button>\n\n <div className=\"eidos-number-input-divider\" aria-hidden=\"true\" />\n\n {allowTyping ? (\n <input\n id={inputId}\n type=\"text\"\n className=\"eidos-number-input-display\"\n value={displayedText}\n onChange={handleTextChange}\n onFocus={handleTextFocus}\n onBlur={handleTextBlur}\n onKeyDown={handleDisplayKeyDown}\n placeholder={placeholder}\n disabled={disabled}\n readOnly={readOnly}\n inputMode=\"decimal\"\n role=\"spinbutton\"\n aria-invalid={error ? true : undefined}\n aria-describedby={error && errorMessage ? errorId : undefined}\n aria-valuemin={min}\n aria-valuemax={max}\n aria-valuenow={currentValue}\n aria-label={label ?? 'Number input'}\n />\n ) : (\n <span className=\"eidos-number-input-display\" aria-hidden=\"true\">\n {placeholder && formattedValue === (0).toFixed(precision) && !isFocused\n ? placeholder\n : formattedValue}\n </span>\n )}\n\n <div className=\"eidos-number-input-divider\" aria-hidden=\"true\" />\n\n <button\n type=\"button\"\n className=\"eidos-number-input-btn\"\n onClick={handleIncrement}\n disabled={isIncrementDisabled}\n aria-label=\"Increase\"\n >\n <Plus />\n </button>\n </div>\n\n {/* Hidden native <input type=\"number\"> for form integration */}\n <input\n ref={ref}\n type=\"number\"\n name={name}\n value={currentValue}\n onChange={() => {}}\n className=\"eidos-number-input-hidden\"\n tabIndex={-1}\n aria-hidden=\"true\"\n disabled={disabled}\n readOnly={readOnly}\n min={min}\n max={max}\n step={step}\n />\n\n {helperText && !error && <span className=\"eidos-number-input-helper\">{helperText}</span>}\n\n {error && errorMessage && (\n <span id={errorId} className=\"eidos-number-input-error\" role=\"alert\">\n {errorMessage}\n </span>\n )}\n </div>\n );\n },\n);\n\nNumberInput.displayName = 'NumberInput';\n"],"mappings":"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","names":[]}
|