@feugene/granularity 0.30.0 → 0.31.1
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-DO8mjeIo.js → config-CIkdsWUk.js} +6 -2
- package/dist/chunks/config-CIkdsWUk.js.map +1 -0
- 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/{grInputStyles-CAk3xKIb.js → grInputStyles-DgQYjJvD.js} +17 -1
- package/dist/chunks/{grInputStyles-CAk3xKIb.js.map → grInputStyles-DgQYjJvD.js.map} +1 -1
- 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-CHFaw3wU.js → granular-provider-Da2CJO6j.js} +4 -4
- package/dist/chunks/{granular-provider-CHFaw3wU.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.map +1 -1
- 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.map +1 -1
- 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-CE-xtNP-.js → GrColorPicker-B20gQ6AR.js} +2 -2
- package/dist/components/GrColorPicker/chunks/GrColorPicker-B20gQ6AR.js.map +1 -0
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-C5abL2Y7.js → GrCommandPalette-Cd0Yo3Dw.js} +1 -1
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-Cd0Yo3Dw.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-DOsbRhoZ.js} +3 -3
- package/dist/components/GrConfirmDialog/chunks/GrConfirmDialog-DOsbRhoZ.js.map +1 -0
- package/dist/components/GrConfirmDialog/index.js +1 -1
- package/dist/components/GrContextMenu/chunks/GrContextMenu-_FKwQvuQ.js.map +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-S1Vl-OhX.js} +1 -1
- package/dist/components/GrDialog/chunks/{GrDialog-bOUDYfmh.js.map → GrDialog-S1Vl-OhX.js.map} +1 -1
- package/dist/components/GrDialog/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-k4SYIv64.js → GrDialogService-Cuhc4YLp.js} +4 -4
- package/dist/components/GrDialogService/chunks/GrDialogService-Cuhc4YLp.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-C0YqVE9e.js} +1 -1
- package/dist/components/GrDrawer/chunks/GrDrawer-C0YqVE9e.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.map +1 -1
- package/dist/components/GrInput/chunks/{GrInput-BqqoEl1b.js → GrInput-BLywRZUN.js} +10 -6
- package/dist/components/GrInput/chunks/GrInput-BLywRZUN.js.map +1 -0
- package/dist/components/GrInput/index.js +2 -2
- package/dist/components/GrInputTag/chunks/GrInputTag-Cj4A9Xgs.js.map +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-CgwjoUJt.js → GrJsonViewer-BCZ7lE2k.js} +3 -3
- 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.map +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-kYCx6PAS.js → GrPagination-B_kAhoAV.js} +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-kYCx6PAS.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.map +1 -1
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-D1ADRFxx.js → GrPromptDialog-BuJQMZLM.js} +5 -5
- package/dist/components/GrPromptDialog/chunks/GrPromptDialog-BuJQMZLM.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-CBp0twbF.js → GrSelect-gCeRFgQo.js} +2 -2
- 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-CymO68Cv.js → GrTreeSelect-BHwgGJcT.js} +2 -2
- 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/useFocusTrap.js.map +1 -1
- 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 +35 -35
- 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 +29 -13
- package/dist/types/components/GrInput/grInputStyles.d.ts +16 -0
- package/dist/types/components/GrSlider/grSliderStyles.d.ts +9 -1
- package/dist/types/components/GrTabPanels/GrTabPanel.vue.d.ts +3 -3
- package/dist/vue/index.js.map +1 -1
- package/dist/web-types.json +10 -1
- package/package.json +3 -3
- package/dist/chunks/config-DO8mjeIo.js.map +0 -1
- package/dist/chunks/grButtonStyles-B_-YRE__.js.map +0 -1
- package/dist/chunks/grSliderStyles-B5uNxNJq.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-CE-xtNP-.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/GrDataTable/chunks/GrDataTable-C8a9_nCg.js.map +0 -1
- package/dist/components/GrDialogService/chunks/GrDialogService-k4SYIv64.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/GrInput/chunks/GrInput-BqqoEl1b.js.map +0 -1
- package/dist/components/GrJsonViewer/chunks/GrJsonViewer-CgwjoUJt.js.map +0 -1
- package/dist/components/GrList/chunks/GrList-DmT4qzLO.js.map +0 -1
- package/dist/components/GrPromptDialog/chunks/GrPromptDialog-D1ADRFxx.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-CBp0twbF.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-CymO68Cv.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrPromptDialog-D1ADRFxx.js","names":[],"sources":["../../../../src/components/GrPromptDialog/GrPromptDialog.vue","../../../../src/components/GrPromptDialog/GrPromptDialog.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/**\n * GrPromptDialog — GR-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n *\n * Проверки сверх `required` описываются пропом `rules` — тем же движком, что и\n * у `GrForm`: третий частный случай валидации в пакете заводить незачем.\n */\nimport { useGrComponentProp } from '../GrConfigProvider/context'\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTextarea from '../GrTextarea/GrTextarea.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n// Именованные импорты чистых функций: разметки не тянут, поэтому в\n// `config.dependencies` не объявляются (см. правило про зависимости).\nimport { createGrFormMessageResolver, rulesForTrigger, runFieldRules } from '../GrForm/validation'\nimport type { GrFormRule, GrFormTrigger } from '../GrForm/validation'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\n/**\n * Тип поля выводим из самого `GrInput`, а не заводим ему именованный алиас:\n * алиас в пропе `GrInput` заменил бы в таблице API витрины список допустимых\n * значений на своё имя — документация стала бы беднее ради удобства обёртки.\n */\ntype GrInputType = NonNullable<InstanceType<typeof GrInput>['$props']['type']>\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\nimport type { InputHTMLAttributes } from 'vue'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /** Тип однострочного поля. В многострочном режиме не применяется. */\n inputType?: GrInputType\n /** Программная клавиатура на мобильных. */\n inputmode?: InputHTMLAttributes['inputmode']\n /** Ограничение длины; со `showCount` рисуется счётчик. */\n maxlength?: number\n showCount?: boolean\n /** Многострочный ввод: вместо `GrInput` рисуется `GrTextarea`. */\n multiline?: boolean\n /** Высота многострочного поля в строках. */\n rows?: number\n /** Автоподбор высоты многострочного поля под содержимое. */\n autosize?: boolean\n /**\n * Правила проверки значения — те же, что у `GrForm` (`required`, `type`,\n * `min`/`max`/`len`, `pattern`, свой в т.ч. асинхронный `validator`).\n * Прогоняются на blur (после первого касания) и на подтверждении.\n */\n rules?: GrFormRule | GrFormRule[]\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n /**\n * Запрет закрытия «мягкими» способами (Esc, клик по бэкдропу), пока идёт\n * подтверждение или проверка `rules`. Кнопка закрытия и «Отмена» остаются:\n * окно без единого выхода — ловушка.\n */\n persistent?: boolean\n}\n\nexport interface GrPromptDialogEmits {\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}\n\nimport './defaults'\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: undefined,\n description: undefined,\n label: undefined,\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n // Дефолт `size` живёт в резолвере ниже, а не здесь: Vue подставил бы его\n // до того, как компонент заглянет в `GrConfigProvider`.\n size: undefined,\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: undefined,\n buttonSize: undefined,\n confirmText: undefined,\n cancelText: undefined,\n requiredErrorText: undefined,\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n inputType: 'text',\n inputmode: undefined,\n maxlength: undefined,\n showCount: false,\n multiline: false,\n rows: undefined,\n autosize: false,\n rules: undefined,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n persistent: false,\n})\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentProp('GrPromptDialog', 'size', () => props.size, 'md')\n\nconst emit = defineEmits<GrPromptDialogEmits>()\n\n// Дефолты берём из общего i18n-блока пакета (fallback — англ.), а не хардкодим.\nconst { t } = useGranularityTranslations()\nconst resolvedTitle = computed(() => props.title ?? t('gr.dialog.prompt.title', 'Prompt'))\nconst resolvedLabel = computed(() => props.label ?? t('gr.dialog.prompt.label', 'Value'))\nconst resolvedRequiredErrorText = computed(() => props.requiredErrorText ?? t('gr.dialog.prompt.required', 'Enter a value.'))\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? t('gr.common.close', 'Close'))\nconst resolvedConfirmText = computed(() => props.confirmText ?? t('gr.common.confirm', 'Confirm'))\nconst resolvedCancelText = computed(() => props.cancelText ?? t('gr.common.cancel', 'Cancel'))\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst textareaRef = ref<InstanceType<typeof GrTextarea> | null>(null)\n\n/** Ошибка последнего прогона `rules`; `required` считается отдельно и мгновенно. */\nconst ruleError = ref<string | undefined>(undefined)\nconst validating = ref(false)\n\n// Сторож устаревших прогонов: асинхронное правило может ответить после того,\n// как значение уже сменилось, и дописать чужую ошибку.\nlet validationRun = 0\n\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (!isOpen) return\n\n touched.value = false\n ruleError.value = undefined\n validationRun += 1\n },\n)\n\nconst resolveMessage = createGrFormMessageResolver(t)\n\nconst normalizedRules = computed<GrFormRule[]>(() => {\n if (!props.rules) return []\n return Array.isArray(props.rules) ? props.rules : [props.rules]\n})\n\nconst requiredError = computed(() => {\n if (!props.required) return undefined\n return valueModel.value.trim().length > 0 ? undefined : resolvedRequiredErrorText.value\n})\n\nconst canConfirm = computed(() => !requiredError.value)\n\nconst validationError = computed(() => {\n if (!touched.value) return undefined\n return requiredError.value ?? ruleError.value\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\n/**\n * Прогоняет `rules` для триггера. Возвращает сообщение или `undefined`.\n * `required` здесь не участвует: он синхронный и считается отдельно, чтобы\n * пустое поле блокировало кнопку без похода в асинхронный движок.\n */\nasync function runRules(trigger: GrFormTrigger): Promise<string | undefined> {\n const rules = rulesForTrigger(normalizedRules.value, trigger)\n if (!rules.length) return undefined\n\n const run = ++validationRun\n validating.value = true\n try {\n const message = await runFieldRules(valueModel.value, rules, { value: valueModel.value }, resolveMessage)\n // Ответ устарел — значение или сам диалог успели смениться.\n if (run !== validationRun) return undefined\n\n ruleError.value = message\n return message\n }\n finally {\n if (run === validationRun) validating.value = false\n }\n}\n\nfunction onBlur(): void {\n touched.value = true\n void runRules('blur')\n}\n\n// Пока подтверждение или проверка в полёте, случайное движение не должно\n// оборвать операцию.\nconst softCloseBlocked = computed(() => props.persistent && (props.confirmLoading || validating.value))\nconst resolvedCloseOnBackdrop = computed(() => (softCloseBlocked.value ? false : props.closeOnBackdrop))\nconst resolvedCloseOnEsc = computed(() => (softCloseBlocked.value ? false : props.closeOnEsc))\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction focusField(): void {\n if (props.multiline) textareaRef.value?.focus()\n else inputRef.value?.focus()\n}\n\nasync function onConfirm(): Promise<void> {\n touched.value = true\n\n if (!canConfirm.value) {\n focusField()\n return\n }\n\n if (await runRules('submit')) {\n focusField()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n\n/**\n * `Enter` подтверждает — базовое ожидание от окна, единственный смысл которого\n * ввести одно значение. В многострочном режиме `Enter` остаётся переводом\n * строки, поэтому обработчик висит только на однострочном поле.\n */\nfunction onEnter(): void {\n void onConfirm()\n}\n\n// Фокус в поле, а не на панель: диалог существует ровно ради ввода.\n//\n// Через `initialFocus` у `GrModal` это не решается: элемент рождается внутри\n// поддерева диалога, и возврат его же пропом наверх замыкает рендер в цикл\n// («Maximum recursive updates» — проверено). Поэтому фокус ставит содержимое,\n// после отрисовки (`flush: 'post'` + кадр), то есть заведомо позже, чем\n// ловушка фокуса `GrModal` переводит фокус на панель.\n// Источник — не только `modelValue`, но и само поле: содержимое панели\n// появляется на такт позже открытия, и фокус по фиксированному `nextTick`\n// приходился на момент, когда фокусировать ещё нечего.\nwatch(\n [() => props.modelValue, inputRef, textareaRef],\n () => {\n if (!props.modelValue) return\n focusField()\n },\n // `immediate`: окно могут смонтировать уже открытым — тогда смены пропа не\n // будет вовсе, и без этого фокус остался бы на панели.\n { immediate: true, flush: 'post' },\n)\n\ndefineSlots<{\n /** Содержимое диалога вместо пропа `message`. */\n default?: () => any\n /** Разбор ошибки вместо встроенного баннера. */\n error?: (props: { error: ResponseErrorInfo | null }) => any\n /** Кнопки диалога вместо пары «отмена и подтверждение». */\n footer?: () => any\n}>()\n\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"resolvedTitle\"\n :size=\"resolvedSize\"\n :close-on-backdrop=\"resolvedCloseOnBackdrop\"\n :close-on-esc=\"resolvedCloseOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"resolvedCloseLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)] text-[var(--gr-muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <!-- `id` полю не задаём: `GrFormField` генерирует его сам, а поле читает\n из контекста. Литеральный id ломал два открытых диалога сразу. -->\n <GrFormField :label=\"resolvedLabel\" :error=\"fieldErrorMessage\">\n <GrTextarea\n v-if=\"multiline\"\n ref=\"textareaRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :autosize=\"autosize\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n />\n <GrInput\n v-else\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :type=\"inputType\"\n :inputmode=\"inputmode\"\n :placeholder=\"placeholder\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n @keydown.enter=\"onEnter\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"gr-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ resolvedCancelText }}\n </GrButton>\n <GrButton\n data-testid=\"gr-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading || validating\"\n :disabled=\"confirmDisabled || (touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ resolvedConfirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrPromptDialog — GR-примитив диалога ввода значения поверх `GrDialog`.\n *\n * Синхронизирован с `GrDialog` по набору проп-проксирования:\n * `size`, `closeOnBackdrop`, `closeOnEsc`, `showHeader`, `showCloseButton`,\n * `headerConfig`, `footerConfig`, `bodyConfig`, `closeLabel`.\n *\n * Поведение `required=true` (по умолчанию): пустое значение подсвечивается\n * ошибкой только после первого «касания» (blur или попытка confirm),\n * чтобы не шуметь при открытии.\n *\n * Проверки сверх `required` описываются пропом `rules` — тем же движком, что и\n * у `GrForm`: третий частный случай валидации в пакете заводить незачем.\n */\nimport { useGrComponentProp } from '../GrConfigProvider/context'\nimport { computed, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrDialog from '../GrDialog/GrDialog.vue'\nimport GrFormField from '../GrFormField/GrFormField.vue'\nimport GrInput from '../GrInput/GrInput.vue'\nimport GrTextarea from '../GrTextarea/GrTextarea.vue'\nimport GrResponseErrorBanner from '../GrResponseErrorBanner/GrResponseErrorBanner.vue'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n// Именованные импорты чистых функций: разметки не тянут, поэтому в\n// `config.dependencies` не объявляются (см. правило про зависимости).\nimport { createGrFormMessageResolver, rulesForTrigger, runFieldRules } from '../GrForm/validation'\nimport type { GrFormRule, GrFormTrigger } from '../GrForm/validation'\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\nimport type { GrDialogSectionConfig, GrDialogSize } from '../GrDialog'\n\n/**\n * Тип поля выводим из самого `GrInput`, а не заводим ему именованный алиас:\n * алиас в пропе `GrInput` заменил бы в таблице API витрины список допустимых\n * значений на своё имя — документация стала бы беднее ради удобства обёртки.\n */\ntype GrInputType = NonNullable<InstanceType<typeof GrInput>['$props']['type']>\nimport type { ResponseErrorInfo } from '../GrResponseErrorBanner'\nimport type { InputHTMLAttributes } from 'vue'\n\nexport interface GrPromptDialogProps {\n modelValue: boolean\n value: string\n title?: string\n description?: string\n label?: string\n placeholder?: string\n closeOnBackdrop?: boolean\n closeOnEsc?: boolean\n showHeader?: boolean\n showCloseButton?: boolean\n size?: GrDialogSize\n headerConfig?: GrDialogSectionConfig\n footerConfig?: GrDialogSectionConfig\n bodyConfig?: GrDialogSectionConfig\n /** A11y-лейбл кнопки закрытия (i18n). */\n closeLabel?: string\n buttonSize?: GrButtonSize\n confirmText?: string\n cancelText?: string\n /** Текст ошибки для пустого значения при `required=true` (i18n). */\n requiredErrorText?: string\n confirmVariant?: GrButtonVariant\n confirmTone?: GrButtonTone\n required?: boolean\n /** Тип однострочного поля. В многострочном режиме не применяется. */\n inputType?: GrInputType\n /** Программная клавиатура на мобильных. */\n inputmode?: InputHTMLAttributes['inputmode']\n /** Ограничение длины; со `showCount` рисуется счётчик. */\n maxlength?: number\n showCount?: boolean\n /** Многострочный ввод: вместо `GrInput` рисуется `GrTextarea`. */\n multiline?: boolean\n /** Высота многострочного поля в строках. */\n rows?: number\n /** Автоподбор высоты многострочного поля под содержимое. */\n autosize?: boolean\n /**\n * Правила проверки значения — те же, что у `GrForm` (`required`, `type`,\n * `min`/`max`/`len`, `pattern`, свой в т.ч. асинхронный `validator`).\n * Прогоняются на blur (после первого касания) и на подтверждении.\n */\n rules?: GrFormRule | GrFormRule[]\n /**\n * Структура ошибки ответа сервера для показа общим блоком в теле диалога\n * (через `GrResponseErrorBanner`). `null` — блок скрыт.\n */\n error?: ResponseErrorInfo | null\n /**\n * Внешняя ошибка поля ввода (например, серверная валидация). Имеет приоритет\n * над встроенной проверкой. `null`/`undefined` — нет внешней ошибки.\n */\n fieldError?: string | null\n /** Состояние загрузки кнопки Confirm (async-`onConfirm` in-flight). */\n confirmLoading?: boolean\n /** Принудительно дизейблит кнопку Confirm. */\n confirmDisabled?: boolean\n /**\n * Закрывать ли диалог автоматически по клику Confirm. По умолчанию `true`.\n * `false` — отдаёт управление закрытием наружу (нужно `useDialogService`).\n */\n closeOnConfirm?: boolean\n /**\n * Запрет закрытия «мягкими» способами (Esc, клик по бэкдропу), пока идёт\n * подтверждение или проверка `rules`. Кнопка закрытия и «Отмена» остаются:\n * окно без единого выхода — ловушка.\n */\n persistent?: boolean\n}\n\nexport interface GrPromptDialogEmits {\n (e: 'update:modelValue', value: boolean): void\n (e: 'update:value', value: string): void\n (e: 'confirm', value: string): void\n (e: 'cancel'): void\n}\n\nimport './defaults'\n\nconst props = withDefaults(defineProps<GrPromptDialogProps>(), {\n title: undefined,\n description: undefined,\n label: undefined,\n placeholder: undefined,\n closeOnBackdrop: true,\n closeOnEsc: true,\n showHeader: true,\n showCloseButton: true,\n // Дефолт `size` живёт в резолвере ниже, а не здесь: Vue подставил бы его\n // до того, как компонент заглянет в `GrConfigProvider`.\n size: undefined,\n headerConfig: undefined,\n footerConfig: undefined,\n bodyConfig: undefined,\n closeLabel: undefined,\n buttonSize: undefined,\n confirmText: undefined,\n cancelText: undefined,\n requiredErrorText: undefined,\n confirmVariant: 'primary',\n confirmTone: 'primary',\n required: true,\n inputType: 'text',\n inputmode: undefined,\n maxlength: undefined,\n showCount: false,\n multiline: false,\n rows: undefined,\n autosize: false,\n rules: undefined,\n error: null,\n fieldError: null,\n confirmLoading: false,\n confirmDisabled: false,\n closeOnConfirm: true,\n persistent: false,\n})\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentProp('GrPromptDialog', 'size', () => props.size, 'md')\n\nconst emit = defineEmits<GrPromptDialogEmits>()\n\n// Дефолты берём из общего i18n-блока пакета (fallback — англ.), а не хардкодим.\nconst { t } = useGranularityTranslations()\nconst resolvedTitle = computed(() => props.title ?? t('gr.dialog.prompt.title', 'Prompt'))\nconst resolvedLabel = computed(() => props.label ?? t('gr.dialog.prompt.label', 'Value'))\nconst resolvedRequiredErrorText = computed(() => props.requiredErrorText ?? t('gr.dialog.prompt.required', 'Enter a value.'))\nconst resolvedCloseLabel = computed(() => props.closeLabel ?? t('gr.common.close', 'Close'))\nconst resolvedConfirmText = computed(() => props.confirmText ?? t('gr.common.confirm', 'Confirm'))\nconst resolvedCancelText = computed(() => props.cancelText ?? t('gr.common.cancel', 'Cancel'))\n\nconst open = computed({\n get: () => props.modelValue,\n set: (v: boolean) => emit('update:modelValue', v),\n})\n\nconst valueModel = computed({\n get: () => props.value,\n set: (v: string) => emit('update:value', v),\n})\n\nconst touched = ref(false)\nconst inputRef = ref<InstanceType<typeof GrInput> | null>(null)\nconst textareaRef = ref<InstanceType<typeof GrTextarea> | null>(null)\n\n/** Ошибка последнего прогона `rules`; `required` считается отдельно и мгновенно. */\nconst ruleError = ref<string | undefined>(undefined)\nconst validating = ref(false)\n\n// Сторож устаревших прогонов: асинхронное правило может ответить после того,\n// как значение уже сменилось, и дописать чужую ошибку.\nlet validationRun = 0\n\nwatch(\n () => props.modelValue,\n (isOpen) => {\n if (!isOpen) return\n\n touched.value = false\n ruleError.value = undefined\n validationRun += 1\n },\n)\n\nconst resolveMessage = createGrFormMessageResolver(t)\n\nconst normalizedRules = computed<GrFormRule[]>(() => {\n if (!props.rules) return []\n return Array.isArray(props.rules) ? props.rules : [props.rules]\n})\n\nconst requiredError = computed(() => {\n if (!props.required) return undefined\n return valueModel.value.trim().length > 0 ? undefined : resolvedRequiredErrorText.value\n})\n\nconst canConfirm = computed(() => !requiredError.value)\n\nconst validationError = computed(() => {\n if (!touched.value) return undefined\n return requiredError.value ?? ruleError.value\n})\n\n// Внешняя ошибка поля (серверная валидация) имеет приоритет над встроенной.\nconst fieldErrorMessage = computed(() => props.fieldError ?? validationError.value ?? undefined)\n\n/**\n * Прогоняет `rules` для триггера. Возвращает сообщение или `undefined`.\n * `required` здесь не участвует: он синхронный и считается отдельно, чтобы\n * пустое поле блокировало кнопку без похода в асинхронный движок.\n */\nasync function runRules(trigger: GrFormTrigger): Promise<string | undefined> {\n const rules = rulesForTrigger(normalizedRules.value, trigger)\n if (!rules.length) return undefined\n\n const run = ++validationRun\n validating.value = true\n try {\n const message = await runFieldRules(valueModel.value, rules, { value: valueModel.value }, resolveMessage)\n // Ответ устарел — значение или сам диалог успели смениться.\n if (run !== validationRun) return undefined\n\n ruleError.value = message\n return message\n }\n finally {\n if (run === validationRun) validating.value = false\n }\n}\n\nfunction onBlur(): void {\n touched.value = true\n void runRules('blur')\n}\n\n// Пока подтверждение или проверка в полёте, случайное движение не должно\n// оборвать операцию.\nconst softCloseBlocked = computed(() => props.persistent && (props.confirmLoading || validating.value))\nconst resolvedCloseOnBackdrop = computed(() => (softCloseBlocked.value ? false : props.closeOnBackdrop))\nconst resolvedCloseOnEsc = computed(() => (softCloseBlocked.value ? false : props.closeOnEsc))\n\nfunction onCancel(): void {\n emit('cancel')\n emit('update:modelValue', false)\n}\n\nfunction focusField(): void {\n if (props.multiline) textareaRef.value?.focus()\n else inputRef.value?.focus()\n}\n\nasync function onConfirm(): Promise<void> {\n touched.value = true\n\n if (!canConfirm.value) {\n focusField()\n return\n }\n\n if (await runRules('submit')) {\n focusField()\n return\n }\n\n emit('confirm', valueModel.value)\n if (props.closeOnConfirm)\n emit('update:modelValue', false)\n}\n\n/**\n * `Enter` подтверждает — базовое ожидание от окна, единственный смысл которого\n * ввести одно значение. В многострочном режиме `Enter` остаётся переводом\n * строки, поэтому обработчик висит только на однострочном поле.\n */\nfunction onEnter(): void {\n void onConfirm()\n}\n\n// Фокус в поле, а не на панель: диалог существует ровно ради ввода.\n//\n// Через `initialFocus` у `GrModal` это не решается: элемент рождается внутри\n// поддерева диалога, и возврат его же пропом наверх замыкает рендер в цикл\n// («Maximum recursive updates» — проверено). Поэтому фокус ставит содержимое,\n// после отрисовки (`flush: 'post'` + кадр), то есть заведомо позже, чем\n// ловушка фокуса `GrModal` переводит фокус на панель.\n// Источник — не только `modelValue`, но и само поле: содержимое панели\n// появляется на такт позже открытия, и фокус по фиксированному `nextTick`\n// приходился на момент, когда фокусировать ещё нечего.\nwatch(\n [() => props.modelValue, inputRef, textareaRef],\n () => {\n if (!props.modelValue) return\n focusField()\n },\n // `immediate`: окно могут смонтировать уже открытым — тогда смены пропа не\n // будет вовсе, и без этого фокус остался бы на панели.\n { immediate: true, flush: 'post' },\n)\n\ndefineSlots<{\n /** Содержимое диалога вместо пропа `message`. */\n default?: () => any\n /** Разбор ошибки вместо встроенного баннера. */\n error?: (props: { error: ResponseErrorInfo | null }) => any\n /** Кнопки диалога вместо пары «отмена и подтверждение». */\n footer?: () => any\n}>()\n\n</script>\n\n<template>\n <GrDialog\n v-model=\"open\"\n :title=\"resolvedTitle\"\n :size=\"resolvedSize\"\n :close-on-backdrop=\"resolvedCloseOnBackdrop\"\n :close-on-esc=\"resolvedCloseOnEsc\"\n :show-header=\"showHeader\"\n :show-close-button=\"showCloseButton\"\n :header-config=\"headerConfig\"\n :footer-config=\"footerConfig\"\n :body-config=\"bodyConfig\"\n :close-label=\"resolvedCloseLabel\"\n >\n <div class=\"grid gap-4\">\n <slot>\n <div v-if=\"description\" class=\"text-[length:var(--gr-control-text-md)] leading-[var(--gr-control-leading-md)] text-[var(--gr-muted-fg)]\">\n {{ description }}\n </div>\n </slot>\n\n <!-- `id` полю не задаём: `GrFormField` генерирует его сам, а поле читает\n из контекста. Литеральный id ломал два открытых диалога сразу. -->\n <GrFormField :label=\"resolvedLabel\" :error=\"fieldErrorMessage\">\n <GrTextarea\n v-if=\"multiline\"\n ref=\"textareaRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :placeholder=\"placeholder\"\n :rows=\"rows\"\n :autosize=\"autosize\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n />\n <GrInput\n v-else\n ref=\"inputRef\"\n v-model=\"valueModel\"\n data-testid=\"gr-prompt-input\"\n :type=\"inputType\"\n :inputmode=\"inputmode\"\n :placeholder=\"placeholder\"\n :maxlength=\"maxlength\"\n :show-count=\"showCount\"\n :invalid=\"!!fieldErrorMessage\"\n @blur=\"onBlur\"\n @keydown.enter=\"onEnter\"\n />\n </GrFormField>\n\n <slot name=\"error\" :error=\"error\">\n <GrResponseErrorBanner v-if=\"error\" :error=\"error\" :can-dismiss=\"false\" />\n </slot>\n </div>\n\n <template #footer>\n <slot name=\"footer\">\n <div class=\"flex items-center justify-end gap-3\">\n <GrButton data-testid=\"gr-prompt-cancel\" variant=\"outline\" :size=\"buttonSize\" @click=\"onCancel\">\n {{ resolvedCancelText }}\n </GrButton>\n <GrButton\n data-testid=\"gr-prompt-confirm\"\n :variant=\"confirmVariant\"\n :tone=\"confirmTone\"\n :size=\"buttonSize\"\n :loading=\"confirmLoading || validating\"\n :disabled=\"confirmDisabled || (touched && !canConfirm)\"\n @click=\"onConfirm\"\n >\n {{ resolvedConfirmText }}\n </GrButton>\n </div>\n </slot>\n </template>\n </GrDialog>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrRadio-C_rESc5g.js","names":[],"sources":["../../../../src/components/GrRadio/grRadioGroupContext.ts","../../../../src/components/GrRadio/GrRadio.vue","../../../../src/components/GrRadio/GrRadio.vue"],"sourcesContent":["import type { ComputedRef, InjectionKey } from 'vue'\n\nimport type { GrButtonSize } from '../GrButton/grButtonStyles'\n\n/**\n * Значение переключателя. Не только строка: перечисления в реальных формах —\n * это id числом или флаг. Объекты сюда намеренно не входят: значение уходит в\n * `data-value` и в скрытый input нативной формы, а значит должно иметь\n * однозначное строковое представление.\n */\nexport type GrRadioValue = string | number | boolean\n\nexport type GrRadioEntry = {\n value: () => GrRadioValue\n disabled: () => boolean\n /**\n * Корневой узел переключателя. Геттер, а не значение: регистрация идёт в\n * `setup`, когда элемента ещё нет. Благодаря ему группа переносит фокус, не\n * обходя DOM селекторами.\n */\n el: () => HTMLElement | null\n}\n\nexport type GrRadioGroupContext = {\n modelValue: ComputedRef<GrRadioValue>\n name: ComputedRef<string | undefined>\n disabled: ComputedRef<boolean>\n /**\n * Только для чтения: выбор видно, но он не меняется. У роли `radio` нет\n * `aria-readonly` — состояние объявляет сама группа, переключателю остаётся\n * не прикидываться кликабельным.\n */\n readonly: ComputedRef<boolean>\n /** Ошибка группы: до вида переключателя она доходит только через контекст. */\n invalid: ComputedRef<boolean>\n size: ComputedRef<GrButtonSize>\n setValue: (value: GrRadioValue) => void\n /**\n * Регистрация переключателя в группе. Порядок регистрации совпадает с\n * порядком в документе (setup детей идёт сверху вниз), поэтому группа знает\n * состав и в slot-режиме, и на сервере — без обхода DOM.\n */\n register: (entry: GrRadioEntry) => () => void\n /**\n * Значение переключателя, который держит `tabindex=\"0\"`. Roving tabindex\n * WAI-ARIA: группа — одна остановка Tab, внутрь попадают стрелками. Если не\n * выбрано ничего, остановкой становится первый доступный переключатель.\n */\n rovingValue: ComputedRef<GrRadioValue | undefined>\n /**\n * Стрелки, `Home`, `End`: переносят выбор и фокус на соседний доступный\n * переключатель. Вернул `true` — клавиша обработана (с `preventDefault`).\n * Кольцо ведёт группа, а не переключатель: только она знает состав.\n */\n handleNavigationKeys: (event: KeyboardEvent) => boolean\n}\n\nexport const GR_RADIO_GROUP_CONTEXT: InjectionKey<GrRadioGroupContext> = Symbol('GR_RADIO_GROUP_CONTEXT')\n","<script setup lang=\"ts\">\nimport { computed, inject, onUnmounted, ref, useId, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDescriptionSizes,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioDotSizes,\n grRadioLabelClassFor,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT, type GrRadioValue } from './grRadioGroupContext'\n\nexport type { GrRadioValue } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name`/`invalid` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n *\n * Доступным контролом является сам элемент с `role=\"radio\"`: он держит фокус,\n * `aria-checked` и клавиатуру. Внутрь него ничего интерактивного не вкладывается —\n * `role=\"radio\"` объявляет потомков презентационными, и вложенный native `<input>`\n * (даже скрытый и с `tabindex=\"-1\"`) ломает виджет для скринридеров и падает\n * в axe на `nested-interactive`. Для отправки нативной формой рядом рендерится\n * `input[type=\"hidden\"]` — он не фокусируется и не является интерактивным.\n */\nexport interface GrRadioProps {\n value: GrRadioValue\n modelValue?: GrRadioValue\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. Складывается с `invalid` группы. */\n invalid?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nexport interface GrRadioEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n}\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n invalid: false,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrRadioEmits>()\n\nconst slots = useSlots()\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedReadonly = computed(() => group?.readonly.value ?? false)\n\n// «Или», а не `??`: ошибку может объявить и группа, и сам переключатель.\nconst resolvedInvalid = computed(() => props.invalid || (group?.invalid.value ?? false))\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\n// Приоритет: локальный проп → размер группы → `GrConfigProvider` → `md`.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize(\n () => props.size ?? group?.size.value,\n { component: 'GrRadio' },\n)\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\n/** В DOM значение живёт строкой: `data-value` и скрытый input иначе не умеют. */\nconst domValue = computed(() => String(props.value))\n\n// Нативная форма получает значение только от выбранного и не-disabled элемента —\n// ровно как поступил бы native radio. Без `name` отправлять нечего.\nconst submitsValue = computed(\n () => checked.value && !resolvedDisabled.value && Boolean(resolvedName.value),\n)\n\nconst descriptionId = useId()\nconst hasDescription = computed(() => Boolean(slots.description))\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass({\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n}))\nconst controlClassName = computed(() => grRadioControlClass({\n size: resolvedSize.value,\n checked: checked.value,\n disabled: resolvedDisabled.value,\n invalid: resolvedInvalid.value,\n}))\nconst dotClassName = computed(() => grRadioDotClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n}))\nconst labelClassName = computed(() => grRadioLabelClassFor(resolvedSize.value, checked.value))\nconst dotSizeClassName = computed(() => grRadioDotSizes[resolvedSize.value])\nconst descriptionClassName = computed(() => grRadioDescriptionSizes[resolvedSize.value])\n\nfunction setValue(next: GrRadioValue): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n\n// Паттерн radiogroup: группа — одна остановка Tab, внутрь попадают стрелками.\n// Вне группы (одиночный `GrRadio`) переключатель остаётся обычной остановкой.\nconst rovingTabindex = computed(() => {\n if (resolvedDisabled.value) return -1\n if (!group) return 0\n return group.rovingValue.value === props.value ? 0 : -1\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n/** Кольцо ведёт группа: только она знает состав и порядок. */\nfunction onKeydown(event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n group.handleNavigationKeys(event)\n}\n\nconst unregister = group?.register({\n value: () => props.value,\n disabled: () => resolvedDisabled.value,\n el: () => rootEl.value,\n})\n\nonUnmounted(() => unregister?.())\n\ndefineSlots<{\n /** Подпись переключателя вместо пропа `label`. */\n default?: () => any\n /** Пояснение под подписью. */\n description?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :aria-describedby=\"hasDescription ? descriptionId : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n class=\"inline-flex items-start gap-2 select-none focus-visible:outline-none focus-visible:rounded-[var(--gr-radius-md)] focus-visible:shadow-[0_0_0_2px_var(--gr-ring),0_0_0_4px_var(--gr-bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"mt-0.5 shrink-0 rounded-[var(--gr-radius-full)] border flex items-center justify-center transition-colors duration-[var(--gr-duration-fast)]\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotSizeClassName, dotClassName]\"\n />\n </span>\n\n <span class=\"grid gap-0.5\">\n <span data-gr-radio-label :class=\"labelClassName\">\n <slot />\n </span>\n\n <span\n v-if=\"hasDescription\"\n :id=\"descriptionId\"\n data-gr-radio-description\n :class=\"descriptionClassName\"\n >\n <slot name=\"description\" />\n </span>\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, onUnmounted, ref, useId, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDescriptionSizes,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioDotSizes,\n grRadioLabelClassFor,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT, type GrRadioValue } from './grRadioGroupContext'\n\nexport type { GrRadioValue } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name`/`invalid` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n *\n * Доступным контролом является сам элемент с `role=\"radio\"`: он держит фокус,\n * `aria-checked` и клавиатуру. Внутрь него ничего интерактивного не вкладывается —\n * `role=\"radio\"` объявляет потомков презентационными, и вложенный native `<input>`\n * (даже скрытый и с `tabindex=\"-1\"`) ломает виджет для скринридеров и падает\n * в axe на `nested-interactive`. Для отправки нативной формой рядом рендерится\n * `input[type=\"hidden\"]` — он не фокусируется и не является интерактивным.\n */\nexport interface GrRadioProps {\n value: GrRadioValue\n modelValue?: GrRadioValue\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. Складывается с `invalid` группы. */\n invalid?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nexport interface GrRadioEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n}\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n invalid: false,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrRadioEmits>()\n\nconst slots = useSlots()\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedReadonly = computed(() => group?.readonly.value ?? false)\n\n// «Или», а не `??`: ошибку может объявить и группа, и сам переключатель.\nconst resolvedInvalid = computed(() => props.invalid || (group?.invalid.value ?? false))\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\n// Приоритет: локальный проп → размер группы → `GrConfigProvider` → `md`.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize(\n () => props.size ?? group?.size.value,\n { component: 'GrRadio' },\n)\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\n/** В DOM значение живёт строкой: `data-value` и скрытый input иначе не умеют. */\nconst domValue = computed(() => String(props.value))\n\n// Нативная форма получает значение только от выбранного и не-disabled элемента —\n// ровно как поступил бы native radio. Без `name` отправлять нечего.\nconst submitsValue = computed(\n () => checked.value && !resolvedDisabled.value && Boolean(resolvedName.value),\n)\n\nconst descriptionId = useId()\nconst hasDescription = computed(() => Boolean(slots.description))\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass({\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n}))\nconst controlClassName = computed(() => grRadioControlClass({\n size: resolvedSize.value,\n checked: checked.value,\n disabled: resolvedDisabled.value,\n invalid: resolvedInvalid.value,\n}))\nconst dotClassName = computed(() => grRadioDotClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n}))\nconst labelClassName = computed(() => grRadioLabelClassFor(resolvedSize.value, checked.value))\nconst dotSizeClassName = computed(() => grRadioDotSizes[resolvedSize.value])\nconst descriptionClassName = computed(() => grRadioDescriptionSizes[resolvedSize.value])\n\nfunction setValue(next: GrRadioValue): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n\n// Паттерн radiogroup: группа — одна остановка Tab, внутрь попадают стрелками.\n// Вне группы (одиночный `GrRadio`) переключатель остаётся обычной остановкой.\nconst rovingTabindex = computed(() => {\n if (resolvedDisabled.value) return -1\n if (!group) return 0\n return group.rovingValue.value === props.value ? 0 : -1\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n/** Кольцо ведёт группа: только она знает состав и порядок. */\nfunction onKeydown(event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n group.handleNavigationKeys(event)\n}\n\nconst unregister = group?.register({\n value: () => props.value,\n disabled: () => resolvedDisabled.value,\n el: () => rootEl.value,\n})\n\nonUnmounted(() => unregister?.())\n\ndefineSlots<{\n /** Подпись переключателя вместо пропа `label`. */\n default?: () => any\n /** Пояснение под подписью. */\n description?: () => any\n}>()\n\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :aria-describedby=\"hasDescription ? descriptionId : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n class=\"inline-flex items-start gap-2 select-none focus-visible:outline-none focus-visible:rounded-[var(--gr-radius-md)] focus-visible:shadow-[0_0_0_2px_var(--gr-ring),0_0_0_4px_var(--gr-bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown=\"onKeydown\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"mt-0.5 shrink-0 rounded-[var(--gr-radius-full)] border flex items-center justify-center transition-colors duration-[var(--gr-duration-fast)]\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotSizeClassName, dotClassName]\"\n />\n </span>\n\n <span class=\"grid gap-0.5\">\n <span data-gr-radio-label :class=\"labelClassName\">\n <slot />\n </span>\n\n <span\n v-if=\"hasDescription\"\n :id=\"descriptionId\"\n data-gr-radio-description\n :class=\"descriptionClassName\"\n >\n <slot name=\"description\" />\n </span>\n </span>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrRadioGroup-CdQO4G-U.js","names":["$slots"],"sources":["../../../../src/components/GrRadioGroup/GrRadioGroup.vue","../../../../src/components/GrRadioGroup/GrRadioGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, provide, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\n\nimport type { GrButtonSize } from '../GrButton'\nimport GrButtonGroup from '../GrButtonGroup/GrButtonGroup.vue'\nimport GrRadio from '../GrRadio/GrRadio.vue'\nimport { GR_RADIO_GROUP_CONTEXT } from '../GrRadio/grRadioGroupContext'\nimport type { GrRadioEntry, GrRadioValue } from '../GrRadio/grRadioGroupContext'\n\nexport type GrRadioGroupVariant = 'radiobox' | 'button'\nexport type GrRadioGroupOrientation = 'vertical' | 'horizontal'\nexport interface GrRadioGroupOption {\n value: GrRadioValue\n label: string\n /** Отключить одну опцию, не переходя на слот. */\n disabled?: boolean\n /** Пояснение под подписью. Рисуется только в варианте `radiobox`. */\n description?: string\n}\n\n/**\n * GrRadioGroup — контейнер группы `GrRadio`.\n *\n * Может работать как через слот (ручной рендер `GrRadio`), так и через проп `options`.\n * Предоставляет дочерним `GrRadio` общий `modelValue`/`disabled`/`size`/`name` через `inject`.\n */\nexport interface GrRadioGroupProps {\n modelValue: GrRadioValue\n options?: GrRadioGroupOption[]\n name?: string\n disabled?: boolean\n /** Только для чтения: выбор видно, но он не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n variant?: GrRadioGroupVariant\n /**\n * Раскладка варианта `radiobox`. Кнопочный вариант всегда горизонтальный —\n * его собирает `GrButtonGroup`.\n */\n orientation?: GrRadioGroupOrientation\n size?: GrButtonSize\n ariaLabel?: string\n}\n\nexport interface GrRadioGroupEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n (e: 'change', value: GrRadioValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrRadioGroupProps>(), {\n options: undefined,\n name: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n variant: 'radiobox',\n orientation: 'vertical',\n size: undefined,\n ariaLabel: undefined,\n})\n\n// Эффективный размер группы: локальный проп → `GrConfigProvider` → `md`.\n// Дочерние `GrRadio` получают уже разрешённое значение через контекст, поэтому\n// провайдер работает и для них — без второго чтения конфига.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrRadioGroup' })\n\n// Контекст `GrFormField`. Группа — не labelable-элемент, поэтому имя приходит\n// через `aria-labelledby` на подпись поля, а не через `<label for>`.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\n\nconst emit = defineEmits<GrRadioGroupEmits>()\ndefineSlots<{\n /** Собственная разметка переключателей вместо генерации из `options`. */\n default?: () => any\n}>()\n\n\nfunction setValue(next: GrRadioValue): void {\n if (isDisabled.value || isReadonly.value)\n return\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nconst listClass = computed(() => (props.orientation === 'horizontal'\n ? 'flex flex-wrap items-start gap-x-6 gap-y-2'\n : 'grid gap-2'))\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// Фокус ходит между радиокнопками: без границы каждая стрелка давала бы\n// потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\ndefineExpose({ focus, blur })\n\nconst entries = ref<GrRadioEntry[]>([])\n\nfunction register(entry: GrRadioEntry): () => void {\n entries.value.push(entry)\n return () => {\n const index = entries.value.indexOf(entry)\n if (index >= 0) entries.value.splice(index, 1)\n }\n}\n\nfunction entryOf(value: GrRadioValue): GrRadioEntry | undefined {\n return entries.value.find(entry => entry.value() === value)\n}\n\n/**\n * Кольцо roving-фокуса. Живёт в группе, а не в переключателе: состав знает\n * только она. Обе оси работают всегда — требование паттерна `radiogroup`,\n * стрелки зациклены, выключенные перешагиваются.\n */\nconst roving = useRovingFocus<GrRadioValue>({\n items: () => entries.value.map(entry => entry.value()),\n elementFor: value => entryOf(value)?.el() ?? null,\n isDisabled: value => entryOf(value)?.disabled() ?? true,\n orientation: () => 'both',\n skipDisabled: () => true,\n // Выбранное значение держит остановку `Tab` — но только пока переключатель\n // доступен: на выключенном `GrRadio` ставит `tabindex=\"-1\"` сам, и остановка\n // на нём означала бы, что `Tab` не приводит никуда.\n initialKey: () => {\n const entry = entryOf(props.modelValue)\n return entry && !entry.disabled() ? props.modelValue : undefined\n },\n // В `radiogroup` стрелка переносит выбор, а не только фокус.\n onMove: value => setValue(value),\n // Выбор меняет разметку: фокус ставим по перерисовке, иначе следующая\n // стрелка отсчитывалась бы от прежнего элемента.\n beforeFocus: () => nextTick(),\n})\n\n/** Императивный фокус ведёт на текущую остановку `Tab`, а не на первый элемент. */\nfunction rovingElement(): HTMLElement | null {\n const value = roving.rovingKey.value\n return value === undefined ? null : entryOf(value)?.el() ?? null\n}\n\nfunction focus(): void {\n rovingElement()?.focus()\n}\n\nfunction blur(): void {\n rovingElement()?.blur()\n}\n\nprovide(GR_RADIO_GROUP_CONTEXT, {\n modelValue: computed(() => props.modelValue),\n name: computed(() => props.name),\n disabled: computed(() => isDisabled.value),\n readonly: isReadonly,\n invalid: isInvalid,\n size: resolvedSize,\n setValue,\n register,\n rovingValue: roving.rovingKey,\n handleNavigationKeys: roving.handleNavigationKeys,\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootEl\"\n data-gr-radio-group\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <template v-if=\"$slots.default\">\n <GrButtonGroup v-if=\"variant === 'button'\">\n <slot />\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <slot />\n </div>\n </template>\n <template v-else>\n <GrButtonGroup v-if=\"variant === 'button'\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n variant=\"button\"\n :size=\"resolvedSize\"\n >\n {{ opt.label }}\n </GrRadio>\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n >\n {{ opt.label }}\n\n <template v-if=\"opt.description\" #description>\n {{ opt.description }}\n </template>\n </GrRadio>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, provide, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\n\nimport type { GrButtonSize } from '../GrButton'\nimport GrButtonGroup from '../GrButtonGroup/GrButtonGroup.vue'\nimport GrRadio from '../GrRadio/GrRadio.vue'\nimport { GR_RADIO_GROUP_CONTEXT } from '../GrRadio/grRadioGroupContext'\nimport type { GrRadioEntry, GrRadioValue } from '../GrRadio/grRadioGroupContext'\n\nexport type GrRadioGroupVariant = 'radiobox' | 'button'\nexport type GrRadioGroupOrientation = 'vertical' | 'horizontal'\nexport interface GrRadioGroupOption {\n value: GrRadioValue\n label: string\n /** Отключить одну опцию, не переходя на слот. */\n disabled?: boolean\n /** Пояснение под подписью. Рисуется только в варианте `radiobox`. */\n description?: string\n}\n\n/**\n * GrRadioGroup — контейнер группы `GrRadio`.\n *\n * Может работать как через слот (ручной рендер `GrRadio`), так и через проп `options`.\n * Предоставляет дочерним `GrRadio` общий `modelValue`/`disabled`/`size`/`name` через `inject`.\n */\nexport interface GrRadioGroupProps {\n modelValue: GrRadioValue\n options?: GrRadioGroupOption[]\n name?: string\n disabled?: boolean\n /** Только для чтения: выбор видно, но он не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n variant?: GrRadioGroupVariant\n /**\n * Раскладка варианта `radiobox`. Кнопочный вариант всегда горизонтальный —\n * его собирает `GrButtonGroup`.\n */\n orientation?: GrRadioGroupOrientation\n size?: GrButtonSize\n ariaLabel?: string\n}\n\nexport interface GrRadioGroupEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n (e: 'change', value: GrRadioValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrRadioGroupProps>(), {\n options: undefined,\n name: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n variant: 'radiobox',\n orientation: 'vertical',\n size: undefined,\n ariaLabel: undefined,\n})\n\n// Эффективный размер группы: локальный проп → `GrConfigProvider` → `md`.\n// Дочерние `GrRadio` получают уже разрешённое значение через контекст, поэтому\n// провайдер работает и для них — без второго чтения конфига.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrRadioGroup' })\n\n// Контекст `GrFormField`. Группа — не labelable-элемент, поэтому имя приходит\n// через `aria-labelledby` на подпись поля, а не через `<label for>`.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\n\nconst emit = defineEmits<GrRadioGroupEmits>()\ndefineSlots<{\n /** Собственная разметка переключателей вместо генерации из `options`. */\n default?: () => any\n}>()\n\n\nfunction setValue(next: GrRadioValue): void {\n if (isDisabled.value || isReadonly.value)\n return\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nconst listClass = computed(() => (props.orientation === 'horizontal'\n ? 'flex flex-wrap items-start gap-x-6 gap-y-2'\n : 'grid gap-2'))\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// Фокус ходит между радиокнопками: без границы каждая стрелка давала бы\n// потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\ndefineExpose({ focus, blur })\n\nconst entries = ref<GrRadioEntry[]>([])\n\nfunction register(entry: GrRadioEntry): () => void {\n entries.value.push(entry)\n return () => {\n const index = entries.value.indexOf(entry)\n if (index >= 0) entries.value.splice(index, 1)\n }\n}\n\nfunction entryOf(value: GrRadioValue): GrRadioEntry | undefined {\n return entries.value.find(entry => entry.value() === value)\n}\n\n/**\n * Кольцо roving-фокуса. Живёт в группе, а не в переключателе: состав знает\n * только она. Обе оси работают всегда — требование паттерна `radiogroup`,\n * стрелки зациклены, выключенные перешагиваются.\n */\nconst roving = useRovingFocus<GrRadioValue>({\n items: () => entries.value.map(entry => entry.value()),\n elementFor: value => entryOf(value)?.el() ?? null,\n isDisabled: value => entryOf(value)?.disabled() ?? true,\n orientation: () => 'both',\n skipDisabled: () => true,\n // Выбранное значение держит остановку `Tab` — но только пока переключатель\n // доступен: на выключенном `GrRadio` ставит `tabindex=\"-1\"` сам, и остановка\n // на нём означала бы, что `Tab` не приводит никуда.\n initialKey: () => {\n const entry = entryOf(props.modelValue)\n return entry && !entry.disabled() ? props.modelValue : undefined\n },\n // В `radiogroup` стрелка переносит выбор, а не только фокус.\n onMove: value => setValue(value),\n // Выбор меняет разметку: фокус ставим по перерисовке, иначе следующая\n // стрелка отсчитывалась бы от прежнего элемента.\n beforeFocus: () => nextTick(),\n})\n\n/** Императивный фокус ведёт на текущую остановку `Tab`, а не на первый элемент. */\nfunction rovingElement(): HTMLElement | null {\n const value = roving.rovingKey.value\n return value === undefined ? null : entryOf(value)?.el() ?? null\n}\n\nfunction focus(): void {\n rovingElement()?.focus()\n}\n\nfunction blur(): void {\n rovingElement()?.blur()\n}\n\nprovide(GR_RADIO_GROUP_CONTEXT, {\n modelValue: computed(() => props.modelValue),\n name: computed(() => props.name),\n disabled: computed(() => isDisabled.value),\n readonly: isReadonly,\n invalid: isInvalid,\n size: resolvedSize,\n setValue,\n register,\n rovingValue: roving.rovingKey,\n handleNavigationKeys: roving.handleNavigationKeys,\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootEl\"\n data-gr-radio-group\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <template v-if=\"$slots.default\">\n <GrButtonGroup v-if=\"variant === 'button'\">\n <slot />\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <slot />\n </div>\n </template>\n <template v-else>\n <GrButtonGroup v-if=\"variant === 'button'\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n variant=\"button\"\n :size=\"resolvedSize\"\n >\n {{ opt.label }}\n </GrRadio>\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n >\n {{ opt.label }}\n\n <template v-if=\"opt.description\" #description>\n {{ opt.description }}\n </template>\n </GrRadio>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrResponseErrorBanner-DudGc9LO.js","names":[],"sources":["../../../../src/components/GrResponseErrorBanner/responseError.defaults.ts","../../../../src/components/GrResponseErrorBanner/parsers/abortErrorParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/fileValidationParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/httpStatusParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/jsonApiErrorParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/laravelValidationParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/networkErrorParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/normalizeError.ts","../../../../src/components/GrResponseErrorBanner/parsers/plainMessageParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/problemDetailsParser.ts","../../../../src/components/GrResponseErrorBanner/parsers/createResponseErrorClassifier.ts","../../../../src/components/GrResponseErrorBanner/GrResponseErrorBanner.vue","../../../../src/components/GrResponseErrorBanner/GrResponseErrorBanner.vue"],"sourcesContent":["import type { ResponseErrorKind, ResponseErrorTexts, ResponseErrorTone } from './responseError.types'\n\n/**\n * Default English texts. Any field can be overridden via the `texts` prop\n * on `GrResponseErrorBanner` or the `texts` option of `useResponseError`.\n */\nexport const DEFAULT_RESPONSE_ERROR_TEXTS: ResponseErrorTexts = {\n networkTitle: 'No connection to the server',\n networkMessage: 'Check your internet connection and try again.',\n abortedTitle: 'Request cancelled',\n abortedMessage: 'The request was cancelled.',\n validationTitle: 'Check the data',\n validationMessage: 'The server returned a validation error. Fix the fields and try again.',\n clientTitle: 'Request error',\n clientMessage: 'The server rejected the request.',\n serverTitle: 'Server error',\n serverMessage: 'A server error occurred. Please try again.',\n unknownTitle: 'Something went wrong',\n unknownMessage: 'The operation could not be completed. Please try again.',\n statusLabel: 'HTTP {status}',\n retryLabel: 'Retry',\n dismissLabel: 'Dismiss',\n}\n\n/**\n * Маппинг `kind` → `tone` по умолчанию. Применяется внутри баннера, если\n * пользователь не передал `tone` или `toneByKind`. Цель — визуально отделить\n * «исправь ввод» (warning) от «упало по сети/серверу» (danger).\n */\nexport const DEFAULT_TONE_BY_KIND: Record<ResponseErrorKind, ResponseErrorTone> = {\n network: 'danger',\n aborted: 'info',\n validation: 'warning',\n client: 'warning',\n server: 'danger',\n unknown: 'danger',\n}\n\n/**\n * `kind`'ы, которые по умолчанию не показываются (баннер скрыт).\n * Семантика: отмена пользователем — не «ошибка», UI обычно тихо проглатывает.\n * Пользователь может переопределить через `autoHideKinds: []`, чтобы показать info-плашку.\n */\nexport const DEFAULT_AUTO_HIDE_KINDS: ResponseErrorKind[] = []\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Распознаёт отмену запроса клиентом.\n *\n * Триггеры:\n * - `ctx.isAbort === true` (выставляется `normalizeError` для `AbortError`,\n * axios `isCancel`/`ERR_CANCELED`, xhr `status === 0 && readyState === 4`).\n *\n * Возвращает `kind: 'aborted'` и `stop: true` — другие парсеры пропускаются,\n * чтобы случайный `status === 0` не интерпретировался как сетевая ошибка.\n *\n * По умолчанию `aborted` показывается info-плашкой; чтобы скрыть совсем —\n * добавьте `'aborted'` в `autoHideKinds`.\n */\nexport const abortErrorParser: ResponseErrorParser = (ctx) => {\n if (!ctx.isAbort) return null\n // Текст по `kind` подставит классификатор — и пометит его фолбэком.\n return {\n kind: 'aborted',\n stop: true,\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Распознаёт `FileValidationError` от `GrFileUpload` (или совместимый по форме\n * объект). Не зависит от жёсткого импорта класса — определяет по `name`.\n *\n * Активируется, если `ctx.raw.name === 'FileValidationError'` и есть массив\n * `issues`/`errors` с полями `{ file?, code?, message }`.\n *\n * Возвращает `kind: 'validation'` и `stop: true` — это локальная клиентская\n * валидация, незачем дальше идти по HTTP-парсерам.\n */\nexport const fileValidationParser: ResponseErrorParser = (ctx) => {\n const raw = ctx.raw as\n | undefined\n | {\n name?: string\n message?: string\n issues?: Array<{ file?: { name?: string }, message?: unknown, code?: string }>\n errors?: Array<{ file?: { name?: string }, message?: unknown, code?: string }>\n }\n if (!raw || typeof raw !== 'object') return null\n if (raw.name !== 'FileValidationError') return null\n\n const list = raw.issues ?? raw.errors ?? []\n const details: string[] = []\n for (const it of list) {\n if (!it || typeof it !== 'object') continue\n const msg = typeof it.message === 'string' ? it.message : ''\n if (!msg) continue\n const fileName = it.file?.name\n details.push(fileName ? `${fileName}: ${msg}` : msg)\n }\n\n return {\n kind: 'validation',\n message: raw.message || undefined,\n details: details.length ? details : undefined,\n stop: true,\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Базовая классификация по HTTP-статусу.\n *\n * Правила:\n * - `400` / `422` ⇒ `kind: 'validation'`,\n * - остальные `4xx` ⇒ `kind: 'client'`,\n * - `>= 500` ⇒ `kind: 'server'`.\n *\n * Не использует `stop` — даёт более специализированным парсерам\n * (Laravel/JSON:API/RFC 7807) уточнить сообщение и `fieldErrors`.\n *\n * `message` **не** заполняет: общий текст по `kind` — работа классификатора,\n * который помечает такое сообщение `isFallbackMessage`. Только по этому флагу\n * баннер вправе заменить текст переводом.\n */\nexport const httpStatusParser: ResponseErrorParser = (ctx) => {\n const status = ctx.status\n if (typeof status !== 'number') return null\n\n if (status >= 500) {\n return { kind: 'server', status }\n }\n if (status === 400 || status === 422) {\n return { kind: 'validation', status }\n }\n if (status >= 400) {\n return { kind: 'client', status }\n }\n return null\n}\n","import type { ResponseErrorFieldError, ResponseErrorParser } from '../responseError.types'\n\n/**\n * Парсер JSON:API-стиля:\n *\n * ```json\n * { \"errors\": [\n * { \"status\": \"422\", \"title\": \"Invalid Attribute\",\n * \"detail\": \"Email is required.\",\n * \"source\": { \"pointer\": \"/data/attributes/email\" } }\n * ] }\n * ```\n *\n * Активируется, когда `ctx.body.errors` — массив объектов с `title`/`detail`.\n * Поле извлекается из последнего сегмента `source.pointer`. Все `detail`\n * собираются в `details`, первый `title` — в `message`.\n */\nexport const jsonApiErrorParser: ResponseErrorParser = (ctx) => {\n const body = ctx.body\n if (!body || typeof body !== 'object') return null\n\n const errors = (body as { errors?: unknown }).errors\n if (!Array.isArray(errors) || errors.length === 0) return null\n\n // Эвристика: первый элемент должен иметь title/detail/source — иначе это не JSON:API\n const first = errors[0]\n if (!first || typeof first !== 'object') return null\n const has = (key: string) => key in (first as object)\n if (!has('title') && !has('detail') && !has('source')) return null\n\n const fieldErrorsMap = new Map<string, string[]>()\n const details: string[] = []\n let message: string | undefined\n\n for (const e of errors) {\n if (!e || typeof e !== 'object') continue\n const eo = e as { title?: unknown, detail?: unknown, source?: { pointer?: unknown } }\n const text = (typeof eo.detail === 'string' && eo.detail) || (typeof eo.title === 'string' && eo.title) || ''\n if (!text) continue\n if (!message && typeof eo.title === 'string') message = eo.title\n\n const pointer = typeof eo.source?.pointer === 'string' ? eo.source.pointer : ''\n const field = pointer ? pointer.split('/').filter(Boolean).pop() : undefined\n\n if (field) {\n const list = fieldErrorsMap.get(field) ?? []\n list.push(text)\n fieldErrorsMap.set(field, list)\n }\n details.push(text)\n }\n\n if (!details.length) return null\n\n const fieldErrors: ResponseErrorFieldError[] = Array.from(fieldErrorsMap, ([field, messages]) => ({ field, messages }))\n\n const kind = fieldErrors.length ? 'validation' : undefined\n\n return {\n kind,\n // Без `title` сообщение не выдумываем: общий текст по `kind` — работа\n // классификатора, и только он помечает такое сообщение фолбэком.\n message,\n details,\n fieldErrors: fieldErrors.length ? fieldErrors : undefined,\n }\n}\n","import type { ResponseErrorFieldError, ResponseErrorParser } from '../responseError.types'\n\n/**\n * Парсит Laravel-формат ответа об ошибке валидации:\n *\n * ```json\n * { \"message\": \"The given data was invalid.\",\n * \"errors\": { \"file\": [\"The file is required.\"], \"amount\": [\"...\"] } }\n * ```\n *\n * Активируется при наличии `ctx.body.errors` как plain-объекта (значения —\n * массив строк или строка). Если есть `fieldErrors`, повышает `kind` до\n * `validation` независимо от статуса (полезно, когда сервер прислал 200/4xx\n * без 422). `message` берётся из `body.message` / `body.error`.\n */\nexport const laravelValidationParser: ResponseErrorParser = (ctx) => {\n const body = ctx.body\n if (!body || typeof body !== 'object' || Array.isArray(body)) return null\n\n const data = body as Record<string, unknown>\n const errors = data.errors\n if (!errors || typeof errors !== 'object' || Array.isArray(errors)) return null\n\n const fieldErrors: ResponseErrorFieldError[] = []\n for (const [field, raw] of Object.entries(errors as Record<string, unknown>)) {\n const messages = Array.isArray(raw)\n ? raw.filter((m): m is string => typeof m === 'string')\n : typeof raw === 'string' ? [raw] : []\n if (messages.length) fieldErrors.push({ field, messages })\n }\n\n if (!fieldErrors.length) return null\n\n // Только то, что действительно прислал сервер: не нашли — пусть общий текст\n // подставит классификатор, пометив его фолбэком (иначе баннер не переведёт).\n const message = (typeof data.message === 'string' && data.message)\n || (typeof data.error === 'string' && data.error)\n || undefined\n\n const details = fieldErrors.flatMap(fe => fe.messages)\n\n return {\n kind: 'validation',\n message,\n details,\n fieldErrors,\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Распознаёт транспортные ошибки **без ответа сервера**: отсутствие интернета,\n * DNS-фейл, CORS-preflight, fetch `TypeError('Failed to fetch')`,\n * axios `ERR_NETWORK`/`ECONNABORTED`.\n *\n * Триггеры:\n * - `ctx.isNetwork === true` (выставлено `normalizeError`),\n * - либо нет ни `status`, ни `body`, ни признака отмены, но есть исходная\n * ошибка с `code === 'ERR_NETWORK'` / `message ~ /network|fetch/i/`.\n *\n * Возвращает `kind: 'network'` и `stop: true`.\n */\nexport const networkErrorParser: ResponseErrorParser = (ctx) => {\n if (ctx.isAbort) return null\n\n if (ctx.isNetwork) {\n // Текст по `kind` подставит классификатор — и пометит его фолбэком.\n return {\n kind: 'network',\n stop: true,\n }\n }\n\n // Эвристика для случаев, когда normalizeError не пометил, но статус-кода нет.\n if (typeof ctx.status !== 'number' && ctx.body == null && ctx.raw && typeof ctx.raw === 'object') {\n const r = ctx.raw as { code?: string, message?: string, name?: string }\n if (r.code === 'ERR_NETWORK' || (typeof r.message === 'string' && /network|failed to fetch/i.test(r.message))) {\n return {\n kind: 'network',\n stop: true,\n }\n }\n }\n\n return null\n}\n","import type { NormalizedRawError } from '../responseError.types'\n\n/**\n * Универсально приводит «сырой» вход к {@link NormalizedRawError}.\n *\n * Поддерживаемые источники:\n * - **axios** — `AxiosError` с `response.status`/`response.data`/`response.headers`,\n * либо `request` без `response` + `code === 'ERR_NETWORK'`, либо `code === 'ERR_CANCELED'`.\n * - **fetch `Response`** — объект `Response` с `ok=false`. Тело читается через\n * `clone().json()` (с фолбэком на `.text()`); поэтому функция асинхронная.\n * - **fetch `TypeError`** — `TypeError('Failed to fetch')` ⇒ `isNetwork=true`.\n * - **`{ response: Response, body: unknown }`** — если потребитель уже прочитал тело сам.\n * - **`XMLHttpRequest`** / `{ status, responseText, response }` — пара полей.\n * - **`AbortError` / `DOMException`** ⇒ `isAbort=true`.\n * - **`Error`/строка/произвольный объект** — кладётся в `body`/`raw` как есть.\n *\n * Цель — отделить «откуда пришла ошибка» от «как её классифицировать»: парсеры\n * работают только с нормализованным `ResponseErrorContext`, не зная о транспорте.\n */\nexport async function normalizeError(raw: unknown): Promise<NormalizedRawError> {\n // 1) Уже нормализованный вход (потребитель сам подготовил)\n if (isPlainNormalized(raw)) {\n return { ...(raw), raw }\n }\n\n // 2) `{ response: Response, body }` — уже прочитанное тело\n if (isResponseEnvelope(raw)) {\n const res = raw.response\n return {\n raw,\n status: res.status,\n body: raw.body,\n headers: readHeaders(res),\n }\n }\n\n // 3) Голый fetch Response\n if (isFetchResponse(raw)) {\n const body = await readFetchBody(raw)\n return {\n raw,\n status: raw.status,\n body,\n headers: readHeaders(raw),\n }\n }\n\n // 4) XMLHttpRequest\n if (isXhr(raw)) {\n const body = parseMaybeJson(raw.responseText) ?? raw.response\n return {\n raw,\n status: typeof raw.status === 'number' ? raw.status : undefined,\n body,\n isAbort: raw.status === 0 && raw.readyState === 4,\n }\n }\n\n // 5) axios error (есть `isAxiosError` или `response`/`request` + `code`)\n if (isAxiosLike(raw)) {\n const e = raw\n const status = e.response?.status\n const code = e.code\n const isAbort = code === 'ERR_CANCELED' || code === 'CANCELED' || (e as { __CANCEL__?: boolean }).__CANCEL__ === true\n const isNetwork = !e.response && (code === 'ERR_NETWORK' || code === 'ECONNABORTED')\n\n return {\n raw,\n status,\n body: e.response?.data,\n headers: normalizeHeaderRecord(e.response?.headers),\n isAbort,\n isNetwork,\n }\n }\n\n // 6) AbortError / DOMException\n if (isAbortError(raw)) {\n return { raw, isAbort: true }\n }\n\n // 7) Сетевой TypeError от fetch\n if (raw instanceof TypeError && /fetch|network/i.test(raw.message || '')) {\n return { raw, isNetwork: true }\n }\n\n // 8) Произвольный Error / строка / объект\n if (raw instanceof Error) {\n return { raw, body: { message: raw.message } }\n }\n\n if (typeof raw === 'string') {\n return { raw, body: { message: raw } }\n }\n\n if (raw && typeof raw === 'object') {\n return { raw, body: raw }\n }\n\n return { raw }\n}\n\n// ----------------- helpers -----------------\n\ntype AxiosLikeError = Error & {\n isAxiosError?: boolean\n code?: string\n response?: { status?: number, data?: unknown, headers?: unknown }\n request?: unknown\n}\n\nfunction isPlainNormalized(v: unknown): v is NormalizedRawError {\n if (!v || typeof v !== 'object') return false\n const o = v as Record<string, unknown>\n return 'raw' in o && (\n 'status' in o || 'body' in o || 'isAbort' in o || 'isNetwork' in o || 'headers' in o\n )\n}\n\nfunction isResponseEnvelope(v: unknown): v is { response: Response, body: unknown } {\n return !!v && typeof v === 'object'\n && 'response' in (v)\n && isFetchResponse((v).response)\n}\n\nfunction isFetchResponse(v: unknown): v is Response {\n return typeof Response !== 'undefined' && v instanceof Response\n}\n\nfunction isXhr(v: unknown): v is XMLHttpRequest {\n return typeof XMLHttpRequest !== 'undefined' && v instanceof XMLHttpRequest\n}\n\nfunction isAxiosLike(v: unknown): v is AxiosLikeError {\n if (!v || typeof v !== 'object') return false\n const o = v as Record<string, unknown>\n return o.isAxiosError === true\n || ('response' in o && typeof o.response === 'object' && o.response !== null)\n || ('code' in o && typeof o.code === 'string')\n}\n\nfunction isAbortError(v: unknown): boolean {\n if (!v || typeof v !== 'object') return false\n const o = v as { name?: string, code?: string }\n return o.name === 'AbortError' || o.name === 'CanceledError' || o.code === 'ERR_CANCELED'\n}\n\nasync function readFetchBody(res: Response): Promise<unknown> {\n if (res.bodyUsed) return undefined\n try {\n const clone = res.clone()\n const text = await clone.text()\n return parseMaybeJson(text) ?? text\n }\n catch {\n return undefined\n }\n}\n\nfunction readHeaders(res: Response): Record<string, string> {\n const out: Record<string, string> = {}\n res.headers.forEach((value, key) => {\n out[key.toLowerCase()] = value\n })\n return out\n}\n\nfunction normalizeHeaderRecord(h: unknown): Record<string, string> | undefined {\n if (!h || typeof h !== 'object') return undefined\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(h as Record<string, unknown>)) {\n if (typeof v === 'string') out[k.toLowerCase()] = v\n }\n return out\n}\n\nfunction parseMaybeJson(text: string | undefined): unknown {\n if (typeof text !== 'string' || !text) return undefined\n try {\n return JSON.parse(text)\n }\n catch {\n return undefined\n }\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Финальный фолбэк-парсер. Достаёт хоть какое-нибудь человекочитаемое\n * сообщение, чтобы баннер не был пустым.\n *\n * Источники (по приоритету):\n * 1. `ctx.body` — если строка ⇒ message,\n * 2. `ctx.body[messageKey]` (default `'message'`),\n * 3. `ctx.body.error` (строка),\n * 4. `ctx.raw.message` — **только когда ответа не было вовсе** (нет ни статуса,\n * ни тела). Иначе это строка транспорта (`Request failed with status 500` у\n * axios), а не сообщение сервера: пользователь получил бы техническую фразу\n * на английском вместо переведённого текста по `kind`.\n *\n * Не выставляет `kind` — оставляет на усмотрение классификатора.\n * Использует ключ `messageKey` из `ctx.meta._messageKey`, если задан.\n */\nexport const plainMessageParser: ResponseErrorParser = (ctx) => {\n const messageKey = (ctx.meta._messageKey as string | undefined) || 'message'\n\n if (typeof ctx.body === 'string' && ctx.body.trim()) {\n return { message: ctx.body }\n }\n\n if (ctx.body && typeof ctx.body === 'object') {\n const data = ctx.body as Record<string, unknown>\n const fromKey = data[messageKey]\n if (typeof fromKey === 'string' && fromKey) return { message: fromKey }\n if (typeof data.error === 'string' && data.error) return { message: data.error }\n }\n\n const hadResponse = typeof ctx.status === 'number' || ctx.body != null\n if (!hadResponse && ctx.raw instanceof Error && ctx.raw.message) {\n return { message: ctx.raw.message }\n }\n\n return null\n}\n","import type { ResponseErrorParser } from '../responseError.types'\n\n/**\n * Парсер RFC 7807 «Problem Details for HTTP APIs»:\n *\n * ```json\n * { \"type\": \"https://example.com/probs/out-of-credit\",\n * \"title\": \"You do not have enough credit.\",\n * \"status\": 403,\n * \"detail\": \"Your current balance is 30.\",\n * \"errors\": { \"balance\": [\"...\"] } }\n * ```\n *\n * Активируется, если есть `body.type` + `body.title`, либо\n * Content-Type заголовка `application/problem+json`. Берёт `title` как\n * `message`, `detail` — в `details[0]`, status — в `status`.\n */\nexport const problemDetailsParser: ResponseErrorParser = (ctx) => {\n const body = ctx.body\n if (!body || typeof body !== 'object' || Array.isArray(body)) return null\n\n const data = body as { type?: unknown, title?: unknown, detail?: unknown, status?: unknown }\n const isProblemContentType = ctx.headers?.['content-type']?.includes('application/problem+json')\n const hasShape = typeof data.title === 'string' && (typeof data.type === 'string' || typeof data.status === 'number')\n\n if (!isProblemContentType && !hasShape) return null\n\n const message = typeof data.title === 'string' ? data.title : undefined\n const details: string[] = []\n if (typeof data.detail === 'string' && data.detail) details.push(data.detail)\n\n const status = typeof data.status === 'number' ? data.status : ctx.status\n\n return {\n message,\n details: details.length ? details : undefined,\n status,\n }\n}\n","import { DEFAULT_RESPONSE_ERROR_TEXTS } from '../responseError.defaults'\nimport type {\n ResponseErrorContext,\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorParser,\n ResponseErrorTexts,\n} from '../responseError.types'\n\nimport { abortErrorParser } from './abortErrorParser'\nimport { fileValidationParser } from './fileValidationParser'\nimport { httpStatusParser } from './httpStatusParser'\nimport { jsonApiErrorParser } from './jsonApiErrorParser'\nimport { laravelValidationParser } from './laravelValidationParser'\nimport { networkErrorParser } from './networkErrorParser'\nimport { normalizeError } from './normalizeError'\nimport { plainMessageParser } from './plainMessageParser'\nimport { problemDetailsParser } from './problemDetailsParser'\n\n/**\n * Дефолтная цепочка парсеров. Порядок важен:\n * `abort → network → fileValidation → laravel → problemDetails → jsonApi → httpStatus → plainMessage`.\n *\n * - Ранние специализированные парсеры могут поставить `stop` (abort/file).\n * - HTTP-парсер даёт fallback-kind/message, чтобы остальное могло уточнить.\n * - `plainMessage` всегда последний — добывает хоть какое-то сообщение.\n */\nexport const defaultResponseErrorParsers: ResponseErrorParser[] = [\n abortErrorParser,\n networkErrorParser,\n fileValidationParser,\n laravelValidationParser,\n problemDetailsParser,\n jsonApiErrorParser,\n httpStatusParser,\n plainMessageParser,\n]\n\n/**\n * Универсальное «ядро» парсеров — транспортно-независимый набор, безопасный\n * для любого бэкенда (Laravel / Symfony / Go / …). Не делает формат-специфичных\n * допущений: разбирает только отмену, сетевые ошибки, HTTP-статус и «плоское»\n * сообщение из тела. Используется как дефолт в `useDialogService`.\n *\n * Порядок: `abort → network → httpStatus → plainMessage`.\n */\nexport const coreResponseErrorParsers: ResponseErrorParser[] = [\n abortErrorParser,\n networkErrorParser,\n httpStatusParser,\n plainMessageParser,\n]\n\n/**\n * Именованные пресеты-строительные блоки для удобной композиции цепочки\n * парсеров без ручного импорта каждого из них.\n *\n * - `core` — универсальное ядро (см. `coreResponseErrorParsers`).\n * - `all` — полная дефолтная цепочка (`defaultResponseErrorParsers`).\n * - остальные — серверо-специфичные парсеры, подключаемые осознанно.\n */\nexport type ResponseErrorParserPresets = {\n core: ResponseErrorParser[]\n all: ResponseErrorParser[]\n laravel: ResponseErrorParser\n problemDetails: ResponseErrorParser\n jsonApi: ResponseErrorParser\n fileValidation: ResponseErrorParser\n}\n\n/** Объект пресетов, передаётся builder-функции `errorParsers`. */\nexport const responseErrorParserPresets: ResponseErrorParserPresets = {\n core: coreResponseErrorParsers,\n all: defaultResponseErrorParsers,\n laravel: laravelValidationParser,\n problemDetails: problemDetailsParser,\n jsonApi: jsonApiErrorParser,\n fileValidation: fileValidationParser,\n}\n\n/** Добавляет пользовательские парсеры в начало дефолтной цепочки. */\nexport function extendDefaultParsers(extra: ResponseErrorParser[]): ResponseErrorParser[] {\n return [...extra, ...defaultResponseErrorParsers]\n}\n\nexport type CreateResponseErrorClassifierOptions = {\n parsers?: ResponseErrorParser[]\n texts?: Partial<ResponseErrorTexts>\n /** Имя поля основного сообщения в body (для `plainMessageParser`). */\n messageKey?: string\n}\n\n/**\n * Фабрика классификатора. Возвращает функцию `(raw, meta?) => Promise<ResponseErrorInfo>`,\n * которая:\n * 1. нормализует вход через `normalizeError`,\n * 2. строит `ResponseErrorContext`,\n * 3. прогоняет цепочку парсеров (с поддержкой `stop`),\n * 4. собирает финальный `ResponseErrorInfo` с фолбэками из `texts`.\n */\nexport function createResponseErrorClassifier(options: CreateResponseErrorClassifierOptions = {}) {\n const parsers = options.parsers ?? defaultResponseErrorParsers\n const texts: ResponseErrorTexts = { ...DEFAULT_RESPONSE_ERROR_TEXTS, ...(options.texts ?? {}) }\n const messageKey = options.messageKey ?? 'message'\n\n return async function classify(raw: unknown, meta: Record<string, unknown> = {}): Promise<ResponseErrorInfo> {\n const normalized = await normalizeError(raw)\n\n const ctx: ResponseErrorContext = {\n ...normalized,\n meta: { ...meta, _messageKey: messageKey },\n texts,\n }\n\n let kind: ResponseErrorKind | undefined\n let status: number | undefined = ctx.status\n let message: string | undefined\n let details: string[] | undefined\n let fieldErrors: ResponseErrorInfo['fieldErrors']\n\n for (const parser of parsers) {\n const result = parser(ctx)\n if (!result) continue\n if (result.kind) kind = result.kind\n if (typeof result.status === 'number') status = result.status\n if (result.message) message = result.message\n if (result.details?.length) details = result.details\n if (result.fieldErrors?.length) fieldErrors = result.fieldErrors\n if (result.stop) break\n }\n\n if (!kind) kind = 'unknown'\n\n // Ни один парсер не дал сообщения — подставляем своё и помечаем это флагом:\n // по нему (и только по нему) баннер вправе заменить текст переводом.\n const isFallbackMessage = !message\n if (!message) message = resolveDefaultMessage(kind, texts)\n\n return {\n kind,\n status,\n message,\n details,\n fieldErrors,\n raw,\n meta,\n isFallbackMessage,\n }\n }\n}\n\nfunction resolveDefaultMessage(kind: ResponseErrorKind, texts: ResponseErrorTexts): string {\n switch (kind) {\n case 'network': return texts.networkMessage\n case 'aborted': return texts.abortedMessage\n case 'validation': return texts.validationMessage\n case 'client': return texts.clientMessage\n case 'server': return texts.serverMessage\n case 'unknown':\n default: return texts.unknownMessage\n }\n}\n\nexport function resolveResponseErrorTitle(kind: ResponseErrorKind, texts: ResponseErrorTexts): string {\n switch (kind) {\n case 'network': return texts.networkTitle\n case 'aborted': return texts.abortedTitle\n case 'validation': return texts.validationTitle\n case 'client': return texts.clientTitle\n case 'server': return texts.serverTitle\n case 'unknown':\n default: return texts.unknownTitle\n }\n}\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrAlert from '../GrAlert/GrAlert.vue'\nimport GrButton from '../GrButton/GrButton.vue'\n\nimport { resolveResponseErrorTitle } from './parsers/createResponseErrorClassifier'\nimport {\n DEFAULT_AUTO_HIDE_KINDS,\n DEFAULT_RESPONSE_ERROR_TEXTS,\n DEFAULT_TONE_BY_KIND,\n} from './responseError.defaults'\nimport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nexport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nexport interface GrResponseErrorBannerProps {\n /** Готовая структура ошибки. Если `null` — баннер не рендерится. */\n error: ResponseErrorInfo | null\n /** Частичный override текстов. Мерджится с `DEFAULT_RESPONSE_ERROR_TEXTS`. */\n texts?: Partial<ResponseErrorTexts>\n /** Жёсткий оверрайд тона. Бьёт `toneByKind`. */\n tone?: ResponseErrorTone\n /** Маппинг `kind` → `tone`. Мерджится с `DEFAULT_TONE_BY_KIND`. */\n toneByKind?: Partial<Record<ResponseErrorKind, ResponseErrorTone>>\n /** Показывать ли блок `details`/`fieldErrors` под основным сообщением. */\n showDetails?: boolean\n /** Префиксить ли `field:` перед сообщениями по полям. */\n showFieldLabels?: boolean\n /** Человекочитаемые подписи полей (`{ file: 'Файл' }`). */\n fieldLabels?: Record<string, string>\n /** Удалять дубликаты сообщений между `message` и `details`. */\n dedupeDetails?: boolean\n /** Показывать ли кнопку «Повторить». */\n canRetry?: boolean\n /** Показывать ли кнопку «Скрыть». */\n canDismiss?: boolean\n /** Какие `kind` не рендерить вообще (баннер скрывается). */\n autoHideKinds?: ResponseErrorKind[]\n /** Показывать ли HTTP-status бейдж. */\n showStatus?: boolean\n /** Префикс `data-testid`. */\n testIdPrefix?: string\n}\n\nexport interface GrResponseErrorBannerEmits {\n (e: 'retry', error: ResponseErrorInfo): void\n (e: 'dismiss'): void\n}\n\nconst props = withDefaults(defineProps<GrResponseErrorBannerProps>(), {\n texts: () => ({}),\n tone: undefined,\n toneByKind: () => ({}),\n showDetails: true,\n showFieldLabels: true,\n fieldLabels: () => ({}),\n dedupeDetails: true,\n canRetry: false,\n canDismiss: true,\n autoHideKinds: () => DEFAULT_AUTO_HIDE_KINDS,\n showStatus: true,\n testIdPrefix: 'response-error',\n})\n\nconst emit = defineEmits<GrResponseErrorBannerEmits>()\n\nconst { t } = useGranularityTranslations()\n\nconst i18nTexts = computed<ResponseErrorTexts>(() => {\n const out = {} as ResponseErrorTexts\n for (const key of Object.keys(DEFAULT_RESPONSE_ERROR_TEXTS) as (keyof ResponseErrorTexts)[]) {\n out[key] = t(`gr.responseError.${key}`, DEFAULT_RESPONSE_ERROR_TEXTS[key])\n }\n return out\n})\n\nconst mergedTexts = computed<ResponseErrorTexts>(() => ({\n ...DEFAULT_RESPONSE_ERROR_TEXTS,\n ...i18nTexts.value,\n ...props.texts,\n}))\n\nconst mergedToneByKind = computed<Record<ResponseErrorKind, ResponseErrorTone>>(() => ({\n ...DEFAULT_TONE_BY_KIND,\n ...props.toneByKind,\n}))\n\nconst isHidden = computed(() => {\n if (!props.error) return true\n return props.autoHideKinds.includes(props.error.kind)\n})\n\nconst tone = computed<ResponseErrorTone>(() => {\n if (props.tone) return props.tone\n if (!props.error) return 'danger'\n return mergedToneByKind.value[props.error.kind] ?? 'danger'\n})\n\nconst title = computed(() => {\n if (!props.error) return ''\n return resolveResponseErrorTitle(props.error.kind, mergedTexts.value)\n})\n\n/**\n * Маппинг `kind` → ключ поля сообщения в `ResponseErrorTexts`.\n * Используется, чтобы подменить «дефолтное английское» fallback-сообщение\n * классификатора на i18n-перевод (или текущий `mergedTexts.<kind>Message`).\n */\nconst KIND_MESSAGE_KEY: Record<ResponseErrorKind, keyof ResponseErrorTexts> = {\n network: 'networkMessage',\n aborted: 'abortedMessage',\n validation: 'validationMessage',\n client: 'clientMessage',\n server: 'serverMessage',\n unknown: 'unknownMessage',\n}\n\n// Через `mergedTexts`, а не напрямую через `t`: подпись — такой же текст\n// баннера, как и остальные, и проп `texts` обязан её перебивать.\nconst statusLabel = computed(() =>\n mergedTexts.value.statusLabel.replace('{status}', String(props.error?.status ?? '')),\n)\n\nconst message = computed(() => {\n const raw = props.error?.message ?? ''\n if (!props.error) return raw\n\n // Заменяем переводом только то сообщение, которое сам классификатор и\n // подставил (`isFallbackMessage`). Прежнее опознание сравнением строк\n // выбрасывало ответ сервера, если тот дословно совпал с дефолтом.\n if (!raw || props.error.isFallbackMessage) {\n return mergedTexts.value[KIND_MESSAGE_KEY[props.error.kind]]\n }\n return raw\n})\n\n/**\n * Сводный список «деталей» под основным `message`.\n * Предпочитаем `fieldErrors` (поле → сообщения), затем — плоский `details`.\n * Дедуплицируем и убираем повтор с `message`.\n */\nconst detailEntries = computed<{ field?: string, text: string }[]>(() => {\n if (!props.error || !props.showDetails) return []\n\n const seen = new Set<string>()\n if (props.dedupeDetails) {\n const main = message.value.trim()\n if (main) seen.add(main)\n }\n\n const result: { field?: string, text: string }[] = []\n const fieldErrors = props.error.fieldErrors\n if (fieldErrors?.length) {\n for (const fe of fieldErrors) {\n for (const m of fe.messages) {\n const text = m.trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ field: fe.field, text })\n }\n }\n if (result.length) return result\n }\n\n const details = props.error.details\n if (details?.length) {\n for (const d of details) {\n const text = (d ?? '').trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ text })\n }\n }\n return result\n})\n\nfunction fieldLabel(field?: string): string | undefined {\n if (!field) return undefined\n return props.fieldLabels[field] ?? field\n}\n\nfunction onRetry(): void {\n if (props.error) emit('retry', props.error)\n}\n\nfunction onDismiss(): void {\n emit('dismiss')\n}\n</script>\n\n<template>\n <GrAlert\n v-if=\"!isHidden && props.error\"\n :tone=\"tone\"\n :title=\"title\"\n :closable=\"props.canDismiss\"\n :data-testid=\"`${props.testIdPrefix}-banner`\"\n :data-kind=\"props.error.kind\"\n @close=\"onDismiss\"\n >\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n v-if=\"props.showStatus && typeof props.error.status === 'number'\"\n class=\"inline-flex items-center rounded-[var(--gr-radius-sm)] bg-[color-mix(in_srgb,currentColor_12%,transparent)] px-1.5 py-0.5 text-[length:var(--gr-control-text-2xs)] font-700 leading-none\"\n :data-testid=\"`${props.testIdPrefix}-status`\"\n >\n {{ statusLabel }}\n </span>\n <span\n class=\"text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]\"\n :data-testid=\"`${props.testIdPrefix}-message`\"\n >\n {{ message }}\n </span>\n </div>\n\n <ul\n v-if=\"detailEntries.length\"\n class=\"m-0 list-disc pl-5 text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]\"\n :data-testid=\"`${props.testIdPrefix}-details`\"\n >\n <li\n v-for=\"(entry, idx) in detailEntries\"\n :key=\"`${entry.field ?? ''}:${idx}`\"\n >\n <template v-if=\"props.showFieldLabels && entry.field\">\n <strong>{{ fieldLabel(entry.field) }}:</strong> \n </template>{{ entry.text }}\n </li>\n </ul>\n\n <div\n v-if=\"props.canRetry\"\n class=\"mt-1 flex flex-wrap items-center gap-2\"\n >\n <!--\n Тон кнопки — тон баннера: `primary` по умолчанию давал синюю кнопку на\n красной и оранжевой подложке. Шкала тонов у кнопки и у баннера одна,\n поэтому это проброс, а не маппинг.\n -->\n <GrButton\n size=\"sm\"\n variant=\"outline\"\n :tone=\"tone\"\n :data-testid=\"`${props.testIdPrefix}-retry`\"\n @click=\"onRetry\"\n >\n {{ mergedTexts.retryLabel }}\n </GrButton>\n </div>\n </div>\n </GrAlert>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrAlert from '../GrAlert/GrAlert.vue'\nimport GrButton from '../GrButton/GrButton.vue'\n\nimport { resolveResponseErrorTitle } from './parsers/createResponseErrorClassifier'\nimport {\n DEFAULT_AUTO_HIDE_KINDS,\n DEFAULT_RESPONSE_ERROR_TEXTS,\n DEFAULT_TONE_BY_KIND,\n} from './responseError.defaults'\nimport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nexport type {\n ResponseErrorInfo,\n ResponseErrorKind,\n ResponseErrorTexts,\n ResponseErrorTone,\n} from './responseError.types'\n\nexport interface GrResponseErrorBannerProps {\n /** Готовая структура ошибки. Если `null` — баннер не рендерится. */\n error: ResponseErrorInfo | null\n /** Частичный override текстов. Мерджится с `DEFAULT_RESPONSE_ERROR_TEXTS`. */\n texts?: Partial<ResponseErrorTexts>\n /** Жёсткий оверрайд тона. Бьёт `toneByKind`. */\n tone?: ResponseErrorTone\n /** Маппинг `kind` → `tone`. Мерджится с `DEFAULT_TONE_BY_KIND`. */\n toneByKind?: Partial<Record<ResponseErrorKind, ResponseErrorTone>>\n /** Показывать ли блок `details`/`fieldErrors` под основным сообщением. */\n showDetails?: boolean\n /** Префиксить ли `field:` перед сообщениями по полям. */\n showFieldLabels?: boolean\n /** Человекочитаемые подписи полей (`{ file: 'Файл' }`). */\n fieldLabels?: Record<string, string>\n /** Удалять дубликаты сообщений между `message` и `details`. */\n dedupeDetails?: boolean\n /** Показывать ли кнопку «Повторить». */\n canRetry?: boolean\n /** Показывать ли кнопку «Скрыть». */\n canDismiss?: boolean\n /** Какие `kind` не рендерить вообще (баннер скрывается). */\n autoHideKinds?: ResponseErrorKind[]\n /** Показывать ли HTTP-status бейдж. */\n showStatus?: boolean\n /** Префикс `data-testid`. */\n testIdPrefix?: string\n}\n\nexport interface GrResponseErrorBannerEmits {\n (e: 'retry', error: ResponseErrorInfo): void\n (e: 'dismiss'): void\n}\n\nconst props = withDefaults(defineProps<GrResponseErrorBannerProps>(), {\n texts: () => ({}),\n tone: undefined,\n toneByKind: () => ({}),\n showDetails: true,\n showFieldLabels: true,\n fieldLabels: () => ({}),\n dedupeDetails: true,\n canRetry: false,\n canDismiss: true,\n autoHideKinds: () => DEFAULT_AUTO_HIDE_KINDS,\n showStatus: true,\n testIdPrefix: 'response-error',\n})\n\nconst emit = defineEmits<GrResponseErrorBannerEmits>()\n\nconst { t } = useGranularityTranslations()\n\nconst i18nTexts = computed<ResponseErrorTexts>(() => {\n const out = {} as ResponseErrorTexts\n for (const key of Object.keys(DEFAULT_RESPONSE_ERROR_TEXTS) as (keyof ResponseErrorTexts)[]) {\n out[key] = t(`gr.responseError.${key}`, DEFAULT_RESPONSE_ERROR_TEXTS[key])\n }\n return out\n})\n\nconst mergedTexts = computed<ResponseErrorTexts>(() => ({\n ...DEFAULT_RESPONSE_ERROR_TEXTS,\n ...i18nTexts.value,\n ...props.texts,\n}))\n\nconst mergedToneByKind = computed<Record<ResponseErrorKind, ResponseErrorTone>>(() => ({\n ...DEFAULT_TONE_BY_KIND,\n ...props.toneByKind,\n}))\n\nconst isHidden = computed(() => {\n if (!props.error) return true\n return props.autoHideKinds.includes(props.error.kind)\n})\n\nconst tone = computed<ResponseErrorTone>(() => {\n if (props.tone) return props.tone\n if (!props.error) return 'danger'\n return mergedToneByKind.value[props.error.kind] ?? 'danger'\n})\n\nconst title = computed(() => {\n if (!props.error) return ''\n return resolveResponseErrorTitle(props.error.kind, mergedTexts.value)\n})\n\n/**\n * Маппинг `kind` → ключ поля сообщения в `ResponseErrorTexts`.\n * Используется, чтобы подменить «дефолтное английское» fallback-сообщение\n * классификатора на i18n-перевод (или текущий `mergedTexts.<kind>Message`).\n */\nconst KIND_MESSAGE_KEY: Record<ResponseErrorKind, keyof ResponseErrorTexts> = {\n network: 'networkMessage',\n aborted: 'abortedMessage',\n validation: 'validationMessage',\n client: 'clientMessage',\n server: 'serverMessage',\n unknown: 'unknownMessage',\n}\n\n// Через `mergedTexts`, а не напрямую через `t`: подпись — такой же текст\n// баннера, как и остальные, и проп `texts` обязан её перебивать.\nconst statusLabel = computed(() =>\n mergedTexts.value.statusLabel.replace('{status}', String(props.error?.status ?? '')),\n)\n\nconst message = computed(() => {\n const raw = props.error?.message ?? ''\n if (!props.error) return raw\n\n // Заменяем переводом только то сообщение, которое сам классификатор и\n // подставил (`isFallbackMessage`). Прежнее опознание сравнением строк\n // выбрасывало ответ сервера, если тот дословно совпал с дефолтом.\n if (!raw || props.error.isFallbackMessage) {\n return mergedTexts.value[KIND_MESSAGE_KEY[props.error.kind]]\n }\n return raw\n})\n\n/**\n * Сводный список «деталей» под основным `message`.\n * Предпочитаем `fieldErrors` (поле → сообщения), затем — плоский `details`.\n * Дедуплицируем и убираем повтор с `message`.\n */\nconst detailEntries = computed<{ field?: string, text: string }[]>(() => {\n if (!props.error || !props.showDetails) return []\n\n const seen = new Set<string>()\n if (props.dedupeDetails) {\n const main = message.value.trim()\n if (main) seen.add(main)\n }\n\n const result: { field?: string, text: string }[] = []\n const fieldErrors = props.error.fieldErrors\n if (fieldErrors?.length) {\n for (const fe of fieldErrors) {\n for (const m of fe.messages) {\n const text = m.trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ field: fe.field, text })\n }\n }\n if (result.length) return result\n }\n\n const details = props.error.details\n if (details?.length) {\n for (const d of details) {\n const text = (d ?? '').trim()\n if (!text || seen.has(text)) continue\n seen.add(text)\n result.push({ text })\n }\n }\n return result\n})\n\nfunction fieldLabel(field?: string): string | undefined {\n if (!field) return undefined\n return props.fieldLabels[field] ?? field\n}\n\nfunction onRetry(): void {\n if (props.error) emit('retry', props.error)\n}\n\nfunction onDismiss(): void {\n emit('dismiss')\n}\n</script>\n\n<template>\n <GrAlert\n v-if=\"!isHidden && props.error\"\n :tone=\"tone\"\n :title=\"title\"\n :closable=\"props.canDismiss\"\n :data-testid=\"`${props.testIdPrefix}-banner`\"\n :data-kind=\"props.error.kind\"\n @close=\"onDismiss\"\n >\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n v-if=\"props.showStatus && typeof props.error.status === 'number'\"\n class=\"inline-flex items-center rounded-[var(--gr-radius-sm)] bg-[color-mix(in_srgb,currentColor_12%,transparent)] px-1.5 py-0.5 text-[length:var(--gr-control-text-2xs)] font-700 leading-none\"\n :data-testid=\"`${props.testIdPrefix}-status`\"\n >\n {{ statusLabel }}\n </span>\n <span\n class=\"text-[length:var(--gr-control-text-sm)] leading-[var(--gr-control-leading-sm)]\"\n :data-testid=\"`${props.testIdPrefix}-message`\"\n >\n {{ message }}\n </span>\n </div>\n\n <ul\n v-if=\"detailEntries.length\"\n class=\"m-0 list-disc pl-5 text-[length:var(--gr-control-text-xs)] leading-[var(--gr-control-leading-xs)]\"\n :data-testid=\"`${props.testIdPrefix}-details`\"\n >\n <li\n v-for=\"(entry, idx) in detailEntries\"\n :key=\"`${entry.field ?? ''}:${idx}`\"\n >\n <template v-if=\"props.showFieldLabels && entry.field\">\n <strong>{{ fieldLabel(entry.field) }}:</strong> \n </template>{{ entry.text }}\n </li>\n </ul>\n\n <div\n v-if=\"props.canRetry\"\n class=\"mt-1 flex flex-wrap items-center gap-2\"\n >\n <!--\n Тон кнопки — тон баннера: `primary` по умолчанию давал синюю кнопку на\n красной и оранжевой подложке. Шкала тонов у кнопки и у баннера одна,\n поэтому это проброс, а не маппинг.\n -->\n <GrButton\n size=\"sm\"\n variant=\"outline\"\n :tone=\"tone\"\n :data-testid=\"`${props.testIdPrefix}-retry`\"\n @click=\"onRetry\"\n >\n {{ mergedTexts.retryLabel }}\n </GrButton>\n </div>\n </div>\n </GrAlert>\n</template>\n"],"mappings":"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"}
|