@feugene/granularity 0.23.0 → 0.25.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/chunks/config-BFJIxE8b.js +62 -0
- package/dist/chunks/config-BFJIxE8b.js.map +1 -0
- package/dist/chunks/{config-DBQQT-WP.js → config-BIa3ilQm.js} +18 -3
- package/dist/chunks/config-BIa3ilQm.js.map +1 -0
- package/dist/chunks/config-BfsBepkH.js.map +1 -0
- package/dist/chunks/config-BoNR7_PD.js.map +1 -0
- package/dist/chunks/config-BuGlNPE92.js.map +1 -0
- package/dist/chunks/{config-Be9k9bOf.js → config-C8tVRv8i.js} +16 -2
- package/dist/chunks/config-C8tVRv8i.js.map +1 -0
- package/dist/chunks/config-CPSxM7XE.js +8 -0
- package/dist/chunks/config-CPSxM7XE.js.map +1 -0
- package/dist/chunks/config-D7zgE_Gr.js +107 -0
- package/dist/chunks/config-D7zgE_Gr.js.map +1 -0
- package/dist/chunks/config-DAX8Deh1.js.map +1 -0
- package/dist/chunks/config-DGCMHPCR.js +19 -0
- package/dist/chunks/config-DGCMHPCR.js.map +1 -0
- package/dist/chunks/config-DGWFal5h.js +128 -0
- package/dist/chunks/config-DGWFal5h.js.map +1 -0
- package/dist/chunks/config-DKISz9gR.js +39 -0
- package/dist/chunks/config-DKISz9gR.js.map +1 -0
- package/dist/chunks/{config-Bm_Sovla.js → config-DYgfMrIF.js} +14 -2
- package/dist/chunks/config-DYgfMrIF.js.map +1 -0
- package/dist/chunks/{config-BSyiFlbi.js → config-DtgYuGUn2.js} +2 -1
- package/dist/chunks/config-DtgYuGUn2.js.map +1 -0
- package/dist/chunks/config-Iu684VNI.js +21 -0
- package/dist/chunks/config-Iu684VNI.js.map +1 -0
- package/dist/chunks/config-T3qdQBgU.js.map +1 -0
- package/dist/chunks/{config-DCwQBMmJ.js → config-rkcKArZQ.js} +27 -8
- package/dist/chunks/config-rkcKArZQ.js.map +1 -0
- package/dist/chunks/config-zjeAJ4ho2.js.map +1 -0
- package/dist/chunks/context-BfIQv7m12.js.map +1 -0
- package/dist/chunks/context-CTbuwcYs.js.map +1 -1
- package/dist/chunks/{en-DCCFP4qu.js → en-NpP9e7lC.js} +16 -2
- package/dist/chunks/en-NpP9e7lC.js.map +1 -0
- package/dist/chunks/{es-CT34HBVM.js → es-DaeA2Ql-.js} +16 -2
- package/dist/chunks/es-DaeA2Ql-.js.map +1 -0
- package/dist/chunks/{es-DdJsxlUr.js → es-Do2CNrC2.js} +3 -3
- package/dist/chunks/{es-DdJsxlUr.js.map → es-Do2CNrC2.js.map} +1 -1
- package/dist/chunks/{generated-CiCrGDQO.js → generated-CJe6hdiE.js} +138 -6
- package/dist/chunks/{generated-CiCrGDQO.js.map → generated-CJe6hdiE.js.map} +1 -1
- package/dist/chunks/{grBadgeStyles-C2wLlkjF.js → grBadgeStyles-C1S56Alo.js} +6 -1
- package/dist/chunks/{grBadgeStyles-C2wLlkjF.js.map → grBadgeStyles-C1S56Alo.js.map} +1 -1
- package/dist/chunks/grChipGroupContext-DZXkACap.js +4 -0
- package/dist/chunks/grChipGroupContext-DZXkACap.js.map +1 -0
- package/dist/chunks/{config-C5mO5DYZ.js → grDropdownMenuStyles-D1LOkdVj.js} +1 -38
- package/dist/chunks/grDropdownMenuStyles-D1LOkdVj.js.map +1 -0
- package/dist/chunks/{grPopoverStyles-Rz_Z5nLW.js → grPopoverStyles-Cke_BZsM.js} +16 -3
- package/dist/chunks/grPopoverStyles-Cke_BZsM.js.map +1 -0
- package/dist/chunks/{granular-provider-CQKhl5C_.js → granular-provider-BaHkLjFX.js} +26 -16
- package/dist/chunks/granular-provider-BaHkLjFX.js.map +1 -0
- package/dist/chunks/{ru-CLiAMwrx.js → ru-Bg9-DPmg.js} +16 -2
- package/dist/chunks/ru-Bg9-DPmg.js.map +1 -0
- package/dist/chunks/{config-qVg3Zz_A.js → safelist-DOfCQR5O.js} +2 -19
- package/dist/chunks/safelist-DOfCQR5O.js.map +1 -0
- package/dist/chunks/useMenuItemsFocus-OyOnIAlr.js +117 -0
- package/dist/chunks/useMenuItemsFocus-OyOnIAlr.js.map +1 -0
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-DsevPtiS.js → GrAutocomplete-D9c9BNFg.js} +1 -1
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-DsevPtiS.js.map → GrAutocomplete-D9c9BNFg.js.map} +1 -1
- package/dist/components/GrAutocomplete/index.js +1 -1
- package/dist/components/GrBadge/chunks/{GrBadge-u2ZdS7CN.js → GrBadge-_9rCHuE1.js} +1 -1
- package/dist/components/GrBadge/chunks/{GrBadge-u2ZdS7CN.js.map → GrBadge-_9rCHuE1.js.map} +1 -1
- package/dist/components/GrBadge/index.js +3 -2
- package/dist/components/GrButton/chunks/{GrButton-Bw2AH4uX.js → GrButton-DCz-Wn-V.js} +1 -1
- package/dist/components/GrButton/chunks/{GrButton-Bw2AH4uX.js.map → GrButton-DCz-Wn-V.js.map} +1 -1
- package/dist/components/GrButton/index.js +1 -1
- package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-CKyiAV8K.js → GrButtonGroup-BQo5VQ_c.js} +1 -1
- package/dist/components/GrButtonGroup/chunks/{GrButtonGroup-CKyiAV8K.js.map → GrButtonGroup-BQo5VQ_c.js.map} +1 -1
- package/dist/components/GrButtonGroup/index.js +2 -2
- package/dist/components/GrCard/chunks/{GrCard-Bc7nRfbe.js → GrCard-aQIjk6I9.js} +9 -3
- package/dist/components/GrCard/chunks/GrCard-aQIjk6I9.js.map +1 -0
- package/dist/components/GrCard/index.js +2 -2
- package/dist/components/GrCheckboxGroup/index.js +1 -1
- package/dist/components/GrChip/chunks/GrChip-BnL2FedO.js +222 -0
- package/dist/components/GrChip/chunks/GrChip-BnL2FedO.js.map +1 -0
- package/dist/components/GrChip/index.js +3 -0
- package/dist/components/GrChipGroup/chunks/GrChipGroup-DefmSdY9.js +209 -0
- package/dist/components/GrChipGroup/chunks/GrChipGroup-DefmSdY9.js.map +1 -0
- package/dist/components/GrChipGroup/index.js +3 -0
- package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-BmXCLgLS.js → GrCodeBlock-CyptiEo4.js} +1 -1
- package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-BmXCLgLS.js.map → GrCodeBlock-CyptiEo4.js.map} +1 -1
- package/dist/components/GrCodeBlock/index.js +1 -1
- package/dist/components/GrCollapse/chunks/{GrCollapse-C1rCfbc0.js → GrCollapse-DUROgAgc.js} +1 -1
- package/dist/components/GrCollapse/chunks/{GrCollapse-C1rCfbc0.js.map → GrCollapse-DUROgAgc.js.map} +1 -1
- package/dist/components/GrCollapse/index.js +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-qQdoEPHF.js → GrColorPicker-CDaJRVIM.js} +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-qQdoEPHF.js.map → GrColorPicker-CDaJRVIM.js.map} +1 -1
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BFcMf4Pn.js → GrCommandPalette-BPsVN_XU.js} +3 -3
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BPsVN_XU.js.map +1 -0
- package/dist/components/GrCommandPalette/index.js +1 -1
- package/dist/components/GrCommandPalette/styles.css +1 -1
- package/dist/components/GrConfigProvider/chunks/{GrConfigProvider-UP-EaoAf.js → GrConfigProvider-BF7R3hgg.js} +1 -1
- package/dist/components/GrConfigProvider/chunks/GrConfigProvider-BF7R3hgg.js.map +1 -0
- package/dist/components/GrConfigProvider/index.js +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-rRtMScJX.js → GrConfirmDialog-CwXNLyzk.js} +3 -3
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-rRtMScJX.js.map → GrConfirmDialog-CwXNLyzk.js.map} +1 -1
- package/dist/components/GrConfirmDialog/index.js +1 -1
- package/dist/components/GrContextMenu/chunks/GrContextMenu-BbAJholy.js +331 -0
- package/dist/components/GrContextMenu/chunks/GrContextMenu-BbAJholy.js.map +1 -0
- package/dist/components/GrContextMenu/index.js +3 -0
- package/dist/components/GrDataTable/chunks/{GrDataTable-B5MmZFUg.js → GrDataTable-NRbrXpdx.js} +2 -2
- package/dist/components/GrDataTable/chunks/GrDataTable-NRbrXpdx.js.map +1 -0
- package/dist/components/GrDataTable/index.js +1 -1
- package/dist/components/GrDelta/chunks/{GrDelta-DFvxNpmN.js → GrDelta-NE5HhXoo.js} +0 -0
- package/dist/components/GrDelta/chunks/GrDelta-NE5HhXoo.js.map +1 -0
- package/dist/components/GrDelta/index.js +2 -2
- package/dist/components/GrDescriptionList/chunks/{GrDescriptionList-2zFXzX83.js → GrDescriptionList-1mICfrqX.js} +8 -4
- package/dist/components/GrDescriptionList/chunks/GrDescriptionList-1mICfrqX.js.map +1 -0
- package/dist/components/GrDescriptionList/index.js +2 -2
- package/dist/components/GrDialog/chunks/{GrDialog-D0U5iRqV.js → GrDialog-5Y5K87ss.js} +2 -2
- package/dist/components/GrDialog/chunks/{GrDialog-D0U5iRqV.js.map → GrDialog-5Y5K87ss.js.map} +1 -1
- package/dist/components/GrDialog/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-CrwA4aJq.js → GrDialogService-CHhzGRWy.js} +6 -6
- package/dist/components/GrDialogService/chunks/GrDialogService-CHhzGRWy.js.map +1 -0
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-C3zAg2tG.js → GrDrawer-CXy3-SVd.js} +2 -2
- package/dist/components/GrDrawer/chunks/GrDrawer-CXy3-SVd.js.map +1 -0
- package/dist/components/GrDrawer/index.js +1 -1
- package/dist/components/GrDropdown/chunks/{GrDropdown-BSfFOcDx.js → GrDropdown-BfTmdPyh.js} +10 -79
- package/dist/components/GrDropdown/chunks/GrDropdown-BfTmdPyh.js.map +1 -0
- package/dist/components/GrDropdown/index.js +1 -1
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-l5HEqzjr.js +130 -0
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-l5HEqzjr.js.map +1 -0
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-DyS6dWDp.js → GrDropdownMenuList-BGA_xNVo.js} +57 -159
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-BGA_xNVo.js.map +1 -0
- package/dist/components/GrDropdownMenu/index.js +3 -2
- package/dist/components/GrFileUpload/chunks/{GrFileUpload-D3-vC-ap.js → GrFileUpload-wNXzHb5N.js} +1 -1
- package/dist/components/GrFileUpload/chunks/GrFileUpload-wNXzHb5N.js.map +1 -0
- package/dist/components/GrFileUpload/index.js +1 -1
- package/dist/components/GrFormField/chunks/{GrFormField-kqfVY30N.js → GrFormField-DSylU7IU.js} +1 -1
- package/dist/components/GrFormField/chunks/GrFormField-DSylU7IU.js.map +1 -0
- package/dist/components/GrFormField/index.js +1 -1
- package/dist/components/GrFormFile/chunks/{GrFormFile-D722xHn8.js → GrFormFile-DhVkB2gm.js} +1 -1
- package/dist/components/GrFormFile/chunks/{GrFormFile-D722xHn8.js.map → GrFormFile-DhVkB2gm.js.map} +1 -1
- package/dist/components/GrFormFile/index.js +1 -1
- package/dist/components/GrInputTag/chunks/{GrInputTag-C7QVPijj.js → GrInputTag-UI8kIB_F.js} +2 -2
- package/dist/components/GrInputTag/chunks/{GrInputTag-C7QVPijj.js.map → GrInputTag-UI8kIB_F.js.map} +1 -1
- package/dist/components/GrInputTag/index.js +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-DFUppRNb.js → GrJsonViewer-jk3ylOTc.js} +2 -2
- package/dist/components/GrJsonViewer/chunks/GrJsonViewer-jk3ylOTc.js.map +1 -0
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrList/chunks/{GrList-BRYhpqIe.js → GrList-B5bclmHV.js} +30 -3
- package/dist/components/GrList/chunks/GrList-B5bclmHV.js.map +1 -0
- package/dist/components/GrList/index.js +1 -1
- package/dist/components/GrList/styles.css +1 -1
- package/dist/components/GrLoading/chunks/{GrLoading-C3DZSSuh.js → GrLoading-DeBFTvFX.js} +1 -1
- package/dist/components/GrLoading/chunks/GrLoading-DeBFTvFX.js.map +1 -0
- package/dist/components/GrLoading/index.js +1 -1
- package/dist/components/GrModal/chunks/{GrModal-CPz7SWD_.js → GrModal-CCoWZuIl.js} +2 -2
- package/dist/components/GrModal/chunks/GrModal-CCoWZuIl.js.map +1 -0
- package/dist/components/GrModal/index.js +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-DWaaoV7n.js → GrNavbar-DPj30sHX.js} +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-DWaaoV7n.js.map → GrNavbar-DPj30sHX.js.map} +1 -1
- package/dist/components/GrNavbar/index.js +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-BotnzW77.js → GrNumberInput-V2mZvDDC.js} +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-BotnzW77.js.map → GrNumberInput-V2mZvDDC.js.map} +1 -1
- package/dist/components/GrNumberInput/index.js +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-GtGtSMJG.js → GrPagination-DG7kp6VL.js} +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-GtGtSMJG.js.map → GrPagination-DG7kp6VL.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrPopover/chunks/{GrPopover-b70bIlQ9.js → GrPopover-DDChM1jD.js} +28 -10
- package/dist/components/GrPopover/chunks/GrPopover-DDChM1jD.js.map +1 -0
- package/dist/components/GrPopover/index.js +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-CEI8bf-t.js → GrPromptDialog-DjF-5oD9.js} +4 -4
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-CEI8bf-t.js.map → GrPromptDialog-DjF-5oD9.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-BVmjKXcv.js → GrRadioGroup-7w4bXIg_.js} +1 -1
- package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-BVmjKXcv.js.map → GrRadioGroup-7w4bXIg_.js.map} +1 -1
- package/dist/components/GrRadioGroup/index.js +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CZONq8uT.js → GrResponseErrorBanner-BhP-xw2P.js} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CZONq8uT.js.map → GrResponseErrorBanner-BhP-xw2P.js.map} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CgfAwlg3.js → GrResponseErrorBanner-DA4q90Zt.js} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-CgfAwlg3.js.map → GrResponseErrorBanner-DA4q90Zt.js.map} +1 -1
- package/dist/components/GrResponseErrorBanner/index.js +2 -2
- package/dist/components/GrSegmented/chunks/{GrSegmented-D1mjxF_O.js → GrSegmented-DKpo3cnC.js} +30 -5
- package/dist/components/GrSegmented/chunks/GrSegmented-DKpo3cnC.js.map +1 -0
- package/dist/components/GrSegmented/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-CaGuyvtb.js → GrSelect-BA1wfX3c.js} +19 -3
- package/dist/components/GrSelect/chunks/GrSelect-BA1wfX3c.js.map +1 -0
- package/dist/components/GrSelect/index.js +1 -1
- package/dist/components/GrSelect/styles.css +1 -1
- package/dist/components/GrSidebar/chunks/{GrSidebar-D7WxRtIu.js → GrSidebar-DY-LgIbS.js} +2 -2
- package/dist/components/GrSidebar/chunks/{GrSidebar-D7WxRtIu.js.map → GrSidebar-DY-LgIbS.js.map} +1 -1
- package/dist/components/GrSidebar/index.js +2 -2
- package/dist/components/GrSlider/index.js +1 -1
- package/dist/components/GrSortableList/chunks/{GrSortableList-DPjxilmN.js → GrSortableList-BMXyxmRc.js} +3 -3
- package/dist/components/GrSortableList/chunks/{GrSortableList-DPjxilmN.js.map → GrSortableList-BMXyxmRc.js.map} +1 -1
- package/dist/components/GrSortableList/index.js +2 -2
- package/dist/components/GrStatistic/chunks/{GrStatistic-C7wQECeS.js → GrStatistic-BJQv5aOS.js} +41 -26
- package/dist/components/GrStatistic/chunks/GrStatistic-BJQv5aOS.js.map +1 -0
- package/dist/components/GrStatistic/index.js +2 -2
- package/dist/components/GrSteps/chunks/GrSteps-Cce4rfr3.js +282 -0
- package/dist/components/GrSteps/chunks/GrSteps-Cce4rfr3.js.map +1 -0
- package/dist/components/GrSteps/index.js +3 -0
- package/dist/components/GrSwitch/chunks/{GrSwitch-x_vQQ6ao.js → GrSwitch-CHiIj9GT.js} +1 -1
- package/dist/components/GrSwitch/chunks/{GrSwitch-x_vQQ6ao.js.map → GrSwitch-CHiIj9GT.js.map} +1 -1
- package/dist/components/GrSwitch/index.js +2 -2
- package/dist/components/GrTabPanels/chunks/{GrTabPanels-AJt1w1XC.js → GrTabPanels-BZQS8-JM.js} +12 -1
- package/dist/components/GrTabPanels/chunks/{GrTabPanels-AJt1w1XC.js.map → GrTabPanels-BZQS8-JM.js.map} +1 -1
- package/dist/components/GrTabPanels/index.js +1 -1
- package/dist/components/GrTimeline/chunks/{GrTimeline-Ch9sHzwL.js → GrTimeline-DIDdkTn7.js} +12 -1
- package/dist/components/GrTimeline/chunks/GrTimeline-DIDdkTn7.js.map +1 -0
- package/dist/components/GrTimeline/index.js +1 -1
- package/dist/components/GrToaster/chunks/{GrToaster-DJUzx3sR.js → GrToaster-CC49q3a4.js} +1 -1
- package/dist/components/GrToaster/chunks/{GrToaster-DJUzx3sR.js.map → GrToaster-CC49q3a4.js.map} +1 -1
- package/dist/components/GrToaster/index.js +1 -1
- package/dist/components/GrTree/chunks/{GrTree-BvOeQ3xJ.js.map → GrTree-J5q8aCya.js.map} +1 -1
- package/dist/components/GrTree/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-_LjTRR7X.js → GrTreeSelect-DEfxWAc3.js} +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-_LjTRR7X.js.map → GrTreeSelect-DEfxWAc3.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/components/GrValue/chunks/GrValue-CZsuZ4x-.js +38 -0
- package/dist/components/GrValue/chunks/GrValue-CZsuZ4x-.js.map +1 -0
- package/dist/components/GrValue/index.js +3 -0
- package/dist/composables/useFloating.js +47 -7
- package/dist/composables/useFloating.js.map +1 -1
- package/dist/composables/useTheme.js.map +1 -1
- package/dist/composables/useToast.js.map +1 -1
- package/dist/directives/loading.js +1 -1
- package/dist/granular-provider-node.js +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 +74 -62
- package/dist/styles/themes/dark.css +11 -6
- package/dist/styles/themes/light.css +5 -0
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrBadge/grBadgeStyles.d.ts +7 -0
- package/dist/types/components/GrCard/GrCard.vue.d.ts +1 -1
- package/dist/types/components/GrCard/grCardStyles.d.ts +11 -0
- package/dist/types/components/GrChip/GrChip.vue.d.ts +118 -0
- package/dist/types/components/GrChip/config.d.ts +1 -0
- package/dist/types/components/GrChip/defaults.d.ts +16 -0
- package/dist/types/components/GrChip/grChipGroupContext.d.ts +52 -0
- package/dist/types/components/GrChip/grChipStyles.d.ts +64 -0
- package/dist/types/components/GrChip/index.d.ts +11 -0
- package/dist/types/components/GrChip/safelist.d.ts +7 -0
- package/dist/types/components/GrChipGroup/GrChipGroup.vue.d.ts +128 -0
- package/dist/types/components/GrChipGroup/config.d.ts +1 -0
- package/dist/types/components/GrChipGroup/defaults.d.ts +13 -0
- package/dist/types/components/GrChipGroup/grChipGroupStyles.d.ts +7 -0
- package/dist/types/components/GrChipGroup/index.d.ts +9 -0
- package/dist/types/components/GrChipGroup/safelist.d.ts +1 -0
- package/dist/types/components/GrContextMenu/GrContextMenu.vue.d.ts +157 -0
- package/dist/types/components/GrContextMenu/config.d.ts +1 -0
- package/dist/types/components/GrContextMenu/contextMenuAnchor.d.ts +29 -0
- package/dist/types/components/GrContextMenu/index.d.ts +7 -0
- package/dist/types/components/GrDataTable/GrDataTable.vue.d.ts +1 -1
- package/dist/types/components/GrDelta/grDeltaStyles.d.ts +13 -1
- package/dist/types/components/GrDescriptionList/grDescriptionListStyles.d.ts +29 -1
- package/dist/types/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +3 -3
- package/dist/types/components/GrDropdownMenu/GrDropdownMenuEntries.vue.d.ts +22 -0
- package/dist/types/components/GrFilePreview/GrFilePreview.vue.d.ts +1 -1
- package/dist/types/components/GrList/GrListItem.vue.d.ts +1 -1
- package/dist/types/components/GrPopover/GrPopover.vue.d.ts +21 -2
- package/dist/types/components/GrPopover/grPopoverStyles.d.ts +11 -1
- package/dist/types/components/GrPopover/index.d.ts +1 -1
- package/dist/types/components/GrStatistic/GrStatistic.vue.d.ts +6 -6
- package/dist/types/components/GrStatistic/grStatisticStyles.d.ts +9 -0
- package/dist/types/components/GrSteps/GrSteps.vue.d.ts +118 -0
- package/dist/types/components/GrSteps/config.d.ts +1 -0
- package/dist/types/components/GrSteps/defaults.d.ts +18 -0
- package/dist/types/components/GrSteps/grStepsStyles.d.ts +63 -0
- package/dist/types/components/GrSteps/index.d.ts +11 -0
- package/dist/types/components/GrSteps/safelist.d.ts +1 -0
- package/dist/types/components/GrSteps/stepsModel.d.ts +57 -0
- package/dist/types/components/GrValue/GrValue.vue.d.ts +51 -0
- package/dist/types/components/GrValue/config.d.ts +1 -0
- package/dist/types/components/GrValue/grValueStyles.d.ts +34 -0
- package/dist/types/components/GrValue/index.d.ts +5 -0
- package/dist/types/components/GrValue/safelist.d.ts +1 -0
- package/dist/types/components/shared/truncationTitle.d.ts +16 -0
- package/dist/types/composables/internal/useMenuItemsFocus.d.ts +24 -0
- package/dist/types/composables/internal/useTypeahead.d.ts +30 -0
- package/dist/types/composables/useFloating.d.ts +33 -2
- package/dist/types/granular-provider/shared.d.ts +5 -0
- package/dist/types/i18n/messages/en.d.ts +26 -0
- package/dist/types/i18n/messages/es.d.ts +26 -0
- package/dist/types/i18n/messages/ru.d.ts +26 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/web-types.json +799 -195
- package/package.json +22 -2
- package/tokens/themes/dark.json +26 -6
- package/tokens/themes/light.json +20 -0
- package/dist/chunks/config-BSyiFlbi.js.map +0 -1
- package/dist/chunks/config-Be9k9bOf.js.map +0 -1
- package/dist/chunks/config-BfsBepkH2.js.map +0 -1
- package/dist/chunks/config-Bm_Sovla.js.map +0 -1
- package/dist/chunks/config-BoNR7_PD2.js.map +0 -1
- package/dist/chunks/config-BuGlNPE9.js.map +0 -1
- package/dist/chunks/config-C5mO5DYZ.js.map +0 -1
- package/dist/chunks/config-DAX8Deh12.js.map +0 -1
- package/dist/chunks/config-DBQQT-WP.js.map +0 -1
- package/dist/chunks/config-DCwQBMmJ.js.map +0 -1
- package/dist/chunks/config-T3qdQBgU2.js.map +0 -1
- package/dist/chunks/config-qVg3Zz_A.js.map +0 -1
- package/dist/chunks/config-zjeAJ4ho.js.map +0 -1
- package/dist/chunks/context-BfIQv7m1.js.map +0 -1
- package/dist/chunks/en-DCCFP4qu.js.map +0 -1
- package/dist/chunks/es-CT34HBVM.js.map +0 -1
- package/dist/chunks/grPopoverStyles-Rz_Z5nLW.js.map +0 -1
- package/dist/chunks/granular-provider-CQKhl5C_.js.map +0 -1
- package/dist/chunks/ru-CLiAMwrx.js.map +0 -1
- package/dist/components/GrCard/chunks/GrCard-Bc7nRfbe.js.map +0 -1
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BFcMf4Pn.js.map +0 -1
- package/dist/components/GrConfigProvider/chunks/GrConfigProvider-UP-EaoAf.js.map +0 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-B5MmZFUg.js.map +0 -1
- package/dist/components/GrDelta/chunks/GrDelta-DFvxNpmN.js.map +0 -1
- package/dist/components/GrDescriptionList/chunks/GrDescriptionList-2zFXzX83.js.map +0 -1
- package/dist/components/GrDialogService/chunks/GrDialogService-CrwA4aJq.js.map +0 -1
- package/dist/components/GrDrawer/chunks/GrDrawer-C3zAg2tG.js.map +0 -1
- package/dist/components/GrDropdown/chunks/GrDropdown-BSfFOcDx.js.map +0 -1
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-DyS6dWDp.js.map +0 -1
- package/dist/components/GrFileUpload/chunks/GrFileUpload-D3-vC-ap.js.map +0 -1
- package/dist/components/GrFormField/chunks/GrFormField-kqfVY30N.js.map +0 -1
- package/dist/components/GrJsonViewer/chunks/GrJsonViewer-DFUppRNb.js.map +0 -1
- package/dist/components/GrList/chunks/GrList-BRYhpqIe.js.map +0 -1
- package/dist/components/GrLoading/chunks/GrLoading-C3DZSSuh.js.map +0 -1
- package/dist/components/GrModal/chunks/GrModal-CPz7SWD_.js.map +0 -1
- package/dist/components/GrPopover/chunks/GrPopover-b70bIlQ9.js.map +0 -1
- package/dist/components/GrSegmented/chunks/GrSegmented-D1mjxF_O.js.map +0 -1
- package/dist/components/GrSelect/chunks/GrSelect-CaGuyvtb.js.map +0 -1
- package/dist/components/GrStatistic/chunks/GrStatistic-C7wQECeS.js.map +0 -1
- package/dist/components/GrTimeline/chunks/GrTimeline-Ch9sHzwL.js.map +0 -1
- package/dist/chunks/{config-BfsBepkH2.js → config-BfsBepkH.js} +0 -0
- package/dist/chunks/{config-BoNR7_PD2.js → config-BoNR7_PD.js} +0 -0
- package/dist/chunks/{config-BuGlNPE9.js → config-BuGlNPE92.js} +0 -0
- package/dist/chunks/{config-DAX8Deh12.js → config-DAX8Deh1.js} +0 -0
- package/dist/chunks/{config-T3qdQBgU2.js → config-T3qdQBgU.js} +0 -0
- package/dist/chunks/{config-zjeAJ4ho.js → config-zjeAJ4ho2.js} +0 -0
- package/dist/chunks/{context-BfIQv7m1.js → context-BfIQv7m12.js} +0 -0
- package/dist/components/GrTree/chunks/{GrTree-BvOeQ3xJ.js → GrTree-J5q8aCya.js} +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrJsonViewer-jk3ylOTc.js","names":[],"sources":["../../../../src/components/GrJsonViewer/jsonToNodes.ts","../../../../src/components/GrJsonViewer/GrJsonViewer.vue","../../../../src/components/GrJsonViewer/GrJsonViewer.vue"],"sourcesContent":["/**\n * Разбор произвольного значения в узлы дерева.\n *\n * Компонент показывает данные из БД и от чужих сервисов, поэтому обход обязан\n * пережить всё: цикл, `BigInt`, функцию в поле, массив на десять тысяч записей,\n * строку в миллион символов. Уронить страницу он права не имеет.\n */\n\n/** Что за значение в узле. Решает и цвет, и то, раскрывается ли узел. */\nexport type GrJsonNodeKind =\n | 'object'\n | 'array'\n | 'string'\n | 'number'\n | 'boolean'\n | 'null'\n /** Значения, которых в JSON не бывает: функция, символ, `undefined`. */\n | 'unsupported'\n /** Хвост обрезанного массива — «ещё N элементов». Своего значения не имеет. */\n | 'truncation'\n\nexport type GrJsonNode = {\n /** Читаемый адрес узла (`$.items[3].name`). Он же ключ для `GrTree`. */\n path: string\n /** Имя ключа или индекс. Показывается слева от двоеточия. */\n label: string\n kind: GrJsonNodeKind\n /** Что видно в строке. У ветки — счётчик, у листа — значение, возможно обрезанное. */\n preview: string\n /** Полное значение. Копирование берёт его, а не `preview`. */\n value: unknown\n /** Показ обрезан: копирование всё равно отдаёт целое. */\n truncated: boolean\n isLeaf: boolean\n children?: GrJsonNode[]\n}\n\nexport type GrJsonToNodesOptions = {\n /** Подпись корня. */\n rootLabel?: string\n /** Длина строки, после которой значение обрезается в показе. */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n}\n\n/** Маркер ветки, которая уже встречалась выше по этой же цепочке предков. */\nexport const CIRCULAR_MARKER = '[Circular]'\n\nconst DEFAULT_ROOT_LABEL = '$'\nconst DEFAULT_MAX_STRING_LENGTH = 200\nconst DEFAULT_MAX_ARRAY_ITEMS = 100\n\n/** Ключ, который можно дописать через точку, не сломав адрес. */\nconst PLAIN_KEY = /^[A-Za-z_$][\\w$]*$/\n\nfunction appendKey(parentPath: string, key: string): string {\n return PLAIN_KEY.test(key) ? `${parentPath}.${key}` : `${parentPath}[${JSON.stringify(key)}]`\n}\n\n/**\n * Счётчик у ветки вместо её содержимого: свёрнутый узел обязан сказать, стоит\n * ли его раскрывать. Слово не склоняется — компонент не знает языка потребителя,\n * а число само по себе читается в любом.\n */\nfunction branchPreview(kind: 'object' | 'array', count: number): string {\n return kind === 'array' ? `[${count}]` : `{${count}}`\n}\n\nfunction scalarPreview(value: unknown, maxStringLength: number): { preview: string, truncated: boolean } {\n if (typeof value === 'string') {\n if (value.length <= maxStringLength) return { preview: JSON.stringify(value), truncated: false }\n\n // Кавычка открывается и не закрывается намеренно: закрытая читалась бы как\n // конец строки, а строка продолжается — её просто не показывают целиком.\n return { preview: `${JSON.stringify(value.slice(0, maxStringLength))}…`, truncated: true }\n }\n\n // `BigInt` представления в JSON не имеет, и `JSON.stringify` на нём бросает.\n if (typeof value === 'bigint') return { preview: `${value}n`, truncated: false }\n\n if (typeof value === 'function' || typeof value === 'symbol' || value === undefined)\n return { preview: String(value), truncated: false }\n\n return { preview: JSON.stringify(value) ?? String(value), truncated: false }\n}\n\nfunction kindOf(value: unknown): GrJsonNodeKind {\n if (value === null) return 'null'\n if (Array.isArray(value)) return 'array'\n\n switch (typeof value) {\n case 'string': return 'string'\n case 'number': return 'number'\n case 'bigint': return 'number'\n case 'boolean': return 'boolean'\n case 'object': return 'object'\n // `undefined`, функция и символ представления в JSON не имеют — узел\n // остаётся, чтобы ключ не исчез из показа молча.\n case 'undefined':\n case 'function':\n case 'symbol': return 'unsupported'\n }\n\n // Недостижимо: ветки выше покрывают `typeof` целиком. Оставлено ради вывода\n // типов — без него TS не считает функцию всегда возвращающей.\n return 'unsupported'\n}\n\ntype BuildContext = {\n maxStringLength: number\n maxArrayItems: number\n /**\n * Цепочка предков текущего узла, а не все встреченные объекты.\n *\n * Разница принципиальна и отличает обход дерева от `replacer` у\n * `JSON.stringify`: тому стек недоступен, поэтому он метит `[Circular]` любую\n * повторную ссылку — в том числе объект, честно положенный в данные дважды.\n * Здесь стек есть, и маркер достаётся только настоящему циклу.\n */\n ancestors: Set<object>\n}\n\nfunction buildNode(label: string, path: string, value: unknown, context: BuildContext): GrJsonNode {\n const kind = kindOf(value)\n\n if (kind !== 'object' && kind !== 'array') {\n const { preview, truncated } = scalarPreview(value, context.maxStringLength)\n\n return { path, label, kind, preview, value, truncated, isLeaf: true }\n }\n\n const container = value as object\n\n if (context.ancestors.has(container)) {\n return { path, label, kind, preview: CIRCULAR_MARKER, value, truncated: false, isLeaf: true }\n }\n\n const entries = Array.isArray(value)\n ? value.map((item, index) => [String(index), item] as const)\n : Object.entries(container)\n\n // Пустой объект и пустой массив — листья: раскрывать нечего, а ветка без\n // детей рисует стрелку, которая ничего не делает.\n if (entries.length === 0) {\n return {\n path,\n label,\n kind,\n preview: Array.isArray(value) ? '[]' : '{}',\n value,\n truncated: false,\n isLeaf: true,\n }\n }\n\n context.ancestors.add(container)\n\n const limit = Math.max(0, context.maxArrayItems)\n const isOverLimit = Array.isArray(value) && entries.length > limit\n const visible = isOverLimit ? entries.slice(0, limit) : entries\n\n const children: GrJsonNode[] = visible.map(([key, item]) => buildNode(\n key,\n Array.isArray(value) ? `${path}[${key}]` : appendKey(path, key),\n item,\n context,\n ))\n\n if (isOverLimit) {\n const rest = entries.length - limit\n\n children.push({\n path: `${path}[…]`,\n label: '…',\n kind: 'truncation',\n preview: String(rest),\n value: (value as unknown[]).slice(limit),\n truncated: true,\n isLeaf: true,\n })\n }\n\n context.ancestors.delete(container)\n\n return {\n path,\n label,\n kind,\n preview: branchPreview(kind, entries.length),\n value,\n truncated: isOverLimit,\n isLeaf: false,\n children,\n }\n}\n\n/**\n * Значение → узлы дерева. Корень всегда один: даже скаляр на входе — это узел,\n * иначе дерево пришлось бы то показывать, то нет.\n */\nexport function jsonToNodes(value: unknown, options: GrJsonToNodesOptions = {}): GrJsonNode[] {\n const context: BuildContext = {\n maxStringLength: options.maxStringLength ?? DEFAULT_MAX_STRING_LENGTH,\n maxArrayItems: options.maxArrayItems ?? DEFAULT_MAX_ARRAY_ITEMS,\n ancestors: new Set(),\n }\n\n return [buildNode(options.rootLabel ?? DEFAULT_ROOT_LABEL, DEFAULT_ROOT_LABEL, value, context)]\n}\n\n/** Пути всех узлов до заданной глубины — ими задаётся начальная свёртка. */\nexport function pathsToDepth(nodes: GrJsonNode[], depth: number, level = 0): string[] {\n if (level >= depth) return []\n\n return nodes.flatMap(node => (\n node.children ? [node.path, ...pathsToDepth(node.children, depth, level + 1)] : []\n ))\n}\n\n/** Пути всех веток — для «раскрыть всё». */\nexport function branchPaths(nodes: GrJsonNode[]): string[] {\n return nodes.flatMap(node => (node.children ? [node.path, ...branchPaths(node.children)] : []))\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree from '../GrTree/GrTree.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { branchPaths, jsonToNodes, pathsToDepth, type GrJsonNode } from './jsonToNodes'\nimport {\n jsonValueClass,\n jsonViewerContentClass,\n jsonViewerCopyClass,\n jsonViewerGroupClass,\n jsonViewerKeyClass,\n jsonViewerMutedClass,\n jsonViewerPunctuationClass,\n jsonViewerRootClass,\n jsonViewerRowClass,\n jsonViewerSearchClass,\n jsonViewerToolbarClass,\n jsonViewerValueClass,\n} from './grJsonViewerStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrJsonNode, GrJsonNodeKind } from './jsonToNodes'\n\n/**\n * JSON со свёрткой по узлам, поиском по ключу и значению и копированием узла.\n *\n * Дерево, клавиатуру и виртуализацию несёт `GrTree`; здесь — разбор `unknown` в\n * узлы и разметка строки. Плоский текст целиком, который копируют в тикет, —\n * `GrCodeBlock`.\n */\nexport interface GrJsonViewerProps {\n /** Показываемое значение. `unknown`, потому что приходит из БД или чужого сервиса. */\n value: unknown\n /** Подпись корня. По умолчанию `$` — так адресуют путь JSONPath и dev-tools. */\n rootLabel?: string\n /** Сколько уровней раскрыто изначально. */\n defaultExpandDepth?: number\n /**\n * Длина строкового значения, после которой показ обрезается.\n *\n * Не косметика: запрос к модели с картинкой в base64 — это один лист на\n * сотни тысяч символов, и ни свёртка, ни виртуализация по строкам его не\n * берут. Копирование при этом отдаёт значение целиком.\n */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n /** Поле поиска над деревом. Выключено — поиск остаётся на `filter()` из `defineExpose`. */\n searchable?: boolean\n /** Кнопка копирования узла в строке. */\n copyable?: boolean\n /** Оставлять в DOM только окно вокруг вьюпорта. Требует `maxHeight`. */\n virtual?: boolean\n /** Высота области просмотра: число — пиксели, строка — как есть. */\n maxHeight?: string | number\n /** Имя области для скринридера. Безымянное дерево объявляется просто «дерево». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrJsonViewerEmits {\n /** Узел скопирован: путь для тикета и полное значение. */\n (e: 'copy', payload: { path: string, value: unknown }): void\n}\n\nconst props = withDefaults(defineProps<GrJsonViewerProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n rootLabel: undefined,\n defaultExpandDepth: undefined,\n maxStringLength: undefined,\n maxArrayItems: undefined,\n searchable: undefined,\n copyable: undefined,\n virtual: false,\n maxHeight: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrJsonViewerEmits>()\n\ndefineSlots<{\n /** Значение листа: ссылка, дата, денежная сумма — оформление знает приложение. */\n leaf?: (props: { node: GrJsonNode }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrJsonViewer' })\nconst resolvedDepth = useGrComponentProp('GrJsonViewer', 'defaultExpandDepth', () => props.defaultExpandDepth, 1)\nconst resolvedMaxString = useGrComponentProp('GrJsonViewer', 'maxStringLength', () => props.maxStringLength, 200)\nconst resolvedMaxArray = useGrComponentProp('GrJsonViewer', 'maxArrayItems', () => props.maxArrayItems, 100)\nconst resolvedSearchable = useGrComponentProp('GrJsonViewer', 'searchable', () => props.searchable, true)\nconst resolvedCopyable = useGrComponentProp('GrJsonViewer', 'copyable', () => props.copyable, true)\n\nconst nodes = computed(() => jsonToNodes(props.value, {\n rootLabel: props.rootLabel,\n maxStringLength: resolvedMaxString.value,\n maxArrayItems: resolvedMaxArray.value,\n}))\n\n/**\n * Раскрытие ведётся подстановкой набора путей.\n *\n * Контролируемого `expandedKeys` у `GrTree` нет, но `defaultExpandedKeys` —\n * не «начальное значение»: дерево следит за пропом и заменяет набор целиком.\n * Этого хватает и глубине по умолчанию, и кнопкам «раскрыть/свернуть всё»;\n * ручное раскрытие пользователя при этом сбрасывается — от этих кнопок ровно\n * того и ждут.\n */\nconst expandedPaths = ref<string[]>([])\n\nwatch(\n [nodes, resolvedDepth],\n ([list, depth]) => { expandedPaths.value = pathsToDepth(list, depth) },\n { immediate: true },\n)\n\nfunction expandAll(): void {\n expandedPaths.value = branchPaths(nodes.value)\n}\n\nfunction collapseAll(): void {\n expandedPaths.value = []\n}\n\nconst query = ref('')\nconst treeRef = ref<{ filter: (value: string) => void } | null>(null)\n\nwatch(query, (value) => { treeRef.value?.filter(value) })\n\nfunction filter(value: string): void {\n query.value = value\n}\n\n/** Поиск идёт и по ключу, и по значению: в чужом ответе ищут то одно, то другое. */\nfunction matchKeyOrValue(value: string, data: GrJsonNode): boolean {\n if (!value) return true\n\n const needle = value.toLowerCase()\n\n return data.label.toLowerCase().includes(needle) || data.preview.toLowerCase().includes(needle)\n}\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n\n if (props.virtual && props.maxHeight === undefined && __GR_DEV__) {\n console.warn('[GrJsonViewer] `virtual` без `maxHeight` не включает виртуализацию: окно считается от высоты области.')\n }\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copiedPath = ref<string | null>(null)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\n/**\n * В буфер уходит **полное** значение узла, а не то, что видно: обрезка\n * принадлежит показу, и вставить её обратно нельзя.\n */\nasync function copyNode(node: GrJsonNode): Promise<void> {\n const text = typeof node.value === 'string' ? node.value : JSON.stringify(node.value, null, 2) ?? String(node.value)\n\n try {\n await navigator.clipboard.writeText(text)\n }\n catch {\n announce(t('gr.jsonViewer.copyFailed', 'Could not copy value'))\n return\n }\n\n copiedPath.value = node.path\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copiedPath.value = null\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.jsonViewer.copied', 'Value copied'))\n emit('copy', { path: node.path, value: node.value })\n}\n\ndefineExpose({ filter, expandAll, collapseAll })\n</script>\n\n<template>\n <div data-gr-json-viewer :class=\"jsonViewerRootClass\">\n <div v-if=\"resolvedSearchable\" data-gr-json-viewer-toolbar :class=\"jsonViewerToolbarClass\">\n <GrInput\n v-model=\"query\"\n :class=\"jsonViewerSearchClass\"\n :size=\"resolvedSize\"\n clearable\n :placeholder=\"t('gr.jsonViewer.search', 'Search key or value')\"\n :aria-label=\"t('gr.jsonViewer.search', 'Search key or value')\"\n />\n\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"expandAll\">\n {{ t('gr.jsonViewer.expandAll', 'Expand all') }}\n </GrButton>\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"collapseAll\">\n {{ t('gr.jsonViewer.collapseAll', 'Collapse all') }}\n </GrButton>\n </div>\n\n <GrTree\n ref=\"treeRef\"\n :data=\"nodes\"\n node-key=\"path\"\n :props=\"{ children: 'children', label: 'label', isLeaf: 'isLeaf' }\"\n :default-expanded-keys=\"expandedPaths\"\n :filter-node-method=\"matchKeyOrValue\"\n :content-class=\"jsonViewerContentClass\"\n :virtual=\"virtual\"\n :max-height=\"maxHeight\"\n :size=\"resolvedSize\"\n :aria-label=\"ariaLabel\"\n >\n <template #default=\"{ data }\">\n <span data-gr-json-viewer-row :class=\"[jsonViewerRowClass, jsonViewerGroupClass]\">\n <span data-gr-json-viewer-key :class=\"jsonViewerKeyClass\">{{ data.label }}</span>\n <span :class=\"jsonViewerPunctuationClass\">:</span>\n\n <span\n data-gr-json-viewer-value\n :class=\"[jsonViewerValueClass, jsonValueClass[(data as GrJsonNode).kind]]\"\n >\n <slot name=\"leaf\" :node=\"data as GrJsonNode\">{{ (data as GrJsonNode).preview }}</slot>\n </span>\n\n <span\n v-if=\"(data as GrJsonNode).kind === 'truncation'\"\n :class=\"jsonViewerMutedClass\"\n >{{ t('gr.jsonViewer.moreItems', 'more') }}</span>\n\n <GrButton\n v-if=\"showCopy && (data as GrJsonNode).kind !== 'truncation'\"\n data-gr-json-viewer-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"jsonViewerCopyClass\"\n :aria-label=\"t('gr.jsonViewer.copy', 'Copy value')\"\n :title=\"t('gr.jsonViewer.copy', 'Copy value')\"\n @click.stop=\"copyNode(data as GrJsonNode)\"\n >\n <component\n :is=\"copiedPath === (data as GrJsonNode).path ? IconCheck : IconCopy\"\n class=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n </GrButton>\n </span>\n </template>\n </GrTree>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTree from '../GrTree/GrTree.vue'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\n\nimport IconCheck from '~icons/lucide/check'\nimport IconCopy from '~icons/lucide/copy'\n\nimport { branchPaths, jsonToNodes, pathsToDepth, type GrJsonNode } from './jsonToNodes'\nimport {\n jsonValueClass,\n jsonViewerContentClass,\n jsonViewerCopyClass,\n jsonViewerGroupClass,\n jsonViewerKeyClass,\n jsonViewerMutedClass,\n jsonViewerPunctuationClass,\n jsonViewerRootClass,\n jsonViewerRowClass,\n jsonViewerSearchClass,\n jsonViewerToolbarClass,\n jsonViewerValueClass,\n} from './grJsonViewerStyles'\n\nimport type { GrComponentSize } from '../shared/sizes'\n\nexport type { GrJsonNode, GrJsonNodeKind } from './jsonToNodes'\n\n/**\n * JSON со свёрткой по узлам, поиском по ключу и значению и копированием узла.\n *\n * Дерево, клавиатуру и виртуализацию несёт `GrTree`; здесь — разбор `unknown` в\n * узлы и разметка строки. Плоский текст целиком, который копируют в тикет, —\n * `GrCodeBlock`.\n */\nexport interface GrJsonViewerProps {\n /** Показываемое значение. `unknown`, потому что приходит из БД или чужого сервиса. */\n value: unknown\n /** Подпись корня. По умолчанию `$` — так адресуют путь JSONPath и dev-tools. */\n rootLabel?: string\n /** Сколько уровней раскрыто изначально. */\n defaultExpandDepth?: number\n /**\n * Длина строкового значения, после которой показ обрезается.\n *\n * Не косметика: запрос к модели с картинкой в base64 — это один лист на\n * сотни тысяч символов, и ни свёртка, ни виртуализация по строкам его не\n * берут. Копирование при этом отдаёт значение целиком.\n */\n maxStringLength?: number\n /** Сколько элементов массива разбирать до заглушки «ещё N». */\n maxArrayItems?: number\n /** Поле поиска над деревом. Выключено — поиск остаётся на `filter()` из `defineExpose`. */\n searchable?: boolean\n /** Кнопка копирования узла в строке. */\n copyable?: boolean\n /** Оставлять в DOM только окно вокруг вьюпорта. Требует `maxHeight`. */\n virtual?: boolean\n /** Высота области просмотра: число — пиксели, строка — как есть. */\n maxHeight?: string | number\n /** Имя области для скринридера. Безымянное дерево объявляется просто «дерево». */\n ariaLabel?: string\n size?: GrComponentSize\n}\n\nexport interface GrJsonViewerEmits {\n /** Узел скопирован: путь для тикета и полное значение. */\n (e: 'copy', payload: { path: string, value: unknown }): void\n}\n\nconst props = withDefaults(defineProps<GrJsonViewerProps>(), {\n // Дефолты живут в резолверах ниже: Vue подставил бы свой раньше, чем\n // компонент заглянет в `GrConfigProvider`.\n rootLabel: undefined,\n defaultExpandDepth: undefined,\n maxStringLength: undefined,\n maxArrayItems: undefined,\n searchable: undefined,\n copyable: undefined,\n virtual: false,\n maxHeight: undefined,\n ariaLabel: undefined,\n size: undefined,\n})\n\nconst emit = defineEmits<GrJsonViewerEmits>()\n\ndefineSlots<{\n /** Значение листа: ссылка, дата, денежная сумма — оформление знает приложение. */\n leaf?: (props: { node: GrJsonNode }) => unknown\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrJsonViewer' })\nconst resolvedDepth = useGrComponentProp('GrJsonViewer', 'defaultExpandDepth', () => props.defaultExpandDepth, 1)\nconst resolvedMaxString = useGrComponentProp('GrJsonViewer', 'maxStringLength', () => props.maxStringLength, 200)\nconst resolvedMaxArray = useGrComponentProp('GrJsonViewer', 'maxArrayItems', () => props.maxArrayItems, 100)\nconst resolvedSearchable = useGrComponentProp('GrJsonViewer', 'searchable', () => props.searchable, true)\nconst resolvedCopyable = useGrComponentProp('GrJsonViewer', 'copyable', () => props.copyable, true)\n\nconst nodes = computed(() => jsonToNodes(props.value, {\n rootLabel: props.rootLabel,\n maxStringLength: resolvedMaxString.value,\n maxArrayItems: resolvedMaxArray.value,\n}))\n\n/**\n * Раскрытие ведётся подстановкой набора путей.\n *\n * Контролируемого `expandedKeys` у `GrTree` нет, но `defaultExpandedKeys` —\n * не «начальное значение»: дерево следит за пропом и заменяет набор целиком.\n * Этого хватает и глубине по умолчанию, и кнопкам «раскрыть/свернуть всё»;\n * ручное раскрытие пользователя при этом сбрасывается — от этих кнопок ровно\n * того и ждут.\n */\nconst expandedPaths = ref<string[]>([])\n\nwatch(\n [nodes, resolvedDepth],\n ([list, depth]) => { expandedPaths.value = pathsToDepth(list, depth) },\n { immediate: true },\n)\n\nfunction expandAll(): void {\n expandedPaths.value = branchPaths(nodes.value)\n}\n\nfunction collapseAll(): void {\n expandedPaths.value = []\n}\n\nconst query = ref('')\nconst treeRef = ref<{ filter: (value: string) => void } | null>(null)\n\nwatch(query, (value) => { treeRef.value?.filter(value) })\n\nfunction filter(value: string): void {\n query.value = value\n}\n\n/** Поиск идёт и по ключу, и по значению: в чужом ответе ищут то одно, то другое. */\nfunction matchKeyOrValue(value: string, data: GrJsonNode): boolean {\n if (!value) return true\n\n const needle = value.toLowerCase()\n\n return data.label.toLowerCase().includes(needle) || data.preview.toLowerCase().includes(needle)\n}\n\n/**\n * Наличие буфера уточняется после монтирования: `navigator` в теле `setup` либо\n * роняет серверный рендер, либо расходится с ним. До этого момента кнопки нет —\n * ровно то же, что отдаёт сервер, поэтому гидрация совпадает.\n */\nconst canCopy = ref(false)\n\nonMounted(() => {\n canCopy.value = typeof navigator !== 'undefined' && typeof navigator.clipboard?.writeText === 'function'\n\n if (props.virtual && props.maxHeight === undefined && __GR_DEV__) {\n console.warn('[GrJsonViewer] `virtual` без `maxHeight` не включает виртуализацию: окно считается от высоты области.')\n }\n})\n\nconst showCopy = computed(() => resolvedCopyable.value && canCopy.value)\n\nconst copiedPath = ref<string | null>(null)\nlet copiedTimer: number | null = null\n\nfunction resetCopiedTimer(): void {\n if (copiedTimer !== null) {\n clearTimeout(copiedTimer)\n copiedTimer = null\n }\n}\n\nonBeforeUnmount(resetCopiedTimer)\n\n/**\n * В буфер уходит **полное** значение узла, а не то, что видно: обрезка\n * принадлежит показу, и вставить её обратно нельзя.\n */\nasync function copyNode(node: GrJsonNode): Promise<void> {\n const text = typeof node.value === 'string' ? node.value : JSON.stringify(node.value, null, 2) ?? String(node.value)\n\n try {\n await navigator.clipboard.writeText(text)\n }\n catch {\n announce(t('gr.jsonViewer.copyFailed', 'Could not copy value'))\n return\n }\n\n copiedPath.value = node.path\n resetCopiedTimer()\n copiedTimer = window.setTimeout(() => {\n copiedPath.value = null\n copiedTimer = null\n }, 1200)\n\n announce(t('gr.jsonViewer.copied', 'Value copied'))\n emit('copy', { path: node.path, value: node.value })\n}\n\ndefineExpose({ filter, expandAll, collapseAll })\n</script>\n\n<template>\n <div data-gr-json-viewer :class=\"jsonViewerRootClass\">\n <div v-if=\"resolvedSearchable\" data-gr-json-viewer-toolbar :class=\"jsonViewerToolbarClass\">\n <GrInput\n v-model=\"query\"\n :class=\"jsonViewerSearchClass\"\n :size=\"resolvedSize\"\n clearable\n :placeholder=\"t('gr.jsonViewer.search', 'Search key or value')\"\n :aria-label=\"t('gr.jsonViewer.search', 'Search key or value')\"\n />\n\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"expandAll\">\n {{ t('gr.jsonViewer.expandAll', 'Expand all') }}\n </GrButton>\n <GrButton variant=\"ghost\" :size=\"resolvedSize\" @click=\"collapseAll\">\n {{ t('gr.jsonViewer.collapseAll', 'Collapse all') }}\n </GrButton>\n </div>\n\n <GrTree\n ref=\"treeRef\"\n :data=\"nodes\"\n node-key=\"path\"\n :props=\"{ children: 'children', label: 'label', isLeaf: 'isLeaf' }\"\n :default-expanded-keys=\"expandedPaths\"\n :filter-node-method=\"matchKeyOrValue\"\n :content-class=\"jsonViewerContentClass\"\n :virtual=\"virtual\"\n :max-height=\"maxHeight\"\n :size=\"resolvedSize\"\n :aria-label=\"ariaLabel\"\n >\n <template #default=\"{ data }\">\n <span data-gr-json-viewer-row :class=\"[jsonViewerRowClass, jsonViewerGroupClass]\">\n <span data-gr-json-viewer-key :class=\"jsonViewerKeyClass\">{{ data.label }}</span>\n <span :class=\"jsonViewerPunctuationClass\">:</span>\n\n <span\n data-gr-json-viewer-value\n :class=\"[jsonViewerValueClass, jsonValueClass[(data as GrJsonNode).kind]]\"\n >\n <slot name=\"leaf\" :node=\"data as GrJsonNode\">{{ (data as GrJsonNode).preview }}</slot>\n </span>\n\n <span\n v-if=\"(data as GrJsonNode).kind === 'truncation'\"\n :class=\"jsonViewerMutedClass\"\n >{{ t('gr.jsonViewer.moreItems', 'more') }}</span>\n\n <GrButton\n v-if=\"showCopy && (data as GrJsonNode).kind !== 'truncation'\"\n data-gr-json-viewer-copy\n variant=\"ghost\"\n size=\"xs\"\n square\n :class=\"jsonViewerCopyClass\"\n :aria-label=\"t('gr.jsonViewer.copy', 'Copy value')\"\n :title=\"t('gr.jsonViewer.copy', 'Copy value')\"\n @click.stop=\"copyNode(data as GrJsonNode)\"\n >\n <component\n :is=\"copiedPath === (data as GrJsonNode).path ? IconCheck : IconCopy\"\n class=\"h-3.5 w-3.5\"\n aria-hidden=\"true\"\n />\n </GrButton>\n </span>\n </template>\n </GrTree>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrJsonViewer_default } from "./chunks/GrJsonViewer-
|
|
1
|
+
import { t as GrJsonViewer_default } from "./chunks/GrJsonViewer-jk3ylOTc.js";
|
|
2
2
|
import { n as grJsonViewerSafelist, t as grJsonViewerConfig } from "../../chunks/config-CIcynHGr.js";
|
|
3
3
|
export { GrJsonViewer_default as GrJsonViewer, GrJsonViewer_default as default, grJsonViewerConfig, grJsonViewerSafelist };
|
|
@@ -2,10 +2,10 @@ import { n as useGranularityTranslations } from "../../../chunks/granularityI18n
|
|
|
2
2
|
import { useVirtualList } from "../../../composables/useVirtualList.js";
|
|
3
3
|
import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-BOaGB7Aw.js";
|
|
4
4
|
import { t as GrSkeleton_default } from "../../GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js";
|
|
5
|
-
import { t as GrCard_default } from "../../GrCard/chunks/GrCard-
|
|
5
|
+
import { t as GrCard_default } from "../../GrCard/chunks/GrCard-aQIjk6I9.js";
|
|
6
6
|
import { n as hasMeaningfulSlotContent } from "../../../chunks/slotNodes-DxNXpC7v.js";
|
|
7
7
|
import { a as grListItemPaddingClass, c as itemHoverClass, d as itemTitleClass, f as listSurfaceClass, i as emptyClass, l as itemInteractiveClass, o as itemDescriptionClass, p as loadingRowClass, r as dividedClass, s as itemDisabledClass, u as itemLayoutClass } from "../../../chunks/config-CKULiuvc.js";
|
|
8
|
-
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, markRaw, normalizeClass, normalizeStyle, onMounted, onUpdated, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useSlots, withCtx } from "vue";
|
|
8
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, markRaw, normalizeClass, normalizeStyle, onMounted, onUpdated, openBlock, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useSlots, watch, withCtx } from "vue";
|
|
9
9
|
import '../styles.css';//#region src/components/GrList/GrList.vue?vue&type=script&setup=true&lang.ts
|
|
10
10
|
var _hoisted_1$1 = [
|
|
11
11
|
"tabindex",
|
|
@@ -141,6 +141,25 @@ var GrList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
|
|
|
141
141
|
virtualizer.scrollToIndex(index, align);
|
|
142
142
|
}
|
|
143
143
|
__expose({ scrollToIndex });
|
|
144
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") {
|
|
145
|
+
watch(() => [
|
|
146
|
+
props.virtual,
|
|
147
|
+
dataMode.value,
|
|
148
|
+
props.maxHeight
|
|
149
|
+
], ([virtual, hasItems, maxHeight]) => {
|
|
150
|
+
if (!virtual) return;
|
|
151
|
+
if (!hasItems) console.warn("[granularity] GrList: `virtual` требует `items` — пункты из слота по умолчанию список не считает и резать окно ему не из чего.");
|
|
152
|
+
else if (maxHeight === void 0) console.warn("[granularity] GrList: `virtual` требует `maxHeight` — без ограничения высоты вьюпорта нет, и в окно попадает весь набор.");
|
|
153
|
+
}, { immediate: true });
|
|
154
|
+
watch(() => [dataMode.value, Boolean(slots.item)], ([hasItems, hasSlot]) => {
|
|
155
|
+
if (hasItems && !hasSlot) console.warn("[granularity] GrList: с `items` пункты рисует слот `#item` — без него список пуст.");
|
|
156
|
+
}, { immediate: true });
|
|
157
|
+
onMounted(() => {
|
|
158
|
+
if (!virtualEnabled.value) return;
|
|
159
|
+
const first = listEl.value?.firstElementChild;
|
|
160
|
+
if (first && !first.hasAttribute("aria-posinset")) console.warn("[granularity] GrList: при `virtual` пункты обязаны нести `aria-setsize`/`aria-posinset` — пробросьте `aria` из слота `#item`: <GrListItem v-bind=\"aria\" … />. Иначе диктор посчитает набор по отрисованному окну.");
|
|
161
|
+
});
|
|
162
|
+
}
|
|
144
163
|
return (_ctx, _cache) => {
|
|
145
164
|
return openBlock(), createBlock(unref(GrCard_default), { variant: __props.variant }, {
|
|
146
165
|
default: withCtx(() => [createElementVNode("div", {
|
|
@@ -174,7 +193,7 @@ var GrList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
|
|
|
174
193
|
}, 8, ["variant"]);
|
|
175
194
|
};
|
|
176
195
|
}
|
|
177
|
-
}), [["__scopeId", "data-v-
|
|
196
|
+
}), [["__scopeId", "data-v-8fdef7fd"]]);
|
|
178
197
|
//#endregion
|
|
179
198
|
//#region src/components/GrList/GrListItem.vue?vue&type=script&setup=true&lang.ts
|
|
180
199
|
var _hoisted_1 = ["aria-disabled"];
|
|
@@ -240,6 +259,14 @@ var GrListItem_default = /* @__PURE__ */ defineComponent({
|
|
|
240
259
|
if (!isInteractive.value) return;
|
|
241
260
|
emit("click", event);
|
|
242
261
|
}
|
|
262
|
+
/** Теги, попадающие в таб-порядок сами. `<a>` — только со ссылкой. */
|
|
263
|
+
function isFocusableTag(tag) {
|
|
264
|
+
return tag === "button" || tag === "a" && !!props.href;
|
|
265
|
+
}
|
|
266
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production") watch(() => [isInteractive.value, props.as], ([interactive, as]) => {
|
|
267
|
+
if (!interactive || typeof as !== "string" || isFocusableTag(as)) return;
|
|
268
|
+
console.warn(`[granularity] GrListItem: as="${as}" не попадает в таб-порядок — строка кликается мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (\`button\`, \`a\` со ссылкой), или компонент роутера.`);
|
|
269
|
+
}, { immediate: true });
|
|
243
270
|
return (_ctx, _cache) => {
|
|
244
271
|
return openBlock(), createElementBlock("div", {
|
|
245
272
|
"data-gr-list-item": "",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrList-B5bclmHV.js","names":["$slots"],"sources":["../../../../src/components/GrList/GrList.vue","../../../../src/components/GrList/GrList.vue","../../../../src/components/GrList/GrListItem.vue","../../../../src/components/GrList/GrListItem.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T = unknown\">\nimport { computed, onMounted, onUpdated, ref, useSlots, watch } from 'vue'\n\nimport GrCard from '../GrCard'\nimport type { GrCardVariant } from '../GrCard/grCardStyles'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { hasMeaningfulSlotContent } from '../shared/slotNodes'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useVirtualList, type GrVirtualAlign } from '../../composables/useVirtualList'\n\nimport { dividedClass, emptyClass, listSurfaceClass, loadingRowClass } from './grListStyles'\n\nexport interface GrListProps<TItem = unknown> {\n /**\n * Поверхность списка — вариант карточки под ним. `ghost` убирает рамку и\n * тень: список внутри уже существующей карточки не должен давать вторую.\n */\n variant?: GrCardVariant\n /** Показывать ли горизонтальные разделители между элементами (по умолчанию — да). */\n divided?: boolean\n /** Идёт загрузка: вместо пунктов — строки-скелетоны, контейнер помечен `aria-busy`. */\n loading?: boolean\n /** Сколько строк-заглушек показать при `loading`. */\n loadingRows?: number\n /**\n * Список пуст. По умолчанию определяется сам — по содержимому слота; проп\n * нужен там, где потребитель знает лучше (например, слот заполнен\n * заголовками групп, а данных в них нет).\n */\n empty?: boolean\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /**\n * Данные списка. С ними пункты рисует слот `#item`, а не слот по умолчанию, —\n * и только так список знает размер набора, то есть может его виртуализировать.\n */\n items?: TItem[]\n /** Ключ пункта для `v-for`: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Высота видимой части: контейнер становится скроллером. Число — пиксели. */\n maxHeight?: number | string\n /** Держать в DOM только окно вокруг вьюпорта. Требует `items` и `maxHeight`. */\n virtual?: boolean\n /** Оценка высоты пункта до замера. Уточняется по факту при первом рендере. */\n estimatedItemSize?: number\n}\n\nconst props = withDefaults(defineProps<GrListProps<T>>(), {\n // Дефолт живёт в `GrCard` (и его резолвере `GrConfigProvider`), поэтому здесь\n // остаётся `undefined` — иначе список перебивал бы настройку карточки.\n variant: undefined,\n divided: true,\n loading: false,\n loadingRows: 3,\n empty: undefined,\n emptyText: undefined,\n items: undefined,\n itemKey: undefined,\n maxHeight: undefined,\n virtual: false,\n // Строка обычной плотности с заголовком и описанием.\n estimatedItemSize: 56,\n})\n\nconst slots = useSlots()\nconst { t } = useGranularityTranslations()\n\nconst listEl = ref<HTMLElement | null>(null)\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.list.empty', 'Nothing here yet'))\n\nconst dataMode = computed(() => props.items !== undefined)\n\n// Пустоту видно по слоту: `v-for` по пустому массиву оставляет фрагмент без\n// узлов, `v-if` — комментарий, и ни то ни другое пунктом списка не является.\nconst isEmpty = computed(() => {\n if (props.empty !== undefined)\n return props.empty\n\n if (dataMode.value)\n return props.items!.length === 0\n\n return !hasMeaningfulSlotContent(slots.default?.() ?? [])\n})\n\nconst showItems = computed(() => !props.loading && !isEmpty.value)\n\n// Разделители нужны только между пунктами: в пустой и загрузочной ветках они\n// оставили бы висящую линию. Клип по радиусу карточки — безусловный.\nconst listClass = computed(() => [\n listSurfaceClass,\n props.divided && showItems.value ? dividedClass : '',\n].filter(Boolean).join(' '))\n\nconst loadingRowCount = computed(() => Math.max(1, Math.trunc(props.loadingRows)))\n\nconst virtualEnabled = computed(() => props.virtual && dataMode.value && props.maxHeight !== undefined)\n\nconst virtualizer = useVirtualList({\n container: listEl,\n count: () => (virtualEnabled.value && showItems.value ? props.items!.length : 0),\n // Замена массива сбрасывает замеры; append в тот же массив их сохраняет.\n source: () => props.items,\n itemSize: () => props.estimatedItemSize,\n // Первый рендер обязан совпасть с серверным: контейнера ещё нет, и окно\n // считается от объявленной высоты, а не от измеренной.\n viewportSize: () => (typeof props.maxHeight === 'number' ? props.maxHeight : undefined),\n})\n\n/** Окно к отрисовке: при выключенной виртуализации — весь набор. */\nconst renderedItems = computed(() => {\n const items = props.items ?? []\n if (!virtualEnabled.value) return items.map((item, index) => ({ item, index }))\n\n const { start, end } = virtualizer.range.value\n return items.slice(start, end).map((item, offset) => ({ item, index: start + offset }))\n})\n\nfunction keyOf(item: T, index: number): string | number {\n const key = props.itemKey\n if (typeof key === 'function') return key(item, index)\n if (typeof key === 'string') return (item as Record<string, unknown>)[key] as string | number\n\n return index\n}\n\n/**\n * ARIA-набор для пункта.\n *\n * Только в виртуальном режиме: в DOM тогда неполный набор, и без этих атрибутов\n * диктор объявил бы «7 из 20» на списке в пять тысяч. Когда набор в DOM целиком,\n * его размер и позицию браузер считает сам, а руками проставленные атрибуты\n * начинают расходиться с разметкой при любой фильтрации.\n */\nfunction ariaOf(index: number): Record<string, number> {\n if (!virtualEnabled.value) return {}\n\n return { 'aria-setsize': props.items!.length, 'aria-posinset': index + 1 }\n}\n\nconst maxHeightStyle = computed(() => {\n const value = props.maxHeight\n if (value == null) return undefined\n return typeof value === 'number' ? `${value}px` : value\n})\n\nconst listStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n const scroller = { overflowY: 'auto' as const, maxHeight: maxHeightStyle.value }\n if (!virtualEnabled.value) return scroller\n\n return { ...scroller, ...virtualizer.spacerStyle.value }\n})\n\n/**\n * Замер отрисованных пунктов.\n *\n * Идёт по детям контейнера, а не по `ref` на элементе: пункт рисует слот\n * потребителя, привязать к нему `ref` неоткуда. В виртуальном режиме прямые\n * дети контейнера — ровно окно (распорки живут псевдоэлементами и детьми не\n * считаются), поэтому сопоставление по позиции однозначно.\n */\nfunction measureRendered(): void {\n if (!virtualEnabled.value) return\n // В состояниях loading/empty дети контейнера — скелетоны и заглушки: их\n // высоты записались бы в замеры под индексами будущих строк данных.\n if (!showItems.value) return\n\n const container = listEl.value\n if (!container) return\n\n const { start } = virtualizer.range.value\n const children = container.children\n for (let offset = 0; offset < children.length; offset++) {\n virtualizer.measure(start + offset, children[offset])\n }\n}\n\nonMounted(measureRendered)\nonUpdated(measureRendered)\n\nfunction scrollToIndex(index: number, align?: GrVirtualAlign): void {\n virtualizer.scrollToIndex(index, align)\n}\n\ndefineExpose({ scrollToIndex })\n\nif (__GR_DEV__) {\n watch(\n () => [props.virtual, dataMode.value, props.maxHeight] as const,\n ([virtual, hasItems, maxHeight]) => {\n if (!virtual) return\n\n if (!hasItems) {\n console.warn(\n '[granularity] GrList: `virtual` требует `items` — пункты из слота по умолчанию '\n + 'список не считает и резать окно ему не из чего.',\n )\n }\n else if (maxHeight === undefined) {\n console.warn(\n '[granularity] GrList: `virtual` требует `maxHeight` — без ограничения высоты '\n + 'вьюпорта нет, и в окно попадает весь набор.',\n )\n }\n },\n { immediate: true },\n )\n\n watch(\n () => [dataMode.value, Boolean(slots.item)] as const,\n ([hasItems, hasSlot]) => {\n if (hasItems && !hasSlot) {\n console.warn(\n '[granularity] GrList: с `items` пункты рисует слот `#item` — без него список пуст.',\n )\n }\n },\n { immediate: true },\n )\n\n onMounted(() => {\n if (!virtualEnabled.value) return\n\n const first = listEl.value?.firstElementChild\n if (first && !first.hasAttribute('aria-posinset')) {\n console.warn(\n '[granularity] GrList: при `virtual` пункты обязаны нести `aria-setsize`/`aria-posinset` — '\n + 'пробросьте `aria` из слота `#item`: <GrListItem v-bind=\"aria\" … />. Иначе диктор '\n + 'посчитает набор по отрисованному окну.',\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n data-gr-list\n role=\"list\"\n :class=\"listClass\"\n :style=\"listStyle\"\n :tabindex=\"maxHeight === undefined ? undefined : 0\"\n :data-gr-virtual=\"virtualEnabled ? '' : undefined\"\n :aria-busy=\"loading ? 'true' : undefined\"\n >\n <template v-if=\"loading\">\n <slot name=\"loading\">\n <div\n v-for=\"row in loadingRowCount\"\n :key=\"row\"\n data-gr-list-loading-row\n :class=\"loadingRowClass\"\n >\n <GrSkeleton />\n </div>\n </slot>\n </template>\n\n <div\n v-else-if=\"isEmpty\"\n data-gr-list-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <template v-else-if=\"dataMode\">\n <template\n v-for=\"entry in renderedItems\"\n :key=\"keyOf(entry.item, entry.index)\"\n >\n <slot\n name=\"item\"\n :item=\"entry.item\"\n :index=\"entry.index\"\n :aria=\"ariaOf(entry.index)\"\n />\n </template>\n </template>\n\n <slot v-else />\n </div>\n </GrCard>\n</template>\n\n<style scoped>\n/* Срезанное сверху и снизу держат псевдоэлементы контейнера: их не за что\n размонтировать, в дереве доступности их нет, а высота приезжает переменными\n в том же патче, что и пункты. Почему не отступы контейнера и не поля крайних\n пунктов — `docs/virtual-list.md`. */\n[data-gr-virtual]::before,\n[data-gr-virtual]::after {\n content: '';\n display: block;\n flex: none;\n}\n\n[data-gr-virtual]::before {\n height: var(--gr-virtual-before, 0px);\n}\n\n[data-gr-virtual]::after {\n height: var(--gr-virtual-after, 0px);\n}\n</style>\n","<script setup lang=\"ts\" generic=\"T = unknown\">\nimport { computed, onMounted, onUpdated, ref, useSlots, watch } from 'vue'\n\nimport GrCard from '../GrCard'\nimport type { GrCardVariant } from '../GrCard/grCardStyles'\nimport GrSkeleton from '../GrSkeleton/GrSkeleton.vue'\nimport { hasMeaningfulSlotContent } from '../shared/slotNodes'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useVirtualList, type GrVirtualAlign } from '../../composables/useVirtualList'\n\nimport { dividedClass, emptyClass, listSurfaceClass, loadingRowClass } from './grListStyles'\n\nexport interface GrListProps<TItem = unknown> {\n /**\n * Поверхность списка — вариант карточки под ним. `ghost` убирает рамку и\n * тень: список внутри уже существующей карточки не должен давать вторую.\n */\n variant?: GrCardVariant\n /** Показывать ли горизонтальные разделители между элементами (по умолчанию — да). */\n divided?: boolean\n /** Идёт загрузка: вместо пунктов — строки-скелетоны, контейнер помечен `aria-busy`. */\n loading?: boolean\n /** Сколько строк-заглушек показать при `loading`. */\n loadingRows?: number\n /**\n * Список пуст. По умолчанию определяется сам — по содержимому слота; проп\n * нужен там, где потребитель знает лучше (например, слот заполнен\n * заголовками групп, а данных в них нет).\n */\n empty?: boolean\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /**\n * Данные списка. С ними пункты рисует слот `#item`, а не слот по умолчанию, —\n * и только так список знает размер набора, то есть может его виртуализировать.\n */\n items?: TItem[]\n /** Ключ пункта для `v-for`: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Высота видимой части: контейнер становится скроллером. Число — пиксели. */\n maxHeight?: number | string\n /** Держать в DOM только окно вокруг вьюпорта. Требует `items` и `maxHeight`. */\n virtual?: boolean\n /** Оценка высоты пункта до замера. Уточняется по факту при первом рендере. */\n estimatedItemSize?: number\n}\n\nconst props = withDefaults(defineProps<GrListProps<T>>(), {\n // Дефолт живёт в `GrCard` (и его резолвере `GrConfigProvider`), поэтому здесь\n // остаётся `undefined` — иначе список перебивал бы настройку карточки.\n variant: undefined,\n divided: true,\n loading: false,\n loadingRows: 3,\n empty: undefined,\n emptyText: undefined,\n items: undefined,\n itemKey: undefined,\n maxHeight: undefined,\n virtual: false,\n // Строка обычной плотности с заголовком и описанием.\n estimatedItemSize: 56,\n})\n\nconst slots = useSlots()\nconst { t } = useGranularityTranslations()\n\nconst listEl = ref<HTMLElement | null>(null)\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.list.empty', 'Nothing here yet'))\n\nconst dataMode = computed(() => props.items !== undefined)\n\n// Пустоту видно по слоту: `v-for` по пустому массиву оставляет фрагмент без\n// узлов, `v-if` — комментарий, и ни то ни другое пунктом списка не является.\nconst isEmpty = computed(() => {\n if (props.empty !== undefined)\n return props.empty\n\n if (dataMode.value)\n return props.items!.length === 0\n\n return !hasMeaningfulSlotContent(slots.default?.() ?? [])\n})\n\nconst showItems = computed(() => !props.loading && !isEmpty.value)\n\n// Разделители нужны только между пунктами: в пустой и загрузочной ветках они\n// оставили бы висящую линию. Клип по радиусу карточки — безусловный.\nconst listClass = computed(() => [\n listSurfaceClass,\n props.divided && showItems.value ? dividedClass : '',\n].filter(Boolean).join(' '))\n\nconst loadingRowCount = computed(() => Math.max(1, Math.trunc(props.loadingRows)))\n\nconst virtualEnabled = computed(() => props.virtual && dataMode.value && props.maxHeight !== undefined)\n\nconst virtualizer = useVirtualList({\n container: listEl,\n count: () => (virtualEnabled.value && showItems.value ? props.items!.length : 0),\n // Замена массива сбрасывает замеры; append в тот же массив их сохраняет.\n source: () => props.items,\n itemSize: () => props.estimatedItemSize,\n // Первый рендер обязан совпасть с серверным: контейнера ещё нет, и окно\n // считается от объявленной высоты, а не от измеренной.\n viewportSize: () => (typeof props.maxHeight === 'number' ? props.maxHeight : undefined),\n})\n\n/** Окно к отрисовке: при выключенной виртуализации — весь набор. */\nconst renderedItems = computed(() => {\n const items = props.items ?? []\n if (!virtualEnabled.value) return items.map((item, index) => ({ item, index }))\n\n const { start, end } = virtualizer.range.value\n return items.slice(start, end).map((item, offset) => ({ item, index: start + offset }))\n})\n\nfunction keyOf(item: T, index: number): string | number {\n const key = props.itemKey\n if (typeof key === 'function') return key(item, index)\n if (typeof key === 'string') return (item as Record<string, unknown>)[key] as string | number\n\n return index\n}\n\n/**\n * ARIA-набор для пункта.\n *\n * Только в виртуальном режиме: в DOM тогда неполный набор, и без этих атрибутов\n * диктор объявил бы «7 из 20» на списке в пять тысяч. Когда набор в DOM целиком,\n * его размер и позицию браузер считает сам, а руками проставленные атрибуты\n * начинают расходиться с разметкой при любой фильтрации.\n */\nfunction ariaOf(index: number): Record<string, number> {\n if (!virtualEnabled.value) return {}\n\n return { 'aria-setsize': props.items!.length, 'aria-posinset': index + 1 }\n}\n\nconst maxHeightStyle = computed(() => {\n const value = props.maxHeight\n if (value == null) return undefined\n return typeof value === 'number' ? `${value}px` : value\n})\n\nconst listStyle = computed(() => {\n if (props.maxHeight === undefined) return undefined\n\n const scroller = { overflowY: 'auto' as const, maxHeight: maxHeightStyle.value }\n if (!virtualEnabled.value) return scroller\n\n return { ...scroller, ...virtualizer.spacerStyle.value }\n})\n\n/**\n * Замер отрисованных пунктов.\n *\n * Идёт по детям контейнера, а не по `ref` на элементе: пункт рисует слот\n * потребителя, привязать к нему `ref` неоткуда. В виртуальном режиме прямые\n * дети контейнера — ровно окно (распорки живут псевдоэлементами и детьми не\n * считаются), поэтому сопоставление по позиции однозначно.\n */\nfunction measureRendered(): void {\n if (!virtualEnabled.value) return\n // В состояниях loading/empty дети контейнера — скелетоны и заглушки: их\n // высоты записались бы в замеры под индексами будущих строк данных.\n if (!showItems.value) return\n\n const container = listEl.value\n if (!container) return\n\n const { start } = virtualizer.range.value\n const children = container.children\n for (let offset = 0; offset < children.length; offset++) {\n virtualizer.measure(start + offset, children[offset])\n }\n}\n\nonMounted(measureRendered)\nonUpdated(measureRendered)\n\nfunction scrollToIndex(index: number, align?: GrVirtualAlign): void {\n virtualizer.scrollToIndex(index, align)\n}\n\ndefineExpose({ scrollToIndex })\n\nif (__GR_DEV__) {\n watch(\n () => [props.virtual, dataMode.value, props.maxHeight] as const,\n ([virtual, hasItems, maxHeight]) => {\n if (!virtual) return\n\n if (!hasItems) {\n console.warn(\n '[granularity] GrList: `virtual` требует `items` — пункты из слота по умолчанию '\n + 'список не считает и резать окно ему не из чего.',\n )\n }\n else if (maxHeight === undefined) {\n console.warn(\n '[granularity] GrList: `virtual` требует `maxHeight` — без ограничения высоты '\n + 'вьюпорта нет, и в окно попадает весь набор.',\n )\n }\n },\n { immediate: true },\n )\n\n watch(\n () => [dataMode.value, Boolean(slots.item)] as const,\n ([hasItems, hasSlot]) => {\n if (hasItems && !hasSlot) {\n console.warn(\n '[granularity] GrList: с `items` пункты рисует слот `#item` — без него список пуст.',\n )\n }\n },\n { immediate: true },\n )\n\n onMounted(() => {\n if (!virtualEnabled.value) return\n\n const first = listEl.value?.firstElementChild\n if (first && !first.hasAttribute('aria-posinset')) {\n console.warn(\n '[granularity] GrList: при `virtual` пункты обязаны нести `aria-setsize`/`aria-posinset` — '\n + 'пробросьте `aria` из слота `#item`: <GrListItem v-bind=\"aria\" … />. Иначе диктор '\n + 'посчитает набор по отрисованному окну.',\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n data-gr-list\n role=\"list\"\n :class=\"listClass\"\n :style=\"listStyle\"\n :tabindex=\"maxHeight === undefined ? undefined : 0\"\n :data-gr-virtual=\"virtualEnabled ? '' : undefined\"\n :aria-busy=\"loading ? 'true' : undefined\"\n >\n <template v-if=\"loading\">\n <slot name=\"loading\">\n <div\n v-for=\"row in loadingRowCount\"\n :key=\"row\"\n data-gr-list-loading-row\n :class=\"loadingRowClass\"\n >\n <GrSkeleton />\n </div>\n </slot>\n </template>\n\n <div\n v-else-if=\"isEmpty\"\n data-gr-list-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <template v-else-if=\"dataMode\">\n <template\n v-for=\"entry in renderedItems\"\n :key=\"keyOf(entry.item, entry.index)\"\n >\n <slot\n name=\"item\"\n :item=\"entry.item\"\n :index=\"entry.index\"\n :aria=\"ariaOf(entry.index)\"\n />\n </template>\n </template>\n\n <slot v-else />\n </div>\n </GrCard>\n</template>\n\n<style scoped>\n/* Срезанное сверху и снизу держат псевдоэлементы контейнера: их не за что\n размонтировать, в дереве доступности их нет, а высота приезжает переменными\n в том же патче, что и пункты. Почему не отступы контейнера и не поля крайних\n пунктов — `docs/virtual-list.md`. */\n[data-gr-virtual]::before,\n[data-gr-virtual]::after {\n content: '';\n display: block;\n flex: none;\n}\n\n[data-gr-virtual]::before {\n height: var(--gr-virtual-before, 0px);\n}\n\n[data-gr-virtual]::after {\n height: var(--gr-virtual-after, 0px);\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, markRaw, useSlots, watch, type Component } from 'vue'\n\nimport {\n grListItemPaddingClass,\n itemDescriptionClass,\n itemDisabledClass,\n itemHoverClass,\n itemInteractiveClass,\n itemLayoutClass,\n itemTitleClass,\n type GrListItemDensity,\n} from './grListStyles'\n\nexport type { GrListItemDensity } from './grListStyles'\n\nexport interface GrListItemProps {\n /** Заголовок строки. Если передан слот `#title`, проп игнорируется. */\n title?: string\n /** Описание под заголовком. Если передан слот `#description`, проп игнорируется. */\n description?: string\n /** Плотность вертикальных отступов: `regular` — 12px, `compact` — 8px. */\n density?: GrListItemDensity\n /** Ссылка: строка становится `<a>`. */\n href?: string\n /** Свой корневой тег строки (`RouterLink`, `Link` от Inertia). Сильнее `href`. */\n as?: string | Component\n /** Кликабельная строка без ссылки — `<button>` с событием `click`. */\n clickable?: boolean\n /** Подсветка при наведении без интерактивности. */\n hoverable?: boolean\n disabled?: boolean\n}\n\nexport interface GrListItemEmits {\n (e: 'click', event: MouseEvent): void\n}\n\nconst props = withDefaults(defineProps<GrListItemProps>(), {\n title: undefined,\n description: undefined,\n density: 'regular',\n href: undefined,\n as: undefined,\n clickable: false,\n hoverable: false,\n disabled: false,\n})\n\nconst emit = defineEmits<GrListItemEmits>()\n\nconst slots = useSlots()\n\nconst hasTitle = computed(() => !!slots.title || !!props.title)\nconst hasDescription = computed(() => !!slots.description || !!props.description)\n\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href || props.clickable))\n\n/**\n * `role=\"listitem\"` остаётся на обёртке, а строка целиком — вложенный элемент:\n * `<a role=\"listitem\">` потерял бы роль ссылки, а интерактив снаружи разорвал бы\n * связку `list` → `listitem`. Обычная строка идёт по той же схеме, чтобы\n * разметка содержимого была написана один раз, а не по копии на ветку.\n */\nconst rowTag = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'div'\n\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n\n return props.href ? 'a' : 'button'\n})\n\nconst rowClass = computed(() => [\n itemLayoutClass,\n grListItemPaddingClass(props.density),\n isInteractive.value ? `${itemInteractiveClass} ${itemHoverClass}` : '',\n !isInteractive.value && props.hoverable && !props.disabled ? itemHoverClass : '',\n props.disabled ? itemDisabledClass : '',\n].filter(Boolean).join(' '))\n\nfunction onClick(event: MouseEvent): void {\n // `disabled` сюда попадает вместе с обычной строкой: и то и другое —\n // не-интерактив, а кликов не эмитит ни один.\n if (!isInteractive.value)\n return\n\n emit('click', event)\n}\n\n/** Теги, попадающие в таб-порядок сами. `<a>` — только со ссылкой. */\nfunction isFocusableTag(tag: string): boolean {\n return tag === 'button' || (tag === 'a' && !!props.href)\n}\n\nif (__GR_DEV__) {\n watch(\n () => [isInteractive.value, props.as] as const,\n ([interactive, as]) => {\n // Компонент из `as` (`RouterLink`, Inertia `Link`) рендерит `<a>`, но узнать\n // это до рендера нельзя — предупреждаем только по явно названному тегу.\n if (!interactive || typeof as !== 'string' || isFocusableTag(as)) return\n\n console.warn(\n `[granularity] GrListItem: as=\"${as}\" не попадает в таб-порядок — строка кликается `\n + 'мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (`button`, `a` со ссылкой), '\n + 'или компонент роутера.',\n )\n },\n { immediate: true },\n )\n}\n</script>\n\n<template>\n <div\n data-gr-list-item\n role=\"listitem\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n >\n <component\n :is=\"rowTag\"\n :data-gr-list-item-action=\"isInteractive ? '' : undefined\"\n :type=\"rowTag === 'button' ? 'button' : undefined\"\n :href=\"isInteractive ? href : undefined\"\n :class=\"rowClass\"\n @click=\"onClick\"\n >\n <div v-if=\"$slots.prefix\" class=\"shrink-0\">\n <slot name=\"prefix\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div v-if=\"hasTitle\" :class=\"itemTitleClass\">\n <slot name=\"title\">\n{{ title }}\n</slot>\n </div>\n <div\n v-if=\"hasDescription\"\n :class=\"itemDescriptionClass\"\n >\n <slot name=\"description\">\n{{ description }}\n</slot>\n </div>\n </div>\n <div v-if=\"$slots.default\" class=\"shrink-0\">\n <slot />\n </div>\n </component>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, markRaw, useSlots, watch, type Component } from 'vue'\n\nimport {\n grListItemPaddingClass,\n itemDescriptionClass,\n itemDisabledClass,\n itemHoverClass,\n itemInteractiveClass,\n itemLayoutClass,\n itemTitleClass,\n type GrListItemDensity,\n} from './grListStyles'\n\nexport type { GrListItemDensity } from './grListStyles'\n\nexport interface GrListItemProps {\n /** Заголовок строки. Если передан слот `#title`, проп игнорируется. */\n title?: string\n /** Описание под заголовком. Если передан слот `#description`, проп игнорируется. */\n description?: string\n /** Плотность вертикальных отступов: `regular` — 12px, `compact` — 8px. */\n density?: GrListItemDensity\n /** Ссылка: строка становится `<a>`. */\n href?: string\n /** Свой корневой тег строки (`RouterLink`, `Link` от Inertia). Сильнее `href`. */\n as?: string | Component\n /** Кликабельная строка без ссылки — `<button>` с событием `click`. */\n clickable?: boolean\n /** Подсветка при наведении без интерактивности. */\n hoverable?: boolean\n disabled?: boolean\n}\n\nexport interface GrListItemEmits {\n (e: 'click', event: MouseEvent): void\n}\n\nconst props = withDefaults(defineProps<GrListItemProps>(), {\n title: undefined,\n description: undefined,\n density: 'regular',\n href: undefined,\n as: undefined,\n clickable: false,\n hoverable: false,\n disabled: false,\n})\n\nconst emit = defineEmits<GrListItemEmits>()\n\nconst slots = useSlots()\n\nconst hasTitle = computed(() => !!slots.title || !!props.title)\nconst hasDescription = computed(() => !!slots.description || !!props.description)\n\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href || props.clickable))\n\n/**\n * `role=\"listitem\"` остаётся на обёртке, а строка целиком — вложенный элемент:\n * `<a role=\"listitem\">` потерял бы роль ссылки, а интерактив снаружи разорвал бы\n * связку `list` → `listitem`. Обычная строка идёт по той же схеме, чтобы\n * разметка содержимого была написана один раз, а не по копии на ветку.\n */\nconst rowTag = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'div'\n\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n\n return props.href ? 'a' : 'button'\n})\n\nconst rowClass = computed(() => [\n itemLayoutClass,\n grListItemPaddingClass(props.density),\n isInteractive.value ? `${itemInteractiveClass} ${itemHoverClass}` : '',\n !isInteractive.value && props.hoverable && !props.disabled ? itemHoverClass : '',\n props.disabled ? itemDisabledClass : '',\n].filter(Boolean).join(' '))\n\nfunction onClick(event: MouseEvent): void {\n // `disabled` сюда попадает вместе с обычной строкой: и то и другое —\n // не-интерактив, а кликов не эмитит ни один.\n if (!isInteractive.value)\n return\n\n emit('click', event)\n}\n\n/** Теги, попадающие в таб-порядок сами. `<a>` — только со ссылкой. */\nfunction isFocusableTag(tag: string): boolean {\n return tag === 'button' || (tag === 'a' && !!props.href)\n}\n\nif (__GR_DEV__) {\n watch(\n () => [isInteractive.value, props.as] as const,\n ([interactive, as]) => {\n // Компонент из `as` (`RouterLink`, Inertia `Link`) рендерит `<a>`, но узнать\n // это до рендера нельзя — предупреждаем только по явно названному тегу.\n if (!interactive || typeof as !== 'string' || isFocusableTag(as)) return\n\n console.warn(\n `[granularity] GrListItem: as=\"${as}\" не попадает в таб-порядок — строка кликается `\n + 'мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (`button`, `a` со ссылкой), '\n + 'или компонент роутера.',\n )\n },\n { immediate: true },\n )\n}\n</script>\n\n<template>\n <div\n data-gr-list-item\n role=\"listitem\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n >\n <component\n :is=\"rowTag\"\n :data-gr-list-item-action=\"isInteractive ? '' : undefined\"\n :type=\"rowTag === 'button' ? 'button' : undefined\"\n :href=\"isInteractive ? href : undefined\"\n :class=\"rowClass\"\n @click=\"onClick\"\n >\n <div v-if=\"$slots.prefix\" class=\"shrink-0\">\n <slot name=\"prefix\" />\n </div>\n <div class=\"min-w-0 flex-1\">\n <div v-if=\"hasTitle\" :class=\"itemTitleClass\">\n <slot name=\"title\">\n{{ title }}\n</slot>\n </div>\n <div\n v-if=\"hasDescription\"\n :class=\"itemDescriptionClass\"\n >\n <slot name=\"description\">\n{{ description }}\n</slot>\n </div>\n </div>\n <div v-if=\"$slots.default\" class=\"shrink-0\">\n <slot />\n </div>\n </component>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { n as grListSafelist, t as grListConfig } from "../../chunks/config-CKULiuvc.js";
|
|
2
|
-
import { n as GrList_default, t as GrListItem_default } from "./chunks/GrList-
|
|
2
|
+
import { n as GrList_default, t as GrListItem_default } from "./chunks/GrList-B5bclmHV.js";
|
|
3
3
|
export { GrList_default as GrList, GrList_default as default, GrListItem_default as GrListItem, grListConfig, grListSafelist };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
[data-gr-virtual][data-v-
|
|
1
|
+
[data-gr-virtual][data-v-8fdef7fd]:before,[data-gr-virtual][data-v-8fdef7fd]:after{content:"";flex:none;display:block}[data-gr-virtual][data-v-8fdef7fd]:before{height:var(--gr-virtual-before,0px)}[data-gr-virtual][data-v-8fdef7fd]:after{height:var(--gr-virtual-after,0px)}
|
|
@@ -6,7 +6,7 @@ import { computed, createBlock, createCommentVNode, createElementBlock, createEl
|
|
|
6
6
|
//#region src/components/GrLoading/GrLoading.vue?vue&type=script&setup=true&lang.ts
|
|
7
7
|
var _hoisted_1 = {
|
|
8
8
|
"data-gr-loading-panel": "",
|
|
9
|
-
class: "flex flex-col items-center justify-center gap-2 text-center rounded-[var(--gr-radius-md)] bg-[var(--gr-bg)]/55 px-5 py-4 shadow-
|
|
9
|
+
class: "flex flex-col items-center justify-center gap-2 text-center rounded-[var(--gr-radius-md)] bg-[var(--gr-bg)]/55 px-5 py-4 shadow-[var(--gr-shadow-2)]"
|
|
10
10
|
};
|
|
11
11
|
var _hoisted_2 = {
|
|
12
12
|
key: 0,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrLoading-DeBFTvFX.js","names":[],"sources":["../../../../src/components/GrLoading/GrLoading.vue","../../../../src/components/GrLoading/GrLoading.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, type Component } from 'vue'\n\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport type { GrIconSize, GrIconTone } from '../GrIcon/grIconStyles'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { grLoadingRootClass } from './grLoadingStyles'\n\n/**\n * Пропы {@link GrLoading}.\n *\n * Оверлей загрузки: спиннер с подписью поверх соседнего контента. `fullscreen`\n * накрывает весь экран, иначе оверлей ложится на ближайшего позиционированного\n * предка.\n */\nexport interface GrLoadingProps {\n /** Подпись под спиннером. Пустая строка убирает её совсем. По умолчанию — из локали. */\n text?: string\n /** Свой компонент спиннера вместо иконки по умолчанию. */\n spinner?: Component\n /** Дополнительные классы обёртки спиннера. */\n spinnerClass?: string\n /** Размер спиннера: шкала пакета либо произвольный в пикселях. */\n spinnerSize?: GrIconSize | number\n /** Тон спиннера из палитры. */\n spinnerTone?: GrIconTone\n /** Вращение спиннера. По умолчанию включено. */\n animated?: boolean\n /** Свой `background-color`. Задан — дефолтный скрим `--gr-overlay-bg` снимается. */\n background?: string\n /** Накрыть весь экран (`position: fixed`) вместо ближайшего позиционированного предка. */\n fullscreen?: boolean\n /** Имя CSS-переменной слоя — escape-hatch мимо `--gr-z-loading`. */\n zIndexVar?: string\n /** Задержка показа в миллисекундах: короткая загрузка не мигает оверлеем. */\n delay?: number\n /** Дополнительные классы корня оверлея. */\n customClass?: string\n}\n\nexport interface GrLoadingEmits {\n /**\n * Оверлей появился на экране — сразу или по истечении `delay`. По этому\n * событию директива блокирует контент под оверлеем: до показа блокировать\n * нечего, иначе быстрый запрос молча «замораживал» бы форму.\n */\n (e: 'show'): void\n}\n\nconst props = withDefaults(\n defineProps<GrLoadingProps>(),\n {\n text: undefined,\n spinner: undefined,\n spinnerClass: undefined,\n // 28px не входит в шкалу иконок (14–20): спиннер загрузки — самостоятельный\n // акцент панели, а не иконка рядом с текстом.\n spinnerSize: 28,\n spinnerTone: 'neutral',\n animated: true,\n background: undefined,\n fullscreen: false,\n zIndexVar: undefined,\n delay: 0,\n customClass: undefined,\n },\n)\n\nconst emit = defineEmits<GrLoadingEmits>()\n\ndefineSlots<{\n /** Содержимое панели целиком вместо спиннера с подписью. */\n default?: () => any\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst Spinner = computed(() => props.spinner ?? IconLoader)\n\nconst displayText = computed(() => props.text ?? t('gr.loading.defaultText', 'Loading...'))\n\nconst rootClass = computed(() => {\n return grLoadingRootClass({\n fullscreen: props.fullscreen,\n hasBackground: props.background !== undefined,\n customClass: props.customClass,\n })\n})\n\nconst rootStyle = computed(() => {\n return {\n backgroundColor: props.background,\n zIndex: props.zIndexVar ? `var(${props.zIndexVar})` : undefined,\n } as Record<string, string | undefined>\n})\n\n// Оверлей на быстром запросе успевает мигнуть и раздражает сильнее, чем его\n// отсутствие. Отсчёт начинается с монтирования: компонент создаётся ровно в\n// момент старта загрузки — и в `v-if`, и в директиве.\nconst visible = ref(props.delay <= 0)\nlet delayTimer: ReturnType<typeof setTimeout> | undefined\n\nonMounted(() => {\n if (visible.value) {\n emit('show')\n return\n }\n\n delayTimer = setTimeout(() => {\n visible.value = true\n emit('show')\n }, props.delay)\n})\n\nonBeforeUnmount(() => {\n clearTimeout(delayTimer)\n})\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n data-gr-loading\n class=\"flex items-center justify-center cursor-wait select-none pointer-events-auto\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n data-gr-loading-panel\n class=\"flex flex-col items-center justify-center gap-2 text-center rounded-[var(--gr-radius-md)] bg-[var(--gr-bg)]/55 px-5 py-4 shadow-[var(--gr-shadow-2)]\"\n >\n <slot>\n <GrIcon\n data-gr-loading-spinner\n :size=\"spinnerSize\"\n :tone=\"spinnerTone\"\n :spin=\"animated\"\n :class=\"spinnerClass\"\n >\n <component :is=\"Spinner\" />\n </GrIcon>\n <div v-if=\"displayText\" data-gr-loading-text class=\"text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] text-[var(--gr-muted-fg)]\">\n {{ displayText }}\n </div>\n </slot>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref, type Component } from 'vue'\n\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport type { GrIconSize, GrIconTone } from '../GrIcon/grIconStyles'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { grLoadingRootClass } from './grLoadingStyles'\n\n/**\n * Пропы {@link GrLoading}.\n *\n * Оверлей загрузки: спиннер с подписью поверх соседнего контента. `fullscreen`\n * накрывает весь экран, иначе оверлей ложится на ближайшего позиционированного\n * предка.\n */\nexport interface GrLoadingProps {\n /** Подпись под спиннером. Пустая строка убирает её совсем. По умолчанию — из локали. */\n text?: string\n /** Свой компонент спиннера вместо иконки по умолчанию. */\n spinner?: Component\n /** Дополнительные классы обёртки спиннера. */\n spinnerClass?: string\n /** Размер спиннера: шкала пакета либо произвольный в пикселях. */\n spinnerSize?: GrIconSize | number\n /** Тон спиннера из палитры. */\n spinnerTone?: GrIconTone\n /** Вращение спиннера. По умолчанию включено. */\n animated?: boolean\n /** Свой `background-color`. Задан — дефолтный скрим `--gr-overlay-bg` снимается. */\n background?: string\n /** Накрыть весь экран (`position: fixed`) вместо ближайшего позиционированного предка. */\n fullscreen?: boolean\n /** Имя CSS-переменной слоя — escape-hatch мимо `--gr-z-loading`. */\n zIndexVar?: string\n /** Задержка показа в миллисекундах: короткая загрузка не мигает оверлеем. */\n delay?: number\n /** Дополнительные классы корня оверлея. */\n customClass?: string\n}\n\nexport interface GrLoadingEmits {\n /**\n * Оверлей появился на экране — сразу или по истечении `delay`. По этому\n * событию директива блокирует контент под оверлеем: до показа блокировать\n * нечего, иначе быстрый запрос молча «замораживал» бы форму.\n */\n (e: 'show'): void\n}\n\nconst props = withDefaults(\n defineProps<GrLoadingProps>(),\n {\n text: undefined,\n spinner: undefined,\n spinnerClass: undefined,\n // 28px не входит в шкалу иконок (14–20): спиннер загрузки — самостоятельный\n // акцент панели, а не иконка рядом с текстом.\n spinnerSize: 28,\n spinnerTone: 'neutral',\n animated: true,\n background: undefined,\n fullscreen: false,\n zIndexVar: undefined,\n delay: 0,\n customClass: undefined,\n },\n)\n\nconst emit = defineEmits<GrLoadingEmits>()\n\ndefineSlots<{\n /** Содержимое панели целиком вместо спиннера с подписью. */\n default?: () => any\n}>()\n\nconst { t } = useGranularityTranslations()\n\nconst Spinner = computed(() => props.spinner ?? IconLoader)\n\nconst displayText = computed(() => props.text ?? t('gr.loading.defaultText', 'Loading...'))\n\nconst rootClass = computed(() => {\n return grLoadingRootClass({\n fullscreen: props.fullscreen,\n hasBackground: props.background !== undefined,\n customClass: props.customClass,\n })\n})\n\nconst rootStyle = computed(() => {\n return {\n backgroundColor: props.background,\n zIndex: props.zIndexVar ? `var(${props.zIndexVar})` : undefined,\n } as Record<string, string | undefined>\n})\n\n// Оверлей на быстром запросе успевает мигнуть и раздражает сильнее, чем его\n// отсутствие. Отсчёт начинается с монтирования: компонент создаётся ровно в\n// момент старта загрузки — и в `v-if`, и в директиве.\nconst visible = ref(props.delay <= 0)\nlet delayTimer: ReturnType<typeof setTimeout> | undefined\n\nonMounted(() => {\n if (visible.value) {\n emit('show')\n return\n }\n\n delayTimer = setTimeout(() => {\n visible.value = true\n emit('show')\n }, props.delay)\n})\n\nonBeforeUnmount(() => {\n clearTimeout(delayTimer)\n})\n</script>\n\n<template>\n <div\n v-if=\"visible\"\n data-gr-loading\n class=\"flex items-center justify-center cursor-wait select-none pointer-events-auto\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n data-gr-loading-panel\n class=\"flex flex-col items-center justify-center gap-2 text-center rounded-[var(--gr-radius-md)] bg-[var(--gr-bg)]/55 px-5 py-4 shadow-[var(--gr-shadow-2)]\"\n >\n <slot>\n <GrIcon\n data-gr-loading-spinner\n :size=\"spinnerSize\"\n :tone=\"spinnerTone\"\n :spin=\"animated\"\n :class=\"spinnerClass\"\n >\n <component :is=\"Spinner\" />\n </GrIcon>\n <div v-if=\"displayText\" data-gr-loading-text class=\"text-[length:var(--gr-text-sm)] leading-[var(--gr-leading-sm)] text-[var(--gr-muted-fg)]\">\n {{ displayText }}\n </div>\n </slot>\n </div>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmDA,MAAM,QAAQ;EAmBd,MAAM,OAAO;EAOb,MAAM,EAAE,MAAM,2BAA2B;EAEzC,MAAM,UAAU,eAAe,MAAM,WAAW,qBAAU;EAE1D,MAAM,cAAc,eAAe,MAAM,QAAQ,EAAE,0BAA0B,YAAY,CAAC;EAE1F,MAAM,YAAY,eAAe;GAC/B,OAAO,mBAAmB;IACxB,YAAY,MAAM;IAClB,eAAe,MAAM,eAAe,KAAA;IACpC,aAAa,MAAM;GACrB,CAAC;EACH,CAAC;EAED,MAAM,YAAY,eAAe;GAC/B,OAAO;IACL,iBAAiB,MAAM;IACvB,QAAQ,MAAM,YAAY,OAAO,MAAM,UAAU,KAAK,KAAA;GACxD;EACF,CAAC;EAKD,MAAM,UAAU,IAAI,MAAM,SAAS,CAAC;EACpC,IAAI;EAEJ,gBAAgB;GACd,IAAI,QAAQ,OAAO;IACjB,KAAK,MAAM;IACX;GACF;GAEA,aAAa,iBAAiB;IAC5B,QAAQ,QAAQ;IAChB,KAAK,MAAM;GACb,GAAG,MAAM,KAAK;EAChB,CAAC;EAED,sBAAsB;GACpB,aAAa,UAAU;EACzB,CAAC;;UAKS,QAAA,SAAA,UAAA,GADR,mBA4BM,OAAA;;IA1BJ,mBAAA;IACA,OAAK,eAAA,CAAC,gFACE,UAAA,KAAS,CAAA;IAChB,OAAK,eAAE,UAAA,KAAS;IACjB,MAAK;IACL,aAAU;OAEV,mBAkBM,OAlBN,YAkBM,CAdJ,WAaO,KAAA,QAAA,WAAA,CAAA,SAAA,CAZL,YAQS,gBAAA;IAPP,2BAAA;IACC,MAAM,QAAA;IACN,MAAM,QAAA;IACN,MAAM,QAAA;IACN,OAAK,eAAE,QAAA,YAAY;;2BAEO,EAAA,UAAA,GAA3B,YAA2B,wBAAX,QAAA,KAAO,CAAA,EAAA,CAAA;;;;;;;OAEd,YAAA,SAAA,UAAA,GAAX,mBAEM,OAFN,YAEM,gBADD,YAAA,KAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrLoading_default } from "./chunks/GrLoading-
|
|
1
|
+
import { t as GrLoading_default } from "./chunks/GrLoading-DeBFTvFX.js";
|
|
2
2
|
import { n as grLoadingSafelist, t as grLoadingConfig } from "../../chunks/config-CjnbevbD.js";
|
|
3
3
|
export { GrLoading_default as GrLoading, GrLoading_default as default, grLoadingConfig, grLoadingSafelist };
|
|
@@ -172,13 +172,13 @@ var GrModal_default = /* @__PURE__ */ defineComponent({
|
|
|
172
172
|
let missingNameWarned = false;
|
|
173
173
|
function warnMissingAccessibleName() {
|
|
174
174
|
if (missingNameWarned) return;
|
|
175
|
-
if (process.env.NODE_ENV
|
|
175
|
+
if (!(typeof process !== "undefined" && process.env.NODE_ENV !== "production")) return;
|
|
176
176
|
if (slots.title || props.ariaLabel) return;
|
|
177
177
|
missingNameWarned = true;
|
|
178
178
|
console.warn("[GrModal] Окно без доступного имени: передайте слот #title или проп ariaLabel. Пока используется обобщённое имя из локали.");
|
|
179
179
|
}
|
|
180
180
|
function warnDuplicateTitle() {
|
|
181
|
-
if (process.env.NODE_ENV
|
|
181
|
+
if (!(typeof process !== "undefined" && process.env.NODE_ENV !== "production")) return;
|
|
182
182
|
if (titleClaims <= 1) return;
|
|
183
183
|
console.warn("[GrModal] На окно заявлено несколько заголовков — имя берётся от первого. Оставьте один: слот #title у GrModal либо заголовок GrDialog.");
|
|
184
184
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrModal-CCoWZuIl.js","names":["$slots"],"sources":["../../../../src/components/GrModal/context.ts","../../../../src/components/GrModal/GrModal.vue","../../../../src/components/GrModal/GrModal.vue"],"sourcesContent":["import type { InjectionKey } from 'vue'\nimport { inject, onScopeDispose, provide, watch } from 'vue'\n\nexport interface GrModalContext {\n /** Id, который обязан оказаться на элементе заголовка окна. */\n titleId: string\n /** Id, который обязан оказаться на элементе описания окна. */\n descriptionId: string\n /** «Заголовок отрисован» — окно ставит `aria-labelledby`. Возвращает отказ от заявки. */\n claimTitle: () => () => void\n /** То же для описания и `aria-describedby`. */\n claimDescription: () => () => void\n}\n\nexport const GR_MODAL_CONTEXT_KEY: InjectionKey<GrModalContext> = Symbol('GrModalContext')\n\nexport function provideGrModalContext(context: GrModalContext): void {\n provide(GR_MODAL_CONTEXT_KEY, context)\n}\n\n/**\n * Контекст окна для его частей.\n *\n * Именно контекст, а не проп: заголовок рисует `GrDialogHeader` — внук\n * `GrModal` через слот, — а `aria-labelledby` стоит на корне окна.\n *\n * **Id принадлежит окну, а не заголовку.** Заголовок только сообщает, что\n * отрисовался. Обратный порядок (id рождается у потомка и уезжает наверх)\n * означает мутацию состояния окна во время его собственного патча: Vue уходит\n * в рекурсивный ре-рендер и монтирует заголовок заново, пока не упрётся в\n * лимит. Стабильный id этого класса ошибок не допускает по построению.\n *\n * `null` вне окна — законное состояние: `GrDialogHeader` используют и отдельно.\n */\nexport function useGrModalContext(): GrModalContext | null {\n return inject(GR_MODAL_CONTEXT_KEY, null)\n}\n\n/**\n * Объявляет элемент заголовком окна, пока `active` истинно.\n *\n * Возвращает id для элемента — или `undefined` вне окна: тогда заголовок просто\n * рисуется без связи.\n */\nexport function useGrModalTitle(active: () => boolean): { titleId: string | undefined } {\n const context = useGrModalContext()\n if (!context) return { titleId: undefined }\n\n let release: (() => void) | undefined\n\n watch(\n active,\n (isActive) => {\n release?.()\n release = isActive ? context.claimTitle() : undefined\n },\n // `post`: заявка меняет состояние окна, а заголовок — его потомок, и в\n // `pre`-фазе мутация пришлась бы на патч самого окна.\n { immediate: true, flush: 'post' },\n )\n\n onScopeDispose(() => release?.())\n\n return { titleId: context.titleId }\n}\n","<script setup lang=\"ts\">\n/**\n * GrModal — GR-примитив модального окна.\n *\n * Публичный API:\n * - `modelValue` (v-model) — открыто/закрыто;\n * - `size` — размер панели (`sm | md | lg | xl | full`);\n * - `scrollBehavior` — кто скроллится при длинном содержимом;\n * - `closeOnBackdrop` (default: true) — закрывать при клике по оверлею;\n * - `closeOnEsc` (default: true) — закрывать по Esc;\n * - слоты `#title` / `#description` — получают id и связываются с окном через\n * `aria-labelledby` / `aria-describedby`;\n * - слоты `#header` / `#footer` — пустые layout-области вне скроллящегося тела:\n * при `inside` они остаются на месте. Своей разметки примитив в них не\n * добавляет — шапку и подвал рисует `GrDialog`.\n *\n * A11y: **имя у модального слоя обязательно**. Имя берётся из `#title` (или из\n * заголовка `GrDialog`, который заявляет себя тем же контекстом), иначе из\n * пропа `ariaLabel`, иначе — из i18n, чтобы безымянного окна не получилось ни\n * при каком употреблении (axe: `aria-dialog-name`).\n *\n * Механика слоя — `useModalOverlay`: стек (Esc верхнему, `inert` нижним окнам,\n * возврат фокуса), ловушка фокуса, блокировка скролла, портал и присутствие в\n * DOM до конца leave-анимации. Инварианты этой сборки описаны там же.\n */\nimport { useGrComponentProp } from '../GrConfigProvider/context'\nimport { computed, ref, useId, useSlots, watch } from 'vue'\n\nimport { useModalOverlay } from '../../composables/internal/useModalOverlay'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { provideGrModalContext } from './context'\n\nimport {\n type GrModalScrollBehavior,\n type GrModalSize,\n getGrModalPanelClass,\n getGrModalShellClass,\n layoutByScroll,\n overlay as overlayClass,\n overlayTransition,\n panelBodyScrollClass,\n panelSectionClass,\n panelTransition,\n root as rootClass,\n} from './grModalStyles'\n\nexport type { GrModalScrollBehavior }\n\nexport interface GrModalProps {\n modelValue: boolean\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n size?: GrModalSize\n /**\n * Кто скроллится при длинном содержимом: весь оверлей (`outside`) или сама\n * панель (`inside` — окно остаётся на месте, шапка и подвал на виду).\n */\n scrollBehavior?: GrModalScrollBehavior\n /**\n * Доступное имя окна, когда заголовка в слоте `#title` нет. Слот сильнее:\n * при нём имя даёт `aria-labelledby`.\n */\n ariaLabel?: string\n /**\n * Элемент, получающий фокус при открытии. По умолчанию — сама панель: она\n * фокусируема программно (`tabindex=\"-1\"`), диктор объявляет окно целиком, а\n * первый Tab приводит в начало содержимого.\n */\n initialFocus?: HTMLElement | null\n}\n\nexport interface GrModalEmits {\n (e: 'update:modelValue', value: boolean): void\n /** Окно открылось и анимация закончилась. */\n (e: 'opened'): void\n /** Окно закрылось и анимация закончилась — содержимое можно размонтировать. */\n (e: 'closed'): void\n}\n\nimport './defaults'\n\nconst props = withDefaults(defineProps<GrModalProps>(), {\n closeOnBackdrop: true,\n closeOnEsc: true,\n // Дефолт `size` живёт в резолвере ниже, а не здесь: Vue подставил бы его\n // до того, как компонент заглянет в `GrConfigProvider`.\n size: undefined,\n scrollBehavior: 'outside',\n ariaLabel: undefined,\n initialFocus: null,\n})\n\nconst emit = defineEmits<GrModalEmits>()\n\ndefineSlots<{\n default?: () => any\n title?: () => any\n description?: () => any\n /** Закреплённая шапка: при `inside` остаётся на месте, скроллится только тело. */\n header?: () => any\n /** Закреплённый подвал: там же, где и шапка, — вне скроллящегося тела. */\n footer?: () => any\n}>()\n\nconst slots = useSlots()\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentProp('GrModal', 'size', () => props.size, 'md')\n\nconst panelClass = computed(() => getGrModalPanelClass(resolvedSize.value, props.scrollBehavior))\nconst shellClass = computed(() => getGrModalShellClass(resolvedSize.value, props.scrollBehavior))\nconst layoutClass = computed(() => layoutByScroll[props.scrollBehavior])\n\n// Тело скроллится только при `inside`, и тогда же обязано попадать в таб-порядок:\n// длинное содержимое без единого фокусируемого элемента иначе недостижимо с\n// клавиатуры (axe: `scrollable-region-focusable`).\nconst isBodyScrollable = computed(() => props.scrollBehavior === 'inside')\n\n// Панель фокусируема программно (`tabindex=\"-1\"`) — это и приёмник фокуса по\n// умолчанию, и запасной, когда фокусировать внутри нечего.\nconst panelRef = ref<HTMLElement | null>(null)\n\nconst { t } = useGranularityTranslations()\n\n// ————— Имя и описание окна.\n// Id принадлежат окну и раздаются вниз контекстом: заголовок рисует либо слот\n// `#title`, либо `GrDialogHeader` глубже по дереву, и оба лишь заявляют, что\n// отрисовались. Обратное владение (id рождается у заголовка) мутировало бы\n// состояние окна во время его же патча — см. `context.ts`.\nconst titleId = useId()\nconst descriptionId = useId()\n\nconst hasTitle = ref(false)\nconst hasDescription = ref(false)\n\n// Счётчики намеренно не реактивные: наружу торчит только «заголовок есть».\n// Пересоздание содержимого слота меняет счётчик, но не флаг, и лишнего рендера\n// окна не случается.\nlet titleClaims = 0\nlet descriptionClaims = 0\n\nfunction claim(add: () => void, remove: () => void): () => void {\n add()\n let released = false\n return () => {\n if (released) return\n released = true\n remove()\n }\n}\n\nfunction claimTitle(): () => void {\n return claim(\n () => { titleClaims += 1; hasTitle.value = true },\n () => { titleClaims -= 1; hasTitle.value = titleClaims > 0 },\n )\n}\n\nfunction claimDescription(): () => void {\n return claim(\n () => { descriptionClaims += 1; hasDescription.value = true },\n () => { descriptionClaims -= 1; hasDescription.value = descriptionClaims > 0 },\n )\n}\n\nprovideGrModalContext({ titleId, descriptionId, claimTitle, claimDescription })\n\n// Собственные слоты заявляются тем же путём, что и заголовок `GrDialog`.\nlet releaseOwnTitle: (() => void) | undefined\nlet releaseOwnDescription: (() => void) | undefined\n\nwatch(\n () => Boolean(slots.title),\n (has) => {\n releaseOwnTitle?.()\n releaseOwnTitle = has ? claimTitle() : undefined\n },\n { immediate: true },\n)\n\nwatch(\n () => Boolean(slots.description),\n (has) => {\n releaseOwnDescription?.()\n releaseOwnDescription = has ? claimDescription() : undefined\n },\n { immediate: true },\n)\n\nconst labelledBy = computed(() => (hasTitle.value ? titleId : undefined))\nconst describedBy = computed(() => (hasDescription.value ? descriptionId : undefined))\n\n// Собственный `aria-label` ставим только в отсутствие заголовка: при обоих\n// атрибутах `aria-labelledby` всё равно сильнее, и второе имя было бы шумом.\nconst ariaLabelAttr = computed(() => {\n if (labelledBy.value) return undefined\n return props.ariaLabel || t('gr.modal.title', 'Dialog')\n})\n\n// Обобщённое имя из локали спасает от безымянного окна, но осмысленное имя\n// знает только автор. Предупреждаем при первом открытии, а не при монтировании:\n// закрытая модалка ещё ничего не нарушила, и ругаться на неё рано.\nlet missingNameWarned = false\n\nfunction warnMissingAccessibleName(): void {\n if (missingNameWarned) return\n if (!__GR_DEV__) return\n if (slots.title || props.ariaLabel) return\n\n missingNameWarned = true\n console.warn(\n '[GrModal] Окно без доступного имени: передайте слот #title или проп ariaLabel. '\n + 'Пока используется обобщённое имя из локали.',\n )\n}\n\n// Два заголовка на окно — это разъехавшийся `aria-labelledby`: диктор прочитает\n// первый, а автор будет уверен во втором.\nfunction warnDuplicateTitle(): void {\n if (!__GR_DEV__) return\n if (titleClaims <= 1) return\n\n console.warn(\n '[GrModal] На окно заявлено несколько заголовков — имя берётся от первого. '\n + 'Оставьте один: слот #title у GrModal либо заголовок GrDialog.',\n )\n}\n\nfunction open(): void {\n emit('update:modelValue', true)\n}\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n\nfunction toggle(): void {\n emit('update:modelValue', !props.modelValue)\n}\n\n// ————— Модальный слой целиком: стек, ловушка фокуса, `inert`, скролл-лок,\n// портал, присутствие в DOM и guard подложки.\nconst {\n rootEl,\n isMounted,\n isVisible,\n inertAttr,\n portalTarget,\n teleportEnabled,\n themeAttrs,\n onPanelAfterLeave: releasePresence,\n backdrop,\n} = useModalOverlay(computed(() => props.modelValue), close, {\n panel: panelRef,\n closeOnEscape: () => props.closeOnEsc,\n closeOnBackdrop: () => props.closeOnBackdrop,\n // Окно фокусирует саму панель, когда внутри фокусировать нечего и когда\n // потребитель не указал цель явно.\n initialFocus: () => props.initialFocus ?? panelRef.value,\n})\n\nfunction onPanelAfterLeave(): void {\n releasePresence()\n emit('closed')\n}\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!value) return\n warnMissingAccessibleName()\n warnDuplicateTitle()\n },\n { immediate: true },\n)\n\ndefineExpose({ open, close, toggle })\n</script>\n\n<template>\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <div\n v-if=\"teleportEnabled && isMounted\"\n ref=\"rootEl\"\n data-gr-overlay-root\n v-bind=\"themeAttrs\"\n role=\"dialog\"\n aria-modal=\"true\"\n :class=\"rootClass\"\n :inert=\"inertAttr\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-label=\"ariaLabelAttr\"\n >\n <div :class=\"shellClass\">\n <div :class=\"layoutClass\">\n <Transition\n appear\n :enter-active-class=\"overlayTransition.enter\"\n :enter-from-class=\"overlayTransition.enterFrom\"\n :enter-to-class=\"overlayTransition.enterTo\"\n :leave-active-class=\"overlayTransition.leave\"\n :leave-from-class=\"overlayTransition.leaveFrom\"\n :leave-to-class=\"overlayTransition.leaveTo\"\n >\n <div\n v-if=\"isVisible\"\n data-gr-modal-overlay\n :class=\"overlayClass\"\n aria-hidden=\"true\"\n v-on=\"backdrop\"\n />\n </Transition>\n\n <!-- `opened`/`closed` вешаем на транзишн панели, а не на корневой:\n анимация живёт здесь, и только её конец что-то значит для\n потребителя, который ждёт момента для размонтирования. -->\n <Transition\n appear\n :enter-active-class=\"panelTransition.enter\"\n :enter-from-class=\"panelTransition.enterFrom\"\n :enter-to-class=\"panelTransition.enterTo\"\n :leave-active-class=\"panelTransition.leave\"\n :leave-from-class=\"panelTransition.leaveFrom\"\n :leave-to-class=\"panelTransition.leaveTo\"\n @after-enter=\"emit('opened')\"\n @after-leave=\"onPanelAfterLeave\"\n >\n <div\n v-if=\"isVisible\"\n ref=\"panelRef\"\n data-gr-modal-panel\n tabindex=\"-1\"\n :class=\"panelClass\"\n >\n <div v-if=\"$slots.title\" :id=\"titleId\" data-gr-modal-title>\n <slot name=\"title\" />\n </div>\n <div v-if=\"$slots.description\" :id=\"descriptionId\" data-gr-modal-description>\n <slot name=\"description\" />\n </div>\n\n <div v-if=\"$slots.header\" data-gr-modal-header :class=\"panelSectionClass\">\n <slot name=\"header\" />\n </div>\n\n <!-- При `inside` тело едет отдельно от шапки и подвала: иначе\n режим ничем не отличался бы от скролла всей панели. -->\n <div\n v-if=\"isBodyScrollable\"\n data-gr-modal-body\n tabindex=\"0\"\n :class=\"panelBodyScrollClass\"\n >\n <slot />\n </div>\n <slot v-else />\n\n <div v-if=\"$slots.footer\" data-gr-modal-footer :class=\"panelSectionClass\">\n <slot name=\"footer\" />\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n </teleport>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrModal — GR-примитив модального окна.\n *\n * Публичный API:\n * - `modelValue` (v-model) — открыто/закрыто;\n * - `size` — размер панели (`sm | md | lg | xl | full`);\n * - `scrollBehavior` — кто скроллится при длинном содержимом;\n * - `closeOnBackdrop` (default: true) — закрывать при клике по оверлею;\n * - `closeOnEsc` (default: true) — закрывать по Esc;\n * - слоты `#title` / `#description` — получают id и связываются с окном через\n * `aria-labelledby` / `aria-describedby`;\n * - слоты `#header` / `#footer` — пустые layout-области вне скроллящегося тела:\n * при `inside` они остаются на месте. Своей разметки примитив в них не\n * добавляет — шапку и подвал рисует `GrDialog`.\n *\n * A11y: **имя у модального слоя обязательно**. Имя берётся из `#title` (или из\n * заголовка `GrDialog`, который заявляет себя тем же контекстом), иначе из\n * пропа `ariaLabel`, иначе — из i18n, чтобы безымянного окна не получилось ни\n * при каком употреблении (axe: `aria-dialog-name`).\n *\n * Механика слоя — `useModalOverlay`: стек (Esc верхнему, `inert` нижним окнам,\n * возврат фокуса), ловушка фокуса, блокировка скролла, портал и присутствие в\n * DOM до конца leave-анимации. Инварианты этой сборки описаны там же.\n */\nimport { useGrComponentProp } from '../GrConfigProvider/context'\nimport { computed, ref, useId, useSlots, watch } from 'vue'\n\nimport { useModalOverlay } from '../../composables/internal/useModalOverlay'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { provideGrModalContext } from './context'\n\nimport {\n type GrModalScrollBehavior,\n type GrModalSize,\n getGrModalPanelClass,\n getGrModalShellClass,\n layoutByScroll,\n overlay as overlayClass,\n overlayTransition,\n panelBodyScrollClass,\n panelSectionClass,\n panelTransition,\n root as rootClass,\n} from './grModalStyles'\n\nexport type { GrModalScrollBehavior }\n\nexport interface GrModalProps {\n modelValue: boolean\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n size?: GrModalSize\n /**\n * Кто скроллится при длинном содержимом: весь оверлей (`outside`) или сама\n * панель (`inside` — окно остаётся на месте, шапка и подвал на виду).\n */\n scrollBehavior?: GrModalScrollBehavior\n /**\n * Доступное имя окна, когда заголовка в слоте `#title` нет. Слот сильнее:\n * при нём имя даёт `aria-labelledby`.\n */\n ariaLabel?: string\n /**\n * Элемент, получающий фокус при открытии. По умолчанию — сама панель: она\n * фокусируема программно (`tabindex=\"-1\"`), диктор объявляет окно целиком, а\n * первый Tab приводит в начало содержимого.\n */\n initialFocus?: HTMLElement | null\n}\n\nexport interface GrModalEmits {\n (e: 'update:modelValue', value: boolean): void\n /** Окно открылось и анимация закончилась. */\n (e: 'opened'): void\n /** Окно закрылось и анимация закончилась — содержимое можно размонтировать. */\n (e: 'closed'): void\n}\n\nimport './defaults'\n\nconst props = withDefaults(defineProps<GrModalProps>(), {\n closeOnBackdrop: true,\n closeOnEsc: true,\n // Дефолт `size` живёт в резолвере ниже, а не здесь: Vue подставил бы его\n // до того, как компонент заглянет в `GrConfigProvider`.\n size: undefined,\n scrollBehavior: 'outside',\n ariaLabel: undefined,\n initialFocus: null,\n})\n\nconst emit = defineEmits<GrModalEmits>()\n\ndefineSlots<{\n default?: () => any\n title?: () => any\n description?: () => any\n /** Закреплённая шапка: при `inside` остаётся на месте, скроллится только тело. */\n header?: () => any\n /** Закреплённый подвал: там же, где и шапка, — вне скроллящегося тела. */\n footer?: () => any\n}>()\n\nconst slots = useSlots()\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentProp('GrModal', 'size', () => props.size, 'md')\n\nconst panelClass = computed(() => getGrModalPanelClass(resolvedSize.value, props.scrollBehavior))\nconst shellClass = computed(() => getGrModalShellClass(resolvedSize.value, props.scrollBehavior))\nconst layoutClass = computed(() => layoutByScroll[props.scrollBehavior])\n\n// Тело скроллится только при `inside`, и тогда же обязано попадать в таб-порядок:\n// длинное содержимое без единого фокусируемого элемента иначе недостижимо с\n// клавиатуры (axe: `scrollable-region-focusable`).\nconst isBodyScrollable = computed(() => props.scrollBehavior === 'inside')\n\n// Панель фокусируема программно (`tabindex=\"-1\"`) — это и приёмник фокуса по\n// умолчанию, и запасной, когда фокусировать внутри нечего.\nconst panelRef = ref<HTMLElement | null>(null)\n\nconst { t } = useGranularityTranslations()\n\n// ————— Имя и описание окна.\n// Id принадлежат окну и раздаются вниз контекстом: заголовок рисует либо слот\n// `#title`, либо `GrDialogHeader` глубже по дереву, и оба лишь заявляют, что\n// отрисовались. Обратное владение (id рождается у заголовка) мутировало бы\n// состояние окна во время его же патча — см. `context.ts`.\nconst titleId = useId()\nconst descriptionId = useId()\n\nconst hasTitle = ref(false)\nconst hasDescription = ref(false)\n\n// Счётчики намеренно не реактивные: наружу торчит только «заголовок есть».\n// Пересоздание содержимого слота меняет счётчик, но не флаг, и лишнего рендера\n// окна не случается.\nlet titleClaims = 0\nlet descriptionClaims = 0\n\nfunction claim(add: () => void, remove: () => void): () => void {\n add()\n let released = false\n return () => {\n if (released) return\n released = true\n remove()\n }\n}\n\nfunction claimTitle(): () => void {\n return claim(\n () => { titleClaims += 1; hasTitle.value = true },\n () => { titleClaims -= 1; hasTitle.value = titleClaims > 0 },\n )\n}\n\nfunction claimDescription(): () => void {\n return claim(\n () => { descriptionClaims += 1; hasDescription.value = true },\n () => { descriptionClaims -= 1; hasDescription.value = descriptionClaims > 0 },\n )\n}\n\nprovideGrModalContext({ titleId, descriptionId, claimTitle, claimDescription })\n\n// Собственные слоты заявляются тем же путём, что и заголовок `GrDialog`.\nlet releaseOwnTitle: (() => void) | undefined\nlet releaseOwnDescription: (() => void) | undefined\n\nwatch(\n () => Boolean(slots.title),\n (has) => {\n releaseOwnTitle?.()\n releaseOwnTitle = has ? claimTitle() : undefined\n },\n { immediate: true },\n)\n\nwatch(\n () => Boolean(slots.description),\n (has) => {\n releaseOwnDescription?.()\n releaseOwnDescription = has ? claimDescription() : undefined\n },\n { immediate: true },\n)\n\nconst labelledBy = computed(() => (hasTitle.value ? titleId : undefined))\nconst describedBy = computed(() => (hasDescription.value ? descriptionId : undefined))\n\n// Собственный `aria-label` ставим только в отсутствие заголовка: при обоих\n// атрибутах `aria-labelledby` всё равно сильнее, и второе имя было бы шумом.\nconst ariaLabelAttr = computed(() => {\n if (labelledBy.value) return undefined\n return props.ariaLabel || t('gr.modal.title', 'Dialog')\n})\n\n// Обобщённое имя из локали спасает от безымянного окна, но осмысленное имя\n// знает только автор. Предупреждаем при первом открытии, а не при монтировании:\n// закрытая модалка ещё ничего не нарушила, и ругаться на неё рано.\nlet missingNameWarned = false\n\nfunction warnMissingAccessibleName(): void {\n if (missingNameWarned) return\n if (!__GR_DEV__) return\n if (slots.title || props.ariaLabel) return\n\n missingNameWarned = true\n console.warn(\n '[GrModal] Окно без доступного имени: передайте слот #title или проп ariaLabel. '\n + 'Пока используется обобщённое имя из локали.',\n )\n}\n\n// Два заголовка на окно — это разъехавшийся `aria-labelledby`: диктор прочитает\n// первый, а автор будет уверен во втором.\nfunction warnDuplicateTitle(): void {\n if (!__GR_DEV__) return\n if (titleClaims <= 1) return\n\n console.warn(\n '[GrModal] На окно заявлено несколько заголовков — имя берётся от первого. '\n + 'Оставьте один: слот #title у GrModal либо заголовок GrDialog.',\n )\n}\n\nfunction open(): void {\n emit('update:modelValue', true)\n}\n\nfunction close(): void {\n emit('update:modelValue', false)\n}\n\nfunction toggle(): void {\n emit('update:modelValue', !props.modelValue)\n}\n\n// ————— Модальный слой целиком: стек, ловушка фокуса, `inert`, скролл-лок,\n// портал, присутствие в DOM и guard подложки.\nconst {\n rootEl,\n isMounted,\n isVisible,\n inertAttr,\n portalTarget,\n teleportEnabled,\n themeAttrs,\n onPanelAfterLeave: releasePresence,\n backdrop,\n} = useModalOverlay(computed(() => props.modelValue), close, {\n panel: panelRef,\n closeOnEscape: () => props.closeOnEsc,\n closeOnBackdrop: () => props.closeOnBackdrop,\n // Окно фокусирует саму панель, когда внутри фокусировать нечего и когда\n // потребитель не указал цель явно.\n initialFocus: () => props.initialFocus ?? panelRef.value,\n})\n\nfunction onPanelAfterLeave(): void {\n releasePresence()\n emit('closed')\n}\n\nwatch(\n () => props.modelValue,\n (value) => {\n if (!value) return\n warnMissingAccessibleName()\n warnDuplicateTitle()\n },\n { immediate: true },\n)\n\ndefineExpose({ open, close, toggle })\n</script>\n\n<template>\n <teleport :to=\"portalTarget\" :disabled=\"!teleportEnabled\">\n <div\n v-if=\"teleportEnabled && isMounted\"\n ref=\"rootEl\"\n data-gr-overlay-root\n v-bind=\"themeAttrs\"\n role=\"dialog\"\n aria-modal=\"true\"\n :class=\"rootClass\"\n :inert=\"inertAttr\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-label=\"ariaLabelAttr\"\n >\n <div :class=\"shellClass\">\n <div :class=\"layoutClass\">\n <Transition\n appear\n :enter-active-class=\"overlayTransition.enter\"\n :enter-from-class=\"overlayTransition.enterFrom\"\n :enter-to-class=\"overlayTransition.enterTo\"\n :leave-active-class=\"overlayTransition.leave\"\n :leave-from-class=\"overlayTransition.leaveFrom\"\n :leave-to-class=\"overlayTransition.leaveTo\"\n >\n <div\n v-if=\"isVisible\"\n data-gr-modal-overlay\n :class=\"overlayClass\"\n aria-hidden=\"true\"\n v-on=\"backdrop\"\n />\n </Transition>\n\n <!-- `opened`/`closed` вешаем на транзишн панели, а не на корневой:\n анимация живёт здесь, и только её конец что-то значит для\n потребителя, который ждёт момента для размонтирования. -->\n <Transition\n appear\n :enter-active-class=\"panelTransition.enter\"\n :enter-from-class=\"panelTransition.enterFrom\"\n :enter-to-class=\"panelTransition.enterTo\"\n :leave-active-class=\"panelTransition.leave\"\n :leave-from-class=\"panelTransition.leaveFrom\"\n :leave-to-class=\"panelTransition.leaveTo\"\n @after-enter=\"emit('opened')\"\n @after-leave=\"onPanelAfterLeave\"\n >\n <div\n v-if=\"isVisible\"\n ref=\"panelRef\"\n data-gr-modal-panel\n tabindex=\"-1\"\n :class=\"panelClass\"\n >\n <div v-if=\"$slots.title\" :id=\"titleId\" data-gr-modal-title>\n <slot name=\"title\" />\n </div>\n <div v-if=\"$slots.description\" :id=\"descriptionId\" data-gr-modal-description>\n <slot name=\"description\" />\n </div>\n\n <div v-if=\"$slots.header\" data-gr-modal-header :class=\"panelSectionClass\">\n <slot name=\"header\" />\n </div>\n\n <!-- При `inside` тело едет отдельно от шапки и подвала: иначе\n режим ничем не отличался бы от скролла всей панели. -->\n <div\n v-if=\"isBodyScrollable\"\n data-gr-modal-body\n tabindex=\"0\"\n :class=\"panelBodyScrollClass\"\n >\n <slot />\n </div>\n <slot v-else />\n\n <div v-if=\"$slots.footer\" data-gr-modal-footer :class=\"panelSectionClass\">\n <slot name=\"footer\" />\n </div>\n </div>\n </Transition>\n </div>\n </div>\n </div>\n </teleport>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrModal_default } from "./chunks/GrModal-
|
|
1
|
+
import { t as GrModal_default } from "./chunks/GrModal-CCoWZuIl.js";
|
|
2
2
|
import { n as grModalSafelist, t as grModalConfig } from "../../chunks/config-BtXRpAtq.js";
|
|
3
3
|
export { GrModal_default as GrModal, GrModal_default as default, grModalConfig, grModalSafelist };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
|
|
2
2
|
import { t as GrIcon_default } from "../../GrIcon/chunks/GrIcon-bQwtZtYT.js";
|
|
3
|
-
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-
|
|
3
|
+
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-DCz-Wn-V.js";
|
|
4
4
|
import { a as navbarRightAlignClass, c as navbarTitleClass, i as navbarCenterClass, o as navbarSideClass, r as grNavbarRootClass, s as navbarSideGrowClass } from "../../../chunks/config-Bpt9Xbxm.js";
|
|
5
5
|
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, markRaw, normalizeClass, openBlock, renderSlot, toDisplayString, unref, useSlots, withCtx } from "vue";
|
|
6
6
|
//#region ~icons/lucide/menu
|
package/dist/components/GrNavbar/chunks/{GrNavbar-DWaaoV7n.js.map → GrNavbar-DPj30sHX.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrNavbar-
|
|
1
|
+
{"version":3,"file":"GrNavbar-DPj30sHX.js","names":[],"sources":["../../../../src/components/GrNavbar/GrNavbar.vue","../../../../src/components/GrNavbar/GrNavbar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport IconMenu from '~icons/lucide/menu'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\n\nimport {\n grNavbarRootClass,\n navbarCenterClass,\n navbarRightAlignClass,\n navbarSideClass,\n navbarSideGrowClass,\n navbarTitleClass,\n} from './grNavbarStyles'\n\n/**\n * GrNavbar — верхняя панель приложения (header) с тремя зонами: слева заголовок\n * и кнопка меню, по центру — свободная зона (поиск, хлебные крошки), справа —\n * действия и аватар.\n *\n * - Корень — `<header>` (landmark), без собственного `role`.\n * - `aria-label` кнопки меню локализуется через `useGranularityTranslations`.\n */\nexport interface GrNavbarProps {\n /** Заголовок строкой. Слот `#title` сильнее и позволяет обойтись без пропа. */\n title?: string\n showMenuButton?: boolean\n /** Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). */\n menuButtonClass?: string\n /**\n * Панель прилипает к верху при прокрутке. Слой — `--gr-z-navbar`: он ниже\n * якорных панелей, чтобы открытый список не уезжал под шапку.\n */\n sticky?: boolean\n}\n\nexport interface GrNavbarEmits {\n (e: 'menu'): void\n}\n\nconst props = withDefaults(defineProps<GrNavbarProps>(), {\n title: undefined,\n showMenuButton: false,\n menuButtonClass: '',\n sticky: false,\n})\n\nconst slots = defineSlots<{\n /** Заголовок целиком — вместо строки `title`. */\n title?: () => unknown\n /** Зона сразу после заголовка: вкладки, переключатель раздела. */\n left?: () => unknown\n /** Центральная зона: поиск, хлебные крошки. */\n center?: () => unknown\n /** Правая зона: действия, аватар. */\n default?: () => unknown\n}>()\n\nconst emit = defineEmits<GrNavbarEmits>()\n\nconst { t } = useGranularityTranslations()\n\nconst menuAriaLabel = computed(() => t('gr.navbar.openMenu', 'Open menu'))\n\n// Пустой блок заголовка съедал бы отступ ряда, поэтому рендерим его только при\n// наличии содержимого — проп `title` для этого не обязателен.\nconst hasTitle = computed(() => Boolean(props.title || slots.title))\n\nconst rootClass = computed(() => grNavbarRootClass(props.sticky))\n\n// С центральной зоной боковые делят остаток поровну — иначе «центр» уезжает\n// вслед за более широким боком. Без неё правая просто прижата к краю.\nconst hasCenter = computed(() => Boolean(slots.center))\n\nconst sideClass = computed(() => [navbarSideClass, hasCenter.value ? navbarSideGrowClass : ''])\nconst rightClass = computed(() => [\n navbarSideClass,\n hasCenter.value ? `${navbarSideGrowClass} ${navbarRightAlignClass}` : 'ml-auto',\n])\n</script>\n\n<template>\n <header\n data-gr-navbar\n :data-sticky=\"sticky ? 'true' : undefined\"\n :class=\"rootClass\"\n >\n <div data-gr-navbar-left :class=\"sideClass\">\n <GrButton\n v-if=\"showMenuButton\"\n data-gr-navbar-menu\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"menuAriaLabel\"\n :class=\"menuButtonClass\"\n @click=\"emit('menu')\"\n >\n <GrIcon :size=\"16\">\n <IconMenu aria-hidden=\"true\" />\n </GrIcon>\n </GrButton>\n\n <div v-if=\"hasTitle\" data-gr-navbar-title :class=\"navbarTitleClass\">\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n\n <slot name=\"left\" />\n </div>\n\n <div v-if=\"hasCenter\" data-gr-navbar-center :class=\"navbarCenterClass\">\n <slot name=\"center\" />\n </div>\n\n <div data-gr-navbar-right :class=\"rightClass\">\n <slot />\n </div>\n </header>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport IconMenu from '~icons/lucide/menu'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\n\nimport {\n grNavbarRootClass,\n navbarCenterClass,\n navbarRightAlignClass,\n navbarSideClass,\n navbarSideGrowClass,\n navbarTitleClass,\n} from './grNavbarStyles'\n\n/**\n * GrNavbar — верхняя панель приложения (header) с тремя зонами: слева заголовок\n * и кнопка меню, по центру — свободная зона (поиск, хлебные крошки), справа —\n * действия и аватар.\n *\n * - Корень — `<header>` (landmark), без собственного `role`.\n * - `aria-label` кнопки меню локализуется через `useGranularityTranslations`.\n */\nexport interface GrNavbarProps {\n /** Заголовок строкой. Слот `#title` сильнее и позволяет обойтись без пропа. */\n title?: string\n showMenuButton?: boolean\n /** Extra classes applied to the menu button wrapper (e.g. `sm:hidden`). */\n menuButtonClass?: string\n /**\n * Панель прилипает к верху при прокрутке. Слой — `--gr-z-navbar`: он ниже\n * якорных панелей, чтобы открытый список не уезжал под шапку.\n */\n sticky?: boolean\n}\n\nexport interface GrNavbarEmits {\n (e: 'menu'): void\n}\n\nconst props = withDefaults(defineProps<GrNavbarProps>(), {\n title: undefined,\n showMenuButton: false,\n menuButtonClass: '',\n sticky: false,\n})\n\nconst slots = defineSlots<{\n /** Заголовок целиком — вместо строки `title`. */\n title?: () => unknown\n /** Зона сразу после заголовка: вкладки, переключатель раздела. */\n left?: () => unknown\n /** Центральная зона: поиск, хлебные крошки. */\n center?: () => unknown\n /** Правая зона: действия, аватар. */\n default?: () => unknown\n}>()\n\nconst emit = defineEmits<GrNavbarEmits>()\n\nconst { t } = useGranularityTranslations()\n\nconst menuAriaLabel = computed(() => t('gr.navbar.openMenu', 'Open menu'))\n\n// Пустой блок заголовка съедал бы отступ ряда, поэтому рендерим его только при\n// наличии содержимого — проп `title` для этого не обязателен.\nconst hasTitle = computed(() => Boolean(props.title || slots.title))\n\nconst rootClass = computed(() => grNavbarRootClass(props.sticky))\n\n// С центральной зоной боковые делят остаток поровну — иначе «центр» уезжает\n// вслед за более широким боком. Без неё правая просто прижата к краю.\nconst hasCenter = computed(() => Boolean(slots.center))\n\nconst sideClass = computed(() => [navbarSideClass, hasCenter.value ? navbarSideGrowClass : ''])\nconst rightClass = computed(() => [\n navbarSideClass,\n hasCenter.value ? `${navbarSideGrowClass} ${navbarRightAlignClass}` : 'ml-auto',\n])\n</script>\n\n<template>\n <header\n data-gr-navbar\n :data-sticky=\"sticky ? 'true' : undefined\"\n :class=\"rootClass\"\n >\n <div data-gr-navbar-left :class=\"sideClass\">\n <GrButton\n v-if=\"showMenuButton\"\n data-gr-navbar-menu\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"menuAriaLabel\"\n :class=\"menuButtonClass\"\n @click=\"emit('menu')\"\n >\n <GrIcon :size=\"16\">\n <IconMenu aria-hidden=\"true\" />\n </GrIcon>\n </GrButton>\n\n <div v-if=\"hasTitle\" data-gr-navbar-title :class=\"navbarTitleClass\">\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n\n <slot name=\"left\" />\n </div>\n\n <div v-if=\"hasCenter\" data-gr-navbar-center :class=\"navbarCenterClass\">\n <slot name=\"center\" />\n </div>\n\n <div data-gr-navbar-right :class=\"rightClass\">\n <slot />\n </div>\n </header>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2CA,MAAM,QAAQ;EAOd,MAAM,QAAQ,SAAA;EAWd,MAAM,OAAO;EAEb,MAAM,EAAE,MAAM,2BAA2B;EAEzC,MAAM,gBAAgB,eAAe,EAAE,sBAAsB,WAAW,CAAC;EAIzE,MAAM,WAAW,eAAe,QAAQ,MAAM,SAAS,MAAM,KAAK,CAAC;EAEnE,MAAM,YAAY,eAAe,kBAAkB,MAAM,MAAM,CAAC;EAIhE,MAAM,YAAY,eAAe,QAAQ,MAAM,MAAM,CAAC;EAEtD,MAAM,YAAY,eAAe,CAAC,iBAAiB,UAAU,QAAQ,sBAAsB,EAAE,CAAC;EAC9F,MAAM,aAAa,eAAe,CAChC,iBACA,UAAU,QAAQ,GAAG,oBAAoB,GAAG,0BAA0B,SACxE,CAAC;;uBAIC,mBAqCS,UAAA;IApCP,kBAAA;IACC,eAAa,QAAA,SAAM,SAAY,KAAA;IAC/B,OAAK,eAAE,UAAA,KAAS;;IAEjB,mBAuBM,OAAA;KAvBD,uBAAA;KAAqB,OAAK,eAAE,UAAA,KAAS;;KAEhC,QAAA,kBAAA,UAAA,GADR,YAaW,kBAAA;;MAXT,uBAAA;MACA,SAAQ;MACR,MAAK;MACL,QAAA;MACC,cAAY,cAAA;MACZ,OAAK,eAAE,QAAA,eAAe;MACtB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,MAAA;;6BAIH,CAFT,YAES,gBAAA,EAFA,MAAM,GAAE,GAAA;8BACgB,CAA/B,YAA+B,MAAA,YAAA,GAAA,EAArB,eAAY,OAAM,CAAA,CAAA,CAAA;;;;;KAIrB,SAAA,SAAA,UAAA,GAAX,mBAIM,OAAA;;MAJe,wBAAA;MAAsB,OAAK,eAAE,MAAA,gBAAA,CAAgB;SAChE,WAEO,KAAA,QAAA,SAAA,CAAA,SAAA,CAAA,gBAAA,gBADF,QAAA,KAAK,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAIZ,WAAoB,KAAA,QAAA,MAAA;;IAGX,UAAA,SAAA,UAAA,GAAX,mBAEM,OAAA;;KAFgB,yBAAA;KAAuB,OAAK,eAAE,MAAA,iBAAA,CAAiB;QACnE,WAAsB,KAAA,QAAA,QAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;IAGxB,mBAEM,OAAA;KAFD,wBAAA;KAAsB,OAAK,eAAE,WAAA,KAAU;QAC1C,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrNavbar_default } from "./chunks/GrNavbar-
|
|
1
|
+
import { t as GrNavbar_default } from "./chunks/GrNavbar-DPj30sHX.js";
|
|
2
2
|
import { n as grNavbarSafelist, t as grNavbarConfig } from "../../chunks/config-Bpt9Xbxm.js";
|
|
3
3
|
export { GrNavbar_default as GrNavbar, GrNavbar_default as default, grNavbarConfig, grNavbarSafelist };
|
package/dist/components/GrNumberInput/chunks/{GrNumberInput-BotnzW77.js → GrNumberInput-V2mZvDDC.js}
RENAMED
|
@@ -8,7 +8,7 @@ import { n as addLen, t as useControlAddons } from "../../../chunks/useControlAd
|
|
|
8
8
|
import { t as chevron_down_default } from "../../../chunks/chevron-down-CQS2OG_s.js";
|
|
9
9
|
import { t as chevron_left_default } from "../../../chunks/chevron-left-D8AUBAM7.js";
|
|
10
10
|
import { t as chevron_right_default } from "../../../chunks/chevron-right-C6Z01h0X.js";
|
|
11
|
-
import { a as grNumberInputShellClass, i as grNumberInputInputClass, o as stepperCompactClass, r as clearButtonClass, s as stepperWideClass } from "../../../chunks/config-
|
|
11
|
+
import { a as grNumberInputShellClass, i as grNumberInputInputClass, o as stepperCompactClass, r as clearButtonClass, s as stepperWideClass } from "../../../chunks/config-zjeAJ4ho2.js";
|
|
12
12
|
import { computed, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, markRaw, mergeProps, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, ref, renderSlot, unref, withCtx, withModifiers } from "vue";
|
|
13
13
|
//#region src/components/GrNumberInput/numberInputMath.ts
|
|
14
14
|
/**
|