@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":"GrRadioGroup-nOdvfV_g.js","names":["$slots"],"sources":["../../../../src/components/GrRadioGroup/GrRadioGroup.vue","../../../../src/components/GrRadioGroup/GrRadioGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\n\nimport type { GrButtonSize } from '../GrButton'\nimport GrButtonGroup from '../GrButtonGroup/GrButtonGroup.vue'\nimport GrRadio from '../GrRadio/GrRadio.vue'\nimport { GR_RADIO_GROUP_CONTEXT } from '../GrRadio'\n\nexport type GrRadioGroupVariant = 'radiobox' | 'button'\nexport interface GrRadioGroupOption { value: string, label: string }\n\n/**\n * GrRadioGroup — контейнер группы `GrRadio`.\n *\n * Может работать как через слот (ручной рендер `GrRadio`), так и через проп `options`.\n * Предоставляет дочерним `GrRadio` общий `modelValue`/`disabled`/`size`/`name` через `inject`.\n */\nexport interface GrRadioGroupProps {\n modelValue: string\n options?: GrRadioGroupOption[]\n name?: string\n disabled?: boolean\n variant?: GrRadioGroupVariant\n size?: GrButtonSize\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrRadioGroupProps>(), {\n options: undefined,\n name: undefined,\n disabled: false,\n variant: 'radiobox',\n size: 'md',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nfunction setValue(next: string): void {\n if (props.disabled)\n return\n emit('update:modelValue', next)\n}\n\nprovide(GR_RADIO_GROUP_CONTEXT, {\n modelValue: computed(() => props.modelValue),\n name: computed(() => props.name),\n disabled: computed(() => props.disabled),\n size: computed(() => props.size),\n setValue,\n})\n</script>\n\n<template>\n <div\n data-gr-radio-group\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n >\n <template v-if=\"$slots.default\">\n <GrButtonGroup v-if=\"variant === 'button'\">\n <slot />\n </GrButtonGroup>\n <div v-else class=\"grid gap-2\">\n <slot />\n </div>\n </template>\n <template v-else>\n <GrButtonGroup v-if=\"variant === 'button'\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n variant=\"button\"\n :size=\"size\"\n >\n {{ opt.label }}\n </GrRadio>\n </GrButtonGroup>\n <div v-else class=\"grid gap-2\">\n <GrRadio v-for=\"opt in options ?? []\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </GrRadio>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide } from 'vue'\n\nimport type { GrButtonSize } from '../GrButton'\nimport GrButtonGroup from '../GrButtonGroup/GrButtonGroup.vue'\nimport GrRadio from '../GrRadio/GrRadio.vue'\nimport { GR_RADIO_GROUP_CONTEXT } from '../GrRadio'\n\nexport type GrRadioGroupVariant = 'radiobox' | 'button'\nexport interface GrRadioGroupOption { value: string, label: string }\n\n/**\n * GrRadioGroup — контейнер группы `GrRadio`.\n *\n * Может работать как через слот (ручной рендер `GrRadio`), так и через проп `options`.\n * Предоставляет дочерним `GrRadio` общий `modelValue`/`disabled`/`size`/`name` через `inject`.\n */\nexport interface GrRadioGroupProps {\n modelValue: string\n options?: GrRadioGroupOption[]\n name?: string\n disabled?: boolean\n variant?: GrRadioGroupVariant\n size?: GrButtonSize\n ariaLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrRadioGroupProps>(), {\n options: undefined,\n name: undefined,\n disabled: false,\n variant: 'radiobox',\n size: 'md',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nfunction setValue(next: string): void {\n if (props.disabled)\n return\n emit('update:modelValue', next)\n}\n\nprovide(GR_RADIO_GROUP_CONTEXT, {\n modelValue: computed(() => props.modelValue),\n name: computed(() => props.name),\n disabled: computed(() => props.disabled),\n size: computed(() => props.size),\n setValue,\n})\n</script>\n\n<template>\n <div\n data-gr-radio-group\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n >\n <template v-if=\"$slots.default\">\n <GrButtonGroup v-if=\"variant === 'button'\">\n <slot />\n </GrButtonGroup>\n <div v-else class=\"grid gap-2\">\n <slot />\n </div>\n </template>\n <template v-else>\n <GrButtonGroup v-if=\"variant === 'button'\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"opt.value\"\n :value=\"opt.value\"\n variant=\"button\"\n :size=\"size\"\n >\n {{ opt.label }}\n </GrRadio>\n </GrButtonGroup>\n <div v-else class=\"grid gap-2\">\n <GrRadio v-for=\"opt in options ?? []\" :key=\"opt.value\" :value=\"opt.value\">\n {{ opt.label }}\n </GrRadio>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2BA,MAAM,QAAQ;EASd,MAAM,OAAO;EAIb,SAAS,SAAS,MAAoB;GACpC,IAAI,MAAM,UACR;GACF,KAAK,qBAAqB,IAAI;EAChC;EAEA,QAAQ,wBAAwB;GAC9B,YAAY,eAAe,MAAM,UAAU;GAC3C,MAAM,eAAe,MAAM,IAAI;GAC/B,UAAU,eAAe,MAAM,QAAQ;GACvC,MAAM,eAAe,MAAM,IAAI;GAC/B;EACF,CAAC;;uBAIC,mBAgCM,OAAA;IA/BJ,uBAAA;IACA,MAAK;IACJ,cAAY,QAAA;IACZ,iBAAe,QAAA,WAAQ,SAAY,KAAA;OAEpBA,KAAAA,OAAO,WAAA,UAAA,GAAvB,mBAOW,UAAA,EAAA,KAAA,EAAA,GAAA,CANY,QAAA,YAAO,YAAA,UAAA,GAA5B,YAEgB,uBAAA,EAAA,KAAA,EAAA,GAAA;2BADN,CAAR,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA;;uBAEV,mBAEM,OAFN,YAEM,CADJ,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA,EAAA,GAAA,EAAA,MAAA,UAAA,GAGZ,mBAiBW,UAAA,EAAA,KAAA,EAAA,GAAA,CAhBY,QAAA,YAAO,YAAA,UAAA,GAA5B,YAUgB,uBAAA,EAAA,KAAA,EAAA,GAAA;2BARgB,EAAA,UAAA,IAAA,GAD9B,mBAQU,UAAA,MAAA,WAPM,QAAA,WAAO,CAAA,IAAd,QAAG;yBADZ,YAQU,iBAAA;MANP,KAAK,IAAI;MACT,OAAO,IAAI;MACZ,SAAQ;MACP,MAAM,QAAA;;6BAEQ,CAAA,gBAAA,gBAAZ,IAAI,KAAK,GAAA,CAAA,CAAA,CAAA;;;;;uBAGhB,mBAIM,OAJN,YAIM,EAAA,UAAA,IAAA,GAHJ,mBAEU,UAAA,MAAA,WAFa,QAAA,WAAO,CAAA,IAAd,QAAG;wBAAnB,YAEU,iBAAA;KAF6B,KAAK,IAAI;KAAQ,OAAO,IAAI;;4BAClD,CAAA,gBAAA,gBAAZ,IAAI,KAAK,GAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrSegmented-C1y0UXpQ.js","names":[],"sources":["../../../../src/components/GrSegmented/GrSegmented.vue","../../../../src/components/GrSegmented/GrSegmented.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного GR-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `gr-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-gr-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-gr-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-gr-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, getCurrentInstance, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport type { ComponentPublicInstance } from 'vue'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\nconst hiddenInputStyle = {\n position: 'absolute',\n opacity: '0',\n width: '0',\n height: '0',\n pointerEvents: 'none',\n} as const\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного GR-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: 'pills',\n size: 'md',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (event: 'update:modelValue', value: GrSegmentedValue): void\n (event: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n}>()\n\nconst instance = getCurrentInstance()\nconst fallbackName = `gr-segmented-${instance?.uid ?? Math.random()}`\n\nconst rootRef = ref<HTMLElement | null>(null)\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: props.variant,\n block: props.block,\n disabled: props.disabled,\n}))\nconst rootStyle = computed<Record<string, string>>(() => ({\n ...grSegmentedRootStyle({\n variant: props.variant,\n size: props.size,\n }),\n gridTemplateColumns: props.options.length > 0\n ? props.options.map(() => props.block ? 'minmax(0,1fr)' : 'minmax(0,max-content)').join(' ')\n : 'none',\n}))\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(props.variant))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return props.disabled || Boolean(option.disabled)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n if (resolveOptionDisabled(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !resolveOptionDisabled(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (props.disabled) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(() => props.variant, () => scheduleMeasure())\nwatch(() => props.size, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n ref=\"rootRef\"\n data-gr-segmented\n :data-variant=\"variant\"\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n >\n <span\n v-if=\"hasIndicator\"\n data-gr-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-gr-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <input\n type=\"radio\"\n tabindex=\"-1\"\n aria-hidden=\"true\"\n :name=\"resolvedName\"\n :value=\"option.value\"\n :checked=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :style=\"hiddenInputStyle\"\n >\n\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n >\n <component\n :is=\"option.icon\"\n v-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrSelect-BVCmitqa.js","names":[],"sources":["../../../../src/components/GrSelect/GrSelect.vue","../../../../src/components/GrSelect/GrSelect.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useId, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/internal/useFloating'\nimport { useEscapeToClose } from '../../composables/internal/useEscapeToClose'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrFormFieldContext } from '../GrFormField/context'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionGroup,\n type GrSelectOptionOrGroup,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionGroup,\n GrSelectOptionOrGroup,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного GR-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n /** Список опций. Поддерживает плоский массив опций и группы опций (`{ label, options }`). */\n options?: GrSelectOptionOrGroup[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). i18n-friendly: если не задан — берётся из адаптера перевода (`gr.select.customValuePlaceholder`), иначе — встроенный fallback. */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: undefined,\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: undefined,\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedCustomValuePlaceholder = computed(() => {\n return props.customValuePlaceholder ?? t('gr.select.customValuePlaceholder', 'Add value…')\n})\n\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.common.clear', 'Clear'))\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required)\n// для связки с лейблом и сообщением об ошибке.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst isInvalid = computed(() => Boolean(field?.invalid.value))\nconst describedBy = computed(() => field?.describedById.value)\nconst isRequired = computed(() => Boolean(field?.required.value))\n\nconst optionsResolved = computed<GrSelectOptionOrGroup[]>(() => props.options ?? [])\n\nfunction isOptionGroup(item: GrSelectOptionOrGroup): item is GrSelectOptionGroup {\n return Array.isArray((item as GrSelectOptionGroup).options)\n}\n\n/** Плоский список всех опций (группы «развёрнуты»). Используется для всех вычислений по значениям. */\nconst flatOptions = computed<GrSelectOption[]>(() => {\n const result: GrSelectOption[] = []\n for (const item of optionsResolved.value) {\n if (isOptionGroup(item)) result.push(...item.options)\n else result.push(item)\n }\n return result\n})\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(flatOptions.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return flatOptions.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => flatOptions.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return flatOptions.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\n// Нативный `<select multiple>` — это многострочный listbox, а не поповер. В режиме\n// `view=\"link\"` он рендерится невидимым overlay'ем, из-за чего multiple-вариант «не\n// открывается» (клик по невидимому listbox'у ничего не показывает). Для этой\n// неподдерживаемой комбинации форсим кастомную панель, которая корректно открывается.\nconst effectiveOptionsView = computed<GrSelectOptionsView>(() => {\n if (props.view === 'link' && props.multiple && props.optionsView === 'native')\n return 'panel'\n return props.optionsView\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (effectiveOptionsView.value !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\n\n// Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n// поэтому панель растёт по контенту (`matchWidth: 'min'` → `width: max-content` +\n// `min-width` от триггера), а не сжимается до неё. Для `view='default'` панель точно\n// повторяет ширину триггера (`matchWidth: true`).\nconst { floatingStyle, update: updateFloatingPosition } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: () => (props.view === 'link' ? 'min' : true),\n zIndexVar: '--gr-z-dropdown',\n})\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nuseEscapeToClose(open, closeDropdown)\n\n// `view` определяет режим `matchWidth` (см. выше) — пересчитываем позицию/ширину\n// панели, если он меняется, пока панель открыта.\nwatch(\n () => props.view,\n () => {\n if (open.value) updateFloatingPosition()\n },\n)\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nfunction matchesQuery(option: GrSelectOption, query: string): boolean {\n if (!query) return true\n return option.label.toLowerCase().includes(query) || option.value.toLowerCase().includes(query)\n}\n\n/**\n * Элемент рендера панели: либо заголовок группы, либо опция.\n * Группировка сохраняется, фильтрация по `customValue` скрывает пустые группы.\n */\ntype GrSelectPanelItem =\n | { kind: 'group', label: string, key: string }\n | { kind: 'option', option: GrSelectOption, key: string }\n\nconst panelItems = computed<GrSelectPanelItem[]>(() => {\n const q = props.allowCustomValue ? customValue.value.trim().toLowerCase() : ''\n const items: GrSelectPanelItem[] = []\n\n // Опция для кастомного значения, которого нет в options (single).\n if (props.allowCustomValue && !props.multiple && modelSingle.value !== '' && !hasModelInOptions.value) {\n const custom: GrSelectOption = { value: modelSingle.value, label: modelSingle.value }\n if (matchesQuery(custom, q)) {\n items.push({ kind: 'option', option: custom, key: `__custom__${custom.value}` })\n }\n }\n\n optionsResolved.value.forEach((item, index) => {\n if (isOptionGroup(item)) {\n const matched = item.options.filter((o) => matchesQuery(o, q))\n if (!matched.length) return\n items.push({ kind: 'group', label: item.label, key: `__group__${index}` })\n for (const option of matched) {\n // Ключ с индексом группы — одинаковое `value` в разных группах больше не даёт дубликат.\n items.push({ kind: 'option', option, key: `${index}:${option.value}` })\n }\n return\n }\n\n if (matchesQuery(item, q)) {\n items.push({ kind: 'option', option: item, key: `${index}:${item.value}` })\n }\n })\n\n return items\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (effectiveOptionsView.value !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !flatOptions.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !flatOptions.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction isOptionDisabled(value: string): boolean {\n return flatOptions.value.find(o => o.value === value)?.disabled === true\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (isOptionDisabled(value)) return\n\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\n// ————— Клавиатурная навигация комбобокса (WAI-ARIA, aria-activedescendant).\nconst listboxId = useId()\nconst activeIndex = ref(-1)\n\nfunction optionDomId(value: string): string {\n return `${listboxId}-opt-${value}`\n}\n\n// Навигируемые (видимые, не-disabled) опции панели в порядке рендера.\nconst navigableValues = computed<string[]>(() =>\n panelItems.value\n .filter((item): item is Extract<GrSelectPanelItem, { kind: 'option' }> => item.kind === 'option' && !item.option.disabled)\n .map(item => item.option.value),\n)\n\nconst activeValue = computed(() => (activeIndex.value >= 0 ? navigableValues.value[activeIndex.value] : undefined))\nconst activeDescendantId = computed(() =>\n open.value && activeValue.value !== undefined ? optionDomId(activeValue.value) : undefined,\n)\n\nfunction clampActive(index: number): number {\n const len = navigableValues.value.length\n if (len === 0) return -1\n return ((index % len) + len) % len\n}\n\nasync function scrollActiveIntoView(): Promise<void> {\n await nextTick()\n const value = activeValue.value\n if (value === undefined) return\n const el = document.getElementById(optionDomId(value))\n el?.scrollIntoView?.({ block: 'nearest' })\n}\n\nfunction setActive(index: number): void {\n activeIndex.value = clampActive(index)\n void scrollActiveIntoView()\n}\n\nfunction initActiveIndex(): void {\n const selectedIdx = navigableValues.value.findIndex(v => isSelected(v))\n activeIndex.value = selectedIdx >= 0 ? selectedIdx : (navigableValues.value.length ? 0 : -1)\n}\n\nfunction openDropdown(): void {\n if (props.disabled || open.value) return\n open.value = true\n}\n\nlet typeaheadBuffer = ''\nlet typeaheadTimer: ReturnType<typeof setTimeout> | null = null\nfunction typeahead(char: string): void {\n typeaheadBuffer += char.toLowerCase()\n if (typeaheadTimer) clearTimeout(typeaheadTimer)\n typeaheadTimer = setTimeout(() => { typeaheadBuffer = '' }, 600)\n\n const idx = navigableValues.value.findIndex((v) => {\n const opt = flatOptions.value.find(o => o.value === v)\n return opt?.label.toLowerCase().startsWith(typeaheadBuffer)\n })\n if (idx >= 0) setActive(idx)\n}\n\nfunction onComboKeydown(event: KeyboardEvent): void {\n if (props.disabled) return\n\n if (!open.value) {\n if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) {\n event.preventDefault()\n openDropdown()\n }\n return\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n setActive(activeIndex.value + 1)\n break\n case 'ArrowUp':\n event.preventDefault()\n setActive(activeIndex.value - 1)\n break\n case 'Home':\n event.preventDefault()\n setActive(0)\n break\n case 'End':\n event.preventDefault()\n setActive(navigableValues.value.length - 1)\n break\n case 'Enter':\n event.preventDefault()\n if (props.allowCustomValue && canAddCustom.value)\n addCustom()\n else if (activeValue.value !== undefined)\n toggleValue(activeValue.value)\n break\n case 'Tab':\n closeDropdown()\n break\n default:\n // typeahead — только без allowCustomValue (иначе мешает вводу в custom-инпут).\n if (!props.allowCustomValue && event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey)\n typeahead(event.key)\n }\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (!isOpen) {\n customValue.value = ''\n activeIndex.value = -1\n return\n }\n\n initActiveIndex()\n\n if (props.allowCustomValue) {\n await nextTick()\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nconst showNativeChevron = computed(() => {\n return effectiveOptionsView.value === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (effectiveOptionsView.value !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !flatOptions.value.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (effectiveOptionsView.value !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && effectiveOptionsView.value === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: effectiveOptionsView.value,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"effectiveOptionsView === 'native'\"\n data-gr-select\n :class=\"rootClass\"\n >\n <!--\n Выбор задаём per-option через `:selected` (а не `:value` на `<select>`):\n для `<select multiple>` биндинг `:value` массивом не работает — DOM не\n отражает программное изменение модели. `onChange` читает выбор из DOM.\n -->\n <select\n :id=\"resolvedId\"\n data-gr-select-native\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" :selected=\"!multiple && modelSingle === ''\">\n {{ placeholder || t('gr.select.clearOption', 'None') }}\n </option>\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\" :selected=\"!multiple\">\n {{ modelSingle }}\n </option>\n <template v-for=\"(item, index) in optionsResolved\" :key=\"index\">\n <optgroup v-if=\"isOptionGroup(item)\" :label=\"item.label\">\n <option\n v-for=\"opt in item.options\"\n :key=\"`${index}:${opt.value}`\"\n :value=\"opt.value\"\n :selected=\"multiple ? modelMultiple.includes(opt.value) : modelSingle === opt.value\"\n :disabled=\"opt.disabled\"\n >\n {{ opt.label }}\n </option>\n </optgroup>\n <option\n v-else\n :value=\"item.value\"\n :selected=\"multiple ? modelMultiple.includes(item.value) : modelSingle === item.value\"\n :disabled=\"item.disabled\"\n >\n {{ item.label }}\n </option>\n </template>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-gr-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"gr-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 flex items-center text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-select\n :class=\"rootClass\"\n >\n <button\n :id=\"resolvedId\"\n data-testid=\"gr-select-trigger\"\n data-gr-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n @keydown=\"onComboKeydown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"gr-select-chevron\"\n class=\"shrink-0 flex items-center text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"gr-select-clear\"\n data-gr-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedClearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-select-panel\"\n data-gr-select-panel\n :style=\"floatingStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"gr-select-custom-input\"\n type=\"text\"\n :placeholder=\"resolvedCustomValuePlaceholder\"\n size=\"sm\"\n @keydown=\"onComboKeydown\"\n />\n </div>\n\n <div\n :id=\"listboxId\"\n data-gr-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"gr-select-add-option\"\n data-gr-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n {{ t('gr.select.addOption', 'Add \"{value}\"', { value: customValue.trim() }) }}\n </button>\n\n <template v-for=\"item in panelItems\" :key=\"item.key\">\n <div\n v-if=\"item.kind === 'group'\"\n data-gr-select-group-label\n role=\"presentation\"\n class=\"px-3 pt-2 pb-1 text-[11px] font-semibold uppercase tracking-wide text-[var(--muted-fg)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : '',\n ]\"\n >\n {{ item.label }}\n </div>\n\n <button\n v-else\n :id=\"optionDomId(item.option.value)\"\n data-gr-select-option\n type=\"button\"\n role=\"option\"\n :disabled=\"item.option.disabled\"\n :aria-selected=\"isSelected(item.option.value) ? 'true' : 'false'\"\n :aria-disabled=\"item.option.disabled ? 'true' : undefined\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n item.option.disabled ? 'cursor-not-allowed opacity-50' : 'hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]',\n activeValue === item.option.value && !item.option.disabled ? 'bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]' : '',\n ]\"\n @click=\"toggleValue(item.option.value)\"\n @mousemove=\"activeIndex = navigableValues.indexOf(item.option.value)\"\n >\n <slot name=\"option\" :option=\"item.option\" :selected=\"isSelected(item.option.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(item.option.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ item.option.label }}</span>\n </span>\n </slot>\n </button>\n </template>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useId, watch } from 'vue'\n\nimport GrInput from '../GrInput/GrInput.vue'\nimport { vClickOutside } from '../../directives'\nimport { useFloating } from '../../composables/internal/useFloating'\nimport { useEscapeToClose } from '../../composables/internal/useEscapeToClose'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrFormFieldContext } from '../GrFormField/context'\n\nimport {\n defaultBaseClass,\n grSelectLinkNativeLabelClass,\n grSelectLinkNativeOverlayClass,\n grSelectNativeClass,\n grSelectPanelClasses,\n grSelectTriggerClass,\n linkBaseClass,\n type GrSelectModelValue,\n type GrSelectOption,\n type GrSelectOptionGroup,\n type GrSelectOptionOrGroup,\n type GrSelectOptionsView,\n type GrSelectSize,\n type GrSelectUnderline,\n type GrSelectVariant,\n type GrSelectView,\n} from './grSelectStyles'\n\nexport type {\n GrSelectModelValue,\n GrSelectOption,\n GrSelectOptionGroup,\n GrSelectOptionOrGroup,\n GrSelectOptionsView,\n GrSelectSize,\n GrSelectUnderline,\n GrSelectVariant,\n GrSelectView,\n} from './grSelectStyles'\n\n/**\n * Пропсы публичного GR-примитива «Select».\n */\nexport interface GrSelectProps {\n modelValue: GrSelectModelValue\n /** Список опций. Поддерживает плоский массив опций и группы опций (`{ label, options }`). */\n options?: GrSelectOptionOrGroup[]\n disabled?: boolean\n ariaLabel?: string\n view?: GrSelectView\n size?: GrSelectSize\n /** Placeholder (показывается, когда значение не выбрано). */\n placeholder?: string\n /** Multiple selection. */\n multiple?: boolean\n /** Как отображать список опций: нативный `<select>` или кастомная панель. */\n optionsView?: GrSelectOptionsView\n /** Разрешает ввод/выбор значения, которого нет в `options`. */\n allowCustomValue?: boolean\n /** Placeholder для инпута кастомного значения (только в `optionsView=\"panel\"`). i18n-friendly: если не задан — берётся из адаптера перевода (`gr.select.customValuePlaceholder`), иначе — встроенный fallback. */\n customValuePlaceholder?: string\n /** Максимальная высота панели (только в `optionsView=\"panel\"`). */\n dropdownMaxHeight?: number\n /** Закрывать панель после выбора (только в `optionsView=\"panel\"`). */\n closeOnSelect?: boolean\n /** Разрешает очистку выбранного значения. */\n clearable?: boolean\n /** i18n-label для кнопки очистки (`aria-label`). */\n clearLabel?: string\n /**\n * Цвет/вариант ссылки для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n variant?: GrSelectVariant\n /**\n * Подчёркивание для `view=\"link\"` (аналогично `GrLink`).\n * В `view=\"default\"` не используется.\n */\n underline?: GrSelectUnderline\n}\n\nconst props = withDefaults(\n defineProps<GrSelectProps>(),\n {\n options: undefined,\n disabled: false,\n ariaLabel: undefined,\n view: 'default',\n size: 'md',\n\n placeholder: undefined,\n multiple: false,\n\n optionsView: 'native',\n allowCustomValue: false,\n customValuePlaceholder: undefined,\n dropdownMaxHeight: 280,\n closeOnSelect: true,\n clearable: false,\n clearLabel: undefined,\n variant: 'primary',\n underline: 'auto',\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst resolvedCustomValuePlaceholder = computed(() => {\n return props.customValuePlaceholder ?? t('gr.select.customValuePlaceholder', 'Add value…')\n})\n\nconst resolvedClearLabel = computed(() => props.clearLabel ?? t('gr.common.clear', 'Clear'))\n\nconst baseClassName = computed(() => props.view === 'link' ? linkBaseClass : defaultBaseClass)\n\nconst rootClass = computed(() => props.view === 'link' ? 'relative inline-block align-baseline' : 'relative w-full')\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: GrSelectModelValue): void\n}>()\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required)\n// для связки с лейблом и сообщением об ошибке.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst isInvalid = computed(() => Boolean(field?.invalid.value))\nconst describedBy = computed(() => field?.describedById.value)\nconst isRequired = computed(() => Boolean(field?.required.value))\n\nconst optionsResolved = computed<GrSelectOptionOrGroup[]>(() => props.options ?? [])\n\nfunction isOptionGroup(item: GrSelectOptionOrGroup): item is GrSelectOptionGroup {\n return Array.isArray((item as GrSelectOptionGroup).options)\n}\n\n/** Плоский список всех опций (группы «развёрнуты»). Используется для всех вычислений по значениям. */\nconst flatOptions = computed<GrSelectOption[]>(() => {\n const result: GrSelectOption[] = []\n for (const item of optionsResolved.value) {\n if (isOptionGroup(item)) result.push(...item.options)\n else result.push(item)\n }\n return result\n})\n\nfunction modelToArray(value: GrSelectModelValue): string[] {\n if (Array.isArray(value)) return value\n if (!value) return []\n return [value]\n}\n\nconst modelSingle = computed(() => {\n return Array.isArray(props.modelValue) ? (props.modelValue[0] ?? '') : props.modelValue\n})\n\nconst modelMultiple = computed(() => modelToArray(props.modelValue))\n\nconst selectedValues = computed(() => {\n return props.multiple ? modelMultiple.value : (modelSingle.value ? [modelSingle.value] : [])\n})\n\nconst hasSelection = computed(() => selectedValues.value.length > 0)\n\nconst selectedOptions = computed<GrSelectOption[]>(() => {\n const byValue = new Map(flatOptions.value.map(o => [o.value, o]))\n return selectedValues.value.map((v) => byValue.get(v) ?? { value: v, label: v })\n})\n\nconst hasModelInOptions = computed(() => {\n if (props.multiple) return false\n return flatOptions.value.some((o) => o.value === modelSingle.value)\n})\n\nconst displayLabel = computed(() => {\n if (props.multiple) {\n if (!selectedValues.value.length) return ''\n return selectedValues.value\n .map((v) => flatOptions.value.find((o) => o.value === v)?.label ?? v)\n .join(', ')\n }\n\n return flatOptions.value.find((o) => o.value === modelSingle.value)?.label ?? modelSingle.value\n})\n\nconst displayText = computed(() => {\n if (hasSelection.value) return displayLabel.value\n return props.placeholder ?? ''\n})\n\n// Нативный `<select multiple>` — это многострочный listbox, а не поповер. В режиме\n// `view=\"link\"` он рендерится невидимым overlay'ем, из-за чего multiple-вариант «не\n// открывается» (клик по невидимому listbox'у ничего не показывает). Для этой\n// неподдерживаемой комбинации форсим кастомную панель, которая корректно открывается.\nconst effectiveOptionsView = computed<GrSelectOptionsView>(() => {\n if (props.view === 'link' && props.multiple && props.optionsView === 'native')\n return 'panel'\n return props.optionsView\n})\n\nconst nativeCustomOptionVisible = computed(() => {\n if (!props.allowCustomValue) return false\n if (effectiveOptionsView.value !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return false\n if (modelSingle.value === '') return false\n return !hasModelInOptions.value\n})\n\nconst customValue = ref('')\nconst open = ref(false)\nconst rootEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\nconst customInputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst clickOutsideExclude = [() => panelEl.value]\n\n// Для `view='link'` ширина триггера = ширине выбранной опции (`inline-block w-auto`),\n// поэтому панель растёт по контенту (`matchWidth: 'min'` → `width: max-content` +\n// `min-width` от триггера), а не сжимается до неё. Для `view='default'` панель точно\n// повторяет ширину триггера (`matchWidth: true`).\nconst { floatingStyle, update: updateFloatingPosition } = useFloating(rootEl, panelEl, open, {\n placement: 'bottom-start',\n matchWidth: () => (props.view === 'link' ? 'min' : true),\n zIndexVar: '--gr-z-dropdown',\n})\n\nfunction closeDropdown(): void {\n open.value = false\n}\n\nfunction toggleDropdown(): void {\n if (props.disabled) return\n open.value = !open.value\n}\n\nuseEscapeToClose(open, closeDropdown)\n\n// `view` определяет режим `matchWidth` (см. выше) — пересчитываем позицию/ширину\n// панели, если он меняется, пока панель открыта.\nwatch(\n () => props.view,\n () => {\n if (open.value) updateFloatingPosition()\n },\n)\n\nconst panelClasses = computed(() => {\n return grSelectPanelClasses\n})\n\nfunction matchesQuery(option: GrSelectOption, query: string): boolean {\n if (!query) return true\n return option.label.toLowerCase().includes(query) || option.value.toLowerCase().includes(query)\n}\n\n/**\n * Элемент рендера панели: либо заголовок группы, либо опция.\n * Группировка сохраняется, фильтрация по `customValue` скрывает пустые группы.\n */\ntype GrSelectPanelItem =\n | { kind: 'group', label: string, key: string }\n | { kind: 'option', option: GrSelectOption, key: string }\n\nconst panelItems = computed<GrSelectPanelItem[]>(() => {\n const q = props.allowCustomValue ? customValue.value.trim().toLowerCase() : ''\n const items: GrSelectPanelItem[] = []\n\n // Опция для кастомного значения, которого нет в options (single).\n if (props.allowCustomValue && !props.multiple && modelSingle.value !== '' && !hasModelInOptions.value) {\n const custom: GrSelectOption = { value: modelSingle.value, label: modelSingle.value }\n if (matchesQuery(custom, q)) {\n items.push({ kind: 'option', option: custom, key: `__custom__${custom.value}` })\n }\n }\n\n optionsResolved.value.forEach((item, index) => {\n if (isOptionGroup(item)) {\n const matched = item.options.filter((o) => matchesQuery(o, q))\n if (!matched.length) return\n items.push({ kind: 'group', label: item.label, key: `__group__${index}` })\n for (const option of matched) {\n // Ключ с индексом группы — одинаковое `value` в разных группах больше не даёт дубликат.\n items.push({ kind: 'option', option, key: `${index}:${option.value}` })\n }\n return\n }\n\n if (matchesQuery(item, q)) {\n items.push({ kind: 'option', option: item, key: `${index}:${item.value}` })\n }\n })\n\n return items\n})\n\nconst canAddCustom = computed(() => {\n if (!props.allowCustomValue) return false\n if (effectiveOptionsView.value !== 'panel') return false\n const v = customValue.value.trim()\n if (!v) return false\n\n if (props.multiple) {\n if (selectedValues.value.includes(v)) return false\n return !flatOptions.value.some((o) => o.value === v)\n }\n\n if (v === modelSingle.value) return false\n return !flatOptions.value.some((o) => o.value === v)\n})\n\nfunction emitValue(value: GrSelectModelValue): void {\n emit('update:modelValue', value)\n}\n\nfunction isSelected(value: string): boolean {\n return selectedValues.value.includes(value)\n}\n\nfunction isOptionDisabled(value: string): boolean {\n return flatOptions.value.find(o => o.value === value)?.disabled === true\n}\n\nfunction selectValue(value: string): void {\n emitValue(value)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction toggleValue(value: string): void {\n if (isOptionDisabled(value)) return\n\n if (!props.multiple) {\n selectValue(value)\n return\n }\n\n const next = selectedValues.value.slice()\n const idx = next.indexOf(value)\n if (idx >= 0) {\n next.splice(idx, 1)\n }\n else {\n next.push(value)\n }\n\n emitValue(next)\n if (props.closeOnSelect) {\n closeDropdown()\n }\n}\n\nfunction addCustom(): void {\n const v = customValue.value.trim()\n if (!v) return\n toggleValue(v)\n}\n\n// ————— Клавиатурная навигация комбобокса (WAI-ARIA, aria-activedescendant).\nconst listboxId = useId()\nconst activeIndex = ref(-1)\n\nfunction optionDomId(value: string): string {\n return `${listboxId}-opt-${value}`\n}\n\n// Навигируемые (видимые, не-disabled) опции панели в порядке рендера.\nconst navigableValues = computed<string[]>(() =>\n panelItems.value\n .filter((item): item is Extract<GrSelectPanelItem, { kind: 'option' }> => item.kind === 'option' && !item.option.disabled)\n .map(item => item.option.value),\n)\n\nconst activeValue = computed(() => (activeIndex.value >= 0 ? navigableValues.value[activeIndex.value] : undefined))\nconst activeDescendantId = computed(() =>\n open.value && activeValue.value !== undefined ? optionDomId(activeValue.value) : undefined,\n)\n\nfunction clampActive(index: number): number {\n const len = navigableValues.value.length\n if (len === 0) return -1\n return ((index % len) + len) % len\n}\n\nasync function scrollActiveIntoView(): Promise<void> {\n await nextTick()\n const value = activeValue.value\n if (value === undefined) return\n const el = document.getElementById(optionDomId(value))\n el?.scrollIntoView?.({ block: 'nearest' })\n}\n\nfunction setActive(index: number): void {\n activeIndex.value = clampActive(index)\n void scrollActiveIntoView()\n}\n\nfunction initActiveIndex(): void {\n const selectedIdx = navigableValues.value.findIndex(v => isSelected(v))\n activeIndex.value = selectedIdx >= 0 ? selectedIdx : (navigableValues.value.length ? 0 : -1)\n}\n\nfunction openDropdown(): void {\n if (props.disabled || open.value) return\n open.value = true\n}\n\nlet typeaheadBuffer = ''\nlet typeaheadTimer: ReturnType<typeof setTimeout> | null = null\nfunction typeahead(char: string): void {\n typeaheadBuffer += char.toLowerCase()\n if (typeaheadTimer) clearTimeout(typeaheadTimer)\n typeaheadTimer = setTimeout(() => { typeaheadBuffer = '' }, 600)\n\n const idx = navigableValues.value.findIndex((v) => {\n const opt = flatOptions.value.find(o => o.value === v)\n return opt?.label.toLowerCase().startsWith(typeaheadBuffer)\n })\n if (idx >= 0) setActive(idx)\n}\n\nfunction onComboKeydown(event: KeyboardEvent): void {\n if (props.disabled) return\n\n if (!open.value) {\n if (['ArrowDown', 'ArrowUp', 'Enter', ' '].includes(event.key)) {\n event.preventDefault()\n openDropdown()\n }\n return\n }\n\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault()\n setActive(activeIndex.value + 1)\n break\n case 'ArrowUp':\n event.preventDefault()\n setActive(activeIndex.value - 1)\n break\n case 'Home':\n event.preventDefault()\n setActive(0)\n break\n case 'End':\n event.preventDefault()\n setActive(navigableValues.value.length - 1)\n break\n case 'Enter':\n event.preventDefault()\n if (props.allowCustomValue && canAddCustom.value)\n addCustom()\n else if (activeValue.value !== undefined)\n toggleValue(activeValue.value)\n break\n case 'Tab':\n closeDropdown()\n break\n default:\n // typeahead — только без allowCustomValue (иначе мешает вводу в custom-инпут).\n if (!props.allowCustomValue && event.key.length === 1 && !event.metaKey && !event.ctrlKey && !event.altKey)\n typeahead(event.key)\n }\n}\n\nwatch(\n open,\n async (isOpen) => {\n if (!isOpen) {\n customValue.value = ''\n activeIndex.value = -1\n return\n }\n\n initActiveIndex()\n\n if (props.allowCustomValue) {\n await nextTick()\n customInputRef.value?.focus()\n }\n },\n { immediate: true },\n)\n\nconst showNativeChevron = computed(() => {\n return effectiveOptionsView.value === 'native' && props.view !== 'link' && !props.multiple\n})\n\nconst nativeClearOptionVisible = computed(() => {\n if (!props.clearable) return false\n if (effectiveOptionsView.value !== 'native') return false\n if (props.multiple) return false\n if (!props.options) return true\n return !flatOptions.value.some(o => o.value === '')\n})\n\nconst panelClearVisible = computed(() => {\n if (!props.clearable) return false\n if (effectiveOptionsView.value !== 'panel') return false\n if (props.view === 'link') return false\n return hasSelection.value\n})\n\nconst nativeClassName = computed(() => {\n return grSelectNativeClass({\n view: props.view,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n showNativeChevron: showNativeChevron.value,\n })\n})\n\n/**\n * `view=\"link\"` в native-режиме: ширина обёртки должна определяться выбранной опцией,\n * а не самой длинной (как делает браузер по умолчанию). Поэтому рендерим прозрачный\n * `<select>`-overlay поверх видимого `<span>` с меткой — overlay принимает клики/клавиатуру,\n * span задаёт ширину компонента в закрытом состоянии.\n */\nconst isLinkNative = computed(() => props.view === 'link' && effectiveOptionsView.value === 'native')\n\nconst linkNativeLabelClassName = computed(() => grSelectLinkNativeLabelClass({\n size: props.size,\n variant: props.variant,\n underline: props.underline,\n disabled: props.disabled,\n}))\n\nconst linkNativeDisplayText = computed(() => {\n return displayText.value || props.placeholder || '\\u00A0'\n})\n\nconst triggerClassName = computed(() => {\n return grSelectTriggerClass({\n view: props.view,\n optionsView: effectiveOptionsView.value,\n size: props.size,\n disabled: props.disabled,\n variant: props.variant,\n underline: props.underline,\n })\n})\n\nfunction onChange(e: Event): void {\n const el = e.target as HTMLSelectElement\n\n if (props.multiple) {\n const values = Array.from(el.selectedOptions, o => o.value)\n emit('update:modelValue', values)\n return\n }\n\n emit('update:modelValue', el.value)\n}\n\nfunction clearSelection(): void {\n if (props.disabled) return\n emitValue(props.multiple ? [] : '')\n}\n</script>\n\n<template>\n <div\n v-if=\"effectiveOptionsView === 'native'\"\n data-gr-select\n :class=\"rootClass\"\n >\n <!--\n Выбор задаём per-option через `:selected` (а не `:value` на `<select>`):\n для `<select multiple>` биндинг `:value` массивом не работает — DOM не\n отражает программное изменение модели. `onChange` читает выбор из DOM.\n -->\n <select\n :id=\"resolvedId\"\n data-gr-select-native\n :multiple=\"multiple\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :class=\"isLinkNative ? grSelectLinkNativeOverlayClass : [baseClassName, nativeClassName]\"\n @change=\"onChange\"\n >\n <option v-if=\"nativeClearOptionVisible\" value=\"\" :selected=\"!multiple && modelSingle === ''\">\n {{ placeholder || t('gr.select.clearOption', 'None') }}\n </option>\n\n <slot>\n <option v-if=\"nativeCustomOptionVisible\" :value=\"modelSingle\" :selected=\"!multiple\">\n {{ modelSingle }}\n </option>\n <template v-for=\"(item, index) in optionsResolved\" :key=\"index\">\n <optgroup v-if=\"isOptionGroup(item)\" :label=\"item.label\">\n <option\n v-for=\"opt in item.options\"\n :key=\"`${index}:${opt.value}`\"\n :value=\"opt.value\"\n :selected=\"multiple ? modelMultiple.includes(opt.value) : modelSingle === opt.value\"\n :disabled=\"opt.disabled\"\n >\n {{ opt.label }}\n </option>\n </optgroup>\n <option\n v-else\n :value=\"item.value\"\n :selected=\"multiple ? modelMultiple.includes(item.value) : modelSingle === item.value\"\n :disabled=\"item.disabled\"\n >\n {{ item.label }}\n </option>\n </template>\n </slot>\n </select>\n\n <span\n v-if=\"isLinkNative\"\n data-gr-select-link-label\n aria-hidden=\"true\"\n :class=\"linkNativeLabelClassName\"\n >{{ linkNativeDisplayText }}</span>\n\n <span\n v-if=\"showNativeChevron\"\n data-testid=\"gr-select-chevron\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 flex items-center text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </div>\n\n <div\n v-else\n ref=\"rootEl\"\n v-click-outside=\"{ handler: closeDropdown, enabled: open, exclude: clickOutsideExclude }\"\n data-gr-select\n :class=\"rootClass\"\n >\n <button\n :id=\"resolvedId\"\n data-testid=\"gr-select-trigger\"\n data-gr-select-trigger\n type=\"button\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n role=\"combobox\"\n aria-haspopup=\"listbox\"\n :aria-controls=\"open ? listboxId : undefined\"\n :aria-activedescendant=\"activeDescendantId\"\n :aria-expanded=\"open ? 'true' : 'false'\"\n :class=\"[baseClassName, triggerClassName]\"\n @click=\"toggleDropdown\"\n @keydown=\"onComboKeydown\"\n >\n <span class=\"min-w-0 flex-1\">\n <slot\n name=\"value\"\n :selected-options=\"selectedOptions\"\n :selected-values=\"selectedValues\"\n :display-label=\"displayLabel\"\n :placeholder=\"placeholder\"\n :has-selection=\"hasSelection\"\n >\n <span\n class=\"block truncate\"\n :class=\"!hasSelection ? 'text-[var(--muted-fg)]' : ''\"\n >\n {{ displayText }}\n </span>\n </slot>\n </span>\n\n <span\n v-if=\"panelClearVisible\"\n class=\"shrink-0 h-4 w-4\"\n aria-hidden=\"true\"\n />\n\n <span\n v-else\n data-testid=\"gr-select-chevron\"\n class=\"shrink-0 flex items-center text-[var(--muted-fg)] pointer-events-none\"\n >\n <span class=\"i-lucide-chevron-down block h-4 w-4\" aria-hidden=\"true\" />\n </span>\n </button>\n\n <button\n v-if=\"panelClearVisible\"\n data-testid=\"gr-select-clear\"\n data-gr-select-clear\n type=\"button\"\n class=\"absolute top-1/2 -translate-y-1/2 right-3 h-6 w-6 inline-flex items-center justify-center rounded-md text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--muted)_25%,transparent)] disabled:opacity-50\"\n :disabled=\"disabled\"\n :aria-label=\"resolvedClearLabel\"\n @click.stop=\"clearSelection\"\n >\n <span class=\"i-lucide-x inline-block h-4 w-4\" aria-hidden=\"true\" />\n </button>\n\n <teleport to=\"body\">\n <transition\n enter-active-class=\"transition ease-out duration-150\"\n enter-from-class=\"transform opacity-0 scale-95\"\n enter-to-class=\"transform opacity-100 scale-100\"\n leave-active-class=\"transition ease-in duration-100\"\n leave-from-class=\"transform opacity-100 scale-100\"\n leave-to-class=\"transform opacity-0 scale-95\"\n >\n <div\n v-show=\"open\"\n ref=\"panelEl\"\n data-testid=\"gr-select-panel\"\n data-gr-select-panel\n :style=\"floatingStyle\"\n >\n <div :class=\"panelClasses\">\n <div v-if=\"allowCustomValue\" class=\"p-2 border-b border-[var(--brd)]\">\n <GrInput\n ref=\"customInputRef\"\n v-model=\"customValue\"\n data-testid=\"gr-select-custom-input\"\n type=\"text\"\n :placeholder=\"resolvedCustomValuePlaceholder\"\n size=\"sm\"\n @keydown=\"onComboKeydown\"\n />\n </div>\n\n <div\n :id=\"listboxId\"\n data-gr-select-listbox\n class=\"p-1 overflow-auto\"\n :style=\"{ maxHeight: `${dropdownMaxHeight}px` }\"\n role=\"listbox\"\n :aria-multiselectable=\"multiple ? 'true' : undefined\"\n >\n <button\n v-if=\"canAddCustom\"\n data-testid=\"gr-select-add-option\"\n data-gr-select-add-option\n type=\"button\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px] hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n ]\"\n @click=\"addCustom\"\n >\n {{ t('gr.select.addOption', 'Add \"{value}\"', { value: customValue.trim() }) }}\n </button>\n\n <template v-for=\"item in panelItems\" :key=\"item.key\">\n <div\n v-if=\"item.kind === 'group'\"\n data-gr-select-group-label\n role=\"presentation\"\n class=\"px-3 pt-2 pb-1 text-[11px] font-semibold uppercase tracking-wide text-[var(--muted-fg)]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : '',\n ]\"\n >\n {{ item.label }}\n </div>\n\n <button\n v-else\n :id=\"optionDomId(item.option.value)\"\n data-gr-select-option\n type=\"button\"\n role=\"option\"\n :disabled=\"item.option.disabled\"\n :aria-selected=\"isSelected(item.option.value) ? 'true' : 'false'\"\n :aria-disabled=\"item.option.disabled ? 'true' : undefined\"\n class=\"rounded-[10px] px-3 py-2 text-left text-[13px]\" :class=\"[\n view === 'link' ? 'block min-w-full w-max whitespace-nowrap' : 'w-full',\n item.option.disabled ? 'cursor-not-allowed opacity-50' : 'hover:bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]',\n activeValue === item.option.value && !item.option.disabled ? 'bg-[color-mix(in_srgb,var(--muted)_30%,transparent)]' : '',\n ]\"\n @click=\"toggleValue(item.option.value)\"\n @mousemove=\"activeIndex = navigableValues.indexOf(item.option.value)\"\n >\n <slot name=\"option\" :option=\"item.option\" :selected=\"isSelected(item.option.value)\">\n <span class=\"flex items-center gap-2 min-w-0\">\n <span\n class=\"inline-block h-4 w-4 shrink-0\"\n :class=\"isSelected(item.option.value) ? 'i-lucide-check text-[var(--primary)]' : ''\"\n aria-hidden=\"true\"\n />\n <span class=\"truncate\">{{ item.option.label }}</span>\n </span>\n </slot>\n </button>\n </template>\n </div>\n </div>\n </div>\n </transition>\n </teleport>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrSidebar-DkWozzwA.js","names":["$slots"],"sources":["../../../../src/components/GrSidebar/sidebarContext.ts","../../../../src/components/GrSidebar/GrSidebar.vue","../../../../src/components/GrSidebar/GrSidebar.vue","../../../../src/components/GrSidebar/GrSidebarItem.vue","../../../../src/components/GrSidebar/GrSidebarItem.vue"],"sourcesContent":["import type { InjectionKey, Ref } from 'vue'\n\n/**\n * Контекст `GrSidebar`, который потребляют `GrSidebarItem`'ы: свёрнута ли панель.\n * В свёрнутом состоянии пункты показывают только иконку (или первую букву метки).\n */\nexport interface GrSidebarContext {\n collapsed: Ref<boolean>\n}\n\nexport const GR_SIDEBAR_KEY: InjectionKey<GrSidebarContext> = Symbol.for('@feugene/granularity/sidebar')\n","<script setup lang=\"ts\">\n/**\n * GrSidebar — боковая панель приложения с опциональным заголовком, кнопкой\n * сворачивания и слотом контента (обычно — список `GrSidebarItem`).\n *\n * - Свёрнутое состояние (`collapsed`, поддерживает `v-model:collapsed`) сужает\n * панель до иконочной ширины; `GrSidebarItem`'ы через inject показывают только\n * иконку (или первую букву метки).\n * - Кнопка тогла (`show-toggle-button`) переключает состояние.\n * - Хедер рендерится только если есть `title`/`subtitle` (или кнопка тогла) —\n * иначе панель начинается сразу с контента.\n * - Слоты `#title` / `#subtitle` имеют приоритет над одноимёнными пропами.\n */\nimport { computed, provide, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconChevronLeft from '~icons/lucide/chevron-left'\nimport IconChevronRight from '~icons/lucide/chevron-right'\n\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nexport interface GrSidebarProps {\n title?: string\n subtitle?: string\n /** Свёрнутое состояние. Поддерживает `v-model:collapsed`. */\n collapsed?: boolean\n /** Показать кнопку сворачивания/разворачивания в хедере. */\n showToggleButton?: boolean\n /** Ширина в развёрнутом состоянии. */\n width?: string\n /** Ширина в свёрнутом состоянии. */\n collapsedWidth?: string\n /** A11y-лейбл кнопки тогла. */\n toggleLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrSidebarProps>(), {\n title: undefined,\n subtitle: undefined,\n collapsed: false,\n showToggleButton: false,\n width: '240px',\n collapsedWidth: '64px',\n toggleLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:collapsed', value: boolean): void\n}>()\n\n// Локальное состояние с синхронизацией из пропа — поддерживает и controlled\n// (`v-model:collapsed`), и uncontrolled (панель сама помнит состояние).\nconst collapsedState = ref(props.collapsed)\nwatch(() => props.collapsed, value => (collapsedState.value = value))\n\nfunction toggle(): void {\n collapsedState.value = !collapsedState.value\n emit('update:collapsed', collapsedState.value)\n}\n\n// Даём `GrSidebarItem`'ам знать про свёрнутость.\nprovide(GR_SIDEBAR_KEY, { collapsed: collapsedState })\n\nconst hasTitle = computed(() => Boolean(props.title))\nconst hasSubtitle = computed(() => Boolean(props.subtitle))\nconst showHeader = computed(() => Boolean(hasTitle.value || hasSubtitle.value || props.showToggleButton))\n\nconst asideStyle = computed(() => ({\n width: collapsedState.value ? props.collapsedWidth : props.width,\n}))\n\nconst resolvedToggleLabel = computed(() =>\n props.toggleLabel ?? (collapsedState.value ? 'Expand sidebar' : 'Collapse sidebar'),\n)\n</script>\n\n<template>\n <aside\n data-gr-sidebar\n :data-collapsed=\"collapsedState ? 'true' : undefined\"\n class=\"flex flex-col border-r border-[var(--sidebar-brd)] bg-[var(--sidebar)] text-[var(--sidebar-fg)] transition-[width] duration-200 ease-out\"\n :style=\"asideStyle\"\n >\n <div\n v-if=\"showHeader\"\n data-gr-sidebar-header\n class=\"flex items-center gap-2 border-b border-[var(--sidebar-brd)] px-3 py-4\"\n :class=\"collapsedState ? 'justify-center' : 'justify-between'\"\n >\n <div\n v-if=\"!collapsedState && (hasTitle || hasSubtitle || $slots.title || $slots.subtitle)\"\n class=\"min-w-0\"\n >\n <div\n v-if=\"$slots.subtitle || hasSubtitle\"\n data-gr-sidebar-subtitle\n class=\"truncate text-[14px] text-[var(--muted-fg)]\"\n >\n <slot name=\"subtitle\">\n {{ subtitle }}\n </slot>\n </div>\n <div\n v-if=\"$slots.title || hasTitle\"\n data-gr-sidebar-title\n class=\"truncate text-[18px] font-700\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <GrButton\n v-if=\"showToggleButton\"\n data-gr-sidebar-toggle\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedToggleLabel\"\n :aria-expanded=\"collapsedState ? 'false' : 'true'\"\n @click=\"toggle\"\n >\n <GrIcon :size=\"16\" aria-hidden=\"true\">\n <IconChevronRight v-if=\"collapsedState\" />\n <IconChevronLeft v-else />\n </GrIcon>\n </GrButton>\n </div>\n\n <div class=\"flex-1 overflow-y-auto\" :class=\"collapsedState ? 'p-2' : 'p-3'\">\n <slot />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebar — боковая панель приложения с опциональным заголовком, кнопкой\n * сворачивания и слотом контента (обычно — список `GrSidebarItem`).\n *\n * - Свёрнутое состояние (`collapsed`, поддерживает `v-model:collapsed`) сужает\n * панель до иконочной ширины; `GrSidebarItem`'ы через inject показывают только\n * иконку (или первую букву метки).\n * - Кнопка тогла (`show-toggle-button`) переключает состояние.\n * - Хедер рендерится только если есть `title`/`subtitle` (или кнопка тогла) —\n * иначе панель начинается сразу с контента.\n * - Слоты `#title` / `#subtitle` имеют приоритет над одноимёнными пропами.\n */\nimport { computed, provide, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconChevronLeft from '~icons/lucide/chevron-left'\nimport IconChevronRight from '~icons/lucide/chevron-right'\n\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nexport interface GrSidebarProps {\n title?: string\n subtitle?: string\n /** Свёрнутое состояние. Поддерживает `v-model:collapsed`. */\n collapsed?: boolean\n /** Показать кнопку сворачивания/разворачивания в хедере. */\n showToggleButton?: boolean\n /** Ширина в развёрнутом состоянии. */\n width?: string\n /** Ширина в свёрнутом состоянии. */\n collapsedWidth?: string\n /** A11y-лейбл кнопки тогла. */\n toggleLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrSidebarProps>(), {\n title: undefined,\n subtitle: undefined,\n collapsed: false,\n showToggleButton: false,\n width: '240px',\n collapsedWidth: '64px',\n toggleLabel: undefined,\n})\n\nconst emit = defineEmits<{\n (e: 'update:collapsed', value: boolean): void\n}>()\n\n// Локальное состояние с синхронизацией из пропа — поддерживает и controlled\n// (`v-model:collapsed`), и uncontrolled (панель сама помнит состояние).\nconst collapsedState = ref(props.collapsed)\nwatch(() => props.collapsed, value => (collapsedState.value = value))\n\nfunction toggle(): void {\n collapsedState.value = !collapsedState.value\n emit('update:collapsed', collapsedState.value)\n}\n\n// Даём `GrSidebarItem`'ам знать про свёрнутость.\nprovide(GR_SIDEBAR_KEY, { collapsed: collapsedState })\n\nconst hasTitle = computed(() => Boolean(props.title))\nconst hasSubtitle = computed(() => Boolean(props.subtitle))\nconst showHeader = computed(() => Boolean(hasTitle.value || hasSubtitle.value || props.showToggleButton))\n\nconst asideStyle = computed(() => ({\n width: collapsedState.value ? props.collapsedWidth : props.width,\n}))\n\nconst resolvedToggleLabel = computed(() =>\n props.toggleLabel ?? (collapsedState.value ? 'Expand sidebar' : 'Collapse sidebar'),\n)\n</script>\n\n<template>\n <aside\n data-gr-sidebar\n :data-collapsed=\"collapsedState ? 'true' : undefined\"\n class=\"flex flex-col border-r border-[var(--sidebar-brd)] bg-[var(--sidebar)] text-[var(--sidebar-fg)] transition-[width] duration-200 ease-out\"\n :style=\"asideStyle\"\n >\n <div\n v-if=\"showHeader\"\n data-gr-sidebar-header\n class=\"flex items-center gap-2 border-b border-[var(--sidebar-brd)] px-3 py-4\"\n :class=\"collapsedState ? 'justify-center' : 'justify-between'\"\n >\n <div\n v-if=\"!collapsedState && (hasTitle || hasSubtitle || $slots.title || $slots.subtitle)\"\n class=\"min-w-0\"\n >\n <div\n v-if=\"$slots.subtitle || hasSubtitle\"\n data-gr-sidebar-subtitle\n class=\"truncate text-[14px] text-[var(--muted-fg)]\"\n >\n <slot name=\"subtitle\">\n {{ subtitle }}\n </slot>\n </div>\n <div\n v-if=\"$slots.title || hasTitle\"\n data-gr-sidebar-title\n class=\"truncate text-[18px] font-700\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <GrButton\n v-if=\"showToggleButton\"\n data-gr-sidebar-toggle\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedToggleLabel\"\n :aria-expanded=\"collapsedState ? 'false' : 'true'\"\n @click=\"toggle\"\n >\n <GrIcon :size=\"16\" aria-hidden=\"true\">\n <IconChevronRight v-if=\"collapsedState\" />\n <IconChevronLeft v-else />\n </GrIcon>\n </GrButton>\n </div>\n\n <div class=\"flex-1 overflow-y-auto\" :class=\"collapsedState ? 'p-2' : 'p-3'\">\n <slot />\n </div>\n </aside>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebarItem — пункт навигации боковой панели `GrSidebar`.\n *\n * Контракт:\n * - развёрнутая панель — `[иконка] метка [бейдж]`;\n * - свёрнутая панель — только иконка; если иконки нет, показывается ПЕРВАЯ БУКВА\n * метки (uppercase). В свёрнутом виде метка уходит в `title`/`aria-label` для\n * доступности и tooltip.\n *\n * Корневой тег: `as` → `<a href>` → `<button>` (в этом порядке).\n */\nimport { computed, inject, markRaw, type Component } from 'vue'\n\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nconst props = withDefaults(defineProps<{\n label: string\n /** Иконка: класс UnoCSS-иконки (`'i-lucide-home'`) или Vue-компонент. */\n icon?: string | Component\n href?: string\n as?: string | Component\n active?: boolean\n disabled?: boolean\n badge?: string | number\n}>(), {\n icon: undefined,\n href: undefined,\n as: undefined,\n active: false,\n disabled: false,\n badge: undefined,\n})\n\nconst sidebar = inject(GR_SIDEBAR_KEY, null)\nconst collapsed = computed(() => sidebar?.collapsed.value ?? false)\n\nconst isStringIcon = computed(() => typeof props.icon === 'string')\nconst iconComponent = computed(() => (props.icon && typeof props.icon !== 'string' ? markRaw(props.icon as Component) : null))\nconst firstLetter = computed(() => props.label.trim().charAt(0).toUpperCase() || '•')\n\nconst rootTag = computed<string | Component>(() => {\n if (props.disabled) return 'span'\n if (props.as) return typeof props.as === 'string' ? props.as : markRaw(props.as)\n return props.href ? 'a' : 'button'\n})\n\nconst rootClass = computed(() => [\n 'group relative flex w-full items-center rounded-lg text-sm transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]',\n collapsed.value ? 'justify-center px-0 py-2' : 'gap-3 px-3 py-2',\n props.disabled\n ? 'cursor-not-allowed opacity-50'\n : props.active\n ? 'bg-[var(--muted)] font-600 text-[var(--fg)]'\n : 'text-[var(--sidebar-fg)] hover:bg-[color-mix(in_srgb,var(--sidebar-fg)_8%,transparent)]',\n])\n</script>\n\n<template>\n <component\n :is=\"rootTag\"\n data-gr-sidebar-item\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootTag === 'a' ? href : undefined\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :title=\"collapsed ? label : undefined\"\n :aria-label=\"collapsed ? label : undefined\"\n :class=\"rootClass\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"collapsed\" class=\"text-[13px] font-700 leading-none\" aria-hidden=\"true\">{{ firstLetter }}</span>\n </span>\n\n <template v-if=\"!collapsed\">\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ label }}</span>\n <span\n v-if=\"badge != null\"\n class=\"shrink-0 rounded-full bg-[color-mix(in_srgb,var(--sidebar-fg)_12%,transparent)] px-1.5 py-0.5 text-[11px] font-600\"\n >{{ badge }}</span>\n </template>\n </component>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebarItem — пункт навигации боковой панели `GrSidebar`.\n *\n * Контракт:\n * - развёрнутая панель — `[иконка] метка [бейдж]`;\n * - свёрнутая панель — только иконка; если иконки нет, показывается ПЕРВАЯ БУКВА\n * метки (uppercase). В свёрнутом виде метка уходит в `title`/`aria-label` для\n * доступности и tooltip.\n *\n * Корневой тег: `as` → `<a href>` → `<button>` (в этом порядке).\n */\nimport { computed, inject, markRaw, type Component } from 'vue'\n\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nconst props = withDefaults(defineProps<{\n label: string\n /** Иконка: класс UnoCSS-иконки (`'i-lucide-home'`) или Vue-компонент. */\n icon?: string | Component\n href?: string\n as?: string | Component\n active?: boolean\n disabled?: boolean\n badge?: string | number\n}>(), {\n icon: undefined,\n href: undefined,\n as: undefined,\n active: false,\n disabled: false,\n badge: undefined,\n})\n\nconst sidebar = inject(GR_SIDEBAR_KEY, null)\nconst collapsed = computed(() => sidebar?.collapsed.value ?? false)\n\nconst isStringIcon = computed(() => typeof props.icon === 'string')\nconst iconComponent = computed(() => (props.icon && typeof props.icon !== 'string' ? markRaw(props.icon as Component) : null))\nconst firstLetter = computed(() => props.label.trim().charAt(0).toUpperCase() || '•')\n\nconst rootTag = computed<string | Component>(() => {\n if (props.disabled) return 'span'\n if (props.as) return typeof props.as === 'string' ? props.as : markRaw(props.as)\n return props.href ? 'a' : 'button'\n})\n\nconst rootClass = computed(() => [\n 'group relative flex w-full items-center rounded-lg text-sm transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]',\n collapsed.value ? 'justify-center px-0 py-2' : 'gap-3 px-3 py-2',\n props.disabled\n ? 'cursor-not-allowed opacity-50'\n : props.active\n ? 'bg-[var(--muted)] font-600 text-[var(--fg)]'\n : 'text-[var(--sidebar-fg)] hover:bg-[color-mix(in_srgb,var(--sidebar-fg)_8%,transparent)]',\n])\n</script>\n\n<template>\n <component\n :is=\"rootTag\"\n data-gr-sidebar-item\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootTag === 'a' ? href : undefined\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :title=\"collapsed ? label : undefined\"\n :aria-label=\"collapsed ? label : undefined\"\n :class=\"rootClass\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"collapsed\" class=\"text-[13px] font-700 leading-none\" aria-hidden=\"true\">{{ firstLetter }}</span>\n </span>\n\n <template v-if=\"!collapsed\">\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ label }}</span>\n <span\n v-if=\"badge != null\"\n class=\"shrink-0 rounded-full bg-[color-mix(in_srgb,var(--sidebar-fg)_12%,transparent)] px-1.5 py-0.5 text-[11px] font-600\"\n >{{ badge }}</span>\n </template>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,IAAa,iBAAiD,OAAO,IAAI,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC2BvG,MAAM,QAAQ;EAUd,MAAM,OAAO;EAMb,MAAM,iBAAiB,IAAI,MAAM,SAAS;EAC1C,YAAY,MAAM,YAAW,UAAU,eAAe,QAAQ,KAAM;EAEpE,SAAS,SAAe;GACtB,eAAe,QAAQ,CAAC,eAAe;GACvC,KAAK,oBAAoB,eAAe,KAAK;EAC/C;EAGA,QAAQ,gBAAgB,EAAE,WAAW,eAAe,CAAC;EAErD,MAAM,WAAW,eAAe,QAAQ,MAAM,KAAK,CAAC;EACpD,MAAM,cAAc,eAAe,QAAQ,MAAM,QAAQ,CAAC;EAC1D,MAAM,aAAa,eAAe,QAAQ,SAAS,SAAS,YAAY,SAAS,MAAM,gBAAgB,CAAC;EAExG,MAAM,aAAa,gBAAgB,EACjC,OAAO,eAAe,QAAQ,MAAM,iBAAiB,MAAM,MAC7D,EAAE;EAEF,MAAM,sBAAsB,eAC1B,MAAM,gBAAgB,eAAe,QAAQ,mBAAmB,mBAClE;;uBAIE,mBAwDQ,SAAA;IAvDN,mBAAA;IACC,kBAAgB,eAAA,QAAc,SAAY,KAAA;IAC3C,OAAM;IACL,OAAK,eAAE,WAAA,KAAU;OAGV,WAAA,SAAA,UAAA,GADR,mBA6CM,OAAA;;IA3CJ,0BAAA;IACA,OAAK,eAAA,CAAC,0EACE,eAAA,QAAc,mBAAA,iBAAA,CAAA;QAGb,eAAA,UAAmB,SAAA,SAAY,YAAA,SAAeA,KAAAA,OAAO,SAASA,KAAAA,OAAO,aAAA,UAAA,GAD9E,mBAsBM,OAtBN,cAsBM,CAjBIA,KAAAA,OAAO,YAAY,YAAA,SAAA,UAAA,GAD3B,mBAQM,OARN,cAQM,CAHJ,WAEO,KAAA,QAAA,YAAA,CAAA,SAAA,CAAA,gBAAA,gBADF,QAAA,QAAQ,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAIPA,KAAAA,OAAO,SAAS,SAAA,SAAA,UAAA,GADxB,mBAQM,OARN,cAQM,CAHJ,WAEO,KAAA,QAAA,SAAA,CAAA,SAAA,CAAA,gBAAA,gBADF,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAMN,QAAA,oBAAA,UAAA,GADR,YAcW,kBAAA;;IAZT,0BAAA;IACA,SAAQ;IACR,MAAK;IACL,QAAA;IACC,cAAY,oBAAA;IACZ,iBAAe,eAAA,QAAc,UAAA;IAC7B,SAAO;;2BAKC,CAHT,YAGS,gBAAA;KAHA,MAAM;KAAI,eAAY;;4BACa,CAAlB,eAAA,SAAA,UAAA,GAAxB,YAA0C,MAAA,qBAAA,GAAA,EAAA,KAAA,EAAA,CAAA,MAAA,UAAA,GAC1C,YAA0B,MAAA,oBAAA,GAAA,EAAA,KAAA,EAAA,CAAA,EAAA,CAAA;;;;gHAKhC,mBAEM,OAAA,EAFD,OAAK,eAAA,CAAC,0BAAiC,eAAA,QAAc,QAAA,KAAA,CAAA,EAAA,GAAA,CACxD,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA,CAAA,GAAA,IAAA,YAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEpHd,MAAM,QAAQ;EAkBd,MAAM,UAAU,OAAO,gBAAgB,IAAI;EAC3C,MAAM,YAAY,eAAe,SAAS,UAAU,SAAS,KAAK;EAElE,MAAM,eAAe,eAAe,OAAO,MAAM,SAAS,QAAQ;EAClE,MAAM,gBAAgB,eAAgB,MAAM,QAAQ,OAAO,MAAM,SAAS,WAAW,QAAQ,MAAM,IAAiB,IAAI,IAAK;EAC7H,MAAM,cAAc,eAAe,MAAM,MAAM,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,KAAK,GAAG;EAEpF,MAAM,UAAU,eAAmC;GACjD,IAAI,MAAM,UAAU,OAAO;GAC3B,IAAI,MAAM,IAAI,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,QAAQ,MAAM,EAAE;GAC/E,OAAO,MAAM,OAAO,MAAM;EAC5B,CAAC;EAED,MAAM,YAAY,eAAe;GAC/B;GACA,UAAU,QAAQ,6BAA6B;GAC/C,MAAM,WACF,kCACA,MAAM,SACJ,gDACA;EACR,CAAC;;uBAIC,YAwBY,wBAvBL,QAAA,KAAO,GAAA;IACZ,wBAAA;IACC,MAAM,QAAA,UAAO,WAAA,WAA2B,KAAA;IACxC,MAAM,QAAA,UAAO,MAAW,QAAA,OAAO,KAAA;IAC/B,gBAAc,QAAA,SAAM,SAAY,KAAA;IAChC,iBAAe,QAAA,WAAQ,SAAY,KAAA;IACnC,OAAO,UAAA,QAAY,QAAA,QAAQ,KAAA;IAC3B,cAAY,UAAA,QAAY,QAAA,QAAQ,KAAA;IAChC,OAAK,eAAE,UAAA,KAAS;;2BAMV,CAJP,mBAIO,QAJP,YAIO,CAHgC,cAAA,SAAA,UAAA,GAArC,YAAyF,wBAAzE,cAAA,KAAa,GAAA;;KAAuB,OAAM;KAAU,eAAY;UAC/D,aAAA,SAAA,UAAA,GAAjB,mBAAwF,QAAA;;KAAxD,OAAK,eAAA,CAAE,QAAA,MAAY,eAAe,CAAA;KAAC,eAAY;mBAC9D,UAAA,SAAA,UAAA,GAAjB,mBAAiH,QAAjH,YAAiH,gBAArB,YAAA,KAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,GAAA,CAGxF,UAAA,SAAA,UAAA,GAAjB,mBAMW,UAAA,EAAA,KAAA,EAAA,GAAA,CALT,mBAAkE,QAAlE,YAAkE,gBAAf,QAAA,KAAK,GAAA,CAAA,GAEhD,QAAA,SAAK,QAAA,UAAA,GADb,mBAGmB,QAHnB,YAGmB,gBAAf,QAAA,KAAK,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,EAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrSkeleton-DsN-_CDZ.js","names":[],"sources":["../../../../src/components/GrSkeleton/GrSkeleton.vue","../../../../src/components/GrSkeleton/GrSkeleton.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrSkeleton — заглушка-плейсхолдер для загружающегося контента.\n *\n * Помечен `aria-hidden=\"true\"`, чтобы скринридер не озвучивал «пустой» узел;\n * внешний контейнер обычно имеет `aria-busy=\"true\"` + `aria-live`.\n */\nexport interface GrSkeletonProps {\n height?: string\n width?: string\n rounded?: string\n}\n\nwithDefaults(defineProps<GrSkeletonProps>(), {\n height: '12px',\n width: '100%',\n rounded: '9999px',\n})\n</script>\n\n<template>\n <div\n data-gr-skeleton\n aria-hidden=\"true\"\n class=\"bg-[var(--muted)] border border-[var(--brd)]\"\n :style=\"{ height, width, borderRadius: rounded }\"\n />\n</template>\n\n<style>\n@keyframes gr-skeleton-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n[data-gr-skeleton] {\n animation: gr-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n [data-gr-skeleton] {\n animation: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * GrSkeleton — заглушка-плейсхолдер для загружающегося контента.\n *\n * Помечен `aria-hidden=\"true\"`, чтобы скринридер не озвучивал «пустой» узел;\n * внешний контейнер обычно имеет `aria-busy=\"true\"` + `aria-live`.\n */\nexport interface GrSkeletonProps {\n height?: string\n width?: string\n rounded?: string\n}\n\nwithDefaults(defineProps<GrSkeletonProps>(), {\n height: '12px',\n width: '100%',\n rounded: '9999px',\n})\n</script>\n\n<template>\n <div\n data-gr-skeleton\n aria-hidden=\"true\"\n class=\"bg-[var(--muted)] border border-[var(--brd)]\"\n :style=\"{ height, width, borderRadius: rounded }\"\n />\n</template>\n\n<style>\n@keyframes gr-skeleton-pulse {\n 0%, 100% {\n opacity: 1;\n }\n 50% {\n opacity: 0.5;\n }\n}\n\n[data-gr-skeleton] {\n animation: gr-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n\n@media (prefers-reduced-motion: reduce) {\n [data-gr-skeleton] {\n animation: none;\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;uBAqBE,mBAKE,OAAA;IAJE,oBAAA;IACA,eAAY;IACZ,OAAM;IACL,OAAK,eAAA;KAAA,QAAI,QAAA;KAAM,OAAE,QAAA;KAAK,cAAgB,QAAA;IAAO,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrSwitch-DAioyccZ.js","names":["$slots"],"sources":["../../../../src/components/GrSwitch/GrSwitch.vue","../../../../src/components/GrSwitch/GrSwitch.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport {computed} from 'vue'\n\nimport {\n grSwitchLabelClass,\n grSwitchThumbClass,\n grSwitchTrackClass,\n type GrSwitchSize,\n} from './grSwitchStyles'\n\nexport type {GrSwitchSize} from './grSwitchStyles'\n\n/**\n * Пропсы публичного GR-примитива «Switch».\n */\nexport interface GrSwitchProps {\n modelValue: boolean\n disabled?: boolean\n ariaLabel?: string\n size?: GrSwitchSize\n /** Кастомный цвет фона в активном состоянии. Если не задан — `var(--primary)`. */\n activeBackgroundColor?: string\n /** Кастомный цвет фона в неактивном состоянии. Если не задан — `var(--muted)`. */\n inactiveBackgroundColor?: string\n}\n\nconst getCustomColor = (value?: string) => value?.trim() || undefined\n\nconst props = withDefaults(\n defineProps<GrSwitchProps>(),\n {\n disabled: false,\n ariaLabel: undefined,\n size: 'md',\n activeBackgroundColor: undefined,\n inactiveBackgroundColor: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst trackClass = computed(() => grSwitchTrackClass(props.size))\n\nconst trackStyle = computed(() => {\n const isChecked = props.modelValue\n const defaultBackgroundColor = isChecked ? 'var(--primary)' : 'var(--muted)'\n const customBackgroundColor = getCustomColor(\n isChecked ? props.activeBackgroundColor : props.inactiveBackgroundColor,\n )\n const backgroundColor = customBackgroundColor ?? defaultBackgroundColor\n\n return {\n '--gr-switch-track-bg': backgroundColor,\n '--gr-switch-track-brd': customBackgroundColor\n ? backgroundColor\n : isChecked\n ? 'var(--primary)'\n : 'var(--brd)',\n backgroundColor: 'var(--gr-switch-track-bg)',\n }\n})\n\nconst thumbClass = computed(() => grSwitchThumbClass({size: props.size, checked: props.modelValue}))\n\nconst labelClass = computed(() => grSwitchLabelClass(props.size))\n\nfunction toggle(): void {\n if (props.disabled) {\n return\n }\n\n emit('update:modelValue', !props.modelValue)\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n role=\"switch\"\n data-gr-switch\n :aria-checked=\"modelValue ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :disabled=\"disabled\"\n class=\"inline-flex items-center gap-2 select-none disabled:opacity-50 disabled:cursor-not-allowed\"\n @click=\"toggle\"\n >\n <span\n data-testid=\"gr-switch-track\"\n data-gr-switch-track\n :class=\"trackClass\"\n :style=\"trackStyle\"\n >\n <span\n data-testid=\"gr-switch-thumb\"\n data-gr-switch-thumb\n :class=\"thumbClass\"\n aria-hidden=\"true\"\n />\n </span>\n <span\n v-if=\"$slots.default\"\n data-gr-switch-label\n :class=\"labelClass\"\n >\n <slot />\n </span>\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport {computed} from 'vue'\n\nimport {\n grSwitchLabelClass,\n grSwitchThumbClass,\n grSwitchTrackClass,\n type GrSwitchSize,\n} from './grSwitchStyles'\n\nexport type {GrSwitchSize} from './grSwitchStyles'\n\n/**\n * Пропсы публичного GR-примитива «Switch».\n */\nexport interface GrSwitchProps {\n modelValue: boolean\n disabled?: boolean\n ariaLabel?: string\n size?: GrSwitchSize\n /** Кастомный цвет фона в активном состоянии. Если не задан — `var(--primary)`. */\n activeBackgroundColor?: string\n /** Кастомный цвет фона в неактивном состоянии. Если не задан — `var(--muted)`. */\n inactiveBackgroundColor?: string\n}\n\nconst getCustomColor = (value?: string) => value?.trim() || undefined\n\nconst props = withDefaults(\n defineProps<GrSwitchProps>(),\n {\n disabled: false,\n ariaLabel: undefined,\n size: 'md',\n activeBackgroundColor: undefined,\n inactiveBackgroundColor: undefined,\n },\n)\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: boolean): void\n}>()\n\nconst trackClass = computed(() => grSwitchTrackClass(props.size))\n\nconst trackStyle = computed(() => {\n const isChecked = props.modelValue\n const defaultBackgroundColor = isChecked ? 'var(--primary)' : 'var(--muted)'\n const customBackgroundColor = getCustomColor(\n isChecked ? props.activeBackgroundColor : props.inactiveBackgroundColor,\n )\n const backgroundColor = customBackgroundColor ?? defaultBackgroundColor\n\n return {\n '--gr-switch-track-bg': backgroundColor,\n '--gr-switch-track-brd': customBackgroundColor\n ? backgroundColor\n : isChecked\n ? 'var(--primary)'\n : 'var(--brd)',\n backgroundColor: 'var(--gr-switch-track-bg)',\n }\n})\n\nconst thumbClass = computed(() => grSwitchThumbClass({size: props.size, checked: props.modelValue}))\n\nconst labelClass = computed(() => grSwitchLabelClass(props.size))\n\nfunction toggle(): void {\n if (props.disabled) {\n return\n }\n\n emit('update:modelValue', !props.modelValue)\n}\n</script>\n\n<template>\n <button\n type=\"button\"\n role=\"switch\"\n data-gr-switch\n :aria-checked=\"modelValue ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :disabled=\"disabled\"\n class=\"inline-flex items-center gap-2 select-none disabled:opacity-50 disabled:cursor-not-allowed\"\n @click=\"toggle\"\n >\n <span\n data-testid=\"gr-switch-track\"\n data-gr-switch-track\n :class=\"trackClass\"\n :style=\"trackStyle\"\n >\n <span\n data-testid=\"gr-switch-thumb\"\n data-gr-switch-thumb\n :class=\"thumbClass\"\n aria-hidden=\"true\"\n />\n </span>\n <span\n v-if=\"$slots.default\"\n data-gr-switch-label\n :class=\"labelClass\"\n >\n <slot />\n </span>\n </button>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;EA0BA,MAAM,kBAAkB,UAAmB,OAAO,KAAK,KAAK,KAAA;EAE5D,MAAM,QAAQ;EAWd,MAAM,OAAO;EAIb,MAAM,aAAa,eAAe,mBAAmB,MAAM,IAAI,CAAC;EAEhE,MAAM,aAAa,eAAe;GAChC,MAAM,YAAY,MAAM;GACxB,MAAM,yBAAyB,YAAY,mBAAmB;GAC9D,MAAM,wBAAwB,eAC1B,YAAY,MAAM,wBAAwB,MAAM,uBACpD;GACA,MAAM,kBAAkB,yBAAyB;GAEjD,OAAO;IACL,wBAAwB;IACxB,yBAAyB,wBACnB,kBACA,YACI,mBACA;IACV,iBAAiB;GACnB;EACF,CAAC;EAED,MAAM,aAAa,eAAe,mBAAmB;GAAC,MAAM,MAAM;GAAM,SAAS,MAAM;EAAU,CAAC,CAAC;EAEnG,MAAM,aAAa,eAAe,mBAAmB,MAAM,IAAI,CAAC;EAEhE,SAAS,SAAe;GACtB,IAAI,MAAM,UACR;GAGF,KAAK,qBAAqB,CAAC,MAAM,UAAU;EAC7C;;uBAIE,mBA8BS,UAAA;IA7BL,MAAK;IACL,MAAK;IACL,kBAAA;IACC,gBAAc,QAAA,aAAU,SAAA;IACxB,cAAY,QAAA;IACZ,UAAU,QAAA;IACX,OAAM;IACL,SAAO;OAEV,mBAYO,QAAA;IAXH,eAAY;IACZ,wBAAA;IACC,OAAK,eAAE,WAAA,KAAU;IACjB,OAAK,eAAE,WAAA,KAAU;OAEpB,mBAKE,QAAA;IAJE,eAAY;IACZ,wBAAA;IACC,OAAK,eAAE,WAAA,KAAU;IAClB,eAAY;qBAIRA,KAAAA,OAAO,WAAA,UAAA,GADjB,mBAMO,QAAA;;IAJH,wBAAA;IACC,OAAK,eAAE,WAAA,KAAU;OAEpB,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,GAAA,UAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrTable-Db3hRnKA.js","names":[],"sources":["../../../../src/components/GrTable/GrTable.vue","../../../../src/components/GrTable/GrTable.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue'\n\nexport type GrTableDensity = 'compact' | 'regular'\n\nexport interface GrTableProps {\n /**\n * Плотность таблицы — влияет на базовый размер шрифта.\n * Паддинги ячеек оставлены за консьюмером (GrTable — «тонкий» контейнер).\n */\n density?: GrTableDensity\n /**\n * Текст caption для screen reader'ов. Рендерится как `<caption class=\"sr-only\">`,\n * если не передан слот `#caption`.\n */\n caption?: string\n /** Прямой ARIA-label для `<table>`. Игнорируется, если задан `ariaLabelledby`. */\n ariaLabel?: string\n /** ID элемента-заголовка, связанного с `<table>` через `aria-labelledby`. */\n ariaLabelledby?: string\n /**\n * ARIA-label для скролл-контейнера (`role=\"region\"` + `tabindex=\"0\"`).\n * Позволяет клавиатурным пользователям проскроллить широкую таблицу.\n * Если не задан — region-роль не включается.\n */\n regionLabel?: string\n}\n\n/**\n * `GrTable` — «тонкий» GR-контейнер для табличных данных.\n *\n * Рендерит `<table>` внутри скролл-обёртки и даёт слоты `#caption`, `#head`,\n * default (tbody) и `#foot`. Стили ячеек (`<th>`/`<td>` паддинги, выравнивание)\n * оставлены на консьюмере.\n *\n * ВАЖНО: `min-w-full` сам по себе не даст горизонтального скролла. Для него\n * консьюмеру нужны `white-space: nowrap` на ячейках или явные ширины колонок.\n */\nconst props = withDefaults(defineProps<GrTableProps>(), {\n density: 'regular',\n caption: undefined,\n ariaLabel: undefined,\n ariaLabelledby: undefined,\n regionLabel: undefined,\n})\n\nconst slots = useSlots()\n\nconst tableTextClass = computed(() => (props.density === 'compact' ? 'text-[13px]' : 'text-sm'))\nconst hasCaption = computed(() => Boolean(slots.caption) || Boolean(props.caption))\n</script>\n\n<template>\n <div\n data-gr-table-scroll\n :role=\"regionLabel ? 'region' : undefined\"\n :aria-label=\"regionLabel\"\n :tabindex=\"regionLabel ? 0 : undefined\"\n class=\"overflow-x-auto rounded-[var(--gr-radius-lg)] border border-[var(--brd)] bg-[var(--card)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n >\n <table\n data-gr-table\n class=\"min-w-full\" :class=\"[tableTextClass]\"\n :aria-label=\"ariaLabelledby ? undefined : ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n >\n <caption v-if=\"hasCaption\" class=\"sr-only\">\n <slot name=\"caption\">\n{{ caption }}\n</slot>\n </caption>\n <thead class=\"bg-[var(--muted)] text-[var(--muted-fg)]\">\n <slot name=\"head\" />\n </thead>\n <tbody class=\"text-[var(--fg)]\">\n <slot />\n </tbody>\n <tfoot v-if=\"slots.foot\" class=\"text-[var(--fg)]\">\n <slot name=\"foot\" />\n </tfoot>\n </table>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, useSlots } from 'vue'\n\nexport type GrTableDensity = 'compact' | 'regular'\n\nexport interface GrTableProps {\n /**\n * Плотность таблицы — влияет на базовый размер шрифта.\n * Паддинги ячеек оставлены за консьюмером (GrTable — «тонкий» контейнер).\n */\n density?: GrTableDensity\n /**\n * Текст caption для screen reader'ов. Рендерится как `<caption class=\"sr-only\">`,\n * если не передан слот `#caption`.\n */\n caption?: string\n /** Прямой ARIA-label для `<table>`. Игнорируется, если задан `ariaLabelledby`. */\n ariaLabel?: string\n /** ID элемента-заголовка, связанного с `<table>` через `aria-labelledby`. */\n ariaLabelledby?: string\n /**\n * ARIA-label для скролл-контейнера (`role=\"region\"` + `tabindex=\"0\"`).\n * Позволяет клавиатурным пользователям проскроллить широкую таблицу.\n * Если не задан — region-роль не включается.\n */\n regionLabel?: string\n}\n\n/**\n * `GrTable` — «тонкий» GR-контейнер для табличных данных.\n *\n * Рендерит `<table>` внутри скролл-обёртки и даёт слоты `#caption`, `#head`,\n * default (tbody) и `#foot`. Стили ячеек (`<th>`/`<td>` паддинги, выравнивание)\n * оставлены на консьюмере.\n *\n * ВАЖНО: `min-w-full` сам по себе не даст горизонтального скролла. Для него\n * консьюмеру нужны `white-space: nowrap` на ячейках или явные ширины колонок.\n */\nconst props = withDefaults(defineProps<GrTableProps>(), {\n density: 'regular',\n caption: undefined,\n ariaLabel: undefined,\n ariaLabelledby: undefined,\n regionLabel: undefined,\n})\n\nconst slots = useSlots()\n\nconst tableTextClass = computed(() => (props.density === 'compact' ? 'text-[13px]' : 'text-sm'))\nconst hasCaption = computed(() => Boolean(slots.caption) || Boolean(props.caption))\n</script>\n\n<template>\n <div\n data-gr-table-scroll\n :role=\"regionLabel ? 'region' : undefined\"\n :aria-label=\"regionLabel\"\n :tabindex=\"regionLabel ? 0 : undefined\"\n class=\"overflow-x-auto rounded-[var(--gr-radius-lg)] border border-[var(--brd)] bg-[var(--card)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)]\"\n >\n <table\n data-gr-table\n class=\"min-w-full\" :class=\"[tableTextClass]\"\n :aria-label=\"ariaLabelledby ? undefined : ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n >\n <caption v-if=\"hasCaption\" class=\"sr-only\">\n <slot name=\"caption\">\n{{ caption }}\n</slot>\n </caption>\n <thead class=\"bg-[var(--muted)] text-[var(--muted-fg)]\">\n <slot name=\"head\" />\n </thead>\n <tbody class=\"text-[var(--fg)]\">\n <slot />\n </tbody>\n <tfoot v-if=\"slots.foot\" class=\"text-[var(--fg)]\">\n <slot name=\"foot\" />\n </tfoot>\n </table>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsCA,MAAM,QAAQ;EAQd,MAAM,QAAQ,SAAS;EAEvB,MAAM,iBAAiB,eAAgB,MAAM,YAAY,YAAY,gBAAgB,SAAU;EAC/F,MAAM,aAAa,eAAe,QAAQ,MAAM,OAAO,KAAK,QAAQ,MAAM,OAAO,CAAC;;uBAIhF,mBA4BM,OAAA;IA3BJ,wBAAA;IACC,MAAM,QAAA,cAAW,WAAc,KAAA;IAC/B,cAAY,QAAA;IACZ,UAAU,QAAA,cAAW,IAAO,KAAA;IAC7B,OAAM;OAEN,mBAoBQ,SAAA;IAnBN,iBAAA;IACA,OAAK,eAAA,CAAC,cAAY,CAAU,eAAA,KAAc,CAAA,CAAA;IACzC,cAAY,QAAA,iBAAiB,KAAA,IAAY,QAAA;IACzC,mBAAiB,QAAA;;IAEH,WAAA,SAAA,UAAA,GAAf,mBAIU,WAJV,YAIU,CAHR,WAED,KAAA,QAAA,WAAA,CAAA,SAAA,CAAA,gBAAA,gBADJ,QAAA,OAAO,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAGJ,mBAEQ,SAFR,YAEQ,CADN,WAAoB,KAAA,QAAA,MAAA,CAAA,CAAA;IAEtB,mBAEQ,SAFR,YAEQ,CADN,WAAQ,KAAA,QAAA,SAAA,CAAA,CAAA;IAEG,MAAA,KAAA,CAAK,CAAC,QAAA,UAAA,GAAnB,mBAEQ,SAFR,YAEQ,CADN,WAAoB,KAAA,QAAA,MAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrTabs-DsLwWoRz.js","names":[],"sources":["../../../../src/components/GrTabs/GrTabs.vue","../../../../src/components/GrTabs/GrTabs.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue'\n\nexport type GrTab = {\n value: string\n label: string\n badge?: string\n disabled?: boolean\n}\n\nexport type GrTabsProps = {\n modelValue: string\n tabs: GrTab[]\n /**\n * База id для ARIA-связки с `GrTabPanels`. Если задана, каждая вкладка\n * получает `id=\"<idBase>-tab-<value>\"` и `aria-controls=\"<idBase>-panel-<value>\"`.\n * Передайте тот же `idBase` в `GrTabPanels`, чтобы связать `tab`↔`tabpanel`.\n */\n idBase?: string\n}\n\n/**\n * GrTabs — горизонтальная группа вкладок с паттерном WAI-ARIA `tablist`.\n *\n * A11y:\n * - `role=\"tablist\"` на корне, `role=\"tab\"` на каждой вкладке.\n * - Roving `tabindex`: активная вкладка `0`, остальные `-1`.\n * - Клавиатура: `ArrowLeft`/`ArrowRight` — цикличный переход, `Home`/`End` — к первой/последней.\n * - При переключении стрелками DOM-фокус программно переносится на новую вкладку.\n *\n * Сам компонент не рендерит `tabpanel` — это ответственность консьюмера (по `aria-controls`/внешней разметке).\n */\nconst props = defineProps<GrTabsProps>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst buttonRefs = ref<HTMLButtonElement[]>([])\n\nfunction setButtonRef(el: unknown, index: number): void {\n if (el instanceof HTMLButtonElement)\n buttonRefs.value[index] = el\n}\n\nconst activeIndex = computed(() => props.tabs.findIndex(t => t.value === props.modelValue))\n\nfunction isEnabled(tab: GrTab): boolean {\n return !tab.disabled\n}\n\nasync function selectByIndex(index: number, focus = false): Promise<void> {\n const tab = props.tabs[index]\n if (!tab || !isEnabled(tab))\n return\n if (tab.value !== props.modelValue)\n emit('update:modelValue', tab.value)\n if (focus) {\n await nextTick()\n buttonRefs.value[index]?.focus()\n }\n}\n\nfunction findNextEnabled(from: number, direction: 1 | -1): number {\n const length = props.tabs.length\n if (length === 0)\n return -1\n let index = from\n for (let i = 0; i < length; i++) {\n index = (index + direction + length) % length\n const tab = props.tabs[index]\n if (tab && isEnabled(tab))\n return index\n }\n return -1\n}\n\nfunction firstEnabled(): number {\n return props.tabs.findIndex(isEnabled)\n}\n\nfunction lastEnabled(): number {\n for (let i = props.tabs.length - 1; i >= 0; i--) {\n const tab = props.tabs[i]\n if (tab && isEnabled(tab))\n return i\n }\n return -1\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (props.tabs.length === 0)\n return\n\n const currentIndex = activeIndex.value < 0 ? 0 : activeIndex.value\n let nextIndex = -1\n\n switch (event.key) {\n case 'ArrowRight':\n nextIndex = findNextEnabled(currentIndex, 1)\n break\n case 'ArrowLeft':\n nextIndex = findNextEnabled(currentIndex, -1)\n break\n case 'Home':\n nextIndex = firstEnabled()\n break\n case 'End':\n nextIndex = lastEnabled()\n break\n default:\n return\n }\n\n if (nextIndex < 0)\n return\n\n event.preventDefault()\n void selectByIndex(nextIndex, true)\n}\n\nfunction onClick(tab: GrTab): void {\n if (!isEnabled(tab))\n return\n if (tab.value !== props.modelValue)\n emit('update:modelValue', tab.value)\n}\n</script>\n\n<template>\n <div\n role=\"tablist\"\n data-gr-tabs\n class=\"inline-flex flex-wrap gap-1 rounded-[var(--gr-radius-lg)] border border-[var(--brd)] bg-[var(--muted)] p-1\"\n @keydown=\"onKeydown\"\n >\n <button\n v-for=\"(tab, index) in tabs\"\n :key=\"tab.value\"\n :ref=\"el => setButtonRef(el, index)\"\n type=\"button\"\n role=\"tab\"\n data-gr-tab\n :id=\"idBase ? `${idBase}-tab-${tab.value}` : undefined\"\n :aria-controls=\"idBase ? `${idBase}-panel-${tab.value}` : undefined\"\n :aria-selected=\"tab.value === modelValue ? 'true' : 'false'\"\n :aria-disabled=\"tab.disabled ? 'true' : undefined\"\n :disabled=\"tab.disabled\"\n :tabindex=\"tab.value === modelValue ? 0 : -1\"\n class=\"h-9 px-3 rounded-[10px] text-sm font-600 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-60 disabled:cursor-not-allowed\"\n :class=\"tab.value === modelValue\n ? 'bg-[var(--card)] text-[var(--fg)] border border-[var(--brd)]'\n : 'text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--card)_70%,transparent)]'\"\n @click=\"onClick(tab)\"\n >\n <span class=\"inline-flex items-center gap-2\">\n <span>{{ tab.label }}</span>\n <span\n v-if=\"tab.badge\"\n class=\"text-[11px] px-1.5 py-0.5 rounded-full bg-[var(--secondary)] text-[var(--secondary-fg)]\"\n >\n {{ tab.badge }}\n </span>\n </span>\n </button>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref } from 'vue'\n\nexport type GrTab = {\n value: string\n label: string\n badge?: string\n disabled?: boolean\n}\n\nexport type GrTabsProps = {\n modelValue: string\n tabs: GrTab[]\n /**\n * База id для ARIA-связки с `GrTabPanels`. Если задана, каждая вкладка\n * получает `id=\"<idBase>-tab-<value>\"` и `aria-controls=\"<idBase>-panel-<value>\"`.\n * Передайте тот же `idBase` в `GrTabPanels`, чтобы связать `tab`↔`tabpanel`.\n */\n idBase?: string\n}\n\n/**\n * GrTabs — горизонтальная группа вкладок с паттерном WAI-ARIA `tablist`.\n *\n * A11y:\n * - `role=\"tablist\"` на корне, `role=\"tab\"` на каждой вкладке.\n * - Roving `tabindex`: активная вкладка `0`, остальные `-1`.\n * - Клавиатура: `ArrowLeft`/`ArrowRight` — цикличный переход, `Home`/`End` — к первой/последней.\n * - При переключении стрелками DOM-фокус программно переносится на новую вкладку.\n *\n * Сам компонент не рендерит `tabpanel` — это ответственность консьюмера (по `aria-controls`/внешней разметке).\n */\nconst props = defineProps<GrTabsProps>()\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nconst buttonRefs = ref<HTMLButtonElement[]>([])\n\nfunction setButtonRef(el: unknown, index: number): void {\n if (el instanceof HTMLButtonElement)\n buttonRefs.value[index] = el\n}\n\nconst activeIndex = computed(() => props.tabs.findIndex(t => t.value === props.modelValue))\n\nfunction isEnabled(tab: GrTab): boolean {\n return !tab.disabled\n}\n\nasync function selectByIndex(index: number, focus = false): Promise<void> {\n const tab = props.tabs[index]\n if (!tab || !isEnabled(tab))\n return\n if (tab.value !== props.modelValue)\n emit('update:modelValue', tab.value)\n if (focus) {\n await nextTick()\n buttonRefs.value[index]?.focus()\n }\n}\n\nfunction findNextEnabled(from: number, direction: 1 | -1): number {\n const length = props.tabs.length\n if (length === 0)\n return -1\n let index = from\n for (let i = 0; i < length; i++) {\n index = (index + direction + length) % length\n const tab = props.tabs[index]\n if (tab && isEnabled(tab))\n return index\n }\n return -1\n}\n\nfunction firstEnabled(): number {\n return props.tabs.findIndex(isEnabled)\n}\n\nfunction lastEnabled(): number {\n for (let i = props.tabs.length - 1; i >= 0; i--) {\n const tab = props.tabs[i]\n if (tab && isEnabled(tab))\n return i\n }\n return -1\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (props.tabs.length === 0)\n return\n\n const currentIndex = activeIndex.value < 0 ? 0 : activeIndex.value\n let nextIndex = -1\n\n switch (event.key) {\n case 'ArrowRight':\n nextIndex = findNextEnabled(currentIndex, 1)\n break\n case 'ArrowLeft':\n nextIndex = findNextEnabled(currentIndex, -1)\n break\n case 'Home':\n nextIndex = firstEnabled()\n break\n case 'End':\n nextIndex = lastEnabled()\n break\n default:\n return\n }\n\n if (nextIndex < 0)\n return\n\n event.preventDefault()\n void selectByIndex(nextIndex, true)\n}\n\nfunction onClick(tab: GrTab): void {\n if (!isEnabled(tab))\n return\n if (tab.value !== props.modelValue)\n emit('update:modelValue', tab.value)\n}\n</script>\n\n<template>\n <div\n role=\"tablist\"\n data-gr-tabs\n class=\"inline-flex flex-wrap gap-1 rounded-[var(--gr-radius-lg)] border border-[var(--brd)] bg-[var(--muted)] p-1\"\n @keydown=\"onKeydown\"\n >\n <button\n v-for=\"(tab, index) in tabs\"\n :key=\"tab.value\"\n :ref=\"el => setButtonRef(el, index)\"\n type=\"button\"\n role=\"tab\"\n data-gr-tab\n :id=\"idBase ? `${idBase}-tab-${tab.value}` : undefined\"\n :aria-controls=\"idBase ? `${idBase}-panel-${tab.value}` : undefined\"\n :aria-selected=\"tab.value === modelValue ? 'true' : 'false'\"\n :aria-disabled=\"tab.disabled ? 'true' : undefined\"\n :disabled=\"tab.disabled\"\n :tabindex=\"tab.value === modelValue ? 0 : -1\"\n class=\"h-9 px-3 rounded-[10px] text-sm font-600 transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-60 disabled:cursor-not-allowed\"\n :class=\"tab.value === modelValue\n ? 'bg-[var(--card)] text-[var(--fg)] border border-[var(--brd)]'\n : 'text-[var(--muted-fg)] hover:text-[var(--fg)] hover:bg-[color-mix(in_srgb,var(--card)_70%,transparent)]'\"\n @click=\"onClick(tab)\"\n >\n <span class=\"inline-flex items-center gap-2\">\n <span>{{ tab.label }}</span>\n <span\n v-if=\"tab.badge\"\n class=\"text-[11px] px-1.5 py-0.5 rounded-full bg-[var(--secondary)] text-[var(--secondary-fg)]\"\n >\n {{ tab.badge }}\n </span>\n </span>\n </button>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrTextarea-CKukGlNl.js","names":[],"sources":["../../../../src/components/GrTextarea/GrTextarea.vue","../../../../src/components/GrTextarea/GrTextarea.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrTextarea — многострочное поле ввода GR-примитива.\n *\n * Состояния:\n * - `state`: визуальный оттенок рамки (`default | success | warning | danger`).\n * - `invalid`: форсирует `danger`-состояние и проставляет `aria-invalid=\"true\"`.\n */\nimport { computed } from 'vue'\nimport { type GrTextareaState, grTextareaClass } from './grTextareaStyles'\nimport { useGrFormFieldContext } from '../GrFormField/context'\n\nconst props = withDefaults(defineProps<{\n modelValue: string\n placeholder?: string\n autocomplete?: string\n disabled?: boolean\n invalid?: boolean\n state?: GrTextareaState\n name?: string\n id?: string\n rows?: number\n}>(), {\n placeholder: undefined,\n autocomplete: undefined,\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n rows: 4,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nexport type GrTextareaProps = typeof props\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\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 className = computed(() => grTextareaClass({\n state: props.state,\n invalid: isInvalid.value,\n}))\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLTextAreaElement).value)\n}\n</script>\n\n<template>\n <textarea\n :id=\"resolvedId\"\n data-gr-textarea\n :name=\"name\"\n :rows=\"rows\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :value=\"modelValue\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] px-3 py-2 text-[14px] text-[var(--fg)] placeholder:text-[var(--muted-fg)] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"className\"\n @input=\"onInput\"\n />\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrTextarea — многострочное поле ввода GR-примитива.\n *\n * Состояния:\n * - `state`: визуальный оттенок рамки (`default | success | warning | danger`).\n * - `invalid`: форсирует `danger`-состояние и проставляет `aria-invalid=\"true\"`.\n */\nimport { computed } from 'vue'\nimport { type GrTextareaState, grTextareaClass } from './grTextareaStyles'\nimport { useGrFormFieldContext } from '../GrFormField/context'\n\nconst props = withDefaults(defineProps<{\n modelValue: string\n placeholder?: string\n autocomplete?: string\n disabled?: boolean\n invalid?: boolean\n state?: GrTextareaState\n name?: string\n id?: string\n rows?: number\n}>(), {\n placeholder: undefined,\n autocomplete: undefined,\n disabled: false,\n invalid: false,\n state: 'default',\n name: undefined,\n id: undefined,\n rows: 4,\n})\n\nconst emit = defineEmits<{\n (e: 'update:modelValue', value: string): void\n}>()\n\nexport type GrTextareaProps = typeof props\n\n// Fallback из контекста `GrFormField` (id/aria-describedby/invalid/required).\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 className = computed(() => grTextareaClass({\n state: props.state,\n invalid: isInvalid.value,\n}))\n\nfunction onInput(e: Event): void {\n emit('update:modelValue', (e.target as HTMLTextAreaElement).value)\n}\n</script>\n\n<template>\n <textarea\n :id=\"resolvedId\"\n data-gr-textarea\n :name=\"name\"\n :rows=\"rows\"\n :autocomplete=\"autocomplete\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :value=\"modelValue\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"describedBy\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n class=\"w-full rounded-md border bg-[var(--bg)] px-3 py-2 text-[14px] text-[var(--fg)] placeholder:text-[var(--muted-fg)] transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] disabled:opacity-50 disabled:cursor-not-allowed\"\n :class=\"className\"\n @input=\"onInput\"\n />\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAYA,MAAM,QAAQ;EAqBd,MAAM,OAAO;EAOb,MAAM,QAAQ,sBAAsB;EACpC,MAAM,aAAa,eAAe,MAAM,MAAM,OAAO,GAAG,KAAK;EAC7D,MAAM,YAAY,eAAe,MAAM,WAAW,QAAQ,OAAO,QAAQ,KAAK,CAAC;EAC/E,MAAM,cAAc,eAAe,OAAO,cAAc,KAAK;EAC7D,MAAM,aAAa,eAAe,QAAQ,OAAO,SAAS,KAAK,CAAC;EAEhE,MAAM,YAAY,eAAe,gBAAgB;GAC/C,OAAO,MAAM;GACb,SAAS,UAAU;EACrB,CAAC,CAAC;EAEF,SAAS,QAAQ,GAAgB;GAC/B,KAAK,qBAAsB,EAAE,OAA+B,KAAK;EACnE;;uBAIE,mBAeE,YAAA;IAdC,IAAI,WAAA;IACL,oBAAA;IACC,MAAM,QAAA;IACN,MAAM,QAAA;IACN,cAAc,QAAA;IACd,aAAa,QAAA;IACb,UAAU,QAAA;IACV,OAAO,QAAA;IACP,gBAAc,UAAA,QAAS,SAAY,KAAA;IACnC,oBAAkB,YAAA;IAClB,iBAAe,WAAA,QAAU,SAAY,KAAA;IACtC,OAAK,eAAA,CAAC,6QACE,UAAA,KAAS,CAAA;IACT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrToaster-L2cu6cSR.js","names":[],"sources":["../../../../src/components/GrToaster/GrToaster.vue","../../../../src/components/GrToaster/GrToaster.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrToaster — GR-примитив области уведомлений (toast).\n *\n * Модель состояния берётся из `useToast`, поэтому в приложении предполагается\n * **один активный `GrToaster`** на корень (или app-scoped через плагин).\n *\n * A11y:\n * - контейнер — `role=\"region\"` с `aria-label` (i18n-проп `regionLabel`);\n * - список обёрнут в ПОСТОЯННЫЙ live-region (`aria-live=\"polite\"`), который\n * существует до вставки тостов — иначе SR часто не озвучивают целиком\n * вставленный узел с `aria-live`;\n * - критичные тосты (`warning`/`danger`) несут `role=\"alert\"` (ассертивно).\n *\n * UX:\n * - `maxVisible` ограничивает стек; лишние ждут в очереди (их таймеры на паузе);\n * - автозакрытие **останавливается под курсором/фокусом** (WCAG 2.2.1) и\n * визуализируется прогресс-баром до закрытия;\n * - `placement` настраивает угол экрана; слой — `--gr-z-toast`.\n */\nimport { computed, ref, watchEffect } from 'vue'\nimport type { Component } from 'vue'\n\nimport { useToast } from '../../composables/useToast'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrToastTone } from '../../composables/useToast'\nimport GrButton from '../GrButton'\nimport GrIcon from '../GrIcon'\nimport { PLACEMENT_CLASS, type GrToasterPlacement } from './grToasterStyles'\n\nimport IconCheck from '~icons/lucide/check-circle'\nimport IconWarning from '~icons/lucide/alert-triangle'\nimport IconError from '~icons/lucide/x-circle'\nimport IconInfo from '~icons/lucide/info'\nimport IconClose from '~icons/lucide/x'\n\nexport type { GrToasterPlacement }\n\ninterface ToneMeta {\n icon: Component\n color: string\n role: 'status' | 'alert'\n}\n\n// Единый маппинг по tone'у: иконка, цвет, a11y-роль. На уровне модуля —\n// чтобы не пересоздавать объект на каждый рендер.\nconst TONE_META: Record<GrToastTone, ToneMeta> = {\n primary: { icon: IconInfo, color: 'var(--primary)', role: 'status' },\n neutral: { icon: IconInfo, color: 'var(--muted-fg)', role: 'status' },\n info: { icon: IconInfo, color: 'var(--gr-info)', role: 'status' },\n success: { icon: IconCheck, color: 'var(--gr-success)', role: 'status' },\n warning: { icon: IconWarning, color: 'var(--gr-warning)', role: 'alert' },\n danger: { icon: IconError, color: 'var(--gr-danger)', role: 'alert' },\n slate: { icon: IconInfo, color: 'var(--gr-slate)', role: 'status' },\n azure: { icon: IconInfo, color: 'var(--gr-azure)', role: 'status' },\n}\n\nexport interface GrToasterProps {\n /** Угол экрана для стека уведомлений. */\n placement?: GrToasterPlacement\n /** Максимум одновременно видимых тостов; остальные ждут в очереди. По умолчанию `4`. */\n maxVisible?: number\n /** A11y-лейбл кнопки закрытия (i18n). */\n dismissLabel?: string\n /** A11y-лейбл контейнера-региона (i18n). */\n regionLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrToasterProps>(), {\n placement: 'top-right',\n maxVisible: 4,\n dismissLabel: undefined,\n regionLabel: undefined,\n})\n\nconst { t } = useGranularityTranslations()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? t('gr.toaster.dismiss', 'Dismiss'))\nconst resolvedRegionLabel = computed(() => props.regionLabel ?? t('gr.toaster.region', 'Notifications'))\n\nconst { list, dismiss, pause, resume } = useToast()\n\n// SSR-guard: на сервере `document.body` недоступен — отключаем `teleport`.\nconst isClient = typeof window !== 'undefined'\n\n// Видимые тосты (не больше `maxVisible`); остальные — в очереди.\nconst visibleToasts = computed(() => list.value.slice(0, Math.max(1, props.maxVisible)))\nconst visibleIds = computed(() => new Set(visibleToasts.value.map(toast => toast.id)))\n\n// Пауза под курсором/фокусом (WCAG 2.2.1). Один флаг на весь стек.\nconst paused = ref(false)\n\n// Единый источник правды по таймерам: тост тикает, только если он видим И стек не на\n// паузе. Очередь и hover — на паузе. Реагирует на изменения списка/hover.\nwatchEffect(() => {\n for (const toast of list.value) {\n if (visibleIds.value.has(toast.id) && !paused.value)\n resume(toast.id)\n else\n pause(toast.id)\n }\n})\n\nfunction metaFor(tone: GrToastTone): ToneMeta {\n return TONE_META[tone] ?? TONE_META.info\n}\n\nconst containerClass = computed(() => PLACEMENT_CLASS[props.placement])\n</script>\n\n<template>\n <teleport to=\"body\" :disabled=\"!isClient\">\n <div\n data-gr-toaster\n role=\"region\"\n :aria-label=\"resolvedRegionLabel\"\n class=\"fixed z-[var(--gr-z-toast)] w-[360px] max-w-[calc(100vw-2rem)]\"\n :class=\"containerClass\"\n @mouseenter=\"paused = true\"\n @mouseleave=\"paused = false\"\n @focusin=\"paused = true\"\n @focusout=\"paused = false\"\n >\n <!-- Постоянный live-region: существует всегда, поэтому вставленные тосты озвучиваются. -->\n <TransitionGroup\n tag=\"div\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n class=\"grid gap-3\"\n enter-active-class=\"transition duration-200 ease-out\"\n enter-from-class=\"opacity-0 translate-y-2\"\n enter-to-class=\"opacity-100 translate-y-0\"\n leave-active-class=\"transition duration-150 ease-in absolute\"\n leave-from-class=\"opacity-100 translate-y-0\"\n leave-to-class=\"opacity-0 translate-y-2\"\n move-class=\"transition-transform duration-200 ease-out\"\n >\n <div\n v-for=\"toast in visibleToasts\"\n :key=\"toast.id\"\n data-gr-toast\n :data-tone=\"toast.tone\"\n :role=\"metaFor(toast.tone).role\"\n aria-atomic=\"true\"\n class=\"relative overflow-hidden rounded-[var(--gr-radius-lg)] border border-[var(--brd)] bg-[var(--card)] px-4 py-3 shadow-[var(--gr-shadow-2)]\"\n >\n <div class=\"flex items-start gap-3\">\n <GrIcon size=\"md\" class=\"mt-0.5\" :style=\"{ color: metaFor(toast.tone).color }\" aria-hidden=\"true\">\n <component :is=\"metaFor(toast.tone).icon\" />\n </GrIcon>\n <div class=\"min-w-0 flex-1\">\n <div class=\"text-[13px] font-700\">\n {{ toast.title }}\n </div>\n <div v-if=\"toast.message\" class=\"mt-0.5 text-[13px] text-[var(--muted-fg)]\">\n {{ toast.message }}\n </div>\n </div>\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedDismissLabel\"\n @click=\"dismiss(toast.id)\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <!-- Прогресс до автозакрытия; замирает вместе с таймером на паузе. -->\n <div\n v-if=\"toast.timeoutMs > 0\"\n data-gr-toast-progress\n aria-hidden=\"true\"\n class=\"gr-toast-progress absolute bottom-0 left-0 h-[2px] w-full origin-left\"\n :style=\"{\n backgroundColor: metaFor(toast.tone).color,\n animationDuration: `${toast.timeoutMs}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }\"\n />\n </div>\n </TransitionGroup>\n </div>\n </teleport>\n</template>\n\n<style scoped>\n.gr-toast-progress {\n animation-name: gr-toast-progress;\n animation-timing-function: linear;\n animation-fill-mode: forwards;\n}\n\n@keyframes gr-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n</style>\n","<script setup lang=\"ts\">\n/**\n * GrToaster — GR-примитив области уведомлений (toast).\n *\n * Модель состояния берётся из `useToast`, поэтому в приложении предполагается\n * **один активный `GrToaster`** на корень (или app-scoped через плагин).\n *\n * A11y:\n * - контейнер — `role=\"region\"` с `aria-label` (i18n-проп `regionLabel`);\n * - список обёрнут в ПОСТОЯННЫЙ live-region (`aria-live=\"polite\"`), который\n * существует до вставки тостов — иначе SR часто не озвучивают целиком\n * вставленный узел с `aria-live`;\n * - критичные тосты (`warning`/`danger`) несут `role=\"alert\"` (ассертивно).\n *\n * UX:\n * - `maxVisible` ограничивает стек; лишние ждут в очереди (их таймеры на паузе);\n * - автозакрытие **останавливается под курсором/фокусом** (WCAG 2.2.1) и\n * визуализируется прогресс-баром до закрытия;\n * - `placement` настраивает угол экрана; слой — `--gr-z-toast`.\n */\nimport { computed, ref, watchEffect } from 'vue'\nimport type { Component } from 'vue'\n\nimport { useToast } from '../../composables/useToast'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport type { GrToastTone } from '../../composables/useToast'\nimport GrButton from '../GrButton'\nimport GrIcon from '../GrIcon'\nimport { PLACEMENT_CLASS, type GrToasterPlacement } from './grToasterStyles'\n\nimport IconCheck from '~icons/lucide/check-circle'\nimport IconWarning from '~icons/lucide/alert-triangle'\nimport IconError from '~icons/lucide/x-circle'\nimport IconInfo from '~icons/lucide/info'\nimport IconClose from '~icons/lucide/x'\n\nexport type { GrToasterPlacement }\n\ninterface ToneMeta {\n icon: Component\n color: string\n role: 'status' | 'alert'\n}\n\n// Единый маппинг по tone'у: иконка, цвет, a11y-роль. На уровне модуля —\n// чтобы не пересоздавать объект на каждый рендер.\nconst TONE_META: Record<GrToastTone, ToneMeta> = {\n primary: { icon: IconInfo, color: 'var(--primary)', role: 'status' },\n neutral: { icon: IconInfo, color: 'var(--muted-fg)', role: 'status' },\n info: { icon: IconInfo, color: 'var(--gr-info)', role: 'status' },\n success: { icon: IconCheck, color: 'var(--gr-success)', role: 'status' },\n warning: { icon: IconWarning, color: 'var(--gr-warning)', role: 'alert' },\n danger: { icon: IconError, color: 'var(--gr-danger)', role: 'alert' },\n slate: { icon: IconInfo, color: 'var(--gr-slate)', role: 'status' },\n azure: { icon: IconInfo, color: 'var(--gr-azure)', role: 'status' },\n}\n\nexport interface GrToasterProps {\n /** Угол экрана для стека уведомлений. */\n placement?: GrToasterPlacement\n /** Максимум одновременно видимых тостов; остальные ждут в очереди. По умолчанию `4`. */\n maxVisible?: number\n /** A11y-лейбл кнопки закрытия (i18n). */\n dismissLabel?: string\n /** A11y-лейбл контейнера-региона (i18n). */\n regionLabel?: string\n}\n\nconst props = withDefaults(defineProps<GrToasterProps>(), {\n placement: 'top-right',\n maxVisible: 4,\n dismissLabel: undefined,\n regionLabel: undefined,\n})\n\nconst { t } = useGranularityTranslations()\nconst resolvedDismissLabel = computed(() => props.dismissLabel ?? t('gr.toaster.dismiss', 'Dismiss'))\nconst resolvedRegionLabel = computed(() => props.regionLabel ?? t('gr.toaster.region', 'Notifications'))\n\nconst { list, dismiss, pause, resume } = useToast()\n\n// SSR-guard: на сервере `document.body` недоступен — отключаем `teleport`.\nconst isClient = typeof window !== 'undefined'\n\n// Видимые тосты (не больше `maxVisible`); остальные — в очереди.\nconst visibleToasts = computed(() => list.value.slice(0, Math.max(1, props.maxVisible)))\nconst visibleIds = computed(() => new Set(visibleToasts.value.map(toast => toast.id)))\n\n// Пауза под курсором/фокусом (WCAG 2.2.1). Один флаг на весь стек.\nconst paused = ref(false)\n\n// Единый источник правды по таймерам: тост тикает, только если он видим И стек не на\n// паузе. Очередь и hover — на паузе. Реагирует на изменения списка/hover.\nwatchEffect(() => {\n for (const toast of list.value) {\n if (visibleIds.value.has(toast.id) && !paused.value)\n resume(toast.id)\n else\n pause(toast.id)\n }\n})\n\nfunction metaFor(tone: GrToastTone): ToneMeta {\n return TONE_META[tone] ?? TONE_META.info\n}\n\nconst containerClass = computed(() => PLACEMENT_CLASS[props.placement])\n</script>\n\n<template>\n <teleport to=\"body\" :disabled=\"!isClient\">\n <div\n data-gr-toaster\n role=\"region\"\n :aria-label=\"resolvedRegionLabel\"\n class=\"fixed z-[var(--gr-z-toast)] w-[360px] max-w-[calc(100vw-2rem)]\"\n :class=\"containerClass\"\n @mouseenter=\"paused = true\"\n @mouseleave=\"paused = false\"\n @focusin=\"paused = true\"\n @focusout=\"paused = false\"\n >\n <!-- Постоянный live-region: существует всегда, поэтому вставленные тосты озвучиваются. -->\n <TransitionGroup\n tag=\"div\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n class=\"grid gap-3\"\n enter-active-class=\"transition duration-200 ease-out\"\n enter-from-class=\"opacity-0 translate-y-2\"\n enter-to-class=\"opacity-100 translate-y-0\"\n leave-active-class=\"transition duration-150 ease-in absolute\"\n leave-from-class=\"opacity-100 translate-y-0\"\n leave-to-class=\"opacity-0 translate-y-2\"\n move-class=\"transition-transform duration-200 ease-out\"\n >\n <div\n v-for=\"toast in visibleToasts\"\n :key=\"toast.id\"\n data-gr-toast\n :data-tone=\"toast.tone\"\n :role=\"metaFor(toast.tone).role\"\n aria-atomic=\"true\"\n class=\"relative overflow-hidden rounded-[var(--gr-radius-lg)] border border-[var(--brd)] bg-[var(--card)] px-4 py-3 shadow-[var(--gr-shadow-2)]\"\n >\n <div class=\"flex items-start gap-3\">\n <GrIcon size=\"md\" class=\"mt-0.5\" :style=\"{ color: metaFor(toast.tone).color }\" aria-hidden=\"true\">\n <component :is=\"metaFor(toast.tone).icon\" />\n </GrIcon>\n <div class=\"min-w-0 flex-1\">\n <div class=\"text-[13px] font-700\">\n {{ toast.title }}\n </div>\n <div v-if=\"toast.message\" class=\"mt-0.5 text-[13px] text-[var(--muted-fg)]\">\n {{ toast.message }}\n </div>\n </div>\n <GrButton\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedDismissLabel\"\n @click=\"dismiss(toast.id)\"\n >\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconClose />\n </GrIcon>\n </GrButton>\n </div>\n\n <!-- Прогресс до автозакрытия; замирает вместе с таймером на паузе. -->\n <div\n v-if=\"toast.timeoutMs > 0\"\n data-gr-toast-progress\n aria-hidden=\"true\"\n class=\"gr-toast-progress absolute bottom-0 left-0 h-[2px] w-full origin-left\"\n :style=\"{\n backgroundColor: metaFor(toast.tone).color,\n animationDuration: `${toast.timeoutMs}ms`,\n animationPlayState: paused ? 'paused' : 'running',\n }\"\n />\n </div>\n </TransitionGroup>\n </div>\n </teleport>\n</template>\n\n<style scoped>\n.gr-toast-progress {\n animation-name: gr-toast-progress;\n animation-timing-function: linear;\n animation-fill-mode: forwards;\n}\n\n@keyframes gr-toast-progress {\n from {\n transform: scaleX(1);\n }\n to {\n transform: scaleX(0);\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CA,MAAM,YAA2C;GAC/C,SAAS;IAAE,MAAM;IAAU,OAAO;IAAkB,MAAM;GAAS;GACnE,SAAS;IAAE,MAAM;IAAU,OAAO;IAAmB,MAAM;GAAS;GACpE,MAAM;IAAE,MAAM;IAAU,OAAO;IAAkB,MAAM;GAAS;GAChE,SAAS;IAAE,MAAM;IAAW,OAAO;IAAqB,MAAM;GAAS;GACvE,SAAS;IAAE,MAAM;IAAa,OAAO;IAAqB,MAAM;GAAQ;GACxE,QAAQ;IAAE,MAAM;IAAW,OAAO;IAAoB,MAAM;GAAQ;GACpE,OAAO;IAAE,MAAM;IAAU,OAAO;IAAmB,MAAM;GAAS;GAClE,OAAO;IAAE,MAAM;IAAU,OAAO;IAAmB,MAAM;GAAS;EACpE;EAaA,MAAM,QAAQ;EAOd,MAAM,EAAE,MAAM,2BAA2B;EACzC,MAAM,uBAAuB,eAAe,MAAM,gBAAgB,EAAE,sBAAsB,SAAS,CAAC;EACpG,MAAM,sBAAsB,eAAe,MAAM,eAAe,EAAE,qBAAqB,eAAe,CAAC;EAEvG,MAAM,EAAE,MAAM,SAAS,OAAO,WAAW,SAAS;EAGlD,MAAM,WAAW,OAAO,WAAW;EAGnC,MAAM,gBAAgB,eAAe,KAAK,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,MAAM,UAAU,CAAC,CAAC;EACvF,MAAM,aAAa,eAAe,IAAI,IAAI,cAAc,MAAM,KAAI,UAAS,MAAM,EAAE,CAAC,CAAC;EAGrF,MAAM,SAAS,IAAI,KAAK;EAIxB,kBAAkB;GAChB,KAAK,MAAM,SAAS,KAAK,OACvB,IAAI,WAAW,MAAM,IAAI,MAAM,EAAE,KAAK,CAAC,OAAO,OAC5C,OAAO,MAAM,EAAE;QAEf,MAAM,MAAM,EAAE;EAEpB,CAAC;EAED,SAAS,QAAQ,MAA6B;GAC5C,OAAO,UAAU,SAAS,UAAU;EACtC;EAEA,MAAM,iBAAiB,eAAe,gBAAgB,MAAM,UAAU;;uBAIpE,YA2EW,UAAA;IA3ED,IAAG;IAAQ,UAAQ,CAAG;OAC9B,mBAyEM,OAAA;IAxEF,mBAAA;IACA,MAAK;IACJ,cAAY,oBAAA;IACb,OAAK,eAAA,CAAC,kEACE,eAAA,KAAc,CAAA;IACrB,cAAU,OAAA,OAAA,OAAA,MAAA,WAAE,OAAA,QAAM;IAClB,cAAU,OAAA,OAAA,OAAA,MAAA,WAAE,OAAA,QAAM;IAClB,WAAO,OAAA,OAAA,OAAA,MAAA,WAAE,OAAA,QAAM;IACf,YAAQ,OAAA,OAAA,OAAA,MAAA,WAAE,OAAA,QAAM;OAGnB,YA4DkB,iBAAA;IA3Dd,KAAI;IACJ,aAAU;IACV,eAAY;IACZ,OAAM;IACN,sBAAmB;IACnB,oBAAiB;IACjB,kBAAe;IACf,sBAAmB;IACnB,oBAAiB;IACjB,kBAAe;IACf,cAAW;;2BAGqB,EAAA,UAAA,IAAA,GADlC,mBA8CM,UAAA,MAAA,WA7Cc,cAAA,QAAT,UAAK;yBADhB,mBA8CM,OAAA;MA5CD,KAAK,MAAM;MACZ,iBAAA;MACC,aAAW,MAAM;MACjB,MAAM,QAAQ,MAAM,IAAI,CAAA,CAAE;MAC3B,eAAY;MACZ,OAAM;SAER,mBAuBM,OAvBN,YAuBM;MAtBJ,YAES,MAAA,cAAA,GAAA;OAFD,MAAK;OAAK,OAAM;OAAU,OAAK,eAAA,EAAA,OAAW,QAAQ,MAAM,IAAI,CAAA,CAAE,MAAK,CAAA;OAAI,eAAY;;8BAC7C,EAAA,UAAA,GAA5C,YAA4C,wBAA5B,QAAQ,MAAM,IAAI,CAAA,CAAE,IAAI,CAAA,EAAA,CAAA;;;MAE1C,mBAOM,OAPN,YAOM,CANJ,mBAEM,OAFN,YAEM,gBADD,MAAM,KAAK,GAAA,CAAA,GAEL,MAAM,WAAA,UAAA,GAAjB,mBAEM,OAFN,YAEM,gBADD,MAAM,OAAO,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA;MAGpB,YAUW,MAAA,gBAAA,GAAA;OATP,SAAQ;OACR,MAAK;OACL,QAAA;OACC,cAAY,qBAAA;OACZ,UAAK,WAAE,MAAA,OAAA,CAAO,CAAC,MAAM,EAAE;;8BAIjB,CAFT,YAES,MAAA,cAAA,GAAA;QAFD,MAAK;QAAK,eAAY;;+BACf,CAAb,YAAa,MAAA,SAAA,CAAA,CAAA,CAAA;;;;;SAOT,MAAM,YAAS,KAAA,UAAA,GADzB,mBAUE,OAAA;;MARE,0BAAA;MACA,eAAY;MACZ,OAAM;MACL,OAAK,eAAA;wBAAqC,QAAQ,MAAM,IAAI,CAAA,CAAE;6BAA6C,MAAM,UAAS;2BAA0C,OAAA,QAAM,WAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrTooltip-BeyZ8ssR.js","names":[],"sources":["../../../../src/components/GrTooltip/GrTooltip.vue","../../../../src/components/GrTooltip/GrTooltip.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrTooltip — GR-примитив инлайн-подсказки на hover/focus.\n *\n * По умолчанию триггер — иконка info; можно заменить через слот.\n * A11y:\n * - `role=\"tooltip\"` связывается с триггером через уникальный `aria-describedby`;\n * - подсказка закрывается по `Escape` (WCAG 1.4.13 «Content on Hover or Focus» —\n * dismissible без необходимости двигать курсор/фокус);\n * - панель не интерактивна (`pointer-events-none`), поэтому «hoverable»-требование\n * того же критерия неприменимо — навести курсор на саму подсказку невозможно.\n *\n * Позиционирование — через общий `useFloating` (см. `composables/internal`):\n * по умолчанию сверху по центру, но `flip` переворачивает подсказку вниз, если\n * места над триггером не хватает, а `shift` не даёт ей вылезти за края viewport.\n */\nimport { computed, onUnmounted, ref, useId, watch } from 'vue'\n\nimport GrIcon from '../GrIcon'\nimport { useFloating } from '../../composables/internal/useFloating'\n\nimport IconInfo from '~icons/lucide/info'\n\nexport interface GrTooltipProps {\n text: string\n /** Цвет триггер-иконки (CSS color). По умолчанию — `var(--muted-fg)`. */\n iconColor?: string\n}\n\nconst props = withDefaults(defineProps<GrTooltipProps>(), {\n iconColor: 'var(--muted-fg)',\n})\n\nconst tooltipId = `gr-tooltip-${useId()}`\n\nconst triggerStyle = computed(() => ({ color: props.iconColor }))\n\nconst isClient = typeof window !== 'undefined'\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst { floatingStyle } = useFloating(triggerEl, panelEl, open, {\n placement: 'top',\n zIndexVar: '--gr-z-tooltip',\n})\n\nfunction show(): void {\n open.value = true\n}\n\nfunction hide(): void {\n open.value = false\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') hide()\n}\n\nwatch(\n open,\n (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n})\n</script>\n\n<template>\n <span data-gr-tooltip class=\"relative inline-flex\">\n <span\n ref=\"triggerEl\"\n data-gr-tooltip-trigger\n data-testid=\"gr-tooltip-trigger\"\n tabindex=\"0\"\n :aria-describedby=\"tooltipId\"\n class=\"inline-flex items-center justify-center focus:outline-none\"\n :style=\"triggerStyle\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focus=\"show\"\n @blur=\"hide\"\n >\n <slot>\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconInfo />\n </GrIcon>\n </slot>\n </span>\n\n <teleport to=\"body\" :disabled=\"!isClient\">\n <transition\n enter-active-class=\"transition-opacity duration-150\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-100\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <span\n v-show=\"open\"\n :id=\"tooltipId\"\n ref=\"panelEl\"\n role=\"tooltip\"\n data-gr-tooltip-panel\n class=\"pointer-events-none max-w-[280px] rounded-md border border-[var(--brd)] bg-[var(--popover)] px-2 py-1 text-[12px] text-[var(--popover-fg)] shadow-[var(--gr-shadow-1)]\"\n :style=\"floatingStyle\"\n >\n {{ text }}\n </span>\n </transition>\n </teleport>\n </span>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrTooltip — GR-примитив инлайн-подсказки на hover/focus.\n *\n * По умолчанию триггер — иконка info; можно заменить через слот.\n * A11y:\n * - `role=\"tooltip\"` связывается с триггером через уникальный `aria-describedby`;\n * - подсказка закрывается по `Escape` (WCAG 1.4.13 «Content on Hover or Focus» —\n * dismissible без необходимости двигать курсор/фокус);\n * - панель не интерактивна (`pointer-events-none`), поэтому «hoverable»-требование\n * того же критерия неприменимо — навести курсор на саму подсказку невозможно.\n *\n * Позиционирование — через общий `useFloating` (см. `composables/internal`):\n * по умолчанию сверху по центру, но `flip` переворачивает подсказку вниз, если\n * места над триггером не хватает, а `shift` не даёт ей вылезти за края viewport.\n */\nimport { computed, onUnmounted, ref, useId, watch } from 'vue'\n\nimport GrIcon from '../GrIcon'\nimport { useFloating } from '../../composables/internal/useFloating'\n\nimport IconInfo from '~icons/lucide/info'\n\nexport interface GrTooltipProps {\n text: string\n /** Цвет триггер-иконки (CSS color). По умолчанию — `var(--muted-fg)`. */\n iconColor?: string\n}\n\nconst props = withDefaults(defineProps<GrTooltipProps>(), {\n iconColor: 'var(--muted-fg)',\n})\n\nconst tooltipId = `gr-tooltip-${useId()}`\n\nconst triggerStyle = computed(() => ({ color: props.iconColor }))\n\nconst isClient = typeof window !== 'undefined'\n\nconst open = ref(false)\nconst triggerEl = ref<HTMLElement | null>(null)\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst { floatingStyle } = useFloating(triggerEl, panelEl, open, {\n placement: 'top',\n zIndexVar: '--gr-z-tooltip',\n})\n\nfunction show(): void {\n open.value = true\n}\n\nfunction hide(): void {\n open.value = false\n}\n\nfunction closeOnEscape(e: KeyboardEvent): void {\n if (e.key === 'Escape') hide()\n}\n\nwatch(\n open,\n (isOpen) => {\n if (typeof document === 'undefined') return\n\n document.removeEventListener('keydown', closeOnEscape)\n if (isOpen) document.addEventListener('keydown', closeOnEscape)\n },\n { immediate: true },\n)\n\nonUnmounted(() => {\n if (typeof document === 'undefined') return\n document.removeEventListener('keydown', closeOnEscape)\n})\n</script>\n\n<template>\n <span data-gr-tooltip class=\"relative inline-flex\">\n <span\n ref=\"triggerEl\"\n data-gr-tooltip-trigger\n data-testid=\"gr-tooltip-trigger\"\n tabindex=\"0\"\n :aria-describedby=\"tooltipId\"\n class=\"inline-flex items-center justify-center focus:outline-none\"\n :style=\"triggerStyle\"\n @mouseenter=\"show\"\n @mouseleave=\"hide\"\n @focus=\"show\"\n @blur=\"hide\"\n >\n <slot>\n <GrIcon size=\"sm\" aria-hidden=\"true\">\n <IconInfo />\n </GrIcon>\n </slot>\n </span>\n\n <teleport to=\"body\" :disabled=\"!isClient\">\n <transition\n enter-active-class=\"transition-opacity duration-150\"\n enter-from-class=\"opacity-0\"\n enter-to-class=\"opacity-100\"\n leave-active-class=\"transition-opacity duration-100\"\n leave-from-class=\"opacity-100\"\n leave-to-class=\"opacity-0\"\n >\n <span\n v-show=\"open\"\n :id=\"tooltipId\"\n ref=\"panelEl\"\n role=\"tooltip\"\n data-gr-tooltip-panel\n class=\"pointer-events-none max-w-[280px] rounded-md border border-[var(--brd)] bg-[var(--popover)] px-2 py-1 text-[12px] text-[var(--popover-fg)] shadow-[var(--gr-shadow-1)]\"\n :style=\"floatingStyle\"\n >\n {{ text }}\n </span>\n </transition>\n </teleport>\n </span>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BA,MAAM,QAAQ;EAId,MAAM,YAAY,cAAc,MAAM;EAEtC,MAAM,eAAe,gBAAgB,EAAE,OAAO,MAAM,UAAU,EAAE;EAEhE,MAAM,WAAW,OAAO,WAAW;EAEnC,MAAM,OAAO,IAAI,KAAK;EACtB,MAAM,YAAY,IAAwB,IAAI;EAC9C,MAAM,UAAU,IAAwB,IAAI;EAE5C,MAAM,EAAE,kBAAkB,YAAY,WAAW,SAAS,MAAM;GAC9D,WAAW;GACX,WAAW;EACb,CAAC;EAED,SAAS,OAAa;GACpB,KAAK,QAAQ;EACf;EAEA,SAAS,OAAa;GACpB,KAAK,QAAQ;EACf;EAEA,SAAS,cAAc,GAAwB;GAC7C,IAAI,EAAE,QAAQ,UAAU,KAAK;EAC/B;EAEA,MACE,OACC,WAAW;GACV,IAAI,OAAO,aAAa,aAAa;GAErC,SAAS,oBAAoB,WAAW,aAAa;GACrD,IAAI,QAAQ,SAAS,iBAAiB,WAAW,aAAa;EAChE,GACA,EAAE,WAAW,KAAK,CACpB;EAEA,kBAAkB;GAChB,IAAI,OAAO,aAAa,aAAa;GACrC,SAAS,oBAAoB,WAAW,aAAa;EACvD,CAAC;;uBAIC,mBA2CO,QA3CP,YA2CO,CA1CL,mBAkBO,QAAA;aAjBD;IAAJ,KAAI;IACJ,2BAAA;IACA,eAAY;IACZ,UAAS;IACR,oBAAkB;IACnB,OAAM;IACL,OAAK,eAAE,aAAA,KAAY;IACnB,cAAY;IACZ,cAAY;IACZ,SAAO;IACP,QAAM;OAEP,WAIO,KAAA,QAAA,WAAA,CAAA,SAAA,CAHL,YAES,MAAA,cAAA,GAAA;IAFD,MAAK;IAAK,eAAY;;2BAChB,CAAZ,YAAY,MAAA,YAAA,CAAA,CAAA,CAAA;;6BAKlB,YAqBW,UAAA;IArBD,IAAG;IAAQ,UAAQ,CAAG;OAC9B,YAmBa,YAAA;IAlBX,sBAAmB;IACnB,oBAAiB;IACjB,kBAAe;IACf,sBAAmB;IACnB,oBAAiB;IACjB,kBAAe;;2BAYR,CAAA,eAVP,mBAUO,QAAA;KARJ,IAAI;cACD;KAAJ,KAAI;KACJ,MAAK;KACL,yBAAA;KACA,OAAM;KACL,OAAK,eAAE,MAAA,aAAA,CAAa;uBAElB,QAAA,IAAI,GAAA,CAAA,GAAA,CAAA,CAAA,OARC,KAAA,KAAI,CAAA,CAAA,CAAA,CAAA"}
|