@feugene/granularity 0.31.0 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/dist/chunks/adapter-CAOxmn2G.js.map +1 -1
- package/dist/chunks/{config-BHcS5Cwm.js → config-87fszqnD.js} +1 -1
- package/dist/chunks/{config-BHcS5Cwm.js.map → config-87fszqnD.js.map} +1 -1
- package/dist/chunks/config-A-bBiotF.js.map +1 -1
- package/dist/chunks/config-B3J0alyY.js.map +1 -1
- package/dist/chunks/config-BhfNgyWL.js.map +1 -1
- package/dist/chunks/config-Bjn30smH.js.map +1 -1
- package/dist/chunks/config-BqT6PMzO.js.map +1 -1
- package/dist/chunks/config-C0Ea9ePP.js.map +1 -1
- package/dist/chunks/config-C0Eu8jpG.js.map +1 -1
- package/dist/chunks/config-CDat1wXf.js.map +1 -1
- package/dist/chunks/config-CQLNjc9X2.js.map +1 -1
- package/dist/chunks/config-CbEbLyDE.js.map +1 -1
- package/dist/chunks/{config-jyr6AIZt2.js → config-CuQmUkMw2.js} +2 -2
- package/dist/chunks/{config-jyr6AIZt2.js.map → config-CuQmUkMw2.js.map} +1 -1
- package/dist/chunks/config-DeyT9dJl.js.map +1 -1
- package/dist/chunks/config-DnF472Ox.js.map +1 -1
- package/dist/chunks/config-DwABJHEo.js.map +1 -1
- package/dist/chunks/{config-tiUtEn4Q.js → config-E1cTyRVV.js} +1 -1
- package/dist/chunks/{config-tiUtEn4Q.js.map → config-E1cTyRVV.js.map} +1 -1
- package/dist/chunks/config-MmPym-Z72.js.map +1 -1
- package/dist/chunks/config-T3-DMXYm.js.map +1 -1
- package/dist/chunks/config-Yy67uWHY.js.map +1 -1
- package/dist/chunks/config-_jbgsFpw.js.map +1 -1
- package/dist/chunks/config-g8-JGZ_Y.js.map +1 -1
- package/dist/chunks/config-mlheIMtV.js.map +1 -1
- package/dist/chunks/config-rLkc9Zn2.js.map +1 -1
- package/dist/chunks/config-uzUuosv7.js.map +1 -1
- package/dist/chunks/context-CTbuwcYs.js.map +1 -1
- package/dist/chunks/deltaTone-B5OoS1vo.js.map +1 -1
- package/dist/chunks/es-Do2CNrC2.js.map +1 -1
- package/dist/chunks/fileValidation-D5TFBhbv.js.map +1 -1
- package/dist/chunks/focusables-BkzK42SQ.js.map +1 -1
- package/dist/chunks/{generated-DJ2dJ__C.js → generated-CSyPsWtM.js} +1 -1
- package/dist/chunks/{generated-DJ2dJ__C.js.map → generated-CSyPsWtM.js.map} +1 -1
- package/dist/chunks/grBadgeStyles-D8Mz73B5.js.map +1 -1
- package/dist/chunks/{grButtonStyles-B_-YRE__.js → grButtonStyles-B3wRWW_i.js} +4 -4
- package/dist/chunks/grButtonStyles-B3wRWW_i.js.map +1 -0
- package/dist/chunks/grLinkStyles-FME_qMpi.js.map +1 -1
- package/dist/chunks/grLoadingStyles-CnQaf5ry.js.map +1 -1
- package/dist/chunks/{grRadioStyles-D4gnU65n.js → grRadioStyles-DGL2QLy6.js} +1 -1
- package/dist/chunks/{grRadioStyles-D4gnU65n.js.map → grRadioStyles-DGL2QLy6.js.map} +1 -1
- package/dist/chunks/grSelectStyles-Cdtd10eY.js.map +1 -1
- package/dist/chunks/{grSliderStyles-B5uNxNJq.js → grSliderStyles-kaz6VXZ3.js} +9 -1
- package/dist/chunks/grSliderStyles-kaz6VXZ3.js.map +1 -0
- package/dist/chunks/{granular-provider-yJNX1g1G.js → granular-provider-Da2CJO6j.js} +3 -3
- package/dist/chunks/{granular-provider-yJNX1g1G.js.map → granular-provider-Da2CJO6j.js.map} +1 -1
- package/dist/chunks/granularityI18n-BC5zsj_d.js.map +1 -1
- package/dist/chunks/icon-Dag2tIMs.js.map +1 -1
- package/dist/chunks/inert-DIHCCn_1.js.map +1 -1
- package/dist/chunks/keyboard-DE2yhz_b.js.map +1 -1
- package/dist/chunks/numericStep-Bq2lj4ew.js.map +1 -1
- package/dist/chunks/optionFilter-DYw9XPPx.js.map +1 -1
- package/dist/chunks/{overlayStack-C8cfsqXa.js → overlayStack-0CZS99Da.js} +40 -6
- package/dist/chunks/overlayStack-0CZS99Da.js.map +1 -0
- package/dist/chunks/portalRoot-CVhCgdG6.js.map +1 -1
- package/dist/chunks/renamedProp-BwcN-SJg.js.map +1 -1
- package/dist/chunks/{safelist-BOq5Li7p.js → safelist-DxkMj5IW.js} +1 -1
- package/dist/chunks/{safelist-BOq5Li7p.js.map → safelist-DxkMj5IW.js.map} +1 -1
- package/dist/chunks/slotNodes-DxNXpC7v.js.map +1 -1
- package/dist/chunks/useControlAddons-asUr10Pf.js.map +1 -1
- package/dist/chunks/useControlledOpen-DV3SqLTP.js.map +1 -1
- package/dist/chunks/useDragSort-0l47h4yb.js.map +1 -1
- package/dist/chunks/useFilePreviews-DnwgdyBs.js.map +1 -1
- package/dist/chunks/useFocusWithin-BwHE0rzm.js.map +1 -1
- package/dist/chunks/{useModalOverlay-Cv5hF63W.js → useModalOverlay-D__CqLGn.js} +3 -0
- package/dist/chunks/useModalOverlay-D__CqLGn.js.map +1 -0
- package/dist/chunks/usePortalTarget-C0p-mhaM.js.map +1 -1
- package/dist/chunks/validation-5HhpanPP.js.map +1 -1
- package/dist/components/GrAlert/chunks/GrAlert-FAVf-ksH.js.map +1 -1
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-X6RlmiJQ.js → GrAutocomplete-rYEY15UI.js} +1 -1
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-rYEY15UI.js.map +1 -0
- package/dist/components/GrAutocomplete/index.js +1 -1
- package/dist/components/GrAutocomplete/styles.css +1 -1
- package/dist/components/GrAvatar/chunks/GrAvatar-CNmLmgcr.js.map +1 -1
- package/dist/components/GrBadge/chunks/{GrBadge-BsoocpF-.js → GrBadge-D2MfThPz.js} +1 -1
- package/dist/components/GrBadge/chunks/GrBadge-D2MfThPz.js.map +1 -0
- package/dist/components/GrBadge/index.js +1 -1
- package/dist/components/GrBadge/styles.css +1 -1
- package/dist/components/GrBadgeWrap/chunks/GrBadgeWrap-B-A3PRkE.js.map +1 -1
- package/dist/components/GrBottomNav/chunks/GrBottomNav-wtFatLjB.js.map +1 -1
- package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-DRDNan_n.js.map +1 -1
- package/dist/components/GrButton/chunks/{GrButton-Bw2AH4uX.js → GrButton-7GP0zObj.js} +1 -1
- package/dist/components/GrButton/chunks/GrButton-7GP0zObj.js.map +1 -0
- package/dist/components/GrButton/index.js +3 -3
- package/dist/components/GrButtonGroup/chunks/GrButtonGroup-CKyiAV8K.js.map +1 -1
- package/dist/components/GrCard/chunks/GrCard-BttXz1pQ.js.map +1 -1
- package/dist/components/GrCheckbox/chunks/GrCheckbox-CEcHIsXM.js.map +1 -1
- package/dist/components/GrCheckboxGroup/chunks/GrCheckboxGroup-BOwrVcFr.js.map +1 -1
- package/dist/components/GrChip/chunks/GrChip-C0vU69tf.js.map +1 -1
- package/dist/components/GrChipGroup/chunks/GrChipGroup-C78Vcwos.js.map +1 -1
- package/dist/components/GrCodeBlock/chunks/{GrCodeBlock-Dkvkr91t.js → GrCodeBlock-Dp0eEBTZ.js} +1 -1
- package/dist/components/GrCodeBlock/chunks/GrCodeBlock-Dp0eEBTZ.js.map +1 -0
- package/dist/components/GrCodeBlock/index.js +1 -1
- package/dist/components/GrCollapse/chunks/GrCollapse-BlW6ybdL.js.map +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-C7ifyQ0R.js → GrColorPicker-p2XH_8cE.js} +2 -2
- package/dist/components/GrColorPicker/chunks/GrColorPicker-p2XH_8cE.js.map +1 -0
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-C5abL2Y7.js → GrCommandPalette-BXbh3q7O.js} +2 -2
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BXbh3q7O.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-BF7R3hgg.js.map +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-B9mIU78E.js → GrConfirmDialog-CUP43q__.js} +3 -3
- package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-CUP43q__.js.map +1 -0
- package/dist/components/GrConfirmDialog/index.js +1 -1
- package/dist/components/GrContextMenu/chunks/{GrContextMenu-_FKwQvuQ.js → GrContextMenu-ClkVjdcP.js} +1 -1
- package/dist/components/GrContextMenu/chunks/GrContextMenu-ClkVjdcP.js.map +1 -0
- package/dist/components/GrContextMenu/index.js +1 -1
- package/dist/components/GrDataTable/chunks/{GrDataTable-C8a9_nCg.js → GrDataTable-CAGgRk27.js} +5 -3
- package/dist/components/GrDataTable/chunks/GrDataTable-CAGgRk27.js.map +1 -0
- package/dist/components/GrDataTable/index.js +1 -1
- package/dist/components/GrDelta/chunks/GrDelta-BZwkgQW-.js.map +1 -1
- package/dist/components/GrDescriptionList/chunks/GrDescriptionList-BMeCgFV3.js.map +1 -1
- package/dist/components/GrDialog/chunks/{GrDialog-bOUDYfmh.js → GrDialog-BiMJ9_T4.js} +2 -2
- package/dist/components/GrDialog/chunks/{GrDialog-bOUDYfmh.js.map → GrDialog-BiMJ9_T4.js.map} +1 -1
- package/dist/components/GrDialog/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-6s2cQ-_6.js → GrDialogService-BxNK6crD.js} +4 -4
- package/dist/components/GrDialogService/chunks/GrDialogService-BxNK6crD.js.map +1 -0
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrDivider/chunks/GrDivider-BXavGcMX.js.map +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-B7PiAcbU.js → GrDrawer-Ce2l3zaG.js} +6 -5
- package/dist/components/GrDrawer/chunks/GrDrawer-Ce2l3zaG.js.map +1 -0
- package/dist/components/GrDrawer/index.js +1 -1
- package/dist/components/GrDropdown/chunks/GrDropdown-13Wt9eB_.js.map +1 -1
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-GOzuAXsi.js.map +1 -1
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenuList-B33ZzwfB.js.map +1 -1
- package/dist/components/GrFilePreview/chunks/GrFilePreview-C9Z7hMK8.js.map +1 -1
- package/dist/components/GrFileUpload/chunks/GrFileUpload-NsHkRRIa.js.map +1 -1
- package/dist/components/GrForm/chunks/GrForm-CrnfCsgj.js.map +1 -1
- package/dist/components/GrFormField/chunks/GrFormField-DSylU7IU.js.map +1 -1
- package/dist/components/GrFormFile/chunks/{GrFormFile-CYtXU9HV.js → GrFormFile-B1jGZyIj.js} +2 -2
- package/dist/components/GrFormFile/chunks/GrFormFile-B1jGZyIj.js.map +1 -0
- package/dist/components/GrFormFile/index.js +1 -1
- package/dist/components/GrFormFile/styles.css +1 -1
- package/dist/components/GrIcon/chunks/GrIcon-Dmf0c9ba.js.map +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-CyVN6Zpc.js → GrImageViewer-CG4vUBg_.js} +4 -3
- package/dist/components/GrImageViewer/chunks/GrImageViewer-CG4vUBg_.js.map +1 -0
- package/dist/components/GrImageViewer/index.js +1 -1
- package/dist/components/GrInput/chunks/GrInput-BLywRZUN.js.map +1 -1
- package/dist/components/GrInputTag/chunks/GrInputTag-Cj4A9Xgs.js.map +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-Cy2bknHO.js → GrJsonViewer-BCZ7lE2k.js} +2 -2
- package/dist/components/GrJsonViewer/chunks/GrJsonViewer-BCZ7lE2k.js.map +1 -0
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrKbd/chunks/GrKbd-D2ga1H9i.js.map +1 -1
- package/dist/components/GrLink/chunks/GrLink-DYXw3W9w.js.map +1 -1
- package/dist/components/GrList/chunks/{GrList-DmT4qzLO.js → GrList-B_CRJB4c.js} +1 -1
- package/dist/components/GrList/chunks/GrList-B_CRJB4c.js.map +1 -0
- package/dist/components/GrList/index.js +1 -1
- package/dist/components/GrList/styles.css +1 -1
- package/dist/components/GrModal/chunks/{GrModal-CCoWZuIl.js → GrModal-D0mf1SAx.js} +3 -2
- package/dist/components/GrModal/chunks/GrModal-D0mf1SAx.js.map +1 -0
- package/dist/components/GrModal/index.js +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-C5EjMKaC.js → GrNavbar-_rtHKwTf.js} +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-C5EjMKaC.js.map → GrNavbar-_rtHKwTf.js.map} +1 -1
- package/dist/components/GrNavbar/index.js +1 -1
- package/dist/components/GrNumberInput/chunks/GrNumberInput-DQ59YICu.js.map +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-Cw3R21dp.js → GrPagination-B_kAhoAV.js} +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-Cw3R21dp.js.map → GrPagination-B_kAhoAV.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrPopover/chunks/{GrPopover-CofG-Tfe.js → GrPopover-faJanmGs.js} +1 -1
- package/dist/components/GrPopover/chunks/GrPopover-faJanmGs.js.map +1 -0
- package/dist/components/GrPopover/index.js +1 -1
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-DLcl1xiq.js → GrPromptDialog-C6QGC7QJ.js} +4 -4
- package/dist/components/GrPromptDialog/chunks/GrPromptDialog-C6QGC7QJ.js.map +1 -0
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrRadio/chunks/{GrRadio-C_rESc5g.js → GrRadio-Aq3v8XgG.js} +1 -1
- package/dist/components/GrRadio/chunks/GrRadio-Aq3v8XgG.js.map +1 -0
- package/dist/components/GrRadio/index.js +2 -2
- package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-CdQO4G-U.js → GrRadioGroup-y9LVdTWh.js} +1 -1
- package/dist/components/GrRadioGroup/chunks/GrRadioGroup-y9LVdTWh.js.map +1 -0
- package/dist/components/GrRadioGroup/index.js +1 -1
- package/dist/components/GrRating/chunks/GrRating-IEiTOdnF.js.map +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-C_skObtv.js → GrResponseErrorBanner-CBVRpgIG.js} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-C_skObtv.js.map → GrResponseErrorBanner-CBVRpgIG.js.map} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-DudGc9LO.js → GrResponseErrorBanner-DMeOWlzj.js} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-DMeOWlzj.js.map +1 -0
- package/dist/components/GrResponseErrorBanner/index.js +2 -2
- package/dist/components/GrSegmented/chunks/GrSegmented-CB-VR-yL.js.map +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-BiUrSSxc.js → GrSelect-gCeRFgQo.js} +1 -1
- package/dist/components/GrSelect/chunks/GrSelect-gCeRFgQo.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-B8EQIlfW.js → GrSidebar-alkwBUzL.js} +1 -1
- package/dist/components/GrSidebar/chunks/GrSidebar-alkwBUzL.js.map +1 -0
- package/dist/components/GrSidebar/index.js +1 -1
- package/dist/components/GrSkeleton/chunks/GrSkeleton-CgfXxPQ2.js.map +1 -1
- package/dist/components/GrSlider/chunks/{GrSlider-DJfeEl4h.js → GrSlider-DTTrZTHK.js} +3 -3
- package/dist/components/GrSlider/chunks/GrSlider-DTTrZTHK.js.map +1 -0
- package/dist/components/GrSlider/index.js +2 -2
- package/dist/components/GrSortableList/chunks/GrSortableList-HyKX--rO.js.map +1 -1
- package/dist/components/GrSplitter/chunks/GrSplitter-MltjaADn.js.map +1 -1
- package/dist/components/GrStatistic/chunks/GrStatistic-qHVasK37.js.map +1 -1
- package/dist/components/GrSteps/chunks/GrSteps-C2jxpZoN.js.map +1 -1
- package/dist/components/GrSwitch/chunks/{GrSwitch-g_OOSkh7.js → GrSwitch-7Oe5QfQZ.js} +1 -1
- package/dist/components/GrSwitch/chunks/GrSwitch-7Oe5QfQZ.js.map +1 -0
- package/dist/components/GrSwitch/index.js +1 -1
- package/dist/components/GrTabPanels/chunks/GrTabPanels-BZQS8-JM.js.map +1 -1
- package/dist/components/GrTable/chunks/GrTable-Zcdz2St_.js.map +1 -1
- package/dist/components/GrTextarea/chunks/{GrTextarea-KREK75Bu.js → GrTextarea-DinXG12-.js} +10 -10
- package/dist/components/GrTextarea/chunks/GrTextarea-DinXG12-.js.map +1 -0
- package/dist/components/GrTextarea/index.js +1 -1
- package/dist/components/GrTimeline/chunks/GrTimeline-BiHpArsg.js.map +1 -1
- package/dist/components/GrToaster/chunks/{GrToaster-Dp7-4B8k.js → GrToaster-COXIBKzq.js} +2 -2
- package/dist/components/GrToaster/chunks/GrToaster-COXIBKzq.js.map +1 -0
- package/dist/components/GrToaster/index.js +1 -1
- package/dist/components/GrToaster/styles.css +1 -1
- package/dist/components/GrTooltip/chunks/GrTooltip-uYETdbCD.js.map +1 -1
- package/dist/components/GrTree/chunks/{GrTree-DC89I4AP.js → GrTree-Ci6CZfKR.js} +5 -5
- package/dist/components/GrTree/chunks/GrTree-Ci6CZfKR.js.map +1 -0
- package/dist/components/GrTree/index.js +1 -1
- package/dist/components/GrTree/styles.css +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-iJnMl2t9.js → GrTreeSelect-BHwgGJcT.js} +1 -1
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-BHwgGJcT.js.map +1 -0
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/composables/useAnnouncer.js.map +1 -1
- package/dist/composables/useComboboxNavigation.js.map +1 -1
- package/dist/composables/useDragGesture.js.map +1 -1
- package/dist/composables/useFloating.js +2 -22
- package/dist/composables/useFloating.js.map +1 -1
- package/dist/composables/useFocusTrap.js.map +1 -1
- package/dist/composables/useOverlayLayer.js +10 -3
- package/dist/composables/useOverlayLayer.js.map +1 -1
- package/dist/composables/useRovingFocus.js.map +1 -1
- package/dist/composables/useTheme.js.map +1 -1
- package/dist/composables/useToast.js.map +1 -1
- package/dist/composables/useVirtualList.js.map +1 -1
- package/dist/directives/autofocus.js.map +1 -1
- package/dist/directives/autosize.js.map +1 -1
- package/dist/directives/clickOutside.js.map +1 -1
- package/dist/directives/dropzone.js.map +1 -1
- package/dist/directives/hotkey.js.map +1 -1
- package/dist/directives/loading.js.map +1 -1
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/index.js +37 -37
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/dist/theme-apply.js.map +1 -1
- package/dist/theme.js +1 -1
- package/dist/theme.js.map +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrCodeBlock/tokenizeJson.d.ts +1 -3
- package/dist/types/components/GrImageViewer/GrImageViewer.vue.d.ts +1 -1
- package/dist/types/components/GrInput/GrInput.vue.d.ts +24 -24
- package/dist/types/components/GrSlider/grSliderStyles.d.ts +9 -1
- package/dist/types/components/GrTabPanels/GrTabPanel.vue.d.ts +3 -3
- package/dist/types/composables/internal/overlayStack.d.ts +30 -0
- package/dist/types/composables/internal/useModalOverlay.d.ts +14 -0
- package/dist/types/composables/useOverlayLayer.d.ts +8 -0
- package/dist/vue/index.js.map +1 -1
- package/dist/web-types.json +1 -1
- package/package.json +3 -3
- package/dist/chunks/grButtonStyles-B_-YRE__.js.map +0 -1
- package/dist/chunks/grSliderStyles-B5uNxNJq.js.map +0 -1
- package/dist/chunks/overlayStack-C8cfsqXa.js.map +0 -1
- package/dist/chunks/useModalOverlay-Cv5hF63W.js.map +0 -1
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-X6RlmiJQ.js.map +0 -1
- package/dist/components/GrBadge/chunks/GrBadge-BsoocpF-.js.map +0 -1
- package/dist/components/GrButton/chunks/GrButton-Bw2AH4uX.js.map +0 -1
- package/dist/components/GrCodeBlock/chunks/GrCodeBlock-Dkvkr91t.js.map +0 -1
- package/dist/components/GrColorPicker/chunks/GrColorPicker-C7ifyQ0R.js.map +0 -1
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-C5abL2Y7.js.map +0 -1
- package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-B9mIU78E.js.map +0 -1
- package/dist/components/GrContextMenu/chunks/GrContextMenu-_FKwQvuQ.js.map +0 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-C8a9_nCg.js.map +0 -1
- package/dist/components/GrDialogService/chunks/GrDialogService-6s2cQ-_6.js.map +0 -1
- package/dist/components/GrDrawer/chunks/GrDrawer-B7PiAcbU.js.map +0 -1
- package/dist/components/GrFormFile/chunks/GrFormFile-CYtXU9HV.js.map +0 -1
- package/dist/components/GrImageViewer/chunks/GrImageViewer-CyVN6Zpc.js.map +0 -1
- package/dist/components/GrJsonViewer/chunks/GrJsonViewer-Cy2bknHO.js.map +0 -1
- package/dist/components/GrList/chunks/GrList-DmT4qzLO.js.map +0 -1
- package/dist/components/GrModal/chunks/GrModal-CCoWZuIl.js.map +0 -1
- package/dist/components/GrPopover/chunks/GrPopover-CofG-Tfe.js.map +0 -1
- package/dist/components/GrPromptDialog/chunks/GrPromptDialog-DLcl1xiq.js.map +0 -1
- package/dist/components/GrRadio/chunks/GrRadio-C_rESc5g.js.map +0 -1
- package/dist/components/GrRadioGroup/chunks/GrRadioGroup-CdQO4G-U.js.map +0 -1
- package/dist/components/GrResponseErrorBanner/chunks/GrResponseErrorBanner-DudGc9LO.js.map +0 -1
- package/dist/components/GrSelect/chunks/GrSelect-BiUrSSxc.js.map +0 -1
- package/dist/components/GrSidebar/chunks/GrSidebar-B8EQIlfW.js.map +0 -1
- package/dist/components/GrSlider/chunks/GrSlider-DJfeEl4h.js.map +0 -1
- package/dist/components/GrSwitch/chunks/GrSwitch-g_OOSkh7.js.map +0 -1
- package/dist/components/GrTextarea/chunks/GrTextarea-KREK75Bu.js.map +0 -1
- package/dist/components/GrToaster/chunks/GrToaster-Dp7-4B8k.js.map +0 -1
- package/dist/components/GrTree/chunks/GrTree-DC89I4AP.js.map +0 -1
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-iJnMl2t9.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrLink-DYXw3W9w.js","names":[],"sources":["../../../../src/components/GrLink/GrLink.vue","../../../../src/components/GrLink/GrLink.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value) return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value) return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoEA,MAAM,QAAQ;EAgBd,MAAM,QAAQ,SAAS;EAIvB,MAAM,gBAAgB,eAAe,CAAC,MAAM,aAAa,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,KAAK;EAEpF,MAAM,WAAW,eAAmC;GAClD,IAAI,CAAC,cAAc,OAAO,OAAO;GACjC,IAAI,MAAM,IAIR,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,QAAQ,MAAM,EAAE;GAEnE,OAAO;EACT,CAAC;EAED,MAAM,iBAAiB,eAAe;GACpC,IAAI,MAAM,QACR,OAAO,MAAM;GAEf,IAAI,MAAM,UACR,OAAO;EAGX,CAAC;EAED,MAAM,cAAc,eAAe;GACjC,IAAI,MAAM,KACR,OAAO,MAAM;GAIf,IAAI,eAAe,UAAU,UAC3B,OAAO;EAGX,CAAC;EAED,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC;EAEjF,MAAM,EAAE,MAAM,2BAA2B;;;;;;EAOzC,MAAM,gBAAgB,eAAe,cAAc,SAAS,eAAe,UAAU,QAAQ;EAC7F,MAAM,aAAa,eAAe,MAAM,eAAe,EAAE,yBAAyB,oBAAoB,CAAC;EAEvG,MAAM,mBAAmB,eAAe,MAAM,gBAAgB,cAAc,KAAK;;;;;EAMjF,MAAM,oBAAoB,eAAe;GACvC,IAAI,CAAC,MAAM,WACT,OAAO,KAAA;GAET,OAAO,cAAc,QAAQ,GAAG,MAAM,UAAU,IAAI,WAAW,UAAU,MAAM;EACjF,CAAC;EAED,MAAM,WAAW,eAAgB,aAAa,UAAU,OAAO,OAAO,IAAK;EAE3E,MAAM,YAAY,eAAe;GAC/B,MAAM,eAAe,YAAY;IAC/B,MAAM,aAAa;IACnB,WAAW,MAAM;IACjB,UAAU,MAAM;GAClB,CAAC;GAID,OAAO,MAAM,WACT,GAAG,cAAc,GAAG,iBACpB,GAAG,cAAc,GAAG,eAAe,GAAG;EAC5C,CAAC;EAGD,MAAM,aAAa,eAAe,iBAAiB;GACjD,MAAM,MAAM;GACZ,SAAS,MAAM;GACf,UAAU,MAAM;EAClB,CAAC,CAAC;;uBAUA,YAmBY,wBAlBL,SAAA,KAAQ,GADf,WAEU,MAiBE,KAAA,GAjBG;IACZ,MAAM,cAAA,QAAgB,QAAA,OAAO,KAAA;IAC7B,QAAQ,cAAA,QAAgB,eAAA,QAAiB,KAAA;IACzC,KAAK,cAAA,QAAgB,YAAA,QAAc,KAAA;IACnC,cAAY,kBAAA;IACZ,iBAAe,QAAA,WAAQ,SAAY,KAAA;IACnC,OAAO,UAAA;IACP,OAAO,WAAA;;2BAEA;KAAR,WAAQ,KAAA,QAAA,SAAA;KAEM,iBAAA,SAAA,UAAA,GAAd,YAES,gBAAA;;MAFwB,MAAM,SAAA;;6BACrB,CAAhB,YAAgB,MAAA,qBAAA,CAAA,CAAA,CAAA;;;KAIN,cAAA,SAAa,CAAK,QAAA,aAAA,UAAA,GAA9B,mBAAkF,QAAlF,YAAyD,MAAC,gBAAG,WAAA,KAAU,IAAG,KAAC,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
1
|
+
{"version":3,"file":"GrLink-DYXw3W9w.js","names":[],"sources":["../../../../src/components/GrLink/GrLink.vue","../../../../src/components/GrLink/GrLink.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n","<script setup lang=\"ts\">\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n/**\n * GrLink — GR-примитив ссылки.\n *\n * Корневой тег зависит от пропов:\n * - кастомный компонент/тег из пропа `as` — когда `as` передан и не `disabled`\n * (например, `Link` от `@inertiajs/vue3` или `RouterLink` от Vue Router);\n * - `<a>` — когда задан `href` и не `disabled`;\n * - `<span>` — во всех остальных случаях (включая `disabled`, даже если задан\n * `href`/`as`).\n *\n * Это осознанное решение, чтобы отключённая ссылка не была кликабельной и\n * не участвовала в порядке табуляции. Учтите это в внешних CSS-селекторах.\n *\n * Проп `as` нужен для интеграции с роутерами (Inertia, Vue Router и пр.):\n * стили/поведение GR остаются едиными, а тег корня подменяется. Все\n * специфичные для роутера атрибуты (`method`, `replace`, `preserve-scroll` и т.п.)\n * проходят через fallthrough-attrs.\n */\nimport { computed, markRaw, useAttrs, type Component } from 'vue'\n\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconExternal from '~icons/lucide/external-link'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n baseRootClass,\n focusRingClass,\n type GrLinkSize,\n type GrLinkTone,\n type GrLinkUnderline,\n type GrLinkVariant,\n grLinkClass,\n grLinkColorStyle,\n} from './grLinkStyles'\n\nexport interface GrLinkProps {\n /**\n * Кастомный корневой тег/компонент. Если передан и компонент не `disabled` —\n * рендерится через `<component :is=\"as\">`. Игнорируется при `disabled`.\n */\n as?: string | Component\n href?: string\n external?: boolean\n target?: string\n rel?: string\n disabled?: boolean\n ariaLabel?: string\n /** Семантический цвет ссылки из палитры `GrTone`. */\n tone?: GrLinkTone\n /** Уровень акцента: `default` (окрашен) или `muted` (приглушён, акцент на hover). */\n variant?: GrLinkVariant\n underline?: GrLinkUnderline\n size?: GrLinkSize\n /**\n * Иконка внешней ссылки. По умолчанию показывается у любой ссылки, которая\n * открывается в новой вкладке, — не только при `external`.\n */\n externalIcon?: boolean\n /** i18n: скрытая подсказка «откроется в новой вкладке». */\n newTabLabel?: string\n}\n\ndefineOptions({\n inheritAttrs: false,\n})\n\nconst props = withDefaults(defineProps<GrLinkProps>(), {\n as: undefined,\n href: undefined,\n external: false,\n target: undefined,\n rel: undefined,\n disabled: false,\n ariaLabel: undefined,\n tone: 'primary',\n variant: 'default',\n underline: 'auto',\n size: undefined,\n externalIcon: undefined,\n newTabLabel: undefined,\n})\n\nconst attrs = useAttrs()\n\n// Кастомный корень имеет приоритет над `<a>` — GR не пытается угадывать\n// «правильный» тег за пользователя, если он явно его указал.\nconst isInteractive = computed(() => !props.disabled && (!!props.as || !!props.href))\n\nconst renderAs = computed<string | Component>(() => {\n if (!isInteractive.value)\n return 'span'\n if (props.as) {\n // Если `as` — объект-компонент, помечаем его как нереактивный, чтобы избежать\n // оверхеда от Vue-прокси и предупреждений в консоли. Строки (нативные теги)\n // оставляем как есть.\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n }\n return 'a'\n})\n\nconst resolvedTarget = computed(() => {\n if (props.target)\n return props.target\n\n if (props.external)\n return '_blank'\n\n return undefined\n})\n\nconst resolvedRel = computed(() => {\n if (props.rel)\n return props.rel\n\n // Авто-защита для любых ссылок, открывающихся в новой вкладке,\n // а не только когда выставлен `external`.\n if (resolvedTarget.value === '_blank')\n return 'noopener noreferrer'\n\n return undefined\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrLink' })\n\nconst { t } = useGranularityTranslations()\n\n/**\n * Смена контекста должна быть объявлена (WCAG 3.2.5). Условие — ссылка реально\n * открывается в новой вкладке, а не наличие пропа `external`: `target=\"_blank\"`\n * снаружи даёт ровно тот же сюрприз (та же генерализация, что у `rel`).\n */\nconst opensInNewTab = computed(() => isInteractive.value && resolvedTarget.value === '_blank')\nconst newTabHint = computed(() => props.newTabLabel ?? t('gr.link.opensInNewTab', 'opens in a new tab'))\n\nconst showExternalIcon = computed(() => props.externalIcon ?? opensInNewTab.value)\n\n/**\n * `aria-label` перекрывает содержимое целиком — вместе со скрытой подсказкой.\n * Поэтому, если имя задано руками, подсказка дописывается к нему.\n */\nconst resolvedAriaLabel = computed(() => {\n if (!props.ariaLabel)\n return undefined\n\n return opensInNewTab.value ? `${props.ariaLabel}, ${newTabHint.value}` : props.ariaLabel\n})\n\nconst iconSize = computed(() => (resolvedSize.value === 'lg' ? 'sm' : 'xs'))\n\nconst rootClass = computed(() => {\n const variantClass = grLinkClass({\n size: resolvedSize.value,\n underline: props.underline,\n disabled: props.disabled,\n })\n\n // На disabled-ветке (`<span>`) focus-ring не нужен: элемент не фокусируется,\n // а если снаружи ему навесят `tabindex`, фокус-рамка на «отключённой» ссылке смотрелась бы странно.\n return props.disabled\n ? `${baseRootClass} ${variantClass}`\n : `${baseRootClass} ${focusRingClass} ${variantClass}`\n})\n\n// Цвет (tone × variant) прокидываем через CSS-переменные, чтобы не плодить классы.\nconst colorStyle = computed(() => grLinkColorStyle({\n tone: props.tone,\n variant: props.variant,\n disabled: props.disabled,\n}))\n\ndefineSlots<{\n /** Текст ссылки. */\n default?: () => any\n}>()\n</script>\n\n<template>\n <component\n :is=\"renderAs\"\n v-bind=\"attrs\"\n :href=\"isInteractive ? href : undefined\"\n :target=\"isInteractive ? resolvedTarget : undefined\"\n :rel=\"isInteractive ? resolvedRel : undefined\"\n :aria-label=\"resolvedAriaLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"colorStyle\"\n >\n <slot />\n\n <GrIcon v-if=\"showExternalIcon\" :size=\"iconSize\">\n <IconExternal />\n </GrIcon>\n\n <!-- Подсказка не дублируется, когда её уже вобрал `aria-label`. -->\n <span v-if=\"opensInNewTab && !ariaLabel\" class=\"sr-only\">({{ newTabHint }})</span>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAoEA,MAAM,QAAQ;EAgBd,MAAM,QAAQ,SAAS;EAIvB,MAAM,gBAAgB,eAAe,CAAC,MAAM,aAAa,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,MAAM,KAAK;EAEpF,MAAM,WAAW,eAAmC;GAClD,IAAI,CAAC,cAAc,OACjB,OAAO;GACT,IAAI,MAAM,IAIR,OAAO,OAAO,MAAM,OAAO,WAAW,MAAM,KAAK,QAAQ,MAAM,EAAE;GAEnE,OAAO;EACT,CAAC;EAED,MAAM,iBAAiB,eAAe;GACpC,IAAI,MAAM,QACR,OAAO,MAAM;GAEf,IAAI,MAAM,UACR,OAAO;EAGX,CAAC;EAED,MAAM,cAAc,eAAe;GACjC,IAAI,MAAM,KACR,OAAO,MAAM;GAIf,IAAI,eAAe,UAAU,UAC3B,OAAO;EAGX,CAAC;EAED,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,SAAS,CAAC;EAEjF,MAAM,EAAE,MAAM,2BAA2B;;;;;;EAOzC,MAAM,gBAAgB,eAAe,cAAc,SAAS,eAAe,UAAU,QAAQ;EAC7F,MAAM,aAAa,eAAe,MAAM,eAAe,EAAE,yBAAyB,oBAAoB,CAAC;EAEvG,MAAM,mBAAmB,eAAe,MAAM,gBAAgB,cAAc,KAAK;;;;;EAMjF,MAAM,oBAAoB,eAAe;GACvC,IAAI,CAAC,MAAM,WACT,OAAO,KAAA;GAET,OAAO,cAAc,QAAQ,GAAG,MAAM,UAAU,IAAI,WAAW,UAAU,MAAM;EACjF,CAAC;EAED,MAAM,WAAW,eAAgB,aAAa,UAAU,OAAO,OAAO,IAAK;EAE3E,MAAM,YAAY,eAAe;GAC/B,MAAM,eAAe,YAAY;IAC/B,MAAM,aAAa;IACnB,WAAW,MAAM;IACjB,UAAU,MAAM;GAClB,CAAC;GAID,OAAO,MAAM,WACT,GAAG,cAAc,GAAG,iBACpB,GAAG,cAAc,GAAG,eAAe,GAAG;EAC5C,CAAC;EAGD,MAAM,aAAa,eAAe,iBAAiB;GACjD,MAAM,MAAM;GACZ,SAAS,MAAM;GACf,UAAU,MAAM;EAClB,CAAC,CAAC;;uBASA,YAmBY,wBAlBL,SAAA,KAAQ,GADf,WAEU,MAiBE,KAAA,GAjBG;IACZ,MAAM,cAAA,QAAgB,QAAA,OAAO,KAAA;IAC7B,QAAQ,cAAA,QAAgB,eAAA,QAAiB,KAAA;IACzC,KAAK,cAAA,QAAgB,YAAA,QAAc,KAAA;IACnC,cAAY,kBAAA;IACZ,iBAAe,QAAA,WAAQ,SAAY,KAAA;IACnC,OAAO,UAAA;IACP,OAAO,WAAA;;2BAEA;KAAR,WAAQ,KAAA,QAAA,SAAA;KAEM,iBAAA,SAAA,UAAA,GAAd,YAES,gBAAA;;MAFwB,MAAM,SAAA;;6BACrB,CAAhB,YAAgB,MAAA,qBAAA,CAAA,CAAA,CAAA;;;KAIN,cAAA,SAAa,CAAK,QAAA,aAAA,UAAA,GAA9B,mBAAkF,QAAlF,YAAyD,MAAC,gBAAG,WAAA,KAAU,IAAG,KAAC,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -193,7 +193,7 @@ var GrList_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE_
|
|
|
193
193
|
}, 8, ["variant"]);
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
|
-
}), [["__scopeId", "data-v-
|
|
196
|
+
}), [["__scopeId", "data-v-82e42e28"]]);
|
|
197
197
|
//#endregion
|
|
198
198
|
//#region src/components/GrList/GrListItem.vue?vue&type=script&setup=true&lang.ts
|
|
199
199
|
var _hoisted_1 = ["aria-disabled"];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrList-B_CRJB4c.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)\n 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')\n return key(item, index)\n if (typeof key === 'string')\n 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)\n 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)\n return undefined\n return typeof value === 'number' ? `${value}px` : value\n})\n\nconst listStyle = computed(() => {\n if (props.maxHeight === undefined)\n return undefined\n\n const scroller = { overflowY: 'auto' as const, maxHeight: maxHeightStyle.value }\n if (!virtualEnabled.value)\n 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)\n return\n // В состояниях loading/empty дети контейнера — скелетоны и заглушки: их\n // высоты записались бы в замеры под индексами будущих строк данных.\n if (!showItems.value)\n return\n\n const container = listEl.value\n if (!container)\n 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)\n 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)\n 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\ndefineSlots<{\n /** Пункты списка, когда разметка пишется руками вместо `items`. */\n default?: () => any\n /**\n * Пункт в data-режиме.\n *\n * `aria` проставляется только в виртуальном режиме — когда набор в DOM\n * неполный и размер списка браузеру взять неоткуда.\n */\n item?: (props: { item: T, index: number, aria: Record<string, number> }) => any\n /** Содержимое, пока едут данные. */\n loading?: () => any\n /** Пустое состояние вместо текста по умолчанию. */\n empty?: () => any\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)\n 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')\n return key(item, index)\n if (typeof key === 'string')\n 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)\n 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)\n return undefined\n return typeof value === 'number' ? `${value}px` : value\n})\n\nconst listStyle = computed(() => {\n if (props.maxHeight === undefined)\n return undefined\n\n const scroller = { overflowY: 'auto' as const, maxHeight: maxHeightStyle.value }\n if (!virtualEnabled.value)\n 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)\n return\n // В состояниях loading/empty дети контейнера — скелетоны и заглушки: их\n // высоты записались бы в замеры под индексами будущих строк данных.\n if (!showItems.value)\n return\n\n const container = listEl.value\n if (!container)\n 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)\n 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)\n 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\ndefineSlots<{\n /** Пункты списка, когда разметка пишется руками вместо `items`. */\n default?: () => any\n /**\n * Пункт в data-режиме.\n *\n * `aria` проставляется только в виртуальном режиме — когда набор в DOM\n * неполный и размер списка браузеру взять неоткуда.\n */\n item?: (props: { item: T, index: number, aria: Record<string, number> }) => any\n /** Содержимое, пока едут данные. */\n loading?: () => any\n /** Пустое состояние вместо текста по умолчанию. */\n empty?: () => any\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))\n return\n\n console.warn(\n `[granularity] GrListItem: as=\"${as}\" не попадает в таб-порядок — строка кликается `\n + 'мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (`button`, `a` со ссылкой), '\n + 'или компонент роутера.',\n )\n },\n { immediate: true },\n )\n}\n\ndefineSlots<{\n /** Содержимое пункта вместо пары «заголовок и описание». */\n default?: () => any\n /** Аддон слева: иконка, аватар, чекбокс. */\n prefix?: () => any\n /** Заголовок пункта вместо пропа `title`. */\n title?: () => any\n /** Описание под заголовком вместо пропа `description`. */\n description?: () => any\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))\n return\n\n console.warn(\n `[granularity] GrListItem: as=\"${as}\" не попадает в таб-порядок — строка кликается `\n + 'мышью, но не с клавиатуры. Возьмите тег, умеющий фокус (`button`, `a` со ссылкой), '\n + 'или компонент роутера.',\n )\n },\n { immediate: true },\n )\n}\n\ndefineSlots<{\n /** Содержимое пункта вместо пары «заголовок и описание». */\n default?: () => any\n /** Аддон слева: иконка, аватар, чекбокс. */\n prefix?: () => any\n /** Заголовок пункта вместо пропа `title`. */\n title?: () => any\n /** Описание под заголовком вместо пропа `description`. */\n description?: () => any\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-D0W8mGFD.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-B_CRJB4c.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-82e42e28]:before,[data-gr-virtual][data-v-82e42e28]:after{content:"";flex:none;display:block}[data-gr-virtual][data-v-82e42e28]:before{height:var(--gr-virtual-before,0px)}[data-gr-virtual][data-v-82e42e28]:after{height:var(--gr-virtual-after,0px)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as useGrComponentProp } from "../../../chunks/context-CTbuwcYs.js";
|
|
2
2
|
import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
|
|
3
|
-
import { t as useModalOverlay } from "../../../chunks/useModalOverlay-
|
|
3
|
+
import { t as useModalOverlay } from "../../../chunks/useModalOverlay-D__CqLGn.js";
|
|
4
4
|
import { a as overlayTransition, d as panelSectionClass, f as panelTransition, i as overlay, m as root, n as getGrModalShellClass, r as layoutByScroll, s as panelBodyScrollClass, t as getGrModalPanelClass } from "../../../chunks/grModalStyles-EZfuEiw_.js";
|
|
5
5
|
import { Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, mergeProps, normalizeClass, onScopeDispose, openBlock, provide, ref, renderSlot, toHandlers, unref, useId, useSlots, watch, withCtx } from "vue";
|
|
6
6
|
//#region src/components/GrModal/context.ts
|
|
@@ -191,7 +191,7 @@ var GrModal_default = /* @__PURE__ */ defineComponent({
|
|
|
191
191
|
function toggle() {
|
|
192
192
|
emit("update:modelValue", !props.modelValue);
|
|
193
193
|
}
|
|
194
|
-
const { rootEl, isMounted, isVisible, inertAttr, portalTarget, teleportEnabled, themeAttrs, onPanelAfterLeave: releasePresence, backdrop } = useModalOverlay(computed(() => props.modelValue), close, {
|
|
194
|
+
const { rootEl, isMounted, isVisible, inertAttr, layerZIndex, portalTarget, teleportEnabled, themeAttrs, onPanelAfterLeave: releasePresence, backdrop } = useModalOverlay(computed(() => props.modelValue), close, {
|
|
195
195
|
panel: panelRef,
|
|
196
196
|
closeOnEscape: () => props.closeOnEsc,
|
|
197
197
|
closeOnBackdrop: () => props.closeOnBackdrop,
|
|
@@ -224,6 +224,7 @@ var GrModal_default = /* @__PURE__ */ defineComponent({
|
|
|
224
224
|
role: "dialog",
|
|
225
225
|
"aria-modal": "true",
|
|
226
226
|
class: unref(root),
|
|
227
|
+
style: { zIndex: unref(layerZIndex) },
|
|
227
228
|
inert: unref(inertAttr),
|
|
228
229
|
"aria-labelledby": labelledBy.value,
|
|
229
230
|
"aria-describedby": describedBy.value,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrModal-D0mf1SAx.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)\n 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)\n return\n released = true\n remove()\n }\n}\n\nfunction claimTitle(): () => void {\n return claim(\n () => {\n titleClaims += 1\n hasTitle.value = true\n },\n () => {\n titleClaims -= 1\n hasTitle.value = titleClaims > 0\n },\n )\n}\n\nfunction claimDescription(): () => void {\n return claim(\n () => {\n descriptionClaims += 1\n hasDescription.value = true\n },\n () => {\n descriptionClaims -= 1\n hasDescription.value = descriptionClaims > 0\n },\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)\n 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)\n return\n if (!__GR_DEV__)\n return\n if (slots.title || props.ariaLabel)\n 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__)\n return\n if (titleClaims <= 1)\n 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 layerZIndex,\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)\n 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 :style=\"{ zIndex: layerZIndex }\"\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)\n return\n released = true\n remove()\n }\n}\n\nfunction claimTitle(): () => void {\n return claim(\n () => {\n titleClaims += 1\n hasTitle.value = true\n },\n () => {\n titleClaims -= 1\n hasTitle.value = titleClaims > 0\n },\n )\n}\n\nfunction claimDescription(): () => void {\n return claim(\n () => {\n descriptionClaims += 1\n hasDescription.value = true\n },\n () => {\n descriptionClaims -= 1\n hasDescription.value = descriptionClaims > 0\n },\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)\n 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)\n return\n if (!__GR_DEV__)\n return\n if (slots.title || props.ariaLabel)\n 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__)\n return\n if (titleClaims <= 1)\n 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 layerZIndex,\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)\n 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 :style=\"{ zIndex: layerZIndex }\"\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-D0mf1SAx.js";
|
|
2
2
|
import { n as grModalSafelist, t as grModalConfig } from "../../chunks/config-mlheIMtV.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-Dmf0c9ba.js";
|
|
3
|
-
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-
|
|
3
|
+
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-7GP0zObj.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-2ZHRsdOc.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-C5EjMKaC.js.map → GrNavbar-_rtHKwTf.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrNavbar-
|
|
1
|
+
{"version":3,"file":"GrNavbar-_rtHKwTf.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-_rtHKwTf.js";
|
|
2
2
|
import { n as grNavbarSafelist, t as grNavbarConfig } from "../../chunks/config-2ZHRsdOc.js";
|
|
3
3
|
export { GrNavbar_default as GrNavbar, GrNavbar_default as default, grNavbarConfig, grNavbarSafelist };
|