@feugene/granularity 0.12.0 → 0.13.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/dist/GrBadge.css +1 -1
- package/dist/GrFormFile.css +1 -1
- package/dist/GrToaster.css +1 -1
- package/dist/GrTree.css +1 -1
- package/dist/chunks/{config-ClhKPJvg.js → config-23WtUiAL.js} +1 -1
- package/dist/chunks/config-23WtUiAL.js.map +1 -0
- package/dist/chunks/config-8Pcutu1j.js.map +1 -0
- package/dist/chunks/{config-1uSYl98Y2.js → config-B0i5r8kH.js} +1 -1
- package/dist/chunks/config-B0i5r8kH.js.map +1 -0
- package/dist/chunks/{config-BOlr7qw7.js → config-B5Rknzkd.js} +33 -39
- package/dist/chunks/config-B5Rknzkd.js.map +1 -0
- package/dist/chunks/config-B7W9VlZ7.js +5 -0
- package/dist/chunks/config-B7W9VlZ7.js.map +1 -0
- package/dist/chunks/config-BDSHekVC.js +84 -0
- package/dist/chunks/config-BDSHekVC.js.map +1 -0
- package/dist/chunks/config-BLHqZhy-.js.map +1 -0
- package/dist/chunks/{config-eYLZYOUS2.js → config-BMKNHFMD2.js} +15 -15
- package/dist/chunks/config-BMKNHFMD2.js.map +1 -0
- package/dist/chunks/config-BUHuGBik2.js.map +1 -0
- package/dist/chunks/config-BX1JTrkV.js.map +1 -0
- package/dist/chunks/config-BXtAmU3s.js.map +1 -0
- package/dist/chunks/config-BZFUM3Ky.js +75 -0
- package/dist/chunks/config-BZFUM3Ky.js.map +1 -0
- package/dist/chunks/{config-DUcu1D682.js → config-BmNT0Fmx.js} +2 -2
- package/dist/chunks/config-BmNT0Fmx.js.map +1 -0
- package/dist/chunks/{config-C49QUoJL.js → config-BvVq2gbd.js} +1 -1
- package/dist/chunks/{config-C49QUoJL.js.map → config-BvVq2gbd.js.map} +1 -1
- package/dist/chunks/{config-CrG1XiE72.js → config-C4hJxXDa.js} +5 -5
- package/dist/chunks/config-C4hJxXDa.js.map +1 -0
- package/dist/chunks/config-CB4RVt9M.js +66 -0
- package/dist/chunks/config-CB4RVt9M.js.map +1 -0
- package/dist/chunks/config-CGHLoBlH2.js.map +1 -0
- package/dist/chunks/config-COE_vZ-l.js +52 -0
- package/dist/chunks/config-COE_vZ-l.js.map +1 -0
- package/dist/chunks/{config-CziwTDDL.js → config-CPdmr7tC.js} +1 -1
- package/dist/chunks/{config-CziwTDDL.js.map → config-CPdmr7tC.js.map} +1 -1
- package/dist/chunks/{config-BIRO0vDJ.js → config-DPDFYzIa.js} +2 -2
- package/dist/chunks/config-DPDFYzIa.js.map +1 -0
- package/dist/chunks/{config-DnvPqu4g2.js → config-DRKXJ9PW2.js} +1 -1
- package/dist/chunks/{config-DnvPqu4g2.js.map → config-DRKXJ9PW2.js.map} +1 -1
- package/dist/chunks/config-DToHb81g.js +45 -0
- package/dist/chunks/config-DToHb81g.js.map +1 -0
- package/dist/chunks/{config-jBp9wVv-.js → config-D_myOZbA.js} +1 -1
- package/dist/chunks/{config-jBp9wVv-.js.map → config-D_myOZbA.js.map} +1 -1
- package/dist/chunks/{config-D_R56_gT.js → config-Dg_6FTvJ.js} +1 -1
- package/dist/chunks/config-Dg_6FTvJ.js.map +1 -0
- package/dist/chunks/config-Dqy0rPPO2.js.map +1 -0
- package/dist/chunks/config-Droqn55j.js +8 -0
- package/dist/chunks/config-Droqn55j.js.map +1 -0
- package/dist/chunks/config-JpHA4Xx0.js.map +1 -0
- package/dist/chunks/{config-C4snOIXg.js → config-KXuionN3.js} +1 -1
- package/dist/chunks/{config-C4snOIXg.js.map → config-KXuionN3.js.map} +1 -1
- package/dist/chunks/{config-DezDih8_.js → config-LqDC_rvp.js} +2 -2
- package/dist/chunks/config-LqDC_rvp.js.map +1 -0
- package/dist/chunks/config-QaIc_gRa2.js.map +1 -0
- package/dist/chunks/config-U4m9PSql2.js.map +1 -0
- package/dist/chunks/{config-B7IIWGt_2.js → config-XmTv4pnI.js} +2 -2
- package/dist/chunks/config-XmTv4pnI.js.map +1 -0
- package/dist/chunks/config-eXB87DOi.js.map +1 -0
- package/dist/chunks/{config-C7NpDrfO.js → config-gZWR75Bh2.js} +1 -1
- package/dist/chunks/config-gZWR75Bh2.js.map +1 -0
- package/dist/chunks/config-ipvU0lzP.js.map +1 -0
- package/dist/chunks/config-xR4XVmcy2.js.map +1 -0
- package/dist/chunks/context-Bfrgsluh.js +9 -0
- package/dist/chunks/context-Bfrgsluh.js.map +1 -0
- package/dist/chunks/context-DfWYCBB-.js +129 -0
- package/dist/chunks/context-DfWYCBB-.js.map +1 -0
- package/dist/chunks/{en-CfkDa4GX.js → en-Cr6EkG8c.js} +46 -4
- package/dist/chunks/en-Cr6EkG8c.js.map +1 -0
- package/dist/chunks/{es-CgaLnFYx.js → es-DuYb1Qju.js} +3 -3
- package/dist/chunks/{es-CgaLnFYx.js.map → es-DuYb1Qju.js.map} +1 -1
- package/dist/chunks/{es-FkgECuCm.js → es-i08e9Sdo.js} +46 -4
- package/dist/chunks/es-i08e9Sdo.js.map +1 -0
- package/dist/chunks/{grBadgeStyles-DrycBuKT.js → grBadgeStyles-D3dFjr_N.js} +12 -12
- package/dist/chunks/grBadgeStyles-D3dFjr_N.js.map +1 -0
- package/dist/chunks/{grButtonStyles-C7Yga_Q1.js → grButtonStyles-DRBgDmOp.js} +28 -28
- package/dist/chunks/grButtonStyles-DRBgDmOp.js.map +1 -0
- package/dist/chunks/{grDropdownStyles-B_-DKytw.js → grDropdownStyles-HBPkghVr.js} +2 -2
- package/dist/chunks/{grDropdownStyles-B_-DKytw.js.map → grDropdownStyles-HBPkghVr.js.map} +1 -1
- package/dist/chunks/{grModalStyles-DWd3i05Q.js → grModalStyles-D8-shx9A.js} +1 -1
- package/dist/chunks/grModalStyles-D8-shx9A.js.map +1 -0
- package/dist/chunks/{grSelectStyles-CLK5hRd3.js → grSelectStyles-CwMUQrBZ.js} +12 -12
- package/dist/chunks/grSelectStyles-CwMUQrBZ.js.map +1 -0
- package/dist/chunks/{grStyle-Blc9otAx.js → grStyle-CdvxTXPH.js} +2 -2
- package/dist/chunks/grStyle-CdvxTXPH.js.map +1 -0
- package/dist/chunks/{granularityI18n-DoV7Ca2m.js → granularityI18n-SH0I--GW.js} +1 -1
- package/dist/chunks/{granularityI18n-DoV7Ca2m.js.map → granularityI18n-SH0I--GW.js.map} +1 -1
- package/dist/chunks/{ru-Bl8CxpEa.js → ru-B5XOOYZt.js} +46 -4
- package/dist/chunks/ru-B5XOOYZt.js.map +1 -0
- package/dist/chunks/{safelist-Do2RtzLB.js → safelist-BvO8HPnE.js} +1 -1
- package/dist/chunks/{safelist-Do2RtzLB.js.map → safelist-BvO8HPnE.js.map} +1 -1
- package/dist/chunks/shared-D-v9lit0.js +188 -0
- package/dist/chunks/shared-D-v9lit0.js.map +1 -0
- package/dist/components/GrAlert/chunks/GrAlert-B9gmpoPC.js +251 -0
- package/dist/components/GrAlert/chunks/GrAlert-B9gmpoPC.js.map +1 -0
- package/dist/components/GrAlert/index.js +2 -2
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-BVLoQ5tZ.js +499 -0
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-BVLoQ5tZ.js.map +1 -0
- package/dist/components/GrAutocomplete/index.js +3 -0
- package/dist/components/GrAvatar/chunks/{GrAvatar-96CpTCq5.js → GrAvatar-CTOUVBF7.js} +1 -1
- package/dist/components/GrAvatar/chunks/GrAvatar-CTOUVBF7.js.map +1 -0
- package/dist/components/GrAvatar/index.js +1 -1
- package/dist/components/GrBadge/chunks/{GrBadge-B77XlaqE.js → GrBadge-D1jefy4S.js} +12 -8
- package/dist/components/GrBadge/chunks/GrBadge-D1jefy4S.js.map +1 -0
- package/dist/components/GrBadge/index.js +2 -2
- package/dist/components/GrBadgeWrap/chunks/{GrBadgeWrap-CcktMOdg.js → GrBadgeWrap-DWDxHdiQ.js} +1 -1
- package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-DWDxHdiQ.js.map +1 -0
- package/dist/components/GrBadgeWrap/index.js +1 -1
- package/dist/components/GrBottomNav/chunks/{GrBottomNav-usuMgG8_.js → GrBottomNav-vSE4LXmk.js} +2 -2
- package/dist/components/GrBottomNav/chunks/GrBottomNav-vSE4LXmk.js.map +1 -0
- package/dist/components/GrBottomNav/index.js +1 -1
- package/dist/components/GrButton/chunks/{GrButton-BlffdeJr.js → GrButton-DCi4CUpo.js} +18 -14
- package/dist/components/GrButton/chunks/GrButton-DCi4CUpo.js.map +1 -0
- package/dist/components/GrButton/index.js +4 -4
- package/dist/components/GrCard/chunks/{GrCard-DUNMkuHW.js → GrCard-CxCjyHMb.js} +1 -1
- package/dist/components/GrCard/chunks/GrCard-CxCjyHMb.js.map +1 -0
- package/dist/components/GrCard/index.js +1 -1
- package/dist/components/GrCheckbox/chunks/{GrCheckbox-DJhzZZdO.js → GrCheckbox-BbIDRXtl.js} +5 -5
- package/dist/components/GrCheckbox/chunks/GrCheckbox-BbIDRXtl.js.map +1 -0
- package/dist/components/GrCheckbox/index.js +1 -1
- package/dist/components/GrCollapse/chunks/{GrCollapse-CPPLh_xU.js → GrCollapse-DApXztn8.js} +6 -6
- package/dist/components/GrCollapse/chunks/GrCollapse-DApXztn8.js.map +1 -0
- package/dist/components/GrCollapse/index.js +2 -2
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-6OyvNbek.js +410 -0
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-6OyvNbek.js.map +1 -0
- package/dist/components/GrCommandPalette/index.js +3 -0
- package/dist/components/GrConfigProvider/chunks/GrConfigProvider-BtyeBGm8.js +57 -0
- package/dist/components/GrConfigProvider/chunks/GrConfigProvider-BtyeBGm8.js.map +1 -0
- package/dist/components/GrConfigProvider/index.js +4 -0
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-D8CkOtqv.js → GrConfirmDialog-e4120mWw.js} +5 -5
- package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-e4120mWw.js.map +1 -0
- package/dist/components/GrConfirmDialog/index.js +2 -2
- package/dist/components/GrDataTable/chunks/{GrDataTable-Ds5OeGat.js → GrDataTable-C1CzxV0p.js} +127 -25
- package/dist/components/GrDataTable/chunks/GrDataTable-C1CzxV0p.js.map +1 -0
- package/dist/components/GrDataTable/index.js +2 -2
- package/dist/components/GrDialog/chunks/{GrDialog-Ctucxju9.js → GrDialog-BPMvDHOz.js} +5 -5
- package/dist/components/GrDialog/chunks/GrDialog-BPMvDHOz.js.map +1 -0
- package/dist/components/GrDialog/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-CU1Fbonc.js → GrDialogService-CYXCTGm3.js} +36 -11
- package/dist/components/GrDialogService/chunks/GrDialogService-CYXCTGm3.js.map +1 -0
- package/dist/components/GrDialogService/index.js +2 -2
- package/dist/components/GrDivider/chunks/{GrDivider-5bcE-BB4.js → GrDivider-BdENZ1w0.js} +5 -5
- package/dist/components/GrDivider/chunks/GrDivider-BdENZ1w0.js.map +1 -0
- package/dist/components/GrDivider/index.js +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-SLGKCY2A.js → GrDrawer-KevdZ5BB.js} +5 -5
- package/dist/components/GrDrawer/chunks/GrDrawer-KevdZ5BB.js.map +1 -0
- package/dist/components/GrDrawer/index.js +2 -2
- package/dist/components/GrDropdown/chunks/{GrDropdown-BnDMYP8v.js → GrDropdown-BKoy8hm-.js} +1 -1
- package/dist/components/GrDropdown/chunks/{GrDropdown-BnDMYP8v.js.map → GrDropdown-BKoy8hm-.js.map} +1 -1
- package/dist/components/GrDropdown/index.js +2 -2
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu--g9Vofcq.js → GrDropdownMenu-zCatXzmt.js} +11 -11
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-zCatXzmt.js.map +1 -0
- package/dist/components/GrDropdownMenu/index.js +1 -1
- package/dist/components/GrEmptyState/chunks/{GrEmptyState-CA3BiTV7.js → GrEmptyState-CNb0BzjG.js} +3 -3
- package/dist/components/GrEmptyState/chunks/GrEmptyState-CNb0BzjG.js.map +1 -0
- package/dist/components/GrEmptyState/index.js +1 -1
- package/dist/components/GrFileUpload/chunks/{GrFileUpload-DvWPpiJS.js → GrFileUpload-CnLgUGDF.js} +12 -12
- package/dist/components/GrFileUpload/chunks/GrFileUpload-CnLgUGDF.js.map +1 -0
- package/dist/components/GrFileUpload/index.js +1 -1
- package/dist/components/GrForm/chunks/GrForm-CdaFjcyl.js +234 -0
- package/dist/components/GrForm/chunks/GrForm-CdaFjcyl.js.map +1 -0
- package/dist/components/GrForm/index.js +4 -0
- package/dist/components/GrFormField/chunks/{GrFormField-DnfMHx_U.js → GrFormField-CXCDBd99.js} +41 -14
- package/dist/components/GrFormField/chunks/GrFormField-CXCDBd99.js.map +1 -0
- package/dist/components/GrFormField/index.js +1 -1
- package/dist/components/GrFormFile/chunks/{GrFormFile-CXcqqdvV.js → GrFormFile-CqacSbUl.js} +9 -9
- package/dist/components/GrFormFile/chunks/GrFormFile-CqacSbUl.js.map +1 -0
- package/dist/components/GrFormFile/index.js +1 -1
- package/dist/components/GrFormSection/chunks/{GrFormSection-Ce21jcgo.js → GrFormSection-C3w9DQJf.js} +1 -1
- package/dist/components/GrFormSection/chunks/GrFormSection-C3w9DQJf.js.map +1 -0
- package/dist/components/GrFormSection/index.js +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-Dla9IBkd.js → GrImageViewer-DDLJkvIU.js} +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-Dla9IBkd.js.map → GrImageViewer-DDLJkvIU.js.map} +1 -1
- package/dist/components/GrImageViewer/index.js +1 -1
- package/dist/components/GrInput/chunks/{GrInput-B8dd3JaA.js → GrInput-C1VBL0dg.js} +18 -15
- package/dist/components/GrInput/chunks/GrInput-C1VBL0dg.js.map +1 -0
- package/dist/components/GrInput/index.js +2 -2
- package/dist/components/GrInputTag/chunks/{GrInputTag-zMgFqp2g.js → GrInputTag-BMk9jm42.js} +6 -6
- package/dist/components/GrInputTag/chunks/GrInputTag-BMk9jm42.js.map +1 -0
- package/dist/components/GrInputTag/index.js +2 -2
- package/dist/components/GrKbd/chunks/{GrKbd-BEoYxWhE.js → GrKbd-DTspwtoN.js} +1 -1
- package/dist/components/GrKbd/chunks/GrKbd-DTspwtoN.js.map +1 -0
- package/dist/components/GrKbd/index.js +1 -1
- package/dist/components/GrLink/chunks/{GrLink-CYUe-Q8k.js → GrLink-BP0YrnL9.js} +1 -1
- package/dist/components/GrLink/chunks/{GrLink-CYUe-Q8k.js.map → GrLink-BP0YrnL9.js.map} +1 -1
- package/dist/components/GrLink/index.js +2 -2
- package/dist/components/GrList/chunks/{GrList-Dx772Ns4.js → GrList-5RA-tZzE.js} +3 -3
- package/dist/components/GrList/chunks/GrList-5RA-tZzE.js.map +1 -0
- package/dist/components/GrList/index.js +1 -1
- package/dist/components/GrLoading/chunks/{GrLoading--X_7rdbb.js → GrLoading-fRDtVfi7.js} +3 -3
- package/dist/components/GrLoading/chunks/GrLoading-fRDtVfi7.js.map +1 -0
- package/dist/components/GrLoading/index.js +2 -2
- package/dist/components/GrModal/chunks/{GrModal-C0GZrHN3.js → GrModal-BTu1-5eY.js} +1 -1
- package/dist/components/GrModal/chunks/{GrModal-C0GZrHN3.js.map → GrModal-BTu1-5eY.js.map} +1 -1
- package/dist/components/GrModal/index.js +3 -3
- package/dist/components/GrNavbar/chunks/{GrNavbar-DW3MiIIf.js → GrNavbar-keKT_mIM.js} +3 -3
- package/dist/components/GrNavbar/chunks/GrNavbar-keKT_mIM.js.map +1 -0
- package/dist/components/GrNavbar/index.js +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-DxCEvL7V.js → GrNumberInput-l9ZOHG9D.js} +19 -17
- package/dist/components/GrNumberInput/chunks/GrNumberInput-l9ZOHG9D.js.map +1 -0
- package/dist/components/GrNumberInput/index.js +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-mlyfLjQY.js → GrPagination-C5Y69CuK.js} +59 -16
- package/dist/components/GrPagination/chunks/GrPagination-C5Y69CuK.js.map +1 -0
- package/dist/components/GrPagination/index.js +2 -2
- package/dist/components/GrProgressBar/chunks/{GrProgressBar-CC3z-Zqp.js → GrProgressBar-Dqg4Fj9S.js} +2 -2
- package/dist/components/GrProgressBar/chunks/GrProgressBar-Dqg4Fj9S.js.map +1 -0
- package/dist/components/GrProgressBar/index.js +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-C-_BMNuY.js → GrPromptDialog-DNUjRVB9.js} +7 -7
- package/dist/components/GrPromptDialog/chunks/GrPromptDialog-DNUjRVB9.js.map +1 -0
- package/dist/components/GrPromptDialog/index.js +2 -2
- package/dist/components/GrRadio/chunks/{GrRadio-Dr-pvs_9.js → GrRadio-DlhxPGqf.js} +5 -7
- package/dist/components/GrRadio/chunks/GrRadio-DlhxPGqf.js.map +1 -0
- package/dist/components/GrRadio/index.js +2 -2
- package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-nOdvfV_g.js → GrRadioGroup-BXt9iw4q.js} +7 -5
- package/dist/components/GrRadioGroup/chunks/GrRadioGroup-BXt9iw4q.js.map +1 -0
- package/dist/components/GrRadioGroup/index.js +2 -2
- package/dist/components/GrRating/chunks/GrRating-Co-3Rk4h.js +241 -0
- package/dist/components/GrRating/chunks/GrRating-Co-3Rk4h.js.map +1 -0
- package/dist/components/GrRating/index.js +3 -0
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-BWJNe2gr.js → GrResponseErrorBanner-2CvM7Z08.js} +2 -2
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-BWJNe2gr.js.map → GrResponseErrorBanner-2CvM7Z08.js.map} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-Cdu9ekK9.js → GrResponseErrorBanner-C8NGx8pE.js} +3 -3
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-Cdu9ekK9.js.map → GrResponseErrorBanner-C8NGx8pE.js.map} +1 -1
- package/dist/components/GrResponseErrorBanner/index.js +2 -2
- package/dist/components/GrSegmented/chunks/{GrSegmented-C1y0UXpQ.js → GrSegmented-CTd3n4PA.js} +14 -11
- package/dist/components/GrSegmented/chunks/GrSegmented-CTd3n4PA.js.map +1 -0
- package/dist/components/GrSegmented/index.js +2 -2
- package/dist/components/GrSelect/chunks/{GrSelect-BVCmitqa.js → GrSelect-Dn3FgJ3Z.js} +161 -75
- package/dist/components/GrSelect/chunks/GrSelect-Dn3FgJ3Z.js.map +1 -0
- package/dist/components/GrSelect/index.js +2 -2
- package/dist/components/GrSidebar/chunks/{GrSidebar-DkWozzwA.js → GrSidebar-cvnzBfj3.js} +7 -7
- package/dist/components/GrSidebar/chunks/GrSidebar-cvnzBfj3.js.map +1 -0
- package/dist/components/GrSidebar/index.js +2 -2
- package/dist/components/GrSkeleton/chunks/{GrSkeleton-DsN-_CDZ.js → GrSkeleton-BucA5F4J.js} +1 -1
- package/dist/components/GrSkeleton/chunks/GrSkeleton-BucA5F4J.js.map +1 -0
- package/dist/components/GrSkeleton/index.js +2 -2
- package/dist/components/GrSlider/chunks/GrSlider-CzzJ3bD-.js +286 -0
- package/dist/components/GrSlider/chunks/GrSlider-CzzJ3bD-.js.map +1 -0
- package/dist/components/GrSlider/index.js +3 -0
- package/dist/components/GrStatistic/chunks/GrStatistic-gHiAwrX5.js +128 -0
- package/dist/components/GrStatistic/chunks/GrStatistic-gHiAwrX5.js.map +1 -0
- package/dist/components/GrStatistic/index.js +3 -0
- package/dist/components/GrSwitch/chunks/{GrSwitch-DAioyccZ.js → GrSwitch-CJdRvS0s.js} +16 -7
- package/dist/components/GrSwitch/chunks/GrSwitch-CJdRvS0s.js.map +1 -0
- package/dist/components/GrSwitch/index.js +2 -2
- package/dist/components/GrTabPanels/chunks/{GrTabPanels-DbKybwe-.js → GrTabPanels-DeEMgb_y.js} +1 -1
- package/dist/components/GrTabPanels/chunks/{GrTabPanels-DbKybwe-.js.map → GrTabPanels-DeEMgb_y.js.map} +1 -1
- package/dist/components/GrTabPanels/index.js +2 -2
- package/dist/components/GrTable/chunks/{GrTable-Db3hRnKA.js → GrTable-sNvsA8wa.js} +24 -11
- package/dist/components/GrTable/chunks/GrTable-sNvsA8wa.js.map +1 -0
- package/dist/components/GrTable/index.js +2 -2
- package/dist/components/GrTabs/chunks/{GrTabs-DsLwWoRz.js → GrTabs-BqTak0SR.js} +3 -3
- package/dist/components/GrTabs/chunks/GrTabs-BqTak0SR.js.map +1 -0
- package/dist/components/GrTabs/index.js +2 -2
- package/dist/components/GrTextarea/chunks/{GrTextarea-CKukGlNl.js → GrTextarea-DIYBAPpP.js} +2 -2
- package/dist/components/GrTextarea/chunks/GrTextarea-DIYBAPpP.js.map +1 -0
- package/dist/components/GrTextarea/index.js +2 -2
- package/dist/components/GrToaster/chunks/{GrToaster-L2cu6cSR.js → GrToaster-BVHjIHcM.js} +51 -10
- package/dist/components/GrToaster/chunks/GrToaster-BVHjIHcM.js.map +1 -0
- package/dist/components/GrToaster/index.js +1 -1
- package/dist/components/GrTooltip/chunks/{GrTooltip-BeyZ8ssR.js → GrTooltip-BMKH2lIx.js} +3 -3
- package/dist/components/GrTooltip/chunks/GrTooltip-BMKH2lIx.js.map +1 -0
- package/dist/components/GrTooltip/index.js +1 -1
- package/dist/components/GrTree/chunks/{GrTree-B2_yW6g1.js → GrTree-_12ChXRQ.js} +3 -3
- package/dist/components/GrTree/chunks/GrTree-_12ChXRQ.js.map +1 -0
- package/dist/components/GrTree/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-0aM62xyu.js → GrTreeSelect-CdaLG7Xq.js} +10 -10
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-CdaLG7Xq.js.map +1 -0
- package/dist/components/GrTreeSelect/index.js +2 -2
- package/dist/composables/useToast.js +3 -1
- package/dist/composables/useToast.js.map +1 -1
- package/dist/directives/loading.js +1 -1
- package/dist/granular-provider-node.js +6 -6
- package/dist/granular-provider-node.js.map +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/i18n/all.js +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/index.js +111 -93
- package/dist/index.js.map +1 -1
- package/dist/styles/base.css +2 -2
- package/dist/styles/index.css +6 -3
- package/dist/styles/preflight.css +3 -3
- package/dist/styles/themes/dark.css +52 -40
- package/dist/styles/themes/light.css +70 -45
- package/dist/styles/tokens.css +20 -20
- package/dist/types/src/components/GrAlert/GrAlert.vue.d.ts +26 -14
- package/dist/types/src/components/GrAlert/defaults.d.ts +17 -0
- package/dist/types/src/components/GrAlert/grAlertStyles.d.ts +55 -0
- package/dist/types/src/components/GrAlert/index.d.ts +4 -1
- package/dist/types/src/components/GrAutocomplete/GrAutocomplete.vue.d.ts +153 -0
- package/dist/types/src/components/GrAutocomplete/config.d.ts +1 -0
- package/dist/types/src/components/GrAutocomplete/defaults.d.ts +15 -0
- package/dist/types/src/components/GrAutocomplete/grAutocompleteStyles.d.ts +27 -0
- package/dist/types/src/components/GrAutocomplete/index.d.ts +6 -0
- package/dist/types/src/components/GrAutocomplete/safelist.d.ts +1 -0
- package/dist/types/src/components/GrAvatar/GrAvatar.vue.d.ts +1 -1
- package/dist/types/src/components/GrBadge/GrBadge.vue.d.ts +7 -7
- package/dist/types/src/components/GrBadge/defaults.d.ts +15 -0
- package/dist/types/src/components/GrBadge/index.d.ts +1 -0
- package/dist/types/src/components/GrBadgeWrap/GrBadgeWrap.vue.d.ts +1 -1
- package/dist/types/src/components/GrButton/GrButton.vue.d.ts +12 -12
- package/dist/types/src/components/GrButton/defaults.d.ts +25 -0
- package/dist/types/src/components/GrButton/grButtonStyles.d.ts +2 -2
- package/dist/types/src/components/GrButton/index.d.ts +1 -0
- package/dist/types/src/components/GrCheckbox/GrCheckbox.vue.d.ts +4 -4
- package/dist/types/src/components/GrCollapse/GrCollapse.vue.d.ts +2 -2
- package/dist/types/src/components/GrCollapse/GrCollapseItem.vue.d.ts +1 -1
- package/dist/types/src/components/GrCommandPalette/GrCommandPalette.vue.d.ts +128 -0
- package/dist/types/src/components/GrCommandPalette/config.d.ts +1 -0
- package/dist/types/src/components/GrCommandPalette/filtering.d.ts +35 -0
- package/dist/types/src/components/GrCommandPalette/grCommandPaletteStyles.d.ts +20 -0
- package/dist/types/src/components/GrCommandPalette/hotkey.d.ts +23 -0
- package/dist/types/src/components/GrCommandPalette/index.d.ts +6 -0
- package/dist/types/src/components/GrCommandPalette/safelist.d.ts +1 -0
- package/dist/types/src/components/GrConfigProvider/GrConfigProvider.vue.d.ts +59 -0
- package/dist/types/src/components/GrConfigProvider/config.d.ts +1 -0
- package/dist/types/src/components/GrConfigProvider/context.d.ts +131 -0
- package/dist/types/src/components/GrConfigProvider/index.d.ts +5 -0
- package/dist/types/src/components/GrConfirmDialog/GrConfirmDialog.vue.d.ts +2 -2
- package/dist/types/src/components/GrDataTable/GrDataTable.vue.d.ts +20 -0
- package/dist/types/src/components/GrDialog/GrDialog.vue.d.ts +1 -1
- package/dist/types/src/components/GrDialogService/store.d.ts +18 -0
- package/dist/types/src/components/GrDrawer/GrDrawer.vue.d.ts +1 -1
- package/dist/types/src/components/GrDropdown/GrDropdown.vue.d.ts +1 -1
- package/dist/types/src/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +1 -1
- package/dist/types/src/components/GrDropdownMenu/GrDropdownMenuItem.vue.d.ts +1 -1
- package/dist/types/src/components/GrFileUpload/GrFileUpload.vue.d.ts +3 -3
- package/dist/types/src/components/GrForm/GrForm.vue.d.ts +93 -0
- package/dist/types/src/components/GrForm/config.d.ts +1 -0
- package/dist/types/src/components/GrForm/context.d.ts +27 -0
- package/dist/types/src/components/GrForm/index.d.ts +6 -0
- package/dist/types/src/components/GrForm/validation.d.ts +52 -0
- package/dist/types/src/components/GrFormField/GrFormField.vue.d.ts +11 -1
- package/dist/types/src/components/GrFormFile/GrFormFile.vue.d.ts +4 -4
- package/dist/types/src/components/GrImageViewer/GrImageViewer.vue.d.ts +3 -3
- package/dist/types/src/components/GrInput/GrInput.vue.d.ts +12 -12
- package/dist/types/src/components/GrInput/defaults.d.ts +18 -0
- package/dist/types/src/components/GrInput/grInputStyles.d.ts +1 -1
- package/dist/types/src/components/GrInput/index.d.ts +1 -0
- package/dist/types/src/components/GrInputTag/GrInputTag.vue.d.ts +3 -3
- package/dist/types/src/components/GrLink/GrLink.vue.d.ts +3 -3
- package/dist/types/src/components/GrLink/grLinkStyles.d.ts +3 -3
- package/dist/types/src/components/GrModal/grModalStyles.d.ts +1 -1
- package/dist/types/src/components/GrNumberInput/GrNumberInput.vue.d.ts +10 -10
- package/dist/types/src/components/GrNumberInput/defaults.d.ts +14 -0
- package/dist/types/src/components/GrNumberInput/index.d.ts +1 -0
- package/dist/types/src/components/GrPagination/GrPagination.vue.d.ts +18 -0
- package/dist/types/src/components/GrPromptDialog/GrPromptDialog.vue.d.ts +4 -4
- package/dist/types/src/components/GrRadio/GrRadio.vue.d.ts +5 -5
- package/dist/types/src/components/GrRadio/defaults.d.ts +14 -0
- package/dist/types/src/components/GrRadio/grRadioStyles.d.ts +3 -3
- package/dist/types/src/components/GrRadio/index.d.ts +1 -0
- package/dist/types/src/components/GrRadioGroup/GrRadioGroup.vue.d.ts +4 -4
- package/dist/types/src/components/GrRadioGroup/defaults.d.ts +14 -0
- package/dist/types/src/components/GrRadioGroup/index.d.ts +1 -0
- package/dist/types/src/components/GrRating/GrRating.vue.d.ts +124 -0
- package/dist/types/src/components/GrRating/config.d.ts +1 -0
- package/dist/types/src/components/GrRating/defaults.d.ts +14 -0
- package/dist/types/src/components/GrRating/grRatingStyles.d.ts +21 -0
- package/dist/types/src/components/GrRating/index.d.ts +6 -0
- package/dist/types/src/components/GrRating/safelist.d.ts +1 -0
- package/dist/types/src/components/GrResponseErrorBanner/GrUploadErrorBanner.vue.d.ts +1 -1
- package/dist/types/src/components/GrSegmented/GrSegmented.vue.d.ts +7 -7
- package/dist/types/src/components/GrSegmented/defaults.d.ts +15 -0
- package/dist/types/src/components/GrSegmented/grSegmentedStyles.d.ts +1 -1
- package/dist/types/src/components/GrSegmented/index.d.ts +1 -0
- package/dist/types/src/components/GrSelect/GrSelect.vue.d.ts +57 -17
- package/dist/types/src/components/GrSelect/defaults.d.ts +17 -0
- package/dist/types/src/components/GrSelect/grSelectStyles.d.ts +3 -3
- package/dist/types/src/components/GrSelect/index.d.ts +1 -0
- package/dist/types/src/components/GrSidebar/GrSidebarItem.vue.d.ts +2 -2
- package/dist/types/src/components/GrSkeleton/GrSkeleton.vue.d.ts +1 -1
- package/dist/types/src/components/GrSlider/GrSlider.vue.d.ts +88 -0
- package/dist/types/src/components/GrSlider/config.d.ts +1 -0
- package/dist/types/src/components/GrSlider/defaults.d.ts +14 -0
- package/dist/types/src/components/GrSlider/grSliderStyles.d.ts +36 -0
- package/dist/types/src/components/GrSlider/index.d.ts +6 -0
- package/dist/types/src/components/GrSlider/safelist.d.ts +1 -0
- package/dist/types/src/components/GrStatistic/GrStatistic.vue.d.ts +116 -0
- package/dist/types/src/components/GrStatistic/config.d.ts +1 -0
- package/dist/types/src/components/GrStatistic/formatStatisticValue.d.ts +16 -0
- package/dist/types/src/components/GrStatistic/grStatisticStyles.d.ts +24 -0
- package/dist/types/src/components/GrStatistic/index.d.ts +6 -0
- package/dist/types/src/components/GrStatistic/safelist.d.ts +1 -0
- package/dist/types/src/components/GrSwitch/GrSwitch.vue.d.ts +4 -4
- package/dist/types/src/components/GrSwitch/defaults.d.ts +14 -0
- package/dist/types/src/components/GrSwitch/grSwitchStyles.d.ts +2 -2
- package/dist/types/src/components/GrSwitch/index.d.ts +1 -0
- package/dist/types/src/components/GrTable/GrTable.vue.d.ts +16 -0
- package/dist/types/src/components/GrTextarea/GrTextarea.vue.d.ts +5 -5
- package/dist/types/src/components/GrToaster/GrToaster.vue.d.ts +19 -2
- package/dist/types/src/components/GrToaster/index.d.ts +1 -1
- package/dist/types/src/components/GrTooltip/GrTooltip.vue.d.ts +1 -1
- package/dist/types/src/components/GrTreeSelect/GrTreeSelect.vue.d.ts +1 -1
- package/dist/types/src/components/GrTreeSelect/grTreeSelectStyles.d.ts +1 -1
- package/dist/types/src/composables/useToast.d.ts +24 -0
- package/dist/types/src/granular-provider/shared.d.ts +7 -0
- package/dist/types/src/i18n/messages/en.d.ts +74 -0
- package/dist/types/src/i18n/messages/es.d.ts +74 -0
- package/dist/types/src/i18n/messages/ru.d.ts +74 -0
- package/dist/types/src/index.d.ts +7 -0
- package/dist/web-types.json +1308 -271
- package/package.json +29 -1
- package/dist/chunks/config-1uSYl98Y2.js.map +0 -1
- package/dist/chunks/config-8Pcutu1j2.js.map +0 -1
- package/dist/chunks/config-B7IIWGt_2.js.map +0 -1
- package/dist/chunks/config-BIRO0vDJ.js.map +0 -1
- package/dist/chunks/config-BLHqZhy-2.js.map +0 -1
- package/dist/chunks/config-BOlr7qw7.js.map +0 -1
- package/dist/chunks/config-BUHuGBik.js.map +0 -1
- package/dist/chunks/config-BX1JTrkV2.js.map +0 -1
- package/dist/chunks/config-BXtAmU3s2.js.map +0 -1
- package/dist/chunks/config-C7NpDrfO.js.map +0 -1
- package/dist/chunks/config-CGHLoBlH.js.map +0 -1
- package/dist/chunks/config-ClhKPJvg.js.map +0 -1
- package/dist/chunks/config-Cpi9Mh2J.js.map +0 -1
- package/dist/chunks/config-CrG1XiE72.js.map +0 -1
- package/dist/chunks/config-DUcu1D682.js.map +0 -1
- package/dist/chunks/config-D_R56_gT.js.map +0 -1
- package/dist/chunks/config-DezDih8_.js.map +0 -1
- package/dist/chunks/config-JpHA4Xx02.js.map +0 -1
- package/dist/chunks/config-QaIc_gRa.js.map +0 -1
- package/dist/chunks/config-U4m9PSql.js.map +0 -1
- package/dist/chunks/config-eXB87DOi2.js.map +0 -1
- package/dist/chunks/config-eYLZYOUS2.js.map +0 -1
- package/dist/chunks/config-ipvU0lzP2.js.map +0 -1
- package/dist/chunks/config-xR4XVmcy.js.map +0 -1
- package/dist/chunks/en-CfkDa4GX.js.map +0 -1
- package/dist/chunks/es-FkgECuCm.js.map +0 -1
- package/dist/chunks/grBadgeStyles-DrycBuKT.js.map +0 -1
- package/dist/chunks/grButtonStyles-C7Yga_Q1.js.map +0 -1
- package/dist/chunks/grModalStyles-DWd3i05Q.js.map +0 -1
- package/dist/chunks/grSelectStyles-CLK5hRd3.js.map +0 -1
- package/dist/chunks/grStyle-Blc9otAx.js.map +0 -1
- package/dist/chunks/ru-Bl8CxpEa.js.map +0 -1
- package/dist/chunks/shared-B6Ay2081.js +0 -174
- package/dist/chunks/shared-B6Ay2081.js.map +0 -1
- package/dist/components/GrAlert/chunks/GrAlert-DPJSU_fG.js +0 -163
- package/dist/components/GrAlert/chunks/GrAlert-DPJSU_fG.js.map +0 -1
- package/dist/components/GrAvatar/chunks/GrAvatar-96CpTCq5.js.map +0 -1
- package/dist/components/GrBadge/chunks/GrBadge-B77XlaqE.js.map +0 -1
- package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-CcktMOdg.js.map +0 -1
- package/dist/components/GrBottomNav/chunks/GrBottomNav-usuMgG8_.js.map +0 -1
- package/dist/components/GrButton/chunks/GrButton-BlffdeJr.js.map +0 -1
- package/dist/components/GrCard/chunks/GrCard-DUNMkuHW.js.map +0 -1
- package/dist/components/GrCheckbox/chunks/GrCheckbox-DJhzZZdO.js.map +0 -1
- package/dist/components/GrCollapse/chunks/GrCollapse-CPPLh_xU.js.map +0 -1
- package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-D8CkOtqv.js.map +0 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-Ds5OeGat.js.map +0 -1
- package/dist/components/GrDialog/chunks/GrDialog-Ctucxju9.js.map +0 -1
- package/dist/components/GrDialogService/chunks/GrDialogService-CU1Fbonc.js.map +0 -1
- package/dist/components/GrDivider/chunks/GrDivider-5bcE-BB4.js.map +0 -1
- package/dist/components/GrDrawer/chunks/GrDrawer-SLGKCY2A.js.map +0 -1
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu--g9Vofcq.js.map +0 -1
- package/dist/components/GrEmptyState/chunks/GrEmptyState-CA3BiTV7.js.map +0 -1
- package/dist/components/GrFileUpload/chunks/GrFileUpload-DvWPpiJS.js.map +0 -1
- package/dist/components/GrFormField/chunks/GrFormField-DnfMHx_U.js.map +0 -1
- package/dist/components/GrFormFile/chunks/GrFormFile-CXcqqdvV.js.map +0 -1
- package/dist/components/GrFormSection/chunks/GrFormSection-Ce21jcgo.js.map +0 -1
- package/dist/components/GrInput/chunks/GrInput-B8dd3JaA.js.map +0 -1
- package/dist/components/GrInputTag/chunks/GrInputTag-zMgFqp2g.js.map +0 -1
- package/dist/components/GrKbd/chunks/GrKbd-BEoYxWhE.js.map +0 -1
- package/dist/components/GrList/chunks/GrList-Dx772Ns4.js.map +0 -1
- package/dist/components/GrLoading/chunks/GrLoading--X_7rdbb.js.map +0 -1
- package/dist/components/GrNavbar/chunks/GrNavbar-DW3MiIIf.js.map +0 -1
- package/dist/components/GrNumberInput/chunks/GrNumberInput-DxCEvL7V.js.map +0 -1
- package/dist/components/GrPagination/chunks/GrPagination-mlyfLjQY.js.map +0 -1
- package/dist/components/GrProgressBar/chunks/GrProgressBar-CC3z-Zqp.js.map +0 -1
- package/dist/components/GrPromptDialog/chunks/GrPromptDialog-C-_BMNuY.js.map +0 -1
- package/dist/components/GrRadio/chunks/GrRadio-Dr-pvs_9.js.map +0 -1
- package/dist/components/GrRadioGroup/chunks/GrRadioGroup-nOdvfV_g.js.map +0 -1
- package/dist/components/GrSegmented/chunks/GrSegmented-C1y0UXpQ.js.map +0 -1
- package/dist/components/GrSelect/chunks/GrSelect-BVCmitqa.js.map +0 -1
- package/dist/components/GrSidebar/chunks/GrSidebar-DkWozzwA.js.map +0 -1
- package/dist/components/GrSkeleton/chunks/GrSkeleton-DsN-_CDZ.js.map +0 -1
- package/dist/components/GrSwitch/chunks/GrSwitch-DAioyccZ.js.map +0 -1
- package/dist/components/GrTable/chunks/GrTable-Db3hRnKA.js.map +0 -1
- package/dist/components/GrTabs/chunks/GrTabs-DsLwWoRz.js.map +0 -1
- package/dist/components/GrTextarea/chunks/GrTextarea-CKukGlNl.js.map +0 -1
- package/dist/components/GrToaster/chunks/GrToaster-L2cu6cSR.js.map +0 -1
- package/dist/components/GrTooltip/chunks/GrTooltip-BeyZ8ssR.js.map +0 -1
- package/dist/components/GrTree/chunks/GrTree-B2_yW6g1.js.map +0 -1
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-0aM62xyu.js.map +0 -1
- package/dist/chunks/{config-8Pcutu1j2.js → config-8Pcutu1j.js} +0 -0
- package/dist/chunks/{config-BLHqZhy-2.js → config-BLHqZhy-.js} +0 -0
- package/dist/chunks/{config-BUHuGBik.js → config-BUHuGBik2.js} +0 -0
- package/dist/chunks/{config-BX1JTrkV2.js → config-BX1JTrkV.js} +0 -0
- package/dist/chunks/{config-BXtAmU3s2.js → config-BXtAmU3s.js} +0 -0
- package/dist/chunks/{config-CGHLoBlH.js → config-CGHLoBlH2.js} +0 -0
- package/dist/chunks/{config-Cpi9Mh2J.js → config-Dqy0rPPO2.js} +1 -1
- /package/dist/chunks/{config-JpHA4Xx02.js → config-JpHA4Xx0.js} +0 -0
- /package/dist/chunks/{config-QaIc_gRa.js → config-QaIc_gRa2.js} +0 -0
- /package/dist/chunks/{config-U4m9PSql.js → config-U4m9PSql2.js} +0 -0
- /package/dist/chunks/{config-eXB87DOi2.js → config-eXB87DOi.js} +0 -0
- /package/dist/chunks/{config-ipvU0lzP2.js → config-ipvU0lzP.js} +0 -0
- /package/dist/chunks/{config-xR4XVmcy.js → config-xR4XVmcy2.js} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrFormFile-CXcqqdvV.js","names":[],"sources":["../../../../src/components/GrFormFile/GrFormFile.vue","../../../../src/components/GrFormFile/GrFormFile.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport IconUpload from '~icons/lucide/upload'\nimport IconX from '~icons/lucide/x'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport { vDropzone } from '../../directives'\nimport { acceptValidator, FileValidationError, runFileValidators } from '../../fileValidation'\nimport type { FileValidationIssue, FileValidator } from '../../fileValidation'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\ndefineOptions({\n name: 'GrFormFile',\n})\n\nexport type GrFormFileError = FileValidationIssue\n\n/**\n * Пропы `GrFormFile` — форма-поле для выбора файлов с встроенной валидацией,\n * drag&drop (через `v-dropzone`) и списком выбранных файлов.\n *\n * Все `*Text`/`placeholder` — i18n-friendly, принимают готовые строки локали.\n */\nexport interface GrFormFileProps {\n modelValue: File | File[] | null\n multiple?: boolean\n disabled?: boolean\n /** W3C `accept` для `<input type=\"file\">` + sugar к `acceptValidator(...)`. */\n accept?: string\n validators?: FileValidator[]\n uploadText?: string\n changeText?: string\n removeText?: string\n clearAllText?: string\n placeholder?: string\n /** Дополнительная (кастомная) валидация на стороне потребителя. */\n validate?: (files: File[]) => GrFormFileError[] | Promise<GrFormFileError[]>\n}\n\nconst props = withDefaults(\n defineProps<GrFormFileProps>(),\n {\n multiple: false,\n disabled: false,\n accept: undefined,\n uploadText: undefined,\n changeText: undefined,\n removeText: undefined,\n clearAllText: undefined,\n placeholder: undefined,\n validators: undefined,\n validate: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: File | File[] | null): void\n (e: 'change', value: File | File[] | null): void\n (e: 'clear'): void\n (e: 'validation', errors: GrFormFileError[]): void\n (e: 'update:errors', errors: GrFormFileError[]): void\n}>()\n\nconst { t } = useGranularityTranslations()\nconst resolvedUploadText = computed(() => props.uploadText ?? t('gr.formFile.upload', 'Upload file'))\nconst resolvedChangeText = computed(() => props.changeText ?? t('gr.formFile.change', 'Change file'))\nconst resolvedRemoveText = computed(() => props.removeText ?? t('gr.formFile.remove', 'Remove'))\nconst resolvedClearAllText = computed(() => props.clearAllText ?? t('gr.formFile.clearAll', 'Clear all'))\nconst resolvedPlaceholder = computed(() => props.placeholder ?? t('gr.formFile.placeholder', 'No files selected'))\n\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst localErrors = ref<GrFormFileError[]>([])\n\nconst files = computed<File[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n return props.modelValue instanceof File ? [props.modelValue] : []\n})\n\nconst hasFiles = computed(() => files.value.length > 0)\n\nfunction clearInputValue() {\n if (!inputRef.value) return\n inputRef.value.value = ''\n}\n\nfunction setErrors(next: GrFormFileError[]) {\n localErrors.value = next\n emit('validation', next)\n emit('update:errors', next)\n}\n\nfunction clearErrors() {\n if (localErrors.value.length === 0) return\n setErrors([])\n}\n\nfunction openDialog() {\n if (props.disabled) return\n inputRef.value?.click()\n}\n\nfunction emitModel(nextFiles: File[]) {\n const value: File | File[] | null = props.multiple\n ? nextFiles\n : (nextFiles[0] ?? null)\n\n emit('update:modelValue', value)\n emit('change', value)\n}\n\nasync function applyFiles(nextFiles: File[]) {\n const isPromiseLike = (value: unknown): value is PromiseLike<any> => {\n return !!value && typeof (value as any).then === 'function'\n }\n\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n const res = runFileValidators(nextFiles, validators, {\n source: 'input',\n multiple: props.multiple,\n })\n\n const { files: picked, issues } = isPromiseLike(res) ? await res : res\n\n if (issues.length > 0) {\n setErrors(issues)\n clearInputValue()\n await nextTick()\n return\n }\n\n clearErrors()\n emitModel(picked)\n clearInputValue()\n await nextTick()\n}\n\nasync function onInputChange(event: Event) {\n if (props.disabled) return\n\n const target = event.target as HTMLInputElement | null\n const nextFiles = target?.files ? Array.prototype.slice.call(target.files) as File[] : []\n\n // Reset value so selecting the same file twice triggers change.\n if (target) target.value = ''\n await nextTick()\n\n await applyFiles(nextFiles)\n}\n\nfunction clearAll() {\n if (props.disabled) return\n clearErrors()\n clearInputValue()\n emit('clear')\n\n emit('update:modelValue', props.multiple ? [] : null)\n emit('change', props.multiple ? [] : null)\n}\n\nfunction removeAt(index: number) {\n if (props.disabled) return\n if (!props.multiple) {\n clearAll()\n return\n }\n\n const next = files.value.slice()\n next.splice(index, 1)\n\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction issueMessage(issue: GrFormFileError): string {\n if (issue.fileName) return `${issue.fileName}: ${issue.message}`\n return issue.message\n}\n\nconst dropzone = computed(() => {\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n return {\n enabled: !props.disabled,\n multiple: props.multiple,\n validators,\n onFiles: async (dropped: File[]) => {\n // `v-dropzone` уже выполнил валидаторы и нормализацию по `multiple`.\n clearErrors()\n emitModel(dropped)\n clearInputValue()\n await nextTick()\n },\n onError: (error: unknown) => {\n if (error instanceof FileValidationError) {\n setErrors(error.issues)\n }\n else {\n setErrors([\n {\n code: 'accept',\n message: t('gr.formFile.validateError', 'Failed to validate dropped files'),\n },\n ])\n }\n },\n }\n})\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!inputRef.value) return\n\n if (!value || (Array.isArray(value) && value.length === 0)) {\n // Позволяет выбрать тот же файл снова после внешнего сброса.\n inputRef.value.value = ''\n }\n },\n)\n</script>\n\n<template>\n <div\n v-dropzone=\"dropzone\"\n data-gr-form-file\n class=\"rounded-[var(--gr-radius-md)]\"\n :class=\"disabled ? 'opacity-60 cursor-not-allowed' : ''\"\n >\n <input\n ref=\"inputRef\"\n data-gr-form-file-input\n type=\"file\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n class=\"absolute opacity-0 w-px h-px pointer-events-none\"\n :multiple=\"multiple\"\n :accept=\"accept\"\n :disabled=\"disabled\"\n @change=\"onInputChange\"\n >\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <GrButton\n variant=\"secondary\"\n size=\"sm\"\n data-gr-form-file-upload-btn\n :disabled=\"disabled\"\n @click.prevent=\"openDialog\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconUpload />\n </GrIcon>\n <span class=\"ml-2\">{{ hasFiles ? resolvedChangeText : resolvedUploadText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"hasFiles && !multiple\"\n variant=\"secondary\"\n size=\"sm\"\n data-gr-form-file-clear-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ resolvedRemoveText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"multiple && hasFiles\"\n variant=\"secondary\"\n size=\"sm\"\n data-gr-form-file-clear-all-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ resolvedClearAllText }}</span>\n </GrButton>\n\n <span\n v-if=\"!multiple && hasFiles\"\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n data-gr-form-file-single-name\n :title=\"files[0]?.name\"\n >\n {{ files[0]?.name }}\n </span>\n\n <span\n v-if=\"!hasFiles\"\n class=\"text-sm text-[var(--muted-fg)]\"\n data-gr-form-file-placeholder\n >\n {{ resolvedPlaceholder }}\n </span>\n </div>\n\n <div v-if=\"multiple && hasFiles\" class=\"flex flex-col gap-2\">\n <div\n v-for=\"(file, index) in files\"\n :key=\"`${file.name}-${file.size}-${index}`\"\n class=\"flex items-center gap-2\"\n data-gr-form-file-item\n >\n <span\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n :title=\"file.name\"\n data-gr-form-file-item-name\n >\n {{ file.name }}\n </span>\n\n <button\n type=\"button\"\n class=\"text-xs text-[var(--muted-fg)] hover:text-[var(--fg)]\"\n data-gr-form-file-item-remove\n :disabled=\"disabled\"\n @click.prevent=\"removeAt(index)\"\n >\n {{ removeText }}\n </button>\n </div>\n </div>\n\n <slot name=\"errors\" :errors=\"localErrors\">\n <div v-if=\"localErrors.length\" class=\"text-sm text-[var(--gr-danger)]\" data-gr-form-file-errors>\n <div v-for=\"(e, i) in localErrors\" :key=\"i\" data-gr-form-file-error>\n {{ issueMessage(e) }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style scoped>\n[data-gr-form-file].gr-dropzone--over {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, watch } from 'vue'\n\nimport IconUpload from '~icons/lucide/upload'\nimport IconX from '~icons/lucide/x'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport { vDropzone } from '../../directives'\nimport { acceptValidator, FileValidationError, runFileValidators } from '../../fileValidation'\nimport type { FileValidationIssue, FileValidator } from '../../fileValidation'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\ndefineOptions({\n name: 'GrFormFile',\n})\n\nexport type GrFormFileError = FileValidationIssue\n\n/**\n * Пропы `GrFormFile` — форма-поле для выбора файлов с встроенной валидацией,\n * drag&drop (через `v-dropzone`) и списком выбранных файлов.\n *\n * Все `*Text`/`placeholder` — i18n-friendly, принимают готовые строки локали.\n */\nexport interface GrFormFileProps {\n modelValue: File | File[] | null\n multiple?: boolean\n disabled?: boolean\n /** W3C `accept` для `<input type=\"file\">` + sugar к `acceptValidator(...)`. */\n accept?: string\n validators?: FileValidator[]\n uploadText?: string\n changeText?: string\n removeText?: string\n clearAllText?: string\n placeholder?: string\n /** Дополнительная (кастомная) валидация на стороне потребителя. */\n validate?: (files: File[]) => GrFormFileError[] | Promise<GrFormFileError[]>\n}\n\nconst props = withDefaults(\n defineProps<GrFormFileProps>(),\n {\n multiple: false,\n disabled: false,\n accept: undefined,\n uploadText: undefined,\n changeText: undefined,\n removeText: undefined,\n clearAllText: undefined,\n placeholder: undefined,\n validators: undefined,\n validate: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: File | File[] | null): void\n (e: 'change', value: File | File[] | null): void\n (e: 'clear'): void\n (e: 'validation', errors: GrFormFileError[]): void\n (e: 'update:errors', errors: GrFormFileError[]): void\n}>()\n\nconst { t } = useGranularityTranslations()\nconst resolvedUploadText = computed(() => props.uploadText ?? t('gr.formFile.upload', 'Upload file'))\nconst resolvedChangeText = computed(() => props.changeText ?? t('gr.formFile.change', 'Change file'))\nconst resolvedRemoveText = computed(() => props.removeText ?? t('gr.formFile.remove', 'Remove'))\nconst resolvedClearAllText = computed(() => props.clearAllText ?? t('gr.formFile.clearAll', 'Clear all'))\nconst resolvedPlaceholder = computed(() => props.placeholder ?? t('gr.formFile.placeholder', 'No files selected'))\n\nconst inputRef = ref<HTMLInputElement | null>(null)\nconst localErrors = ref<GrFormFileError[]>([])\n\nconst files = computed<File[]>(() => {\n if (props.multiple) {\n return Array.isArray(props.modelValue) ? props.modelValue : []\n }\n\n return props.modelValue instanceof File ? [props.modelValue] : []\n})\n\nconst hasFiles = computed(() => files.value.length > 0)\n\nfunction clearInputValue() {\n if (!inputRef.value) return\n inputRef.value.value = ''\n}\n\nfunction setErrors(next: GrFormFileError[]) {\n localErrors.value = next\n emit('validation', next)\n emit('update:errors', next)\n}\n\nfunction clearErrors() {\n if (localErrors.value.length === 0) return\n setErrors([])\n}\n\nfunction openDialog() {\n if (props.disabled) return\n inputRef.value?.click()\n}\n\nfunction emitModel(nextFiles: File[]) {\n const value: File | File[] | null = props.multiple\n ? nextFiles\n : (nextFiles[0] ?? null)\n\n emit('update:modelValue', value)\n emit('change', value)\n}\n\nasync function applyFiles(nextFiles: File[]) {\n const isPromiseLike = (value: unknown): value is PromiseLike<any> => {\n return !!value && typeof (value as any).then === 'function'\n }\n\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n const res = runFileValidators(nextFiles, validators, {\n source: 'input',\n multiple: props.multiple,\n })\n\n const { files: picked, issues } = isPromiseLike(res) ? await res : res\n\n if (issues.length > 0) {\n setErrors(issues)\n clearInputValue()\n await nextTick()\n return\n }\n\n clearErrors()\n emitModel(picked)\n clearInputValue()\n await nextTick()\n}\n\nasync function onInputChange(event: Event) {\n if (props.disabled) return\n\n const target = event.target as HTMLInputElement | null\n const nextFiles = target?.files ? Array.prototype.slice.call(target.files) as File[] : []\n\n // Reset value so selecting the same file twice triggers change.\n if (target) target.value = ''\n await nextTick()\n\n await applyFiles(nextFiles)\n}\n\nfunction clearAll() {\n if (props.disabled) return\n clearErrors()\n clearInputValue()\n emit('clear')\n\n emit('update:modelValue', props.multiple ? [] : null)\n emit('change', props.multiple ? [] : null)\n}\n\nfunction removeAt(index: number) {\n if (props.disabled) return\n if (!props.multiple) {\n clearAll()\n return\n }\n\n const next = files.value.slice()\n next.splice(index, 1)\n\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction issueMessage(issue: GrFormFileError): string {\n if (issue.fileName) return `${issue.fileName}: ${issue.message}`\n return issue.message\n}\n\nconst dropzone = computed(() => {\n const customValidator: FileValidator | undefined = props.validate\n ? async ({ files }) => (await props.validate?.(files)) ?? []\n : undefined\n\n const validators: FileValidator[] = [\n acceptValidator(props.accept),\n ...(props.validators ?? []),\n ...(customValidator ? [customValidator] : []),\n ]\n\n return {\n enabled: !props.disabled,\n multiple: props.multiple,\n validators,\n onFiles: async (dropped: File[]) => {\n // `v-dropzone` уже выполнил валидаторы и нормализацию по `multiple`.\n clearErrors()\n emitModel(dropped)\n clearInputValue()\n await nextTick()\n },\n onError: (error: unknown) => {\n if (error instanceof FileValidationError) {\n setErrors(error.issues)\n }\n else {\n setErrors([\n {\n code: 'accept',\n message: t('gr.formFile.validateError', 'Failed to validate dropped files'),\n },\n ])\n }\n },\n }\n})\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!inputRef.value) return\n\n if (!value || (Array.isArray(value) && value.length === 0)) {\n // Позволяет выбрать тот же файл снова после внешнего сброса.\n inputRef.value.value = ''\n }\n },\n)\n</script>\n\n<template>\n <div\n v-dropzone=\"dropzone\"\n data-gr-form-file\n class=\"rounded-[var(--gr-radius-md)]\"\n :class=\"disabled ? 'opacity-60 cursor-not-allowed' : ''\"\n >\n <input\n ref=\"inputRef\"\n data-gr-form-file-input\n type=\"file\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n class=\"absolute opacity-0 w-px h-px pointer-events-none\"\n :multiple=\"multiple\"\n :accept=\"accept\"\n :disabled=\"disabled\"\n @change=\"onInputChange\"\n >\n\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-3\">\n <GrButton\n variant=\"secondary\"\n size=\"sm\"\n data-gr-form-file-upload-btn\n :disabled=\"disabled\"\n @click.prevent=\"openDialog\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconUpload />\n </GrIcon>\n <span class=\"ml-2\">{{ hasFiles ? resolvedChangeText : resolvedUploadText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"hasFiles && !multiple\"\n variant=\"secondary\"\n size=\"sm\"\n data-gr-form-file-clear-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ resolvedRemoveText }}</span>\n </GrButton>\n\n <GrButton\n v-if=\"multiple && hasFiles\"\n variant=\"secondary\"\n size=\"sm\"\n data-gr-form-file-clear-all-btn\n :disabled=\"disabled\"\n @click.prevent=\"clearAll\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconX />\n </GrIcon>\n <span class=\"ml-2\">{{ resolvedClearAllText }}</span>\n </GrButton>\n\n <span\n v-if=\"!multiple && hasFiles\"\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n data-gr-form-file-single-name\n :title=\"files[0]?.name\"\n >\n {{ files[0]?.name }}\n </span>\n\n <span\n v-if=\"!hasFiles\"\n class=\"text-sm text-[var(--muted-fg)]\"\n data-gr-form-file-placeholder\n >\n {{ resolvedPlaceholder }}\n </span>\n </div>\n\n <div v-if=\"multiple && hasFiles\" class=\"flex flex-col gap-2\">\n <div\n v-for=\"(file, index) in files\"\n :key=\"`${file.name}-${file.size}-${index}`\"\n class=\"flex items-center gap-2\"\n data-gr-form-file-item\n >\n <span\n class=\"text-sm text-[var(--muted-fg)] truncate max-w-[240px]\"\n :title=\"file.name\"\n data-gr-form-file-item-name\n >\n {{ file.name }}\n </span>\n\n <button\n type=\"button\"\n class=\"text-xs text-[var(--muted-fg)] hover:text-[var(--fg)]\"\n data-gr-form-file-item-remove\n :disabled=\"disabled\"\n @click.prevent=\"removeAt(index)\"\n >\n {{ removeText }}\n </button>\n </div>\n </div>\n\n <slot name=\"errors\" :errors=\"localErrors\">\n <div v-if=\"localErrors.length\" class=\"text-sm text-[var(--gr-danger)]\" data-gr-form-file-errors>\n <div v-for=\"(e, i) in localErrors\" :key=\"i\" data-gr-form-file-error>\n {{ issueMessage(e) }}\n </div>\n </div>\n </slot>\n </div>\n </div>\n</template>\n\n<style scoped>\n[data-gr-form-file].gr-dropzone--over {\n outline: 2px solid var(--ring);\n outline-offset: 2px;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCA,MAAM,QAAQ;EAgBd,MAAM,OAAO;EAQb,MAAM,EAAE,MAAM,2BAA2B;EACzC,MAAM,qBAAqB,eAAe,MAAM,cAAc,EAAE,sBAAsB,aAAa,CAAC;EACpG,MAAM,qBAAqB,eAAe,MAAM,cAAc,EAAE,sBAAsB,aAAa,CAAC;EACpG,MAAM,qBAAqB,eAAe,MAAM,cAAc,EAAE,sBAAsB,QAAQ,CAAC;EAC/F,MAAM,uBAAuB,eAAe,MAAM,gBAAgB,EAAE,wBAAwB,WAAW,CAAC;EACxG,MAAM,sBAAsB,eAAe,MAAM,eAAe,EAAE,2BAA2B,mBAAmB,CAAC;EAEjH,MAAM,WAAW,IAA6B,IAAI;EAClD,MAAM,cAAc,IAAuB,CAAC,CAAC;EAE7C,MAAM,QAAQ,eAAuB;GACnC,IAAI,MAAM,UACR,OAAO,MAAM,QAAQ,MAAM,UAAU,IAAI,MAAM,aAAa,CAAC;GAG/D,OAAO,MAAM,sBAAsB,OAAO,CAAC,MAAM,UAAU,IAAI,CAAC;EAClE,CAAC;EAED,MAAM,WAAW,eAAe,MAAM,MAAM,SAAS,CAAC;EAEtD,SAAS,kBAAkB;GACzB,IAAI,CAAC,SAAS,OAAO;GACrB,SAAS,MAAM,QAAQ;EACzB;EAEA,SAAS,UAAU,MAAyB;GAC1C,YAAY,QAAQ;GACpB,KAAK,cAAc,IAAI;GACvB,KAAK,iBAAiB,IAAI;EAC5B;EAEA,SAAS,cAAc;GACrB,IAAI,YAAY,MAAM,WAAW,GAAG;GACpC,UAAU,CAAC,CAAC;EACd;EAEA,SAAS,aAAa;GACpB,IAAI,MAAM,UAAU;GACpB,SAAS,OAAO,MAAM;EACxB;EAEA,SAAS,UAAU,WAAmB;GACpC,MAAM,QAA8B,MAAM,WACtC,YACC,UAAU,MAAM;GAErB,KAAK,qBAAqB,KAAK;GAC/B,KAAK,UAAU,KAAK;EACtB;EAEA,eAAe,WAAW,WAAmB;GAC3C,MAAM,iBAAiB,UAA8C;IACnE,OAAO,CAAC,CAAC,SAAS,OAAQ,MAAc,SAAS;GACnD;GAEA,MAAM,kBAA6C,MAAM,WACrD,OAAO,EAAE,YAAa,MAAM,MAAM,WAAW,KAAK,KAAM,CAAC,IACzD,KAAA;GAQJ,MAAM,MAAM,kBAAkB,WAAW;IALvC,gBAAgB,MAAM,MAAM;IAC5B,GAAI,MAAM,cAAc,CAAC;IACzB,GAAI,kBAAkB,CAAC,eAAe,IAAI,CAAC;GAGJ,GAAY;IACnD,QAAQ;IACR,UAAU,MAAM;GAClB,CAAC;GAED,MAAM,EAAE,OAAO,QAAQ,WAAW,cAAc,GAAG,IAAI,MAAM,MAAM;GAEnE,IAAI,OAAO,SAAS,GAAG;IACrB,UAAU,MAAM;IAChB,gBAAgB;IAChB,MAAM,SAAS;IACf;GACF;GAEA,YAAY;GACZ,UAAU,MAAM;GAChB,gBAAgB;GAChB,MAAM,SAAS;EACjB;EAEA,eAAe,cAAc,OAAc;GACzC,IAAI,MAAM,UAAU;GAEpB,MAAM,SAAS,MAAM;GACrB,MAAM,YAAY,QAAQ,QAAQ,MAAM,UAAU,MAAM,KAAK,OAAO,KAAK,IAAc,CAAC;GAGxF,IAAI,QAAQ,OAAO,QAAQ;GAC3B,MAAM,SAAS;GAEf,MAAM,WAAW,SAAS;EAC5B;EAEA,SAAS,WAAW;GAClB,IAAI,MAAM,UAAU;GACpB,YAAY;GACZ,gBAAgB;GAChB,KAAK,OAAO;GAEZ,KAAK,qBAAqB,MAAM,WAAW,CAAC,IAAI,IAAI;GACpD,KAAK,UAAU,MAAM,WAAW,CAAC,IAAI,IAAI;EAC3C;EAEA,SAAS,SAAS,OAAe;GAC/B,IAAI,MAAM,UAAU;GACpB,IAAI,CAAC,MAAM,UAAU;IACnB,SAAS;IACT;GACF;GAEA,MAAM,OAAO,MAAM,MAAM,MAAM;GAC/B,KAAK,OAAO,OAAO,CAAC;GAEpB,KAAK,qBAAqB,IAAI;GAC9B,KAAK,UAAU,IAAI;EACrB;EAEA,SAAS,aAAa,OAAgC;GACpD,IAAI,MAAM,UAAU,OAAO,GAAG,MAAM,SAAS,IAAI,MAAM;GACvD,OAAO,MAAM;EACf;EAEA,MAAM,WAAW,eAAe;GAC9B,MAAM,kBAA6C,MAAM,WACrD,OAAO,EAAE,YAAa,MAAM,MAAM,WAAW,KAAK,KAAM,CAAC,IACzD,KAAA;GAEJ,MAAM,aAA8B;IAClC,gBAAgB,MAAM,MAAM;IAC5B,GAAI,MAAM,cAAc,CAAC;IACzB,GAAI,kBAAkB,CAAC,eAAe,IAAI,CAAC;GAC7C;GAEA,OAAO;IACL,SAAS,CAAC,MAAM;IAChB,UAAU,MAAM;IAChB;IACA,SAAS,OAAO,YAAoB;KAElC,YAAY;KACZ,UAAU,OAAO;KACjB,gBAAgB;KAChB,MAAM,SAAS;IACjB;IACA,UAAU,UAAmB;KAC3B,IAAI,iBAAiB,qBACnB,UAAU,MAAM,MAAM;UAGtB,UAAU,CACR;MACE,MAAM;MACN,SAAS,EAAE,6BAA6B,kCAAkC;KAC5E,CACF,CAAC;IAEL;GACF;EACF,CAAC;EAED,YACQ,MAAM,aACX,UAAU;GACT,IAAI,CAAC,SAAS,OAAO;GAErB,IAAI,CAAC,SAAU,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW,GAEtD,SAAS,MAAM,QAAQ;EAE3B,CACF;;uCAIE,mBAmHM,OAAA;IAjHJ,qBAAA;IACA,OAAK,eAAA,CAAC,iCACE,QAAA,WAAQ,kCAAA,EAAA,CAAA;OAEhB,mBAWC,SAAA;aAVK;IAAJ,KAAI;IACJ,2BAAA;IACA,MAAK;IACL,UAAS;IACT,eAAY;IACZ,OAAM;IACL,UAAU,QAAA;IACV,QAAQ,QAAA;IACR,UAAU,QAAA;IACV,UAAQ;6BAGX,mBA+FM,OA/FN,YA+FM;IA9FJ,mBA0DM,OA1DN,YA0DM;KAzDJ,YAWW,kBAAA;MAVT,SAAQ;MACR,MAAK;MACL,gCAAA;MACC,UAAU,QAAA;MACV,SAAK,cAAU,YAAU,CAAA,SAAA,CAAA;;6BAIjB,CAFT,YAES,gBAAA;OAFD,MAAK;OAAK,eAAY;;8BACd,CAAd,YAAc,MAAA,cAAA,CAAA,CAAA,CAAA;;UAEhB,mBAAkF,QAAlF,YAAkF,gBAA5D,SAAA,QAAW,mBAAA,QAAqB,mBAAA,KAAkB,GAAA,CAAA,CAAA,CAAA;;;KAIlE,SAAA,SAAQ,CAAK,QAAA,YAAA,UAAA,GADrB,YAYW,kBAAA;;MAVT,SAAQ;MACR,MAAK;MACL,+BAAA;MACC,UAAU,QAAA;MACV,SAAK,cAAU,UAAQ,CAAA,SAAA,CAAA;;6BAIf,CAFT,YAES,gBAAA;OAFD,MAAK;OAAK,eAAY;;8BACnB,CAAT,YAAS,MAAA,SAAA,CAAA,CAAA,CAAA;;UAEX,mBAAkD,QAAlD,YAAkD,gBAA5B,mBAAA,KAAkB,GAAA,CAAA,CAAA,CAAA;;;KAIlC,QAAA,YAAY,SAAA,SAAA,UAAA,GADpB,YAYW,kBAAA;;MAVT,SAAQ;MACR,MAAK;MACL,mCAAA;MACC,UAAU,QAAA;MACV,SAAK,cAAU,UAAQ,CAAA,SAAA,CAAA;;6BAIf,CAFT,YAES,gBAAA;OAFD,MAAK;OAAK,eAAY;;8BACnB,CAAT,YAAS,MAAA,SAAA,CAAA,CAAA,CAAA;;UAEX,mBAAoD,QAApD,YAAoD,gBAA9B,qBAAA,KAAoB,GAAA,CAAA,CAAA,CAAA;;;MAInC,QAAA,YAAY,SAAA,SAAA,UAAA,GADrB,mBAOO,QAAA;;MALL,OAAM;MACN,iCAAA;MACC,OAAO,MAAA,MAAK,EAAA,EAAK;wBAEf,MAAA,MAAK,EAAA,EAAK,IAAI,GAAA,GAAA,UAAA,KAAA,mBAAA,IAAA,IAAA;MAIV,SAAA,SAAA,UAAA,GADT,mBAMO,QANP,YAMO,gBADF,oBAAA,KAAmB,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;;IAIf,QAAA,YAAY,SAAA,SAAA,UAAA,GAAvB,mBAyBM,OAzBN,YAyBM,EAAA,UAAA,IAAA,GAxBJ,mBAuBM,UAAA,MAAA,WAtBoB,MAAA,QAAhB,MAAM,UAAK;yBADrB,mBAuBM,OAAA;MArBH,KAAG,GAAK,KAAK,KAAI,GAAI,KAAK,KAAI,GAAI;MACnC,OAAM;MACN,0BAAA;SAEA,mBAMO,QAAA;MALL,OAAM;MACL,OAAO,KAAK;MACb,+BAAA;wBAEG,KAAK,IAAI,GAAA,GAAA,WAAA,GAGd,mBAQS,UAAA;MAPP,MAAK;MACL,OAAM;MACN,iCAAA;MACC,UAAU,QAAA;MACV,SAAK,eAAA,WAAU,SAAS,KAAK,GAAA,CAAA,SAAA,CAAA;wBAE3B,QAAA,UAAU,GAAA,GAAA,WAAA,CAAA,CAAA;;IAKnB,WAMO,KAAA,QAAA,UAAA,EANc,QAAQ,YAAA,MAAW,SAMjC,CALM,YAAA,MAAY,UAAA,UAAA,GAAvB,mBAIM,OAJN,aAIM,EAAA,UAAA,IAAA,GAHJ,mBAEM,UAAA,MAAA,WAFgB,YAAA,QAAT,GAAG,MAAC;yBAAjB,mBAEM,OAAA;MAF8B,KAAK;MAAG,2BAAA;wBACvC,aAAa,CAAC,CAAA,GAAA,CAAA;;iCA7Gb,SAAA,KAAQ,CAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrFormSection-Ce21jcgo.js","names":[],"sources":["../../../../src/components/GrFormSection/GrFormSection.vue","../../../../src/components/GrFormSection/GrFormSection.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\n/**\n * GrFormSection — секция формы с заголовком, опциональным описанием и контентом.\n *\n * - `section` связан с заголовком через `aria-labelledby` и c описанием\n * (если есть) через `aria-describedby` — для a11y.\n */\nexport interface GrFormSectionProps {\n title: string\n description?: string\n}\n\nwithDefaults(defineProps<GrFormSectionProps>(), {\n description: undefined,\n})\n\nconst titleId = useId()\nconst descriptionId = useId()\n</script>\n\n<template>\n <section\n data-gr-form-section\n class=\"grid gap-4\"\n :aria-labelledby=\"titleId\"\n :aria-describedby=\"description ? descriptionId : undefined\"\n >\n <div>\n <div :id=\"titleId\" class=\"text-[14px] font-700\">\n {{ title }}\n </div>\n <div\n v-if=\"description\"\n :id=\"descriptionId\"\n class=\"mt-1 text-[13px] text-[var(--muted-fg)]\"\n >\n {{ description }}\n </div>\n </div>\n <slot />\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { useId } from 'vue'\n\n/**\n * GrFormSection — секция формы с заголовком, опциональным описанием и контентом.\n *\n * - `section` связан с заголовком через `aria-labelledby` и c описанием\n * (если есть) через `aria-describedby` — для a11y.\n */\nexport interface GrFormSectionProps {\n title: string\n description?: string\n}\n\nwithDefaults(defineProps<GrFormSectionProps>(), {\n description: undefined,\n})\n\nconst titleId = useId()\nconst descriptionId = useId()\n</script>\n\n<template>\n <section\n data-gr-form-section\n class=\"grid gap-4\"\n :aria-labelledby=\"titleId\"\n :aria-describedby=\"description ? descriptionId : undefined\"\n >\n <div>\n <div :id=\"titleId\" class=\"text-[14px] font-700\">\n {{ title }}\n </div>\n <div\n v-if=\"description\"\n :id=\"descriptionId\"\n class=\"mt-1 text-[13px] text-[var(--muted-fg)]\"\n >\n {{ description }}\n </div>\n </div>\n <slot />\n </section>\n</template>\n"],"mappings":";;;;;;;;;;;;;;EAkBA,MAAM,UAAU,MAAM;EACtB,MAAM,gBAAgB,MAAM;;uBAI1B,mBAmBU,WAAA;IAlBR,wBAAA;IACA,OAAM;IACL,mBAAiB,MAAA,OAAA;IACjB,oBAAkB,QAAA,cAAc,MAAA,aAAA,IAAgB,KAAA;OAEjD,mBAWM,OAAA,MAAA,CAVJ,mBAEM,OAAA;IAFA,IAAI,MAAA,OAAA;IAAS,OAAM;sBACpB,QAAA,KAAK,GAAA,GAAA,UAAA,GAGF,QAAA,eAAA,UAAA,GADR,mBAMM,OAAA;;IAJH,IAAI,MAAA,aAAA;IACL,OAAM;sBAEH,QAAA,WAAW,GAAA,GAAA,UAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,GAGlB,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,GAAA,UAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrInput-B8dd3JaA.js","names":["$slots","$attrs"],"sources":["../../../../src/components/GrInput/GrInput.vue","../../../../src/components/GrInput/GrInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {computed, ref, useSlots} from 'vue'\n\nimport {addLen, useAddonMeasurement} from '../../composables/internal/useAddonMeasurement'\nimport {useGrFormFieldContext} from '../GrFormField/context'\nimport {useGranularityTranslations} from '../../internal/granularityI18n'\nimport type {InputHTMLAttributes} from 'vue'\n\nimport IconX from '~icons/lucide/x'\nimport IconEye from '~icons/lucide/eye'\nimport IconEyeOff from '~icons/lucide/eye-off'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nexport type GrInputSize = 'xs' | 'sm' | 'md' | 'lg'\nexport type GrInputTextAlign = 'left' | 'center' | 'right'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string\n type?: 'text' | 'email' | 'password' | 'number' | 'search'\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Только для чтения: значение видно и выделяемо, но не редактируется. */\n readonly?: boolean\n invalid?: boolean\n state?: 'default' | 'success' | 'warning' | 'danger'\n name?: string\n id?: string\n size?: GrInputSize\n\n /** Показывать кнопку очистки, когда есть значение (и не disabled/readonly). */\n clearable?: boolean\n /** i18n aria-label кнопки очистки. */\n clearLabel?: string\n /** Ограничение длины + основа для счётчика символов. */\n maxlength?: number\n /** Показывать счётчик символов (`len` или `len/maxlength`). */\n showCount?: boolean\n /** Кнопка показать/скрыть пароль (только при `type=\"password\"`). */\n passwordToggle?: boolean\n /** i18n aria-label кнопки показать/скрыть пароль. */\n passwordShowLabel?: string\n passwordHideLabel?: string\n\n textAlign?: GrInputTextAlign\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину\n * (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю\n * (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются»\n * под контент (в пределах min/max), а излишек клипается оболочкой.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n }>(),\n {\n type: 'text',\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n clearable: false,\n clearLabel: undefined,\n maxlength: undefined,\n showCount: false,\n passwordToggle: false,\n passwordShowLabel: undefined,\n passwordHideLabel: undefined,\n\n textAlign: 'left',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\n// Контекст `GrFormField` (если инпут внутри него): даёт id/aria-describedby/\n// invalid/required как fallback, чтобы не прокидывать `forId` вручную.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => props.id ?? field?.id.value)\nconst isInvalid = computed(() => props.invalid || Boolean(field?.invalid.value))\nconst describedBy = computed(() => field?.describedById.value)\nconst isRequired = computed(() => Boolean(field?.required.value))\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst defaultAddonMinWidth = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '2rem', // w-8\n sm: '2.25rem', // w-9\n md: '2.5rem', // w-10\n lg: '3rem', // w-12\n }\n\n return map[props.size]\n})\n\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\n// Жёсткая ширина для fixed-режима: max → min → дефолт.\nconst prefixFixedWidth = computed(() => props.prefixMaxWidth ?? props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixFixedWidth = computed(() => props.suffixMaxWidth ?? props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst { prefixEl, suffixEl, measuredPrefixWidth, measuredSuffixWidth } = useAddonMeasurement(hasPrefix, hasSuffix)\n\nconst basePaddingXLen = computed(() => {\n // Must mirror `sizeClass` horizontal padding (px-*) because inline paddings override class paddings.\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '10px', // px-2.5\n sm: '12px', // px-3\n md: '12px', // px-3\n lg: '16px', // px-4\n }\n\n return map[props.size]\n})\n\nconst inputStyle = computed(() => {\n const leftReserved = hasPrefix.value\n ? (props.prefixFixed ? prefixFixedWidth.value : measuredPrefixWidth.value ?? prefixMinWidth.value)\n : '0px'\n const rightReserved = hasSuffix.value\n ? (props.suffixFixed ? suffixFixedWidth.value : measuredSuffixWidth.value ?? suffixMinWidth.value)\n : '0px'\n\n // Keep the same visual text padding as without addons (px-*), but add it on top of reserved space.\n const left = hasPrefix.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const rightSpace = addLen(rightReserved, trailingReserve.value)\n const right = (hasSuffix.value || trailingCount.value > 0)\n ? addLen(rightSpace, basePaddingXLen.value)\n : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst prefixStyle = computed(() => {\n if (props.prefixFixed) {\n return {\n width: prefixFixedWidth.value,\n minWidth: prefixFixedWidth.value,\n maxWidth: prefixFixedWidth.value,\n } as Record<string, string | undefined>\n }\n\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n if (props.suffixFixed) {\n return {\n width: suffixFixedWidth.value,\n minWidth: suffixFixedWidth.value,\n maxWidth: suffixFixedWidth.value,\n } as Record<string, string | undefined>\n }\n\n return {\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst sizeClass = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: 'h-7 px-2.5 text-[12px]',\n sm: 'h-8 px-3 text-[13px]',\n md: 'h-10 px-3 text-[14px]',\n lg: 'h-11 px-4 text-[16px]',\n }\n\n return map[props.size]\n})\n\nconst textAlignClass = computed(() => {\n const map: Record<NonNullable<typeof props.textAlign>, string> = {\n left: 'text-left',\n center: 'text-center',\n right: 'text-right',\n }\n\n return map[props.textAlign]\n})\n\n// Border/ring/disabled — на оболочке (`focus-within`), размеры/выравнивание — на инпуте.\nconst shellClass = computed(() => {\n const state = props.state\n\n const borderByState: Record<typeof state, string> = {\n default: 'border-[var(--brd)]',\n success: 'border-[var(--gr-success)] focus-within:ring-[var(--gr-success)]',\n warning: 'border-[var(--gr-warning)] focus-within:ring-[var(--gr-warning)]',\n danger: 'border-[var(--gr-danger)] focus-within:ring-[var(--gr-danger)]',\n }\n\n return [\n isInvalid.value ? borderByState.danger : borderByState[state],\n props.disabled ? 'opacity-50 cursor-not-allowed' : '',\n ].filter(Boolean).join(' ')\n})\n\nconst className = computed(() => {\n return [\n sizeClass.value,\n textAlignClass.value,\n ].join(' ')\n})\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLInputElement).value)\n}\n\n// ————— Trailing-контролы: очистка, переключатель пароля, счётчик символов.\nconst { t } = useGranularityTranslations()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\nconst resolvedPasswordShowLabel = computed(() => props.passwordShowLabel ?? t('gr.input.showPassword', 'Show password'))\nconst resolvedPasswordHideLabel = computed(() => props.passwordHideLabel ?? t('gr.input.hidePassword', 'Hide password'))\n\nconst passwordVisible = ref(false)\n// Тип поля с учётом переключателя пароля.\nconst resolvedType = computed(() => (props.type === 'password' && passwordVisible.value ? 'text' : props.type))\n\nconst showPasswordToggle = computed(() => props.passwordToggle && props.type === 'password' && !props.disabled)\nconst showClear = computed(() => props.clearable && props.modelValue.length > 0 && !props.disabled && !props.readonly)\n\nconst trailingCount = computed(() => (showClear.value ? 1 : 0) + (showPasswordToggle.value ? 1 : 0))\nconst trailingReserve = computed(() => (trailingCount.value > 0 ? `${trailingCount.value * 28}px` : '0px'))\n\n// Счётчик символов: `len` или `len / maxlength`.\nconst countText = computed(() =>\n props.maxlength !== undefined ? `${props.modelValue.length} / ${props.maxlength}` : String(props.modelValue.length),\n)\n\nfunction clear(): void {\n emit('update:modelValue', '')\n inputEl.value?.focus()\n}\n\nfunction togglePassword(): void {\n passwordVisible.value = !passwordVisible.value\n inputEl.value?.focus()\n}\n</script>\n\n<template>\n <div data-gr-input class=\"w-full\">\n <div\n class=\"relative w-full overflow-hidden rounded-md border bg-[var(--bg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"shellClass\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"gr-input-prefix\"\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"props.name\"\n :type=\"resolvedType\"\n :inputmode=\"props.inputmode\"\n :autocomplete=\"props.autocomplete\"\n :placeholder=\"props.placeholder\"\n :disabled=\"props.disabled\"\n :readonly=\"props.readonly\"\n :maxlength=\"props.maxlength\"\n :value=\"props.modelValue\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"className\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n >\n\n <div\n v-if=\"trailingCount > 0\"\n data-gr-input-trailing\n class=\"absolute inset-y-0 right-1 flex items-center gap-0.5\"\n >\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-input-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"flex h-6 w-6 items-center justify-center rounded text-[var(--muted-fg)] transition-colors hover:bg-[var(--muted)] hover:text-[var(--fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n tabindex=\"-1\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <button\n v-if=\"showPasswordToggle\"\n type=\"button\"\n data-gr-input-password-toggle\n :aria-label=\"passwordVisible ? resolvedPasswordHideLabel : resolvedPasswordShowLabel\"\n :aria-pressed=\"passwordVisible ? 'true' : 'false'\"\n class=\"flex h-6 w-6 items-center justify-center rounded text-[var(--muted-fg)] transition-colors hover:bg-[var(--muted)] hover:text-[var(--fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n tabindex=\"-1\"\n @click=\"togglePassword\"\n >\n <IconEyeOff v-if=\"passwordVisible\" class=\"h-4 w-4\" aria-hidden=\"true\" />\n <IconEye v-else class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"gr-input-suffix\"\n class=\"absolute inset-y-0 right-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n </div>\n\n <div\n v-if=\"showCount\"\n data-gr-input-count\n class=\"mt-1 text-right text-xs text-[var(--muted-fg)] tabular-nums\"\n >\n {{ countText }}\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport {computed, ref, useSlots} from 'vue'\n\nimport {addLen, useAddonMeasurement} from '../../composables/internal/useAddonMeasurement'\nimport {useGrFormFieldContext} from '../GrFormField/context'\nimport {useGranularityTranslations} from '../../internal/granularityI18n'\nimport type {InputHTMLAttributes} from 'vue'\n\nimport IconX from '~icons/lucide/x'\nimport IconEye from '~icons/lucide/eye'\nimport IconEyeOff from '~icons/lucide/eye-off'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nexport type GrInputSize = 'xs' | 'sm' | 'md' | 'lg'\nexport type GrInputTextAlign = 'left' | 'center' | 'right'\n\nconst props = withDefaults(\n defineProps<{\n modelValue: string\n type?: 'text' | 'email' | 'password' | 'number' | 'search'\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Только для чтения: значение видно и выделяемо, но не редактируется. */\n readonly?: boolean\n invalid?: boolean\n state?: 'default' | 'success' | 'warning' | 'danger'\n name?: string\n id?: string\n size?: GrInputSize\n\n /** Показывать кнопку очистки, когда есть значение (и не disabled/readonly). */\n clearable?: boolean\n /** i18n aria-label кнопки очистки. */\n clearLabel?: string\n /** Ограничение длины + основа для счётчика символов. */\n maxlength?: number\n /** Показывать счётчик символов (`len` или `len/maxlength`). */\n showCount?: boolean\n /** Кнопка показать/скрыть пароль (только при `type=\"password\"`). */\n passwordToggle?: boolean\n /** i18n aria-label кнопки показать/скрыть пароль. */\n passwordShowLabel?: string\n passwordHideLabel?: string\n\n textAlign?: GrInputTextAlign\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: аддон получает жёсткую ширину\n * (из `*MaxWidth` → `*MinWidth` → дефолт), а контент обрезается по краю\n * (prefix — справа, suffix — слева). По умолчанию аддоны «растягиваются»\n * под контент (в пределах min/max), а излишек клипается оболочкой.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n }>(),\n {\n type: 'text',\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n clearable: false,\n clearLabel: undefined,\n maxlength: undefined,\n showCount: false,\n passwordToggle: false,\n passwordShowLabel: undefined,\n passwordHideLabel: undefined,\n\n textAlign: 'left',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\n// Контекст `GrFormField` (если инпут внутри него): даёт id/aria-describedby/\n// invalid/required как fallback, чтобы не прокидывать `forId` вручную.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => props.id ?? field?.id.value)\nconst isInvalid = computed(() => props.invalid || Boolean(field?.invalid.value))\nconst describedBy = computed(() => field?.describedById.value)\nconst isRequired = computed(() => Boolean(field?.required.value))\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst defaultAddonMinWidth = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '2rem', // w-8\n sm: '2.25rem', // w-9\n md: '2.5rem', // w-10\n lg: '3rem', // w-12\n }\n\n return map[props.size]\n})\n\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\n// Жёсткая ширина для fixed-режима: max → min → дефолт.\nconst prefixFixedWidth = computed(() => props.prefixMaxWidth ?? props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixFixedWidth = computed(() => props.suffixMaxWidth ?? props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst { prefixEl, suffixEl, measuredPrefixWidth, measuredSuffixWidth } = useAddonMeasurement(hasPrefix, hasSuffix)\n\nconst basePaddingXLen = computed(() => {\n // Must mirror `sizeClass` horizontal padding (px-*) because inline paddings override class paddings.\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: '10px', // px-2.5\n sm: '12px', // px-3\n md: '12px', // px-3\n lg: '16px', // px-4\n }\n\n return map[props.size]\n})\n\nconst inputStyle = computed(() => {\n const leftReserved = hasPrefix.value\n ? (props.prefixFixed ? prefixFixedWidth.value : measuredPrefixWidth.value ?? prefixMinWidth.value)\n : '0px'\n const rightReserved = hasSuffix.value\n ? (props.suffixFixed ? suffixFixedWidth.value : measuredSuffixWidth.value ?? suffixMinWidth.value)\n : '0px'\n\n // Keep the same visual text padding as without addons (px-*), but add it on top of reserved space.\n const left = hasPrefix.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const rightSpace = addLen(rightReserved, trailingReserve.value)\n const right = (hasSuffix.value || trailingCount.value > 0)\n ? addLen(rightSpace, basePaddingXLen.value)\n : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst prefixStyle = computed(() => {\n if (props.prefixFixed) {\n return {\n width: prefixFixedWidth.value,\n minWidth: prefixFixedWidth.value,\n maxWidth: prefixFixedWidth.value,\n } as Record<string, string | undefined>\n }\n\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n if (props.suffixFixed) {\n return {\n width: suffixFixedWidth.value,\n minWidth: suffixFixedWidth.value,\n maxWidth: suffixFixedWidth.value,\n } as Record<string, string | undefined>\n }\n\n return {\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n } as Record<string, string | undefined>\n})\n\nconst sizeClass = computed(() => {\n const map: Record<NonNullable<typeof props.size>, string> = {\n xs: 'h-7 px-2.5 text-[12px]',\n sm: 'h-8 px-3 text-[13px]',\n md: 'h-10 px-3 text-[14px]',\n lg: 'h-11 px-4 text-[16px]',\n }\n\n return map[props.size]\n})\n\nconst textAlignClass = computed(() => {\n const map: Record<NonNullable<typeof props.textAlign>, string> = {\n left: 'text-left',\n center: 'text-center',\n right: 'text-right',\n }\n\n return map[props.textAlign]\n})\n\n// Border/ring/disabled — на оболочке (`focus-within`), размеры/выравнивание — на инпуте.\nconst shellClass = computed(() => {\n const state = props.state\n\n const borderByState: Record<typeof state, string> = {\n default: 'border-[var(--brd)]',\n success: 'border-[var(--gr-success)] focus-within:ring-[var(--gr-success)]',\n warning: 'border-[var(--gr-warning)] focus-within:ring-[var(--gr-warning)]',\n danger: 'border-[var(--gr-danger)] focus-within:ring-[var(--gr-danger)]',\n }\n\n return [\n isInvalid.value ? borderByState.danger : borderByState[state],\n props.disabled ? 'opacity-50 cursor-not-allowed' : '',\n ].filter(Boolean).join(' ')\n})\n\nconst className = computed(() => {\n return [\n sizeClass.value,\n textAlignClass.value,\n ].join(' ')\n})\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLInputElement).value)\n}\n\n// ————— Trailing-контролы: очистка, переключатель пароля, счётчик символов.\nconst { t } = useGranularityTranslations()\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.input.clear', 'Clear'))\nconst resolvedPasswordShowLabel = computed(() => props.passwordShowLabel ?? t('gr.input.showPassword', 'Show password'))\nconst resolvedPasswordHideLabel = computed(() => props.passwordHideLabel ?? t('gr.input.hidePassword', 'Hide password'))\n\nconst passwordVisible = ref(false)\n// Тип поля с учётом переключателя пароля.\nconst resolvedType = computed(() => (props.type === 'password' && passwordVisible.value ? 'text' : props.type))\n\nconst showPasswordToggle = computed(() => props.passwordToggle && props.type === 'password' && !props.disabled)\nconst showClear = computed(() => props.clearable && props.modelValue.length > 0 && !props.disabled && !props.readonly)\n\nconst trailingCount = computed(() => (showClear.value ? 1 : 0) + (showPasswordToggle.value ? 1 : 0))\nconst trailingReserve = computed(() => (trailingCount.value > 0 ? `${trailingCount.value * 28}px` : '0px'))\n\n// Счётчик символов: `len` или `len / maxlength`.\nconst countText = computed(() =>\n props.maxlength !== undefined ? `${props.modelValue.length} / ${props.maxlength}` : String(props.modelValue.length),\n)\n\nfunction clear(): void {\n emit('update:modelValue', '')\n inputEl.value?.focus()\n}\n\nfunction togglePassword(): void {\n passwordVisible.value = !passwordVisible.value\n inputEl.value?.focus()\n}\n</script>\n\n<template>\n <div data-gr-input class=\"w-full\">\n <div\n class=\"relative w-full overflow-hidden rounded-md border bg-[var(--bg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"shellClass\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"gr-input-prefix\"\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"resolvedId\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"props.name\"\n :type=\"resolvedType\"\n :inputmode=\"props.inputmode\"\n :autocomplete=\"props.autocomplete\"\n :placeholder=\"props.placeholder\"\n :disabled=\"props.disabled\"\n :readonly=\"props.readonly\"\n :maxlength=\"props.maxlength\"\n :value=\"props.modelValue\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"className\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n >\n\n <div\n v-if=\"trailingCount > 0\"\n data-gr-input-trailing\n class=\"absolute inset-y-0 right-1 flex items-center gap-0.5\"\n >\n <button\n v-if=\"showClear\"\n type=\"button\"\n data-gr-input-clear\n :aria-label=\"resolvedClearLabel\"\n class=\"flex h-6 w-6 items-center justify-center rounded text-[var(--muted-fg)] transition-colors hover:bg-[var(--muted)] hover:text-[var(--fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n tabindex=\"-1\"\n @click=\"clear\"\n >\n <IconX class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <button\n v-if=\"showPasswordToggle\"\n type=\"button\"\n data-gr-input-password-toggle\n :aria-label=\"passwordVisible ? resolvedPasswordHideLabel : resolvedPasswordShowLabel\"\n :aria-pressed=\"passwordVisible ? 'true' : 'false'\"\n class=\"flex h-6 w-6 items-center justify-center rounded text-[var(--muted-fg)] transition-colors hover:bg-[var(--muted)] hover:text-[var(--fg)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n tabindex=\"-1\"\n @click=\"togglePassword\"\n >\n <IconEyeOff v-if=\"passwordVisible\" class=\"h-4 w-4\" aria-hidden=\"true\" />\n <IconEye v-else class=\"h-4 w-4\" aria-hidden=\"true\" />\n </button>\n </div>\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"gr-input-suffix\"\n class=\"absolute inset-y-0 right-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n </div>\n\n <div\n v-if=\"showCount\"\n data-gr-input-count\n class=\"mt-1 text-right text-xs text-[var(--muted-fg)] tabular-nums\"\n >\n {{ countText }}\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrInputTag-zMgFqp2g.js","names":[],"sources":["../../../../src/components/GrInputTag/GrInputTag.vue","../../../../src/components/GrInputTag/GrInputTag.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconClose from '~icons/lucide/x'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grInputTagInputClass,\n grInputTagWrapperClass,\n type GrInputTagSize,\n type GrInputTagState,\n} from './grInputTagStyles'\n\nexport type { GrInputTagSize, GrInputTagState } from './grInputTagStyles'\n\nconst REGEX_SPECIAL_CHAR_RE = /[.*+?^${}()|[\\]\\\\]/g\n\n/**\n * Props for {@link GrInputTag}.\n * Tag-input primitive: renders a set of badges with an inline `<input>` for entering new tags.\n * Splits input by configurable `separators`, supports paste, Enter and Backspace editing.\n */\nexport interface GrInputTagProps {\n modelValue: string[]\n placeholder?: string\n disabled?: boolean\n readonly?: boolean\n invalid?: boolean\n state?: GrInputTagState\n size?: GrInputTagSize\n separators?: string[]\n allowDuplicates?: boolean\n trim?: boolean\n max?: number\n addOnBlur?: boolean\n clearInputOnAdd?: boolean\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n tagClosable?: boolean\n /** i18n-friendly aria-label for the per-tag remove button. */\n removeTagLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrInputTagProps>(),\n {\n placeholder: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n state: 'default',\n size: 'md',\n separators: () => [','],\n allowDuplicates: false,\n trim: true,\n max: undefined,\n addOnBlur: false,\n clearInputOnAdd: true,\n tagTone: 'neutral',\n tagDark: false,\n tagSize: 'md',\n tagRadius: 'round',\n tagClosable: true,\n removeTagLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string[]): void\n (e: 'add', value: string): void\n (e: 'remove', value: string, index: number): void\n}>()\n\nconst { t } = useGranularityTranslations()\nconst resolvedRemoveTagLabel = computed(() => props.removeTagLabel ?? t('gr.inputTag.removeTag', 'Remove tag'))\n\nconst inputValue = ref('')\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({ focus })\n\nconst isMaxed = computed(() => props.max !== undefined && props.modelValue.length >= props.max)\nconst canEdit = computed(() => !props.disabled && !props.readonly)\nconst showRemove = computed(() => props.tagClosable && canEdit.value)\n\nfunction escapeRegexChar(ch: string): string {\n return ch.replace(REGEX_SPECIAL_CHAR_RE, '\\\\$&')\n}\n\nconst splitRegex = computed(() => {\n const seps = (props.separators ?? []).filter(Boolean).map(s => s[0]!)\n const uniq = [...new Set([...seps, '\\n', '\\r', '\\t'])]\n const body = uniq.map(escapeRegexChar).join('')\n return new RegExp(`[${body}]+`, 'g')\n})\n\nfunction normalizeTag(raw: string): string {\n return props.trim ? raw.trim() : raw\n}\n\nfunction splitToTags(raw: string): string[] {\n return raw\n .split(splitRegex.value)\n .map(v => normalizeTag(v))\n .filter(Boolean)\n}\n\nfunction addMany(rawTags: string[]): boolean {\n if (!canEdit.value || isMaxed.value)\n return false\n\n const next = props.modelValue.slice()\n let changed = false\n\n for (const raw of rawTags) {\n const tag = normalizeTag(raw)\n if (!tag)\n continue\n\n if (!props.allowDuplicates && next.includes(tag))\n continue\n\n if (props.max !== undefined && next.length >= props.max)\n break\n\n next.push(tag)\n changed = true\n emit('add', tag)\n }\n\n if (!changed)\n return false\n\n emit('update:modelValue', next)\n return true\n}\n\nfunction commitInput(): void {\n if (!canEdit.value || isMaxed.value || !inputValue.value)\n return\n\n const tags = splitToTags(inputValue.value)\n if (!tags.length)\n return\n\n const changed = addMany(tags)\n if (changed && props.clearInputOnAdd)\n inputValue.value = ''\n}\n\nfunction removeAt(index: number): void {\n if (!canEdit.value)\n return\n\n const next = props.modelValue.slice()\n const removed = next[index]\n if (removed === undefined)\n return\n\n next.splice(index, 1)\n emit('update:modelValue', next)\n emit('remove', removed, index)\n}\n\nfunction onInput(e: Event): void {\n inputValue.value = (e.target as HTMLInputElement).value\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (!canEdit.value)\n return\n\n if (e.key === 'Enter') {\n e.preventDefault()\n commitInput()\n return\n }\n\n if ((props.separators ?? []).includes(e.key)) {\n e.preventDefault()\n commitInput()\n return\n }\n\n if (e.key === 'Backspace' && inputValue.value.length === 0 && props.modelValue.length > 0) {\n e.preventDefault()\n removeAt(props.modelValue.length - 1)\n }\n}\n\nfunction onBlur(): void {\n if (props.addOnBlur)\n commitInput()\n}\n\nfunction onPaste(e: ClipboardEvent): void {\n if (!canEdit.value || isMaxed.value)\n return\n\n const pasted = e.clipboardData?.getData('text')\n if (!pasted)\n return\n\n const tags = splitToTags(pasted)\n if (tags.length <= 1)\n return\n\n e.preventDefault()\n addMany(tags)\n}\n\nconst wrapperClassName = computed(() => {\n return grInputTagWrapperClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n disabled: props.disabled,\n })\n})\n\nconst inputClassName = computed(() => {\n return grInputTagInputClass(props.size)\n})\n\nconst placeholderText = computed(() => props.modelValue.length > 0 ? undefined : props.placeholder)\n</script>\n\n<template>\n <div\n data-gr-input-tag\n data-testid=\"gr-input-tag\"\n class=\"w-full flex flex-wrap items-center rounded-md border bg-[var(--bg)] text-[var(--fg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"wrapperClassName\"\n @click=\"focus\"\n >\n <GrBadge\n v-for=\"(tag, i) in modelValue\"\n :key=\"`${tag}-${i}`\"\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n data-gr-input-tag-item\n data-testid=\"gr-input-tag-item\"\n :data-index=\"i\"\n >\n <span class=\"inline-flex items-center gap-1 align-middle\">\n <slot name=\"tag\" :tag=\"tag\" :index=\"i\" :remove=\"() => removeAt(i)\">\n <span class=\"truncate max-w-[18rem]\">{{ tag }}</span>\n </slot>\n\n <button\n v-if=\"showRemove\"\n type=\"button\"\n class=\"-mr-0.5 shrink-0 inline-flex items-center justify-center rounded-[6px] p-0.5 opacity-70 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :aria-label=\"resolvedRemoveTagLabel\"\n data-gr-input-tag-remove\n data-testid=\"gr-input-tag-remove\"\n :data-index=\"i\"\n @mousedown.prevent.stop\n @click.stop=\"removeAt(i)\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </button>\n </span>\n </GrBadge>\n\n <input\n ref=\"inputEl\"\n data-gr-input-tag-input\n data-testid=\"gr-input-tag-input\"\n :value=\"inputValue\"\n :disabled=\"disabled || isMaxed\"\n :readonly=\"readonly\"\n :placeholder=\"placeholderText\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"flex-1 min-w-[120px] bg-transparent border-none outline-none placeholder:text-[var(--muted-fg)]\"\n :class=\"inputClassName\"\n @input=\"onInput\"\n @keydown=\"onKeydown\"\n @blur=\"onBlur\"\n @paste=\"onPaste\"\n >\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport GrBadge from '../GrBadge/GrBadge.vue'\nimport type { GrBadgeRadius, GrBadgeSize, GrBadgeTone } from '../GrBadge'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconClose from '~icons/lucide/x'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n grInputTagInputClass,\n grInputTagWrapperClass,\n type GrInputTagSize,\n type GrInputTagState,\n} from './grInputTagStyles'\n\nexport type { GrInputTagSize, GrInputTagState } from './grInputTagStyles'\n\nconst REGEX_SPECIAL_CHAR_RE = /[.*+?^${}()|[\\]\\\\]/g\n\n/**\n * Props for {@link GrInputTag}.\n * Tag-input primitive: renders a set of badges with an inline `<input>` for entering new tags.\n * Splits input by configurable `separators`, supports paste, Enter and Backspace editing.\n */\nexport interface GrInputTagProps {\n modelValue: string[]\n placeholder?: string\n disabled?: boolean\n readonly?: boolean\n invalid?: boolean\n state?: GrInputTagState\n size?: GrInputTagSize\n separators?: string[]\n allowDuplicates?: boolean\n trim?: boolean\n max?: number\n addOnBlur?: boolean\n clearInputOnAdd?: boolean\n tagTone?: GrBadgeTone\n tagDark?: boolean\n tagSize?: GrBadgeSize\n tagRadius?: GrBadgeRadius\n tagClosable?: boolean\n /** i18n-friendly aria-label for the per-tag remove button. */\n removeTagLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrInputTagProps>(),\n {\n placeholder: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n state: 'default',\n size: 'md',\n separators: () => [','],\n allowDuplicates: false,\n trim: true,\n max: undefined,\n addOnBlur: false,\n clearInputOnAdd: true,\n tagTone: 'neutral',\n tagDark: false,\n tagSize: 'md',\n tagRadius: 'round',\n tagClosable: true,\n removeTagLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string[]): void\n (e: 'add', value: string): void\n (e: 'remove', value: string, index: number): void\n}>()\n\nconst { t } = useGranularityTranslations()\nconst resolvedRemoveTagLabel = computed(() => props.removeTagLabel ?? t('gr.inputTag.removeTag', 'Remove tag'))\n\nconst inputValue = ref('')\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({ focus })\n\nconst isMaxed = computed(() => props.max !== undefined && props.modelValue.length >= props.max)\nconst canEdit = computed(() => !props.disabled && !props.readonly)\nconst showRemove = computed(() => props.tagClosable && canEdit.value)\n\nfunction escapeRegexChar(ch: string): string {\n return ch.replace(REGEX_SPECIAL_CHAR_RE, '\\\\$&')\n}\n\nconst splitRegex = computed(() => {\n const seps = (props.separators ?? []).filter(Boolean).map(s => s[0]!)\n const uniq = [...new Set([...seps, '\\n', '\\r', '\\t'])]\n const body = uniq.map(escapeRegexChar).join('')\n return new RegExp(`[${body}]+`, 'g')\n})\n\nfunction normalizeTag(raw: string): string {\n return props.trim ? raw.trim() : raw\n}\n\nfunction splitToTags(raw: string): string[] {\n return raw\n .split(splitRegex.value)\n .map(v => normalizeTag(v))\n .filter(Boolean)\n}\n\nfunction addMany(rawTags: string[]): boolean {\n if (!canEdit.value || isMaxed.value)\n return false\n\n const next = props.modelValue.slice()\n let changed = false\n\n for (const raw of rawTags) {\n const tag = normalizeTag(raw)\n if (!tag)\n continue\n\n if (!props.allowDuplicates && next.includes(tag))\n continue\n\n if (props.max !== undefined && next.length >= props.max)\n break\n\n next.push(tag)\n changed = true\n emit('add', tag)\n }\n\n if (!changed)\n return false\n\n emit('update:modelValue', next)\n return true\n}\n\nfunction commitInput(): void {\n if (!canEdit.value || isMaxed.value || !inputValue.value)\n return\n\n const tags = splitToTags(inputValue.value)\n if (!tags.length)\n return\n\n const changed = addMany(tags)\n if (changed && props.clearInputOnAdd)\n inputValue.value = ''\n}\n\nfunction removeAt(index: number): void {\n if (!canEdit.value)\n return\n\n const next = props.modelValue.slice()\n const removed = next[index]\n if (removed === undefined)\n return\n\n next.splice(index, 1)\n emit('update:modelValue', next)\n emit('remove', removed, index)\n}\n\nfunction onInput(e: Event): void {\n inputValue.value = (e.target as HTMLInputElement).value\n}\n\nfunction onKeydown(e: KeyboardEvent): void {\n if (!canEdit.value)\n return\n\n if (e.key === 'Enter') {\n e.preventDefault()\n commitInput()\n return\n }\n\n if ((props.separators ?? []).includes(e.key)) {\n e.preventDefault()\n commitInput()\n return\n }\n\n if (e.key === 'Backspace' && inputValue.value.length === 0 && props.modelValue.length > 0) {\n e.preventDefault()\n removeAt(props.modelValue.length - 1)\n }\n}\n\nfunction onBlur(): void {\n if (props.addOnBlur)\n commitInput()\n}\n\nfunction onPaste(e: ClipboardEvent): void {\n if (!canEdit.value || isMaxed.value)\n return\n\n const pasted = e.clipboardData?.getData('text')\n if (!pasted)\n return\n\n const tags = splitToTags(pasted)\n if (tags.length <= 1)\n return\n\n e.preventDefault()\n addMany(tags)\n}\n\nconst wrapperClassName = computed(() => {\n return grInputTagWrapperClass({\n size: props.size,\n state: props.state,\n invalid: props.invalid,\n disabled: props.disabled,\n })\n})\n\nconst inputClassName = computed(() => {\n return grInputTagInputClass(props.size)\n})\n\nconst placeholderText = computed(() => props.modelValue.length > 0 ? undefined : props.placeholder)\n</script>\n\n<template>\n <div\n data-gr-input-tag\n data-testid=\"gr-input-tag\"\n class=\"w-full flex flex-wrap items-center rounded-md border bg-[var(--bg)] text-[var(--fg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"wrapperClassName\"\n @click=\"focus\"\n >\n <GrBadge\n v-for=\"(tag, i) in modelValue\"\n :key=\"`${tag}-${i}`\"\n :tone=\"tagTone\"\n :dark=\"tagDark\"\n :size=\"tagSize\"\n :radius=\"tagRadius\"\n data-gr-input-tag-item\n data-testid=\"gr-input-tag-item\"\n :data-index=\"i\"\n >\n <span class=\"inline-flex items-center gap-1 align-middle\">\n <slot name=\"tag\" :tag=\"tag\" :index=\"i\" :remove=\"() => removeAt(i)\">\n <span class=\"truncate max-w-[18rem]\">{{ tag }}</span>\n </slot>\n\n <button\n v-if=\"showRemove\"\n type=\"button\"\n class=\"-mr-0.5 shrink-0 inline-flex items-center justify-center rounded-[6px] p-0.5 opacity-70 hover:opacity-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :aria-label=\"resolvedRemoveTagLabel\"\n data-gr-input-tag-remove\n data-testid=\"gr-input-tag-remove\"\n :data-index=\"i\"\n @mousedown.prevent.stop\n @click.stop=\"removeAt(i)\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </button>\n </span>\n </GrBadge>\n\n <input\n ref=\"inputEl\"\n data-gr-input-tag-input\n data-testid=\"gr-input-tag-input\"\n :value=\"inputValue\"\n :disabled=\"disabled || isMaxed\"\n :readonly=\"readonly\"\n :placeholder=\"placeholderText\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"flex-1 min-w-[120px] bg-transparent border-none outline-none placeholder:text-[var(--muted-fg)]\"\n :class=\"inputClassName\"\n @input=\"onInput\"\n @keydown=\"onKeydown\"\n @blur=\"onBlur\"\n @paste=\"onPaste\"\n >\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrKbd-BEoYxWhE.js","names":[],"sources":["../../../../src/components/GrKbd/GrKbd.vue","../../../../src/components/GrKbd/GrKbd.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrKbd — GR-примитив для отображения клавиш/сочетаний (`<kbd>`).\n *\n * Пример: `<GrKbd>⌘</GrKbd> <GrKbd>K</GrKbd>`.\n */\nexport type GrKbdSize = 'sm' | 'md'\n\nwithDefaults(defineProps<{ size?: GrKbdSize }>(), {\n size: 'md',\n})\n</script>\n\n<template>\n <kbd\n data-gr-kbd\n class=\"inline-flex items-center justify-center rounded-md border border-[var(--brd)] bg-[var(--muted)] font-medium leading-none text-[var(--muted-fg)] shadow-[var(--gr-shadow-1)] tabular-nums\"\n :class=\"size === 'sm'\n ? 'min-w-[1.25rem] h-5 px-1 text-[11px]'\n : 'min-w-[1.5rem] h-6 px-1.5 text-xs'\"\n ><slot /></kbd>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrKbd — GR-примитив для отображения клавиш/сочетаний (`<kbd>`).\n *\n * Пример: `<GrKbd>⌘</GrKbd> <GrKbd>K</GrKbd>`.\n */\nexport type GrKbdSize = 'sm' | 'md'\n\nwithDefaults(defineProps<{ size?: GrKbdSize }>(), {\n size: 'md',\n})\n</script>\n\n<template>\n <kbd\n data-gr-kbd\n class=\"inline-flex items-center justify-center rounded-md border border-[var(--brd)] bg-[var(--muted)] font-medium leading-none text-[var(--muted-fg)] shadow-[var(--gr-shadow-1)] tabular-nums\"\n :class=\"size === 'sm'\n ? 'min-w-[1.25rem] h-5 px-1 text-[11px]'\n : 'min-w-[1.5rem] h-6 px-1.5 text-xs'\"\n ><slot /></kbd>\n</template>\n"],"mappings":";;;;;;;;;;;;;uBAcE,mBAMe,OAAA;IALb,eAAA;IACA,OAAK,eAAA,CAAC,4LACE,QAAA,SAAI,OAAA,yCAAA,mCAAA,CAAA;OAGb,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrList-Dx772Ns4.js","names":["$slots"],"sources":["../../../../src/components/GrList/GrList.vue","../../../../src/components/GrList/GrList.vue","../../../../src/components/GrList/GrListItem.vue","../../../../src/components/GrList/GrListItem.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport GrCard from '../GrCard'\n\nexport interface GrListProps {\n /** Показывать ли горизонтальные разделители между элементами (по умолчанию — да). */\n divided?: boolean\n}\n\nwithDefaults(defineProps<GrListProps>(), {\n divided: true,\n})\n</script>\n\n<template>\n <GrCard>\n <div\n v-if=\"divided\"\n role=\"list\"\n class=\"divide-y divide-[var(--brd)]\"\n >\n <slot />\n </div>\n <div\n v-else\n role=\"list\"\n >\n <slot />\n </div>\n </GrCard>\n</template>","<script setup lang=\"ts\">\nimport GrCard from '../GrCard'\n\nexport interface GrListProps {\n /** Показывать ли горизонтальные разделители между элементами (по умолчанию — да). */\n divided?: boolean\n}\n\nwithDefaults(defineProps<GrListProps>(), {\n divided: true,\n})\n</script>\n\n<template>\n <GrCard>\n <div\n v-if=\"divided\"\n role=\"list\"\n class=\"divide-y divide-[var(--brd)]\"\n >\n <slot />\n </div>\n <div\n v-else\n role=\"list\"\n >\n <slot />\n </div>\n </GrCard>\n</template>","<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue'\n\nexport type GrListItemDensity = 'compact' | 'regular'\n\nexport interface GrListItemProps {\n /** Заголовок строки. Если передан слот `#title`, проп игнорируется. */\n title?: string\n /** Описание под заголовком. Если передан слот `#description`, проп игнорируется. */\n description?: string\n /** Плотность вертикальных отступов: `regular` — 12px, `compact` — 8px. */\n density?: GrListItemDensity\n}\n\nconst props = withDefaults(defineProps<GrListItemProps>(), {\n title: undefined,\n description: undefined,\n density: 'regular',\n})\n\nconst slots = useSlots()\n\nconst paddingClass = computed(() =>\n props.density === 'compact' ? 'px-4 py-2' : 'px-4 py-3',\n)\n\nconst hasTitle = computed(() => !!slots.title || !!props.title)\nconst hasDescription = computed(() => !!slots.description || !!props.description)\n</script>\n\n<template>\n <div\n role=\"listitem\"\n class=\"flex items-start justify-between gap-4\"\n :class=\"paddingClass\"\n >\n <div v-if=\"$slots.prefix\" class=\"shrink-0\">\n <slot name=\"prefix\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div v-if=\"hasTitle\" class=\"text-[13px] font-700\">\n <slot name=\"title\">\n{{ title }}\n</slot>\n </div>\n <div\n v-if=\"hasDescription\"\n class=\"text-[13px] text-[var(--muted-fg)] mt-0.5\"\n >\n <slot name=\"description\">\n{{ description }}\n</slot>\n </div>\n </div>\n <div v-if=\"$slots.default\" class=\"shrink-0\">\n <slot />\n </div>\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue'\n\nexport type GrListItemDensity = 'compact' | 'regular'\n\nexport interface GrListItemProps {\n /** Заголовок строки. Если передан слот `#title`, проп игнорируется. */\n title?: string\n /** Описание под заголовком. Если передан слот `#description`, проп игнорируется. */\n description?: string\n /** Плотность вертикальных отступов: `regular` — 12px, `compact` — 8px. */\n density?: GrListItemDensity\n}\n\nconst props = withDefaults(defineProps<GrListItemProps>(), {\n title: undefined,\n description: undefined,\n density: 'regular',\n})\n\nconst slots = useSlots()\n\nconst paddingClass = computed(() =>\n props.density === 'compact' ? 'px-4 py-2' : 'px-4 py-3',\n)\n\nconst hasTitle = computed(() => !!slots.title || !!props.title)\nconst hasDescription = computed(() => !!slots.description || !!props.description)\n</script>\n\n<template>\n <div\n role=\"listitem\"\n class=\"flex items-start justify-between gap-4\"\n :class=\"paddingClass\"\n >\n <div v-if=\"$slots.prefix\" class=\"shrink-0\">\n <slot name=\"prefix\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div v-if=\"hasTitle\" class=\"text-[13px] font-700\">\n <slot name=\"title\">\n{{ title }}\n</slot>\n </div>\n <div\n v-if=\"hasDescription\"\n class=\"text-[13px] text-[var(--muted-fg)] mt-0.5\"\n >\n <slot name=\"description\">\n{{ description }}\n</slot>\n </div>\n </div>\n <div v-if=\"$slots.default\" class=\"shrink-0\">\n <slot />\n </div>\n </div>\n</template>"],"mappings":";;;;;;;;;;;;;;;;;;;;;;uBAcE,YAcS,MAAA,cAAA,GAAA,MAAA;2BAPD,CALE,QAAA,WAAA,UAAA,GADR,mBAMM,OANN,cAMM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA,MAAA,UAAA,GAEV,mBAKM,OALN,cAKM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA,EAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEZd,MAAM,QAAQ;EAMd,MAAM,QAAQ,SAAS;EAEvB,MAAM,eAAe,eACnB,MAAM,YAAY,YAAY,cAAc,WAC9C;EAEA,MAAM,WAAW,eAAe,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC,MAAM,KAAK;EAC9D,MAAM,iBAAiB,eAAe,CAAC,CAAC,MAAM,eAAe,CAAC,CAAC,MAAM,WAAW;;uBAI9E,mBA0BM,OAAA;IAzBJ,MAAK;IACL,OAAK,eAAA,CAAC,0CACE,aAAA,KAAY,CAAA;;IAETA,KAAAA,OAAO,UAAA,UAAA,GAAlB,mBAEM,OAFN,YAEM,CADJ,WAAsB,KAAA,QAAA,QAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAExB,mBAcM,OAdN,YAcM,CAbO,SAAA,SAAA,UAAA,GAAX,mBAIM,OAJN,YAIM,CAHJ,WAED,KAAA,QAAA,SAAA,CAAA,SAAA,CAAA,gBAAA,gBADJ,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAIM,eAAA,SAAA,UAAA,GADR,mBAOM,OAPN,YAOM,CAHJ,WAED,KAAA,QAAA,eAAA,CAAA,SAAA,CAAA,gBAAA,gBADJ,QAAA,WAAW,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA;IAICA,KAAAA,OAAO,WAAA,UAAA,GAAlB,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrLoading--X_7rdbb.js","names":[],"sources":["../../../../src/components/GrLoading/GrLoading.vue","../../../../src/components/GrLoading/GrLoading.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, type Component } from 'vue'\n\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport { grLoadingRootClass } from './grLoadingStyles'\n\nconst DEFAULT_LOADING_TEXT = 'Loading...'\n\n/**\n * Props for {@link GrLoading}.\n * Overlay primitive that renders a centered spinner with optional text above sibling content.\n * Use `fullscreen` to cover the whole viewport, otherwise overlays the nearest positioned ancestor.\n */\nexport interface GrLoadingProps {\n /** Text under the spinner. Pass an empty string to hide the text entirely. Defaults to `'Loading...'`. */\n text?: string\n /** Custom spinner component (rendered instead of the default loader icon). */\n spinner?: Component\n /** Extra classes for the spinner element. */\n spinnerClass?: string\n /** When `true` (default), the default spinner rotates via `@keyframes gr-loading-spin`. */\n animated?: boolean\n /** Custom CSS `background-color`. When set, disables the default `bg-black/25` dim. */\n background?: string\n /** Cover the whole viewport (`position: fixed`) instead of the nearest positioned ancestor. */\n fullscreen?: boolean\n /** Custom `z-index`. */\n zIndex?: number\n /** Extra classes appended to the overlay root. */\n customClass?: string\n}\n\nconst props = withDefaults(\n defineProps<GrLoadingProps>(),\n {\n text: undefined,\n spinner: undefined,\n spinnerClass: undefined,\n animated: true,\n background: undefined,\n fullscreen: false,\n zIndex: undefined,\n customClass: undefined,\n },\n)\n\nconst Spinner = computed(() => props.spinner ?? IconLoader)\n\nconst displayText = computed(() => props.text ?? DEFAULT_LOADING_TEXT)\n\nconst rootClass = computed(() => {\n return grLoadingRootClass({\n fullscreen: props.fullscreen,\n hasBackground: props.background !== undefined,\n customClass: props.customClass,\n })\n})\n\nconst rootStyle = computed(() => {\n return {\n backgroundColor: props.background,\n zIndex: props.zIndex != null ? String(props.zIndex) : undefined,\n } as Record<string, string | undefined>\n})\n\nconst spinnerClassName = computed(() => props.spinnerClass)\n</script>\n\n<template>\n <div\n data-gr-loading\n class=\"flex items-center justify-center cursor-wait select-none pointer-events-auto\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n data-gr-loading-panel\n class=\"flex flex-col items-center justify-center gap-2 text-center rounded-lg bg-[var(--bg)]/55 px-5 py-4 shadow-lg\"\n >\n <component\n :is=\"Spinner\"\n data-gr-loading-spinner\n class=\"gr-loading__spinner h-7 w-7 text-[var(--muted-fg)]\"\n :class=\"[animated ? 'gr-loading__spinner--animated' : '', spinnerClassName]\"\n aria-hidden=\"true\"\n />\n <div v-if=\"displayText\" data-gr-loading-text class=\"text-sm text-[var(--muted-fg)]\">\n {{ displayText }}\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@keyframes gr-loading-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.gr-loading__spinner--animated {\n transform-origin: center;\n animation: gr-loading-spin 1s linear infinite;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, type Component } from 'vue'\n\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport { grLoadingRootClass } from './grLoadingStyles'\n\nconst DEFAULT_LOADING_TEXT = 'Loading...'\n\n/**\n * Props for {@link GrLoading}.\n * Overlay primitive that renders a centered spinner with optional text above sibling content.\n * Use `fullscreen` to cover the whole viewport, otherwise overlays the nearest positioned ancestor.\n */\nexport interface GrLoadingProps {\n /** Text under the spinner. Pass an empty string to hide the text entirely. Defaults to `'Loading...'`. */\n text?: string\n /** Custom spinner component (rendered instead of the default loader icon). */\n spinner?: Component\n /** Extra classes for the spinner element. */\n spinnerClass?: string\n /** When `true` (default), the default spinner rotates via `@keyframes gr-loading-spin`. */\n animated?: boolean\n /** Custom CSS `background-color`. When set, disables the default `bg-black/25` dim. */\n background?: string\n /** Cover the whole viewport (`position: fixed`) instead of the nearest positioned ancestor. */\n fullscreen?: boolean\n /** Custom `z-index`. */\n zIndex?: number\n /** Extra classes appended to the overlay root. */\n customClass?: string\n}\n\nconst props = withDefaults(\n defineProps<GrLoadingProps>(),\n {\n text: undefined,\n spinner: undefined,\n spinnerClass: undefined,\n animated: true,\n background: undefined,\n fullscreen: false,\n zIndex: undefined,\n customClass: undefined,\n },\n)\n\nconst Spinner = computed(() => props.spinner ?? IconLoader)\n\nconst displayText = computed(() => props.text ?? DEFAULT_LOADING_TEXT)\n\nconst rootClass = computed(() => {\n return grLoadingRootClass({\n fullscreen: props.fullscreen,\n hasBackground: props.background !== undefined,\n customClass: props.customClass,\n })\n})\n\nconst rootStyle = computed(() => {\n return {\n backgroundColor: props.background,\n zIndex: props.zIndex != null ? String(props.zIndex) : undefined,\n } as Record<string, string | undefined>\n})\n\nconst spinnerClassName = computed(() => props.spinnerClass)\n</script>\n\n<template>\n <div\n data-gr-loading\n class=\"flex items-center justify-center cursor-wait select-none pointer-events-auto\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n data-gr-loading-panel\n class=\"flex flex-col items-center justify-center gap-2 text-center rounded-lg bg-[var(--bg)]/55 px-5 py-4 shadow-lg\"\n >\n <component\n :is=\"Spinner\"\n data-gr-loading-spinner\n class=\"gr-loading__spinner h-7 w-7 text-[var(--muted-fg)]\"\n :class=\"[animated ? 'gr-loading__spinner--animated' : '', spinnerClassName]\"\n aria-hidden=\"true\"\n />\n <div v-if=\"displayText\" data-gr-loading-text class=\"text-sm text-[var(--muted-fg)]\">\n {{ displayText }}\n </div>\n </div>\n </div>\n</template>\n\n<style>\n@keyframes gr-loading-spin {\n to {\n transform: rotate(360deg);\n }\n}\n.gr-loading__spinner--animated {\n transform-origin: center;\n animation: gr-loading-spin 1s linear infinite;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;AAOA,IAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;EA0B7B,MAAM,QAAQ;EAcd,MAAM,UAAU,eAAe,MAAM,WAAW,qBAAU;EAE1D,MAAM,cAAc,eAAe,MAAM,QAAQ,oBAAoB;EAErE,MAAM,YAAY,eAAe;GAC/B,OAAO,mBAAmB;IACxB,YAAY,MAAM;IAClB,eAAe,MAAM,eAAe,KAAA;IACpC,aAAa,MAAM;GACrB,CAAC;EACH,CAAC;EAED,MAAM,YAAY,eAAe;GAC/B,OAAO;IACL,iBAAiB,MAAM;IACvB,QAAQ,MAAM,UAAU,OAAO,OAAO,MAAM,MAAM,IAAI,KAAA;GACxD;EACF,CAAC;EAED,MAAM,mBAAmB,eAAe,MAAM,YAAY;;uBAIxD,mBAuBM,OAAA;IAtBJ,mBAAA;IACA,OAAK,eAAA,CAAC,gFACE,UAAA,KAAS,CAAA;IAChB,OAAK,eAAE,UAAA,KAAS;IACjB,MAAK;IACL,aAAU;OAEV,mBAcM,OAdN,YAcM,EAAA,UAAA,GAVJ,YAME,wBALK,QAAA,KAAO,GAAA;IACZ,2BAAA;IACA,OAAK,eAAA,CAAC,sDAAoD,CACjD,QAAA,WAAQ,kCAAA,IAAyC,iBAAA,KAAgB,CAAA,CAAA;IAC1E,eAAY;4BAEH,YAAA,SAAA,UAAA,GAAX,mBAEM,OAFN,YAEM,gBADD,YAAA,KAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrNavbar-DW3MiIIf.js","names":[],"sources":["../../../../src/components/GrNavbar/GrNavbar.vue","../../../../src/components/GrNavbar/GrNavbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport IconMenu from '~icons/lucide/menu'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\n\n/**\n * GrNavbar — верхняя панель приложения (header) с заголовком,\n * опциональной кнопкой меню (обычно — для мобильных) и правым слотом\n * для действий/аватара.\n *\n * - Корень — `<header>` (landmark), без собственного `role`.\n * - `aria-label` кнопки меню локализуется через `useGranularityTranslations`.\n */\nexport interface GrNavbarProps {\n title: string\n showMenuButton?: boolean\n /** Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). */\n menuButtonClass?: string\n}\n\nwithDefaults(defineProps<GrNavbarProps>(), {\n showMenuButton: false,\n menuButtonClass: '',\n})\n\nconst emit = defineEmits<{\n (e: 'menu'): void\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst menuAriaLabel = computed(() => t('gr.navbar.openMenu', 'Open menu'))\n</script>\n\n<template>\n <header\n data-gr-navbar\n class=\"h-[56px] border-b border-[var(--brd)] bg-[var(--bg)] flex items-center justify-between px-4 sm:px-6\"\n >\n <div class=\"flex items-center gap-3\">\n <GrButton\n v-if=\"showMenuButton\"\n data-gr-navbar-menu\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"menuAriaLabel\"\n :class=\"menuButtonClass\"\n @click=\"emit('menu')\"\n >\n <GrIcon :size=\"16\">\n <IconMenu aria-hidden=\"true\" />\n </GrIcon>\n </GrButton>\n <div data-gr-navbar-title class=\"text-[14px] font-700\">\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <div class=\"flex items-center gap-3\">\n <slot />\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport IconMenu from '~icons/lucide/menu'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\n\n/**\n * GrNavbar — верхняя панель приложения (header) с заголовком,\n * опциональной кнопкой меню (обычно — для мобильных) и правым слотом\n * для действий/аватара.\n *\n * - Корень — `<header>` (landmark), без собственного `role`.\n * - `aria-label` кнопки меню локализуется через `useGranularityTranslations`.\n */\nexport interface GrNavbarProps {\n title: string\n showMenuButton?: boolean\n /** Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). */\n menuButtonClass?: string\n}\n\nwithDefaults(defineProps<GrNavbarProps>(), {\n showMenuButton: false,\n menuButtonClass: '',\n})\n\nconst emit = defineEmits<{\n (e: 'menu'): void\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst menuAriaLabel = computed(() => t('gr.navbar.openMenu', 'Open menu'))\n</script>\n\n<template>\n <header\n data-gr-navbar\n class=\"h-[56px] border-b border-[var(--brd)] bg-[var(--bg)] flex items-center justify-between px-4 sm:px-6\"\n >\n <div class=\"flex items-center gap-3\">\n <GrButton\n v-if=\"showMenuButton\"\n data-gr-navbar-menu\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"menuAriaLabel\"\n :class=\"menuButtonClass\"\n @click=\"emit('menu')\"\n >\n <GrIcon :size=\"16\">\n <IconMenu aria-hidden=\"true\" />\n </GrIcon>\n </GrButton>\n <div data-gr-navbar-title class=\"text-[14px] font-700\">\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <div class=\"flex items-center gap-3\">\n <slot />\n </div>\n </header>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BA,MAAM,OAAO;EAIb,MAAM,EAAE,MAAM,2BAA2B;EAEzC,MAAM,gBAAgB,eAAe,EAAE,sBAAsB,WAAW,CAAC;;uBAIvE,mBA6BS,UA7BT,YA6BS,CAzBP,mBAoBM,OApBN,YAoBM,CAlBI,QAAA,kBAAA,UAAA,GADR,YAaW,kBAAA;;IAXT,uBAAA;IACA,SAAQ;IACR,MAAK;IACL,QAAA;IACC,cAAY,cAAA;IACZ,OAAK,eAAE,QAAA,eAAe;IACtB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,MAAA;;2BAIH,CAFT,YAES,gBAAA,EAFA,MAAM,GAAE,GAAA;4BACgB,CAA/B,YAA+B,MAAA,YAAA,GAAA,EAArB,eAAY,OAAM,CAAA,CAAA,CAAA;;;;mEAGhC,mBAIM,OAJN,YAIM,CAHJ,WAEO,KAAA,QAAA,SAAA,CAAA,SAAA,CAAA,gBAAA,gBADF,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GAKd,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrNumberInput-DxCEvL7V.js","names":["$slots","$attrs"],"sources":["../../../../src/components/GrNumberInput/GrNumberInput.vue","../../../../src/components/GrNumberInput/GrNumberInput.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport type { InputHTMLAttributes } from 'vue'\nimport { computed, ref, useSlots } from 'vue'\n\nimport {\n grNumberInputInputClass,\n grNumberInputShellClass,\n type GrNumberInputControlsDirection,\n type GrNumberInputSize,\n type GrNumberInputState,\n type GrNumberInputTextAlign,\n} from './grNumberInputStyles'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { addLen, useAddonMeasurement } from '../../composables/internal/useAddonMeasurement'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst ADDON_PX_BY_SIZE: Record<GrNumberInputSize, number> = {\n xs: 32,\n sm: 36,\n md: 40,\n lg: 48,\n}\n\nconst BASE_PADDING_X_LEN_BY_SIZE: Record<GrNumberInputSize, string> = {\n xs: '10px',\n sm: '12px',\n md: '12px',\n lg: '16px',\n}\n\nfunction px(n: number): string {\n return `${n}px`\n}\n\nexport interface GrNumberInputProps {\n /** Значение (строка — контролируем форматирование внутри). */\n modelValue: string\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Быстрый флаг невалидности; эквивалент `state='danger'` + `aria-invalid`. */\n invalid?: boolean\n state?: GrNumberInputState\n name?: string\n id?: string\n size?: GrNumberInputSize\n\n textAlign?: GrNumberInputTextAlign\n\n decimalSeparator?: string\n step?: number\n min?: number\n max?: number\n precision?: number\n\n /**\n * BCP-47 локаль для отображения значения (группировка разрядов и разделители\n * через `Intl.NumberFormat`). Работает вместе с `useGrouping`.\n */\n locale?: string\n /**\n * Группировать разряды при отображении (когда поле не в фокусе). При фокусе\n * показывается «сырое» значение для редактирования. По умолчанию выключено.\n */\n useGrouping?: boolean\n\n /** Показывать кнопки +/-. */\n controls?: boolean\n controlsDirection?: GrNumberInputControlsDirection\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: жёсткая ширина (из `*MaxWidth` →\n * `*MinWidth` → дефолт) + обрезка контента по краю (prefix — справа,\n * suffix — слева). По умолчанию аддоны растягиваются под контент.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n\n /** i18n-friendly aria-label для кнопки \"увеличить\". */\n increaseLabel?: string\n /** i18n-friendly aria-label для кнопки \"уменьшить\". */\n decreaseLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrNumberInputProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: 'decimal',\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n textAlign: 'left',\n\n decimalSeparator: '.',\n step: 1,\n min: undefined,\n max: undefined,\n precision: undefined,\n locale: undefined,\n useGrouping: false,\n\n controls: false,\n controlsDirection: 'vertical',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n\n increaseLabel: undefined,\n decreaseLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n (e: 'change', value: string): void\n}>()\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst { t } = useGranularityTranslations()\nconst resolvedIncreaseLabel = computed(() => props.increaseLabel ?? t('gr.numberInput.increase', 'Increase'))\nconst resolvedDecreaseLabel = computed(() => props.decreaseLabel ?? t('gr.numberInput.decrease', 'Decrease'))\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst hasHorizontalControls = computed(() => props.controls && props.controlsDirection === 'horizontal')\nconst hasVerticalControls = computed(() => props.controls && props.controlsDirection === 'vertical')\n\nconst addonPx = computed(() => ADDON_PX_BY_SIZE[props.size])\nconst addonLen = computed(() => px(addonPx.value))\nconst basePaddingXLen = computed(() => BASE_PADDING_X_LEN_BY_SIZE[props.size])\n\nconst defaultAddonMinWidth = computed(() => addonLen.value)\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\n// Жёсткая ширина для fixed-режима: max → min → дефолт.\nconst prefixFixedWidth = computed(() => props.prefixMaxWidth ?? props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixFixedWidth = computed(() => props.suffixMaxWidth ?? props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst { prefixEl, suffixEl, measuredPrefixWidth, measuredSuffixWidth } = useAddonMeasurement(hasPrefix, hasSuffix)\n\nconst prefixLen = computed(() => {\n if (!hasPrefix.value) return '0px'\n return props.prefixFixed ? prefixFixedWidth.value : measuredPrefixWidth.value ?? prefixMinWidth.value\n})\nconst suffixLen = computed(() => {\n if (!hasSuffix.value) return '0px'\n return props.suffixFixed ? suffixFixedWidth.value : measuredSuffixWidth.value ?? suffixMinWidth.value\n})\n\nfunction addonStyle(side: 'left' | 'right', offset: string): Record<string, string> {\n return {\n width: addonLen.value,\n [side]: offset,\n }\n}\n\nconst inputStyle = computed(() => {\n const leftControls = hasHorizontalControls.value ? addonLen.value : '0px'\n const rightControlsCount = (hasHorizontalControls.value ? 1 : 0) + (hasVerticalControls.value ? 1 : 0)\n const rightControls = rightControlsCount > 0 ? px(addonPx.value * rightControlsCount) : '0px'\n\n const leftReserved = addLen(prefixLen.value, leftControls)\n const rightReserved = addLen(suffixLen.value, rightControls)\n\n const left = hasPrefix.value || hasHorizontalControls.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const right = hasSuffix.value || rightControlsCount > 0 ? addLen(rightReserved, basePaddingXLen.value) : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n if (props.suffixFixed) {\n return {\n right: '0px',\n width: suffixFixedWidth.value,\n minWidth: suffixFixedWidth.value,\n maxWidth: suffixFixedWidth.value,\n }\n }\n\n return {\n right: '0px',\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n }\n})\n\nconst prefixStyle = computed(() => {\n if (props.prefixFixed) {\n return {\n width: prefixFixedWidth.value,\n minWidth: prefixFixedWidth.value,\n maxWidth: prefixFixedWidth.value,\n }\n }\n\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n }\n})\n\nconst verticalControlsStyle = computed(() => addonStyle('right', suffixLen.value))\nconst horizontalLeftControlsStyle = computed(() => addonStyle('left', prefixLen.value))\nconst horizontalRightControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\nconst shellClassName = computed(() => {\n return grNumberInputShellClass({\n disabled: props.disabled,\n state: props.invalid ? 'danger' : props.state,\n })\n})\n\nconst inputClassName = computed(() => {\n return grNumberInputInputClass({\n size: props.size,\n textAlign: props.textAlign,\n })\n})\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction sanitize(raw: string): string {\n const sep = props.decimalSeparator || '.'\n\n let out = ''\n let hasSepLocal = false\n\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i]\n\n // Ведущий минус: разрешаем один в начале, чтобы можно было ввести\n // отрицательное значение с клавиатуры (границы проверит `clamp` на change).\n if (ch === '-' && out.length === 0) {\n out += '-'\n continue\n }\n\n if (isDigit(ch)) {\n out += ch\n continue\n }\n\n if ((ch === sep || ch === '.' || ch === ',') && !hasSepLocal) {\n out += sep\n hasSepLocal = true\n }\n }\n\n return out\n}\n\nfunction toNumber(value: string): number | null {\n if (value.trim() === '') return null\n const sep = props.decimalSeparator || '.'\n const normalized = sep === '.' ? value : value.replace(sep, '.')\n const n = Number(normalized)\n if (!Number.isFinite(n)) return null\n return n\n}\n\nfunction clamp(v: number): number {\n if (props.min !== undefined) v = Math.max(props.min, v)\n if (props.max !== undefined) v = Math.min(props.max, v)\n return v\n}\n\nfunction normalize(v: number): number {\n if (props.precision === undefined) return v\n const p = Math.max(0, props.precision)\n return Number(v.toFixed(p))\n}\n\nfunction format(n: number): string {\n const sep = props.decimalSeparator || '.'\n const p = props.precision\n\n // Без `precision` избегаем экспоненциальной записи (`String(1e21)` → `\"1e+21\"`),\n // форматируя через `Intl` без группировки разрядов.\n let s = p === undefined\n ? n.toLocaleString('en-US', { useGrouping: false, maximumFractionDigits: 20 })\n : n.toFixed(Math.max(0, p))\n if (sep !== '.') s = s.replace('.', sep)\n return s\n}\n\nfunction onInput(e: Event): void {\n const el = e.target as HTMLInputElement\n const raw = el.value\n const next = sanitize(raw)\n\n if (raw !== next) {\n // Сохраняем позицию каретки: считаем, сколько валидных символов было ДО\n // каретки, и ставим её после стольких же в очищенной строке — иначе при\n // редактировании середины числа каретка прыгала в конец.\n const caret = el.selectionStart ?? raw.length\n const keptBefore = sanitize(raw.slice(0, caret)).length\n el.value = next\n el.setSelectionRange(keptBefore, keptBefore)\n }\n\n emit('update:modelValue', next)\n}\n\nfunction commit(el: HTMLInputElement): void {\n const num = toNumber(sanitize(el.value))\n // Пустое значение (или незавершённый ввод `-`) оставляем как есть.\n const next = num === null ? sanitize(el.value) : format(clamp(normalize(num)))\n\n if (el.value !== next) el.value = next\n if (next !== props.modelValue) emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction onChange(e: Event): void {\n // Клампинг границ (`min`/`max`) и нормализация — на `change`/`blur`, а не только\n // в кнопках: раньше ручной ввод «999» при `max=10` оставался невалидным.\n commit(e.target as HTMLInputElement)\n}\n\nfunction setValue(n: number): void {\n const nextStr = format(clamp(normalize(n)))\n emit('update:modelValue', nextStr)\n emit('change', nextStr)\n}\n\nfunction stepBy(dir: 1 | -1): void {\n if (props.disabled) return\n\n const current = toNumber(props.modelValue) ?? 0\n setValue(current + (props.step ?? 1) * dir)\n focus()\n}\n\n// Клавиатура спинбаттона (WAI-ARIA spinbutton): стрелки шагают, Home/End —\n// к границам `min`/`max` (если заданы).\nfunction onKeydown(e: KeyboardEvent): void {\n if (props.disabled) return\n\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n stepBy(1)\n break\n case 'ArrowDown':\n e.preventDefault()\n stepBy(-1)\n break\n case 'Home':\n if (props.min !== undefined) {\n e.preventDefault()\n setValue(props.min)\n }\n break\n case 'End':\n if (props.max !== undefined) {\n e.preventDefault()\n setValue(props.max)\n }\n break\n }\n}\n\n// `aria-valuenow` — числовое значение для скринридеров; отсутствует, если поле пусто.\nconst ariaValueNow = computed(() => toNumber(props.modelValue) ?? undefined)\n\n// Локале-зависимое отображение: при фокусе показываем «сырое» значение для\n// редактирования, иначе — сгруппированное через `Intl.NumberFormat`.\nconst isFocused = ref(false)\n\nfunction formatGrouped(value: string): string {\n const num = toNumber(value)\n if (num === null) return value\n const sep = props.decimalSeparator || '.'\n // formatToParts позволяет оставить локале-зависимый групповой разделитель,\n // но принудительно подставить наш десятичный разделитель (`decimalSeparator`),\n // корректно работая для любой локали (в т.ч. de-DE, где группа — '.').\n return new Intl.NumberFormat(props.locale, {\n useGrouping: true,\n minimumFractionDigits: props.precision ?? 0,\n maximumFractionDigits: props.precision ?? 20,\n })\n .formatToParts(num)\n .map(part => (part.type === 'decimal' ? sep : part.value))\n .join('')\n}\n\nconst displayValue = computed(() => {\n if (!props.useGrouping || isFocused.value) return props.modelValue\n return formatGrouped(props.modelValue)\n})\n\nfunction onFocus(): void {\n isFocused.value = true\n}\n\nfunction onBlur(): void {\n isFocused.value = false\n}\n</script>\n\n<template>\n <div\n data-gr-number-input\n class=\"relative w-full overflow-hidden rounded-md border bg-[var(--bg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"shellClassName\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"number-input-prefix\"\n data-gr-number-input-prefix\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"id\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"name\"\n type=\"text\"\n :inputmode=\"inputmode\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :value=\"displayValue\"\n role=\"spinbutton\"\n :aria-valuenow=\"ariaValueNow\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"inputClassName\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n @keydown=\"onKeydown\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"number-input-suffix\"\n data-gr-number-input-suffix\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <div\n v-if=\"hasVerticalControls\"\n data-testid=\"number-input-controls-vertical\"\n data-gr-number-input-controls=\"vertical\"\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)]\"\n :style=\"verticalControlsStyle\"\n >\n <div class=\"flex flex-col justify-center gap-1\">\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 14l5-5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 10l5 5 5-5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-left\"\n data-gr-number-input-controls=\"horizontal-left\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-r border-[var(--brd)]\"\n :style=\"horizontalLeftControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M14 7l-5 5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-right\"\n data-gr-number-input-controls=\"horizontal-right\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-l border-[var(--brd)]\"\n :style=\"horizontalRightControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10 7l5 5-5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport type { InputHTMLAttributes } from 'vue'\nimport { computed, ref, useSlots } from 'vue'\n\nimport {\n grNumberInputInputClass,\n grNumberInputShellClass,\n type GrNumberInputControlsDirection,\n type GrNumberInputSize,\n type GrNumberInputState,\n type GrNumberInputTextAlign,\n} from './grNumberInputStyles'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { addLen, useAddonMeasurement } from '../../composables/internal/useAddonMeasurement'\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst ADDON_PX_BY_SIZE: Record<GrNumberInputSize, number> = {\n xs: 32,\n sm: 36,\n md: 40,\n lg: 48,\n}\n\nconst BASE_PADDING_X_LEN_BY_SIZE: Record<GrNumberInputSize, string> = {\n xs: '10px',\n sm: '12px',\n md: '12px',\n lg: '16px',\n}\n\nfunction px(n: number): string {\n return `${n}px`\n}\n\nexport interface GrNumberInputProps {\n /** Значение (строка — контролируем форматирование внутри). */\n modelValue: string\n placeholder?: string\n autocomplete?: string\n inputmode?: InputHTMLAttributes['inputmode']\n disabled?: boolean\n /** Быстрый флаг невалидности; эквивалент `state='danger'` + `aria-invalid`. */\n invalid?: boolean\n state?: GrNumberInputState\n name?: string\n id?: string\n size?: GrNumberInputSize\n\n textAlign?: GrNumberInputTextAlign\n\n decimalSeparator?: string\n step?: number\n min?: number\n max?: number\n precision?: number\n\n /**\n * BCP-47 локаль для отображения значения (группировка разрядов и разделители\n * через `Intl.NumberFormat`). Работает вместе с `useGrouping`.\n */\n locale?: string\n /**\n * Группировать разряды при отображении (когда поле не в фокусе). При фокусе\n * показывается «сырое» значение для редактирования. По умолчанию выключено.\n */\n useGrouping?: boolean\n\n /** Показывать кнопки +/-. */\n controls?: boolean\n controlsDirection?: GrNumberInputControlsDirection\n\n prefixMinWidth?: string\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Фиксированная ширина у prefix/suffix: жёсткая ширина (из `*MaxWidth` →\n * `*MinWidth` → дефолт) + обрезка контента по краю (prefix — справа,\n * suffix — слева). По умолчанию аддоны растягиваются под контент.\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n\n /** i18n-friendly aria-label для кнопки \"увеличить\". */\n increaseLabel?: string\n /** i18n-friendly aria-label для кнопки \"уменьшить\". */\n decreaseLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrNumberInputProps>(), {\n placeholder: undefined,\n autocomplete: undefined,\n inputmode: 'decimal',\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n size: 'md',\n\n textAlign: 'left',\n\n decimalSeparator: '.',\n step: 1,\n min: undefined,\n max: undefined,\n precision: undefined,\n locale: undefined,\n useGrouping: false,\n\n controls: false,\n controlsDirection: 'vertical',\n\n prefixMinWidth: undefined,\n prefixMaxWidth: undefined,\n suffixMinWidth: undefined,\n suffixMaxWidth: undefined,\n prefixFixed: false,\n suffixFixed: false,\n\n increaseLabel: undefined,\n decreaseLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n (e: 'change', value: string): void\n}>()\n\nconst inputEl = ref<HTMLInputElement | null>(null)\n\nfunction focus(): void {\n inputEl.value?.focus()\n}\n\ndefineExpose({\n focus,\n})\n\nconst slots = useSlots()\n\nconst { t } = useGranularityTranslations()\nconst resolvedIncreaseLabel = computed(() => props.increaseLabel ?? t('gr.numberInput.increase', 'Increase'))\nconst resolvedDecreaseLabel = computed(() => props.decreaseLabel ?? t('gr.numberInput.decrease', 'Decrease'))\n\nconst hasPrefix = computed(() => Boolean(slots.prefix))\nconst hasSuffix = computed(() => Boolean(slots.suffix))\n\nconst hasHorizontalControls = computed(() => props.controls && props.controlsDirection === 'horizontal')\nconst hasVerticalControls = computed(() => props.controls && props.controlsDirection === 'vertical')\n\nconst addonPx = computed(() => ADDON_PX_BY_SIZE[props.size])\nconst addonLen = computed(() => px(addonPx.value))\nconst basePaddingXLen = computed(() => BASE_PADDING_X_LEN_BY_SIZE[props.size])\n\nconst defaultAddonMinWidth = computed(() => addonLen.value)\nconst prefixMinWidth = computed(() => props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixMinWidth = computed(() => props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\n// Жёсткая ширина для fixed-режима: max → min → дефолт.\nconst prefixFixedWidth = computed(() => props.prefixMaxWidth ?? props.prefixMinWidth ?? defaultAddonMinWidth.value)\nconst suffixFixedWidth = computed(() => props.suffixMaxWidth ?? props.suffixMinWidth ?? defaultAddonMinWidth.value)\n\nconst { prefixEl, suffixEl, measuredPrefixWidth, measuredSuffixWidth } = useAddonMeasurement(hasPrefix, hasSuffix)\n\nconst prefixLen = computed(() => {\n if (!hasPrefix.value) return '0px'\n return props.prefixFixed ? prefixFixedWidth.value : measuredPrefixWidth.value ?? prefixMinWidth.value\n})\nconst suffixLen = computed(() => {\n if (!hasSuffix.value) return '0px'\n return props.suffixFixed ? suffixFixedWidth.value : measuredSuffixWidth.value ?? suffixMinWidth.value\n})\n\nfunction addonStyle(side: 'left' | 'right', offset: string): Record<string, string> {\n return {\n width: addonLen.value,\n [side]: offset,\n }\n}\n\nconst inputStyle = computed(() => {\n const leftControls = hasHorizontalControls.value ? addonLen.value : '0px'\n const rightControlsCount = (hasHorizontalControls.value ? 1 : 0) + (hasVerticalControls.value ? 1 : 0)\n const rightControls = rightControlsCount > 0 ? px(addonPx.value * rightControlsCount) : '0px'\n\n const leftReserved = addLen(prefixLen.value, leftControls)\n const rightReserved = addLen(suffixLen.value, rightControls)\n\n const left = hasPrefix.value || hasHorizontalControls.value ? addLen(leftReserved, basePaddingXLen.value) : undefined\n const right = hasSuffix.value || rightControlsCount > 0 ? addLen(rightReserved, basePaddingXLen.value) : undefined\n\n return {\n paddingLeft: left,\n paddingRight: right,\n } as Record<string, string | undefined>\n})\n\nconst suffixStyle = computed(() => {\n if (props.suffixFixed) {\n return {\n right: '0px',\n width: suffixFixedWidth.value,\n minWidth: suffixFixedWidth.value,\n maxWidth: suffixFixedWidth.value,\n }\n }\n\n return {\n right: '0px',\n minWidth: suffixMinWidth.value,\n maxWidth: props.suffixMaxWidth,\n }\n})\n\nconst prefixStyle = computed(() => {\n if (props.prefixFixed) {\n return {\n width: prefixFixedWidth.value,\n minWidth: prefixFixedWidth.value,\n maxWidth: prefixFixedWidth.value,\n }\n }\n\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props.prefixMaxWidth,\n }\n})\n\nconst verticalControlsStyle = computed(() => addonStyle('right', suffixLen.value))\nconst horizontalLeftControlsStyle = computed(() => addonStyle('left', prefixLen.value))\nconst horizontalRightControlsStyle = computed(() => addonStyle('right', suffixLen.value))\n\nconst shellClassName = computed(() => {\n return grNumberInputShellClass({\n disabled: props.disabled,\n state: props.invalid ? 'danger' : props.state,\n })\n})\n\nconst inputClassName = computed(() => {\n return grNumberInputInputClass({\n size: props.size,\n textAlign: props.textAlign,\n })\n})\n\nfunction isDigit(ch: string): boolean {\n return ch >= '0' && ch <= '9'\n}\n\nfunction sanitize(raw: string): string {\n const sep = props.decimalSeparator || '.'\n\n let out = ''\n let hasSepLocal = false\n\n for (let i = 0; i < raw.length; i++) {\n const ch = raw[i]\n\n // Ведущий минус: разрешаем один в начале, чтобы можно было ввести\n // отрицательное значение с клавиатуры (границы проверит `clamp` на change).\n if (ch === '-' && out.length === 0) {\n out += '-'\n continue\n }\n\n if (isDigit(ch)) {\n out += ch\n continue\n }\n\n if ((ch === sep || ch === '.' || ch === ',') && !hasSepLocal) {\n out += sep\n hasSepLocal = true\n }\n }\n\n return out\n}\n\nfunction toNumber(value: string): number | null {\n if (value.trim() === '') return null\n const sep = props.decimalSeparator || '.'\n const normalized = sep === '.' ? value : value.replace(sep, '.')\n const n = Number(normalized)\n if (!Number.isFinite(n)) return null\n return n\n}\n\nfunction clamp(v: number): number {\n if (props.min !== undefined) v = Math.max(props.min, v)\n if (props.max !== undefined) v = Math.min(props.max, v)\n return v\n}\n\nfunction normalize(v: number): number {\n if (props.precision === undefined) return v\n const p = Math.max(0, props.precision)\n return Number(v.toFixed(p))\n}\n\nfunction format(n: number): string {\n const sep = props.decimalSeparator || '.'\n const p = props.precision\n\n // Без `precision` избегаем экспоненциальной записи (`String(1e21)` → `\"1e+21\"`),\n // форматируя через `Intl` без группировки разрядов.\n let s = p === undefined\n ? n.toLocaleString('en-US', { useGrouping: false, maximumFractionDigits: 20 })\n : n.toFixed(Math.max(0, p))\n if (sep !== '.') s = s.replace('.', sep)\n return s\n}\n\nfunction onInput(e: Event): void {\n const el = e.target as HTMLInputElement\n const raw = el.value\n const next = sanitize(raw)\n\n if (raw !== next) {\n // Сохраняем позицию каретки: считаем, сколько валидных символов было ДО\n // каретки, и ставим её после стольких же в очищенной строке — иначе при\n // редактировании середины числа каретка прыгала в конец.\n const caret = el.selectionStart ?? raw.length\n const keptBefore = sanitize(raw.slice(0, caret)).length\n el.value = next\n el.setSelectionRange(keptBefore, keptBefore)\n }\n\n emit('update:modelValue', next)\n}\n\nfunction commit(el: HTMLInputElement): void {\n const num = toNumber(sanitize(el.value))\n // Пустое значение (или незавершённый ввод `-`) оставляем как есть.\n const next = num === null ? sanitize(el.value) : format(clamp(normalize(num)))\n\n if (el.value !== next) el.value = next\n if (next !== props.modelValue) emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction onChange(e: Event): void {\n // Клампинг границ (`min`/`max`) и нормализация — на `change`/`blur`, а не только\n // в кнопках: раньше ручной ввод «999» при `max=10` оставался невалидным.\n commit(e.target as HTMLInputElement)\n}\n\nfunction setValue(n: number): void {\n const nextStr = format(clamp(normalize(n)))\n emit('update:modelValue', nextStr)\n emit('change', nextStr)\n}\n\nfunction stepBy(dir: 1 | -1): void {\n if (props.disabled) return\n\n const current = toNumber(props.modelValue) ?? 0\n setValue(current + (props.step ?? 1) * dir)\n focus()\n}\n\n// Клавиатура спинбаттона (WAI-ARIA spinbutton): стрелки шагают, Home/End —\n// к границам `min`/`max` (если заданы).\nfunction onKeydown(e: KeyboardEvent): void {\n if (props.disabled) return\n\n switch (e.key) {\n case 'ArrowUp':\n e.preventDefault()\n stepBy(1)\n break\n case 'ArrowDown':\n e.preventDefault()\n stepBy(-1)\n break\n case 'Home':\n if (props.min !== undefined) {\n e.preventDefault()\n setValue(props.min)\n }\n break\n case 'End':\n if (props.max !== undefined) {\n e.preventDefault()\n setValue(props.max)\n }\n break\n }\n}\n\n// `aria-valuenow` — числовое значение для скринридеров; отсутствует, если поле пусто.\nconst ariaValueNow = computed(() => toNumber(props.modelValue) ?? undefined)\n\n// Локале-зависимое отображение: при фокусе показываем «сырое» значение для\n// редактирования, иначе — сгруппированное через `Intl.NumberFormat`.\nconst isFocused = ref(false)\n\nfunction formatGrouped(value: string): string {\n const num = toNumber(value)\n if (num === null) return value\n const sep = props.decimalSeparator || '.'\n // formatToParts позволяет оставить локале-зависимый групповой разделитель,\n // но принудительно подставить наш десятичный разделитель (`decimalSeparator`),\n // корректно работая для любой локали (в т.ч. de-DE, где группа — '.').\n return new Intl.NumberFormat(props.locale, {\n useGrouping: true,\n minimumFractionDigits: props.precision ?? 0,\n maximumFractionDigits: props.precision ?? 20,\n })\n .formatToParts(num)\n .map(part => (part.type === 'decimal' ? sep : part.value))\n .join('')\n}\n\nconst displayValue = computed(() => {\n if (!props.useGrouping || isFocused.value) return props.modelValue\n return formatGrouped(props.modelValue)\n})\n\nfunction onFocus(): void {\n isFocused.value = true\n}\n\nfunction onBlur(): void {\n isFocused.value = false\n}\n</script>\n\n<template>\n <div\n data-gr-number-input\n class=\"relative w-full overflow-hidden rounded-md border bg-[var(--bg)] transition-colors duration-150 focus-within:ring-2 focus-within:ring-[var(--ring)]\"\n :class=\"shellClassName\"\n >\n <div\n v-if=\"$slots.prefix\"\n ref=\"prefixEl\"\n data-testid=\"number-input-prefix\"\n data-gr-number-input-prefix\n class=\"absolute inset-y-0 left-0 flex items-center justify-center border-r border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :style=\"prefixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"prefix\" />\n </div>\n\n <input\n :id=\"id\"\n ref=\"inputEl\"\n v-bind=\"$attrs\"\n :name=\"name\"\n type=\"text\"\n :inputmode=\"inputmode\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :value=\"displayValue\"\n role=\"spinbutton\"\n :aria-valuenow=\"ariaValueNow\"\n :aria-valuemin=\"min\"\n :aria-valuemax=\"max\"\n :aria-invalid=\"invalid ? 'true' : undefined\"\n class=\"w-full bg-transparent text-[var(--fg)] placeholder:text-[var(--muted-fg)] focus:placeholder:text-transparent focus:outline-none disabled:cursor-not-allowed\"\n :class=\"inputClassName\"\n :style=\"inputStyle\"\n @input=\"onInput\"\n @change=\"onChange\"\n @keydown=\"onKeydown\"\n @focus=\"onFocus\"\n @blur=\"onBlur\"\n >\n\n <div\n v-if=\"$slots.suffix\"\n ref=\"suffixEl\"\n data-testid=\"number-input-suffix\"\n data-gr-number-input-suffix\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)] px-2 text-[var(--muted-fg)] pointer-events-none select-none truncate\"\n :class=\"suffixFixed ? '[direction:rtl]' : ''\"\n :style=\"suffixStyle\"\n aria-hidden=\"true\"\n >\n <slot name=\"suffix\" />\n </div>\n\n <div\n v-if=\"hasVerticalControls\"\n data-testid=\"number-input-controls-vertical\"\n data-gr-number-input-controls=\"vertical\"\n class=\"absolute inset-y-0 flex items-center justify-center border-l border-[var(--brd)]\"\n :style=\"verticalControlsStyle\"\n >\n <div class=\"flex flex-col justify-center gap-1\">\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 14l5-5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n class=\"h-4 w-7 inline-flex items-center justify-center rounded text-[10px] text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M7 10l5 5 5-5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-left\"\n data-gr-number-input-controls=\"horizontal-left\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-r border-[var(--brd)]\"\n :style=\"horizontalLeftControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedDecreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(-1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M14 7l-5 5 5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n\n <div\n v-if=\"hasHorizontalControls\"\n data-testid=\"number-input-controls-horizontal-right\"\n data-gr-number-input-controls=\"horizontal-right\"\n class=\"absolute inset-y-0 flex items-stretch justify-center border-l border-[var(--brd)]\"\n :style=\"horizontalRightControlsStyle\"\n >\n <button\n type=\"button\"\n class=\"h-full w-full inline-flex items-center justify-center text-[var(--muted-fg)] hover:bg-[var(--muted)] active:bg-[var(--muted)] disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedIncreaseLabel\"\n @mousedown.prevent\n @click=\"stepBy(1)\"\n >\n <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path\n d=\"M10 7l5 5-5 5\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </button>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrPagination-mlyfLjQY.js","names":[],"sources":["../../../../src/components/GrPagination/GrPagination.vue","../../../../src/components/GrPagination/GrPagination.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, watch } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Номера усекаются многоточием\n * (алгоритм boundary/sibling, как у MUI): всегда видны первая/последняя страница\n * и `siblingCount` соседей вокруг текущей.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n /** Сколько соседних страниц показывать вокруг текущей. По умолчанию `1`. */\n siblingCount?: number\n /** Сколько крайних страниц всегда показывать с каждого края. По умолчанию `1`. */\n boundaryCount?: number\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n siblingCount: 1,\n boundaryCount: 1,\n})\n\nconst emit = defineEmits<{\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}>()\n\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)))\n\n// Контролируемый компонент: если `total`/`pageSize` уменьшились и текущая `page`\n// вышла за диапазон — просим родителя подтянуть её к последней странице.\nwatch(pageCount, (count) => {\n if (props.page > count)\n emit('update:page', count)\n})\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\ntype PaginationItem = number | 'ellipsis-start' | 'ellipsis-end'\n\nfunction range(start: number, end: number): number[] {\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n return list\n}\n\n// Алгоритм usePagination как в MUI: крайние `boundaryCount` страниц + `siblingCount`\n// соседей вокруг текущей, разрывы схлопываются в многоточие. Одиночный «пропущенный»\n// номер показываем как номер, а не как «…» (визуально ровнее).\nconst items = computed<PaginationItem[]>(() => {\n const count = pageCount.value\n const current = props.page\n const boundaryCount = Math.max(0, props.boundaryCount)\n const siblingCount = Math.max(0, props.siblingCount)\n\n const startPages = range(1, Math.min(boundaryCount, count))\n const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count)\n\n const siblingsStart = Math.max(\n Math.min(current - siblingCount, count - boundaryCount - siblingCount * 2 - 1),\n boundaryCount + 2,\n )\n const siblingsEnd = Math.min(\n Math.max(current + siblingCount, boundaryCount + siblingCount * 2 + 2),\n endPages.length > 0 ? endPages[0] - 2 : count - 1,\n )\n\n return [\n ...startPages,\n ...(siblingsStart > boundaryCount + 2\n ? ['ellipsis-start' as const]\n : boundaryCount + 1 < count - boundaryCount\n ? [boundaryCount + 1]\n : []),\n ...range(siblingsStart, siblingsEnd),\n ...(siblingsEnd < count - boundaryCount - 1\n ? ['ellipsis-end' as const]\n : count - boundaryCount > boundaryCount\n ? [count - boundaryCount]\n : []),\n ...endPages,\n ]\n})\n\nfunction goTo(page: number): void {\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, props.page - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, props.page + 1))\n}\n\nfunction first(): void {\n goTo(1)\n}\n\nfunction last(): void {\n goTo(pageCount.value)\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end gap-3\"\n data-gr-pagination\n role=\"navigation\"\n :aria-label=\"t('gr.pagination.label', 'Pagination')\"\n >\n <div class=\"min-w-[100px]\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" :aria-label=\"t('gr.pagination.first', 'First page')\" data-gr-pagination-first @click=\"first\">\n «\n </GrButton>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" data-gr-pagination-prev @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <div class=\"flex items-center gap-1\">\n <template v-for=\"(item, index) in items\" :key=\"index\">\n <span\n v-if=\"item === 'ellipsis-start' || item === 'ellipsis-end'\"\n data-gr-pagination-ellipsis\n aria-hidden=\"true\"\n class=\"h-8 min-w-8 px-1 grid place-items-center text-sm text-[var(--muted-fg)]\"\n >…</span>\n <button\n v-else\n type=\"button\"\n data-gr-pagination-page\n :aria-current=\"item === page ? 'page' : undefined\"\n :aria-label=\"t('gr.pagination.page', 'Page {n}', { n: item })\"\n class=\"h-8 min-w-8 px-2 rounded-md text-sm font-600 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :class=\"item === page ? 'bg-[var(--primary)] text-[var(--primary-fg)]' : 'text-[var(--muted-fg)] hover:bg-[var(--muted)] hover:text-[var(--fg)]'\"\n @click=\"goTo(item)\"\n >\n {{ item }}\n </button>\n </template>\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" data-gr-pagination-next @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" :aria-label=\"t('gr.pagination.last', 'Last page')\" data-gr-pagination-last @click=\"last\">\n »\n </GrButton>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, watch } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Номера усекаются многоточием\n * (алгоритм boundary/sibling, как у MUI): всегда видны первая/последняя страница\n * и `siblingCount` соседей вокруг текущей.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n /** Сколько соседних страниц показывать вокруг текущей. По умолчанию `1`. */\n siblingCount?: number\n /** Сколько крайних страниц всегда показывать с каждого края. По умолчанию `1`. */\n boundaryCount?: number\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n siblingCount: 1,\n boundaryCount: 1,\n})\n\nconst emit = defineEmits<{\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}>()\n\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / props.pageSize)))\n\n// Контролируемый компонент: если `total`/`pageSize` уменьшились и текущая `page`\n// вышла за диапазон — просим родителя подтянуть её к последней странице.\nwatch(pageCount, (count) => {\n if (props.page > count)\n emit('update:page', count)\n})\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\ntype PaginationItem = number | 'ellipsis-start' | 'ellipsis-end'\n\nfunction range(start: number, end: number): number[] {\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n return list\n}\n\n// Алгоритм usePagination как в MUI: крайние `boundaryCount` страниц + `siblingCount`\n// соседей вокруг текущей, разрывы схлопываются в многоточие. Одиночный «пропущенный»\n// номер показываем как номер, а не как «…» (визуально ровнее).\nconst items = computed<PaginationItem[]>(() => {\n const count = pageCount.value\n const current = props.page\n const boundaryCount = Math.max(0, props.boundaryCount)\n const siblingCount = Math.max(0, props.siblingCount)\n\n const startPages = range(1, Math.min(boundaryCount, count))\n const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count)\n\n const siblingsStart = Math.max(\n Math.min(current - siblingCount, count - boundaryCount - siblingCount * 2 - 1),\n boundaryCount + 2,\n )\n const siblingsEnd = Math.min(\n Math.max(current + siblingCount, boundaryCount + siblingCount * 2 + 2),\n endPages.length > 0 ? endPages[0] - 2 : count - 1,\n )\n\n return [\n ...startPages,\n ...(siblingsStart > boundaryCount + 2\n ? ['ellipsis-start' as const]\n : boundaryCount + 1 < count - boundaryCount\n ? [boundaryCount + 1]\n : []),\n ...range(siblingsStart, siblingsEnd),\n ...(siblingsEnd < count - boundaryCount - 1\n ? ['ellipsis-end' as const]\n : count - boundaryCount > boundaryCount\n ? [count - boundaryCount]\n : []),\n ...endPages,\n ]\n})\n\nfunction goTo(page: number): void {\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, props.page - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, props.page + 1))\n}\n\nfunction first(): void {\n goTo(1)\n}\n\nfunction last(): void {\n goTo(pageCount.value)\n}\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end gap-3\"\n data-gr-pagination\n role=\"navigation\"\n :aria-label=\"t('gr.pagination.label', 'Pagination')\"\n >\n <div class=\"min-w-[100px]\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" :aria-label=\"t('gr.pagination.first', 'First page')\" data-gr-pagination-first @click=\"first\">\n «\n </GrButton>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page <= 1\" data-gr-pagination-prev @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <div class=\"flex items-center gap-1\">\n <template v-for=\"(item, index) in items\" :key=\"index\">\n <span\n v-if=\"item === 'ellipsis-start' || item === 'ellipsis-end'\"\n data-gr-pagination-ellipsis\n aria-hidden=\"true\"\n class=\"h-8 min-w-8 px-1 grid place-items-center text-sm text-[var(--muted-fg)]\"\n >…</span>\n <button\n v-else\n type=\"button\"\n data-gr-pagination-page\n :aria-current=\"item === page ? 'page' : undefined\"\n :aria-label=\"t('gr.pagination.page', 'Page {n}', { n: item })\"\n class=\"h-8 min-w-8 px-2 rounded-md text-sm font-600 transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n :class=\"item === page ? 'bg-[var(--primary)] text-[var(--primary-fg)]' : 'text-[var(--muted-fg)] hover:bg-[var(--muted)] hover:text-[var(--fg)]'\"\n @click=\"goTo(item)\"\n >\n {{ item }}\n </button>\n </template>\n </div>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" data-gr-pagination-next @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n\n <GrButton variant=\"ghost\" size=\"sm\" :disabled=\"page >= pageCount\" :aria-label=\"t('gr.pagination.last', 'Last page')\" data-gr-pagination-last @click=\"last\">\n »\n </GrButton>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrProgressBar-CC3z-Zqp.js","names":[],"sources":["../../../../src/components/GrProgressBar/GrProgressBar.vue","../../../../src/components/GrProgressBar/GrProgressBar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { grProgressBarFillClass, type GrProgressBarTone } from './grStyle'\n\n/**\n * GrProgressBar — линейный индикатор прогресса.\n *\n * @prop value — текущее значение `0..100`; выходящие за границы значения клампятся, `NaN` → `0`.\n * @prop ariaLabel — метка для скринридера (обязательна, если рядом нет видимого заголовка).\n * @prop tone — цветовая тональность заливки (CSS-переменная из `tokens.css`).\n */\nexport interface GrProgressBarProps {\n value: number\n ariaLabel?: string\n tone?: GrProgressBarTone\n}\n\nconst props = withDefaults(defineProps<GrProgressBarProps>(), {\n ariaLabel: undefined,\n tone: 'primary',\n})\n\nconst safe = computed(() => {\n if (Number.isNaN(props.value))\n return 0\n return Math.min(100, Math.max(0, props.value))\n})\n\nconst fillClassName = computed(() => grProgressBarFillClass(props.tone))\n</script>\n\n<template>\n <div\n data-gr-progress-bar\n role=\"progressbar\"\n :aria-label=\"ariaLabel\"\n :aria-valuenow=\"safe\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n class=\"h-2 w-full rounded-full bg-[var(--muted)] border border-[var(--brd)] overflow-hidden\"\n >\n <div\n data-gr-progress-bar-fill\n class=\"h-full\"\n :class=\"fillClassName\"\n :style=\"{ width: `${safe}%` }\"\n />\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { grProgressBarFillClass, type GrProgressBarTone } from './grStyle'\n\n/**\n * GrProgressBar — линейный индикатор прогресса.\n *\n * @prop value — текущее значение `0..100`; выходящие за границы значения клампятся, `NaN` → `0`.\n * @prop ariaLabel — метка для скринридера (обязательна, если рядом нет видимого заголовка).\n * @prop tone — цветовая тональность заливки (CSS-переменная из `tokens.css`).\n */\nexport interface GrProgressBarProps {\n value: number\n ariaLabel?: string\n tone?: GrProgressBarTone\n}\n\nconst props = withDefaults(defineProps<GrProgressBarProps>(), {\n ariaLabel: undefined,\n tone: 'primary',\n})\n\nconst safe = computed(() => {\n if (Number.isNaN(props.value))\n return 0\n return Math.min(100, Math.max(0, props.value))\n})\n\nconst fillClassName = computed(() => grProgressBarFillClass(props.tone))\n</script>\n\n<template>\n <div\n data-gr-progress-bar\n role=\"progressbar\"\n :aria-label=\"ariaLabel\"\n :aria-valuenow=\"safe\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n class=\"h-2 w-full rounded-full bg-[var(--muted)] border border-[var(--brd)] overflow-hidden\"\n >\n <div\n data-gr-progress-bar-fill\n class=\"h-full\"\n :class=\"fillClassName\"\n :style=\"{ width: `${safe}%` }\"\n />\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;EAkBA,MAAM,QAAQ;EAKd,MAAM,OAAO,eAAe;GAC1B,IAAI,OAAO,MAAM,MAAM,KAAK,GAC1B,OAAO;GACT,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,MAAM,KAAK,CAAC;EAC/C,CAAC;EAED,MAAM,gBAAgB,eAAe,uBAAuB,MAAM,IAAI,CAAC;;uBAIrE,mBAeM,OAAA;IAdJ,wBAAA;IACA,MAAK;IACJ,cAAY,QAAA;IACZ,iBAAe,KAAA;IAChB,iBAAc;IACd,iBAAc;IACd,OAAM;OAEN,mBAKE,OAAA;IAJA,6BAAA;IACA,OAAK,eAAA,CAAC,UACE,cAAA,KAAa,CAAA;IACpB,OAAK,eAAA,EAAA,OAAA,GAAc,KAAA,MAAI,GAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrPromptDialog-C-_BMNuY.js","names":[],"sources":["../../../../src/components/GrPromptDialog/GrPromptDialog.vue","../../../../src/components/GrPromptDialog/GrPromptDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrPromptDialog — GR-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n */\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой `required`. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n}\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: undefined,\n description: undefined,\n label: undefined,\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: undefined,\n buttonSize: undefined,\n confirmText: undefined,\n cancelText: undefined,\n requiredErrorText: undefined,\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}>()\n\n// Дефолты берём из общего i18n-блока пакета (fallback — англ.), а не хардкодим.\nconst { t } = useGranularityTranslations()\nconst resolvedTitle = computed(() => props.title ?? t('gr.dialog.prompt.title', 'Prompt'))\nconst resolvedLabel = computed(() => props.label ?? t('gr.dialog.prompt.label', 'Value'))\nconst resolvedRequiredErrorText = computed(() => props.requiredErrorText ?? t('gr.dialog.prompt.required', 'Enter a value.'))\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? t('gr.common.close', 'Close'))\nconst resolvedConfirmText = computed(() => props.confirmText ?? t('gr.common.confirm', 'Confirm'))\nconst resolvedCancelText = computed(() => props.cancelText ?? t('gr.common.cancel', 'Cancel'))\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (v) touched.value = false\n },\n)\n\nconst canConfirm = computed(() => {\n if (!props.required) return true\n return valueModel.value.trim().length > 0\n})\n\nconst validationError = computed(() => {\n if (!props.required) return undefined\n if (!touched.value) return undefined\n return valueModel.value.trim().length > 0\n ? undefined\n : resolvedRequiredErrorText.value\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n if (!canConfirm.value) {\n touched.value = true\n inputRef.value?.focus()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"resolvedTitle\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"resolvedCloseLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <GrFormField :label=\"resolvedLabel\" :error=\"fieldErrorMessage\" for-id=\"gr-prompt-input\">\n <GrInput\n id=\"gr-prompt-input\"\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :placeholder=\"placeholder\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"touched = true\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"gr-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ resolvedCancelText }}\n </GrButton>\n <GrButton\n data-testid=\"gr-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading\"\n :disabled=\"confirmDisabled || (required && touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ resolvedConfirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrPromptDialog — GR-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n */\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой `required`. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n}\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: undefined,\n description: undefined,\n label: undefined,\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n size: 'md',\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: undefined,\n buttonSize: undefined,\n confirmText: undefined,\n cancelText: undefined,\n requiredErrorText: undefined,\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}>()\n\n// Дефолты берём из общего i18n-блока пакета (fallback — англ.), а не хардкодим.\nconst { t } = useGranularityTranslations()\nconst resolvedTitle = computed(() => props.title ?? t('gr.dialog.prompt.title', 'Prompt'))\nconst resolvedLabel = computed(() => props.label ?? t('gr.dialog.prompt.label', 'Value'))\nconst resolvedRequiredErrorText = computed(() => props.requiredErrorText ?? t('gr.dialog.prompt.required', 'Enter a value.'))\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? t('gr.common.close', 'Close'))\nconst resolvedConfirmText = computed(() => props.confirmText ?? t('gr.common.confirm', 'Confirm'))\nconst resolvedCancelText = computed(() => props.cancelText ?? t('gr.common.cancel', 'Cancel'))\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\n\nwatch(\n () => props.modelValue,\n (v) => {\n if (v) touched.value = false\n },\n)\n\nconst canConfirm = computed(() => {\n if (!props.required) return true\n return valueModel.value.trim().length > 0\n})\n\nconst validationError = computed(() => {\n if (!props.required) return undefined\n if (!touched.value) return undefined\n return valueModel.value.trim().length > 0\n ? undefined\n : resolvedRequiredErrorText.value\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction onConfirm(): void {\n if (!canConfirm.value) {\n touched.value = true\n inputRef.value?.focus()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"resolvedTitle\"\n :size=\"size\"\n :close-on-backdrop=\"closeOnBackdrop\"\n :close-on-esc=\"closeOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"resolvedCloseLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[14px] text-[var(--muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <GrFormField :label=\"resolvedLabel\" :error=\"fieldErrorMessage\" for-id=\"gr-prompt-input\">\n <GrInput\n id=\"gr-prompt-input\"\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :placeholder=\"placeholder\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"touched = true\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"gr-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ resolvedCancelText }}\n </GrButton>\n <GrButton\n data-testid=\"gr-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading\"\n :disabled=\"confirmDisabled || (required && touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ resolvedConfirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrRadio-Dr-pvs_9.js","names":[],"sources":["../../../../src/components/GrRadio/grRadioGroupContext.ts","../../../../src/components/GrRadio/GrRadio.vue","../../../../src/components/GrRadio/GrRadio.vue"],"sourcesContent":["import type { ComputedRef, InjectionKey } from 'vue'\n\nimport type { GrButtonSize } from '../GrButton/grButtonStyles'\n\nexport type GrRadioGroupContext = {\n modelValue: ComputedRef<string>\n name: ComputedRef<string | undefined>\n disabled: ComputedRef<boolean>\n size: ComputedRef<GrButtonSize>\n setValue: (value: string) => void\n}\n\nexport const GR_RADIO_GROUP_CONTEXT: InjectionKey<GrRadioGroupContext> = Symbol('GR_RADIO_GROUP_CONTEXT')","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n */\nexport interface GrRadioProps {\n value: string\n modelValue?: string\n disabled?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\nconst resolvedSize = computed<GrButtonSize>(() => {\n if (props.size)\n return props.size\n\n return group?.size.value ?? 'md'\n})\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass(resolvedDisabled.value))\nconst controlClassName = computed(() => grRadioControlClass(checked.value))\nconst dotClassName = computed(() => grRadioDotClass(checked.value))\n\nfunction setValue(next: string): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :tabindex=\"resolvedDisabled ? -1 : 0\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n >\n <input\n :id=\"id\"\n type=\"radio\"\n :checked=\"checked\"\n :disabled=\"resolvedDisabled\"\n :name=\"resolvedName\"\n :value=\"value\"\n :required=\"required\"\n :form=\"form\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :tabindex=\"resolvedDisabled ? -1 : 0\"\n class=\"inline-flex items-center gap-2 select-none focus-visible:outline-none focus-visible:rounded-[8px] focus-visible:shadow-[0_0_0_2px_var(--ring),0_0_0_4px_var(--bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n >\n <input\n :id=\"id\"\n type=\"radio\"\n :checked=\"checked\"\n :disabled=\"resolvedDisabled\"\n :name=\"resolvedName\"\n :value=\"value\"\n :required=\"required\"\n :form=\"form\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :style=\"hiddenInputStyle\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"h-4 w-4 rounded-full border flex items-center justify-center transition-colors duration-150\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotClassName]\"\n />\n </span>\n\n <span class=\"text-sm text-[var(--muted-fg)]\">\n <slot />\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject } from 'vue'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n */\nexport interface GrRadioProps {\n value: string\n modelValue?: string\n disabled?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\nconst resolvedSize = computed<GrButtonSize>(() => {\n if (props.size)\n return props.size\n\n return group?.size.value ?? 'md'\n})\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass(resolvedDisabled.value))\nconst controlClassName = computed(() => grRadioControlClass(checked.value))\nconst dotClassName = computed(() => grRadioDotClass(checked.value))\n\nfunction setValue(next: string): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :tabindex=\"resolvedDisabled ? -1 : 0\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n >\n <input\n :id=\"id\"\n type=\"radio\"\n :checked=\"checked\"\n :disabled=\"resolvedDisabled\"\n :name=\"resolvedName\"\n :value=\"value\"\n :required=\"required\"\n :form=\"form\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :tabindex=\"resolvedDisabled ? -1 : 0\"\n class=\"inline-flex items-center gap-2 select-none focus-visible:outline-none focus-visible:rounded-[8px] focus-visible:shadow-[0_0_0_2px_var(--ring),0_0_0_4px_var(--bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n >\n <input\n :id=\"id\"\n type=\"radio\"\n :checked=\"checked\"\n :disabled=\"resolvedDisabled\"\n :name=\"resolvedName\"\n :value=\"value\"\n :required=\"required\"\n :form=\"form\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :style=\"hiddenInputStyle\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"h-4 w-4 rounded-full border flex items-center justify-center transition-colors duration-150\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotClassName]\"\n />\n </span>\n\n <span class=\"text-sm text-[var(--muted-fg)]\">\n <slot />\n </span>\n </div>\n</template>\n"],"mappings":"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"}
|