@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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inert-DIHCCn_1.js","names":[],"sources":["../../src/composables/internal/inert.ts"],"sourcesContent":["/**\n * `inert` для чужих поддеревьев.\n *\n * Визуального перекрытия мало: без `inert` таб уходит в форму, которую\n * пользователь уже не видит, а скринридер читает её как обычную. `inert`\n * снимает поддерево целиком — фокус, события указателя и дерево доступности.\n *\n * Два правила, из-за которых это отдельный модуль, а не пара строк на месте:\n *\n * - **чужой `inert` не снимаем.** Элемент мог быть инертен и до нас;\n * - **счётчик на элемент.** Два открытых оверлея пометят один и тот же фон, и\n * закрытие первого не должно возвращать его в таб-порядок под вторым.\n */\n\ninterface InertRecord {\n /** Сколько слоёв сейчас держат элемент инертным. */\n count: number\n /** Был ли `inert` до нас: такой не снимаем. */\n foreign: boolean\n /** Прежнее значение `aria-hidden` (или `null`, если атрибута не было). */\n ariaHidden: string | null\n}\n\nconst records = new WeakMap<HTMLElement, InertRecord>()\n\n/** Помечает элементы инертными и возвращает снятие пометки. */\nexport function markInert(elements: HTMLElement[]): () => void {\n const marked: HTMLElement[] = []\n\n for (const element of elements) {\n const existing = records.get(element)\n\n if (existing) {\n existing.count += 1\n marked.push(element)\n continue\n }\n\n const foreign = element.hasAttribute('inert')\n records.set(element, {\n count: 1,\n foreign,\n ariaHidden: element.getAttribute('aria-hidden'),\n })\n marked.push(element)\n\n if (foreign)
|
|
1
|
+
{"version":3,"file":"inert-DIHCCn_1.js","names":[],"sources":["../../src/composables/internal/inert.ts"],"sourcesContent":["/**\n * `inert` для чужих поддеревьев.\n *\n * Визуального перекрытия мало: без `inert` таб уходит в форму, которую\n * пользователь уже не видит, а скринридер читает её как обычную. `inert`\n * снимает поддерево целиком — фокус, события указателя и дерево доступности.\n *\n * Два правила, из-за которых это отдельный модуль, а не пара строк на месте:\n *\n * - **чужой `inert` не снимаем.** Элемент мог быть инертен и до нас;\n * - **счётчик на элемент.** Два открытых оверлея пометят один и тот же фон, и\n * закрытие первого не должно возвращать его в таб-порядок под вторым.\n */\n\ninterface InertRecord {\n /** Сколько слоёв сейчас держат элемент инертным. */\n count: number\n /** Был ли `inert` до нас: такой не снимаем. */\n foreign: boolean\n /** Прежнее значение `aria-hidden` (или `null`, если атрибута не было). */\n ariaHidden: string | null\n}\n\nconst records = new WeakMap<HTMLElement, InertRecord>()\n\n/** Помечает элементы инертными и возвращает снятие пометки. */\nexport function markInert(elements: HTMLElement[]): () => void {\n const marked: HTMLElement[] = []\n\n for (const element of elements) {\n const existing = records.get(element)\n\n if (existing) {\n existing.count += 1\n marked.push(element)\n continue\n }\n\n const foreign = element.hasAttribute('inert')\n records.set(element, {\n count: 1,\n foreign,\n ariaHidden: element.getAttribute('aria-hidden'),\n })\n marked.push(element)\n\n if (foreign)\n continue\n\n element.setAttribute('inert', '')\n // `inert` убирает поддерево из дерева доступности не во всех движках,\n // поэтому намерение дублируется явно.\n element.setAttribute('aria-hidden', 'true')\n }\n\n return () => {\n for (const element of marked) {\n const record = records.get(element)\n if (!record)\n continue\n\n record.count -= 1\n if (record.count > 0)\n continue\n\n records.delete(element)\n if (record.foreign)\n continue\n\n element.removeAttribute('inert')\n if (record.ariaHidden === null)\n element.removeAttribute('aria-hidden')\n else element.setAttribute('aria-hidden', record.ariaHidden)\n }\n }\n}\n\n/**\n * Что гасить, пока открыт модальный слой.\n *\n * Правило одно: **инертно всё, кроме ветки, в которой живёт оверлей**. Идём от\n * него вверх до `<body>` и на каждом уровне помечаем соседей. Наивное «все дети\n * `body`, кроме портала» сломалось бы на `portalTarget` внутри контейнера\n * приложения: `inert` наследуется вниз, и помеченный предок убил бы сам оверлей.\n *\n * Корни других оверлеев пропускаются (`data-gr-overlay-root`): тост обязан\n * оставаться видимым и озвученным поверх открытой модалки — ради этого он и\n * сидит на самом верхнем слое шкалы, — а панель селекта, открытая изнутри окна,\n * принадлежит тому же слою. Порядок между самими оверлеями решает стек слоёв\n * (`isTopmost`), а не этот модуль.\n *\n * По той же причине пропускается хост объявителя (`data-gr-live-region`):\n * `inert` выбрасывает поддерево из дерева доступности, и общий живой регион\n * замолчал бы ровно под открытым диалогом — там, где «сохранено» и «ошибка\n * сети» и нужны.\n */\nexport function inertableOutside(root: HTMLElement | null | undefined): HTMLElement[] {\n if (!root?.isConnected)\n return []\n\n const result: HTMLElement[] = []\n const body = root.ownerDocument.body\n\n let node: HTMLElement | null = root\n\n while (node && node !== body) {\n const parent: HTMLElement | null = node.parentElement\n if (!parent)\n break\n\n for (const sibling of Array.from(parent.children)) {\n if (!(sibling instanceof HTMLElement))\n continue\n if (sibling === node)\n continue\n if (sibling.hasAttribute('data-gr-overlay-root'))\n continue\n if (sibling.hasAttribute('data-gr-portal'))\n continue\n if (sibling.hasAttribute('data-gr-live-region'))\n continue\n result.push(sibling)\n }\n\n node = parent\n }\n\n return result\n}\n"],"mappings":";AAuBA,IAAM,0BAAU,IAAI,QAAkC;;AAGtD,SAAgB,UAAU,UAAqC;CAC7D,MAAM,SAAwB,CAAC;CAE/B,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,WAAW,QAAQ,IAAI,OAAO;EAEpC,IAAI,UAAU;GACZ,SAAS,SAAS;GAClB,OAAO,KAAK,OAAO;GACnB;EACF;EAEA,MAAM,UAAU,QAAQ,aAAa,OAAO;EAC5C,QAAQ,IAAI,SAAS;GACnB,OAAO;GACP;GACA,YAAY,QAAQ,aAAa,aAAa;EAChD,CAAC;EACD,OAAO,KAAK,OAAO;EAEnB,IAAI,SACF;EAEF,QAAQ,aAAa,SAAS,EAAE;EAGhC,QAAQ,aAAa,eAAe,MAAM;CAC5C;CAEA,aAAa;EACX,KAAK,MAAM,WAAW,QAAQ;GAC5B,MAAM,SAAS,QAAQ,IAAI,OAAO;GAClC,IAAI,CAAC,QACH;GAEF,OAAO,SAAS;GAChB,IAAI,OAAO,QAAQ,GACjB;GAEF,QAAQ,OAAO,OAAO;GACtB,IAAI,OAAO,SACT;GAEF,QAAQ,gBAAgB,OAAO;GAC/B,IAAI,OAAO,eAAe,MACxB,QAAQ,gBAAgB,aAAa;QAClC,QAAQ,aAAa,eAAe,OAAO,UAAU;EAC5D;CACF;AACF;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,iBAAiB,MAAqD;CACpF,IAAI,CAAC,MAAM,aACT,OAAO,CAAC;CAEV,MAAM,SAAwB,CAAC;CAC/B,MAAM,OAAO,KAAK,cAAc;CAEhC,IAAI,OAA2B;CAE/B,OAAO,QAAQ,SAAS,MAAM;EAC5B,MAAM,SAA6B,KAAK;EACxC,IAAI,CAAC,QACH;EAEF,KAAK,MAAM,WAAW,MAAM,KAAK,OAAO,QAAQ,GAAG;GACjD,IAAI,EAAE,mBAAmB,cACvB;GACF,IAAI,YAAY,MACd;GACF,IAAI,QAAQ,aAAa,sBAAsB,GAC7C;GACF,IAAI,QAAQ,aAAa,gBAAgB,GACvC;GACF,IAAI,QAAQ,aAAa,qBAAqB,GAC5C;GACF,OAAO,KAAK,OAAO;EACrB;EAEA,OAAO;CACT;CAEA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyboard-DE2yhz_b.js","names":[],"sources":["../../src/internal/keyboard.ts"],"sourcesContent":["/**\n * Общие примитивы разбора клавиатурных событий: IME-композиция и матчинг\n * клавиш, не зависящий от раскладки.\n *\n * Живут в `internal/`, а не рядом с потребителем: одно и то же нужно стеку\n * оверлеев (`composables/internal/overlayStack.ts`), директиве `v-hotkey` и\n * матчеру палитры (`components/shared/hotkey.ts`) — три слоя, у которых нет\n * общего предка ближе этого модуля.\n */\n\n/**\n * Событие пришло во время IME-композиции — обрабатывать его как команду нельзя:\n * Esc здесь отменяет композицию, Enter — коммитит её, стрелки ходят по\n * кандидатам. `keyCode === 229` — страховка для движков, которые не выставляют\n * `isComposing` (исторически WebKit/старый Chromium).\n */\nexport function isComposingEvent(event: KeyboardEvent): boolean {\n return event.isComposing || event.keyCode === 229\n}\n\n/**\n * Фокус на контроле, который сам распоряжается печатными клавишами.\n *\n * Нужен всем, кто вешает клавиатурные команды на контейнер: горячая клавиша и\n * typeahead меню обязаны молчать, пока пользователь печатает в поле внутри них.\n */\nexport function isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement))
|
|
1
|
+
{"version":3,"file":"keyboard-DE2yhz_b.js","names":[],"sources":["../../src/internal/keyboard.ts"],"sourcesContent":["/**\n * Общие примитивы разбора клавиатурных событий: IME-композиция и матчинг\n * клавиш, не зависящий от раскладки.\n *\n * Живут в `internal/`, а не рядом с потребителем: одно и то же нужно стеку\n * оверлеев (`composables/internal/overlayStack.ts`), директиве `v-hotkey` и\n * матчеру палитры (`components/shared/hotkey.ts`) — три слоя, у которых нет\n * общего предка ближе этого модуля.\n */\n\n/**\n * Событие пришло во время IME-композиции — обрабатывать его как команду нельзя:\n * Esc здесь отменяет композицию, Enter — коммитит её, стрелки ходят по\n * кандидатам. `keyCode === 229` — страховка для движков, которые не выставляют\n * `isComposing` (исторически WebKit/старый Chromium).\n */\nexport function isComposingEvent(event: KeyboardEvent): boolean {\n return event.isComposing || event.keyCode === 229\n}\n\n/**\n * Фокус на контроле, который сам распоряжается печатными клавишами.\n *\n * Нужен всем, кто вешает клавиатурные команды на контейнер: горячая клавиша и\n * typeahead меню обязаны молчать, пока пользователь печатает в поле внутри них.\n */\nexport function isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement))\n return false\n if (target.isContentEditable)\n return true\n\n const tag = target.tagName\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT'\n}\n\n/**\n * macOS определяем по платформе: от неё зависит, чем является токен `mod` —\n * Cmd или Ctrl. Живёт здесь, а не рядом с одним потребителем: то же нужно\n * `GrKbd` (что показать), палитре (что слушать) и директиве `v-hotkey`.\n */\nexport function isAppleDevice(): boolean {\n if (typeof navigator === 'undefined')\n return false\n return /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent)\n}\n\n/** Физический код клавиши для латинской буквы или цифры; иначе `null`. */\nexport function codeForChar(ch: string): string | null {\n if (/^[a-z]$/i.test(ch))\n return `Key${ch.toUpperCase()}`\n if (/^\\d$/.test(ch))\n return `Digit${ch}`\n return null\n}\n\nexport interface EventMatchesKeyOptions {\n /**\n * Дополнительно матчить по физическому коду (`event.code`), когда `event.key`\n * не совпал. Нужен комбинациям с модификаторами: на нелатинской раскладке\n * Ctrl/Cmd+K приходит как `key: 'л'`, и без кода сочетание мертво. Для\n * одиночных клавиш без модификаторов фолбэк не включается — там семантика\n * печатная, а не позиционная.\n */\n codeFallback?: boolean\n}\n\n/** Совпадает ли нажатая клавиша с ожидаемой (`'k'`, `'Escape'`, `' '`). */\nexport function eventMatchesKey(\n event: KeyboardEvent,\n expected: string,\n options: EventMatchesKeyOptions = {},\n): boolean {\n if (expected === ' ')\n return event.key === ' ' || event.key === 'Spacebar'\n\n if (expected.length === 1) {\n if (event.key.toLowerCase() === expected.toLowerCase())\n return true\n return Boolean(options.codeFallback) && codeForChar(expected) === event.code\n }\n\n return event.key === expected\n}\n\n/**\n * Удовлетворяет ли состояние Shift ожиданиям комбинации.\n *\n * Объявленный `shift` требует нажатого Shift. Необъявленный — запрещает его\n * с одним исключением: символ, который без Shift не набрать (`?`, `+`, `:`),\n * легален вместе с ним, когда `event.key` уже равен ожидаемому символу.\n * Буквы под исключение не попадают: `'a'` и Shift+A — разные намерения.\n */\nexport function shiftSatisfied(event: KeyboardEvent, expectedKey: string, expectShift: boolean): boolean {\n if (expectShift)\n return event.shiftKey\n if (!event.shiftKey)\n return true\n\n return expectedKey.length === 1 && !/\\p{L}/u.test(expectedKey) && event.key === expectedKey\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,iBAAiB,OAA+B;CAC9D,OAAO,MAAM,eAAe,MAAM,YAAY;AAChD;;;;;;;AAQA,SAAgB,iBAAiB,QAAqC;CACpE,IAAI,EAAE,kBAAkB,cACtB,OAAO;CACT,IAAI,OAAO,mBACT,OAAO;CAET,MAAM,MAAM,OAAO;CACnB,OAAO,QAAQ,WAAW,QAAQ,cAAc,QAAQ;AAC1D;;;;;;AAOA,SAAgB,gBAAyB;CACvC,IAAI,OAAO,cAAc,aACvB,OAAO;CACT,OAAO,uBAAuB,KAAK,UAAU,YAAY,UAAU,SAAS;AAC9E;;AAGA,SAAgB,YAAY,IAA2B;CACrD,IAAI,WAAW,KAAK,EAAE,GACpB,OAAO,MAAM,GAAG,YAAY;CAC9B,IAAI,OAAO,KAAK,EAAE,GAChB,OAAO,QAAQ;CACjB,OAAO;AACT;;AAcA,SAAgB,gBACd,OACA,UACA,UAAkC,CAAC,GAC1B;CACT,IAAI,aAAa,KACf,OAAO,MAAM,QAAQ,OAAO,MAAM,QAAQ;CAE5C,IAAI,SAAS,WAAW,GAAG;EACzB,IAAI,MAAM,IAAI,YAAY,MAAM,SAAS,YAAY,GACnD,OAAO;EACT,OAAO,QAAQ,QAAQ,YAAY,KAAK,YAAY,QAAQ,MAAM,MAAM;CAC1E;CAEA,OAAO,MAAM,QAAQ;AACvB;;;;;;;;;AAUA,SAAgB,eAAe,OAAsB,aAAqB,aAA+B;CACvG,IAAI,aACF,OAAO,MAAM;CACf,IAAI,CAAC,MAAM,UACT,OAAO;CAET,OAAO,YAAY,WAAW,KAAK,CAAC,SAAS,KAAK,WAAW,KAAK,MAAM,QAAQ;AAClF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"numericStep-Bq2lj4ew.js","names":[],"sources":["../../src/components/shared/numericStep.ts"],"sourcesContent":["/**\n * Крупный шаг клавиатуры: `PageUp` и `PageDown`.\n *\n * Десять обычных шагов либо десятая часть диапазона — что крупнее. Правило\n * одно на слайдер и числовое поле: у обоих `PageUp` обязан двигать значение\n * заметно, но кратно шагу, иначе значение уедет мимо сетки.\n *\n * Границы необязательны, потому что у поля их может не быть вовсе — без них\n * диапазона не существует и остаются те же десять шагов. Компонент, у которого\n * границы обязательны, но могут выродиться (`max <= min` у слайдера),\n * подставляет сюда уже нормализованный размах: так обе трактовки вырожденного\n * случая сохраняются без ветвления внутри формулы.\n */\nexport function bigStep(step: number, min?: number, max?: number): number {\n const base = step * 10\n if (min === undefined || max === undefined)
|
|
1
|
+
{"version":3,"file":"numericStep-Bq2lj4ew.js","names":[],"sources":["../../src/components/shared/numericStep.ts"],"sourcesContent":["/**\n * Крупный шаг клавиатуры: `PageUp` и `PageDown`.\n *\n * Десять обычных шагов либо десятая часть диапазона — что крупнее. Правило\n * одно на слайдер и числовое поле: у обоих `PageUp` обязан двигать значение\n * заметно, но кратно шагу, иначе значение уедет мимо сетки.\n *\n * Границы необязательны, потому что у поля их может не быть вовсе — без них\n * диапазона не существует и остаются те же десять шагов. Компонент, у которого\n * границы обязательны, но могут выродиться (`max <= min` у слайдера),\n * подставляет сюда уже нормализованный размах: так обе трактовки вырожденного\n * случая сохраняются без ветвления внутри формулы.\n */\nexport function bigStep(step: number, min?: number, max?: number): number {\n const base = step * 10\n if (min === undefined || max === undefined)\n return base\n\n const span = max - min\n const tenth = Math.round((span / 10) / step) * step\n\n return Math.max(base, tenth || step)\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,QAAQ,MAAc,KAAc,KAAsB;CACxE,MAAM,OAAO,OAAO;CACpB,IAAI,QAAQ,KAAA,KAAa,QAAQ,KAAA,GAC/B,OAAO;CAET,MAAM,OAAO,MAAM;CACnB,MAAM,QAAQ,KAAK,MAAO,OAAO,KAAM,IAAI,IAAI;CAE/C,OAAO,KAAK,IAAI,MAAM,SAAS,IAAI;AACrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"optionFilter-DYw9XPPx.js","names":[],"sources":["../../src/components/shared/optionFilter.ts"],"sourcesContent":["/**\n * Подстрочный поиск по опциям — один на пакет.\n *\n * Тот же предикат был написан трижды, и у каждой копии был свой контракт\n * нормализации: `GrSelect` лоукейсил запрос в вызывающем коде, `GrAutocomplete`\n * внутри матчера, `GrCommandPalette` — внутри своего. Расхождение такого рода\n * не видно на глаз и всплывает как «в одном компоненте регистр учитывается, в\n * другом нет».\n *\n * Нормализация здесь ровно одна (`normalizeOptionQuery`), а компонент выбирает,\n * что ему нужно: готовый плоский список (`filterOptions`) или голый предикат\n * (`matchesOptionQuery`) — `GrSelect` фильтрует внутри обхода групп и списка не\n * имеет.\n */\n\n/** Запрос в сравнимом виде: без краевых пробелов и в нижнем регистре. */\nexport function normalizeOptionQuery(raw: string): string {\n return raw.trim().toLowerCase()\n}\n\n/**\n * Ядро матча: подстрока в любом из полей. `undefined` в наборе пропускается —\n * у команды палитры может не быть ни описания, ни группы.\n *\n * `needle` ожидается уже нормализованным: нормализовать на каждый элемент\n * списка значило бы делать одну и ту же работу n раз.\n */\nexport function matchesQueryParts(parts: readonly (string | undefined)[], needle: string): boolean {\n if (!needle)
|
|
1
|
+
{"version":3,"file":"optionFilter-DYw9XPPx.js","names":[],"sources":["../../src/components/shared/optionFilter.ts"],"sourcesContent":["/**\n * Подстрочный поиск по опциям — один на пакет.\n *\n * Тот же предикат был написан трижды, и у каждой копии был свой контракт\n * нормализации: `GrSelect` лоукейсил запрос в вызывающем коде, `GrAutocomplete`\n * внутри матчера, `GrCommandPalette` — внутри своего. Расхождение такого рода\n * не видно на глаз и всплывает как «в одном компоненте регистр учитывается, в\n * другом нет».\n *\n * Нормализация здесь ровно одна (`normalizeOptionQuery`), а компонент выбирает,\n * что ему нужно: готовый плоский список (`filterOptions`) или голый предикат\n * (`matchesOptionQuery`) — `GrSelect` фильтрует внутри обхода групп и списка не\n * имеет.\n */\n\n/** Запрос в сравнимом виде: без краевых пробелов и в нижнем регистре. */\nexport function normalizeOptionQuery(raw: string): string {\n return raw.trim().toLowerCase()\n}\n\n/**\n * Ядро матча: подстрока в любом из полей. `undefined` в наборе пропускается —\n * у команды палитры может не быть ни описания, ни группы.\n *\n * `needle` ожидается уже нормализованным: нормализовать на каждый элемент\n * списка значило бы делать одну и ту же работу n раз.\n */\nexport function matchesQueryParts(parts: readonly (string | undefined)[], needle: string): boolean {\n if (!needle)\n return true\n\n return parts.some(part => part !== undefined && part.toLowerCase().includes(needle))\n}\n\n/** Опция в терминах поиска: метка видна пользователю, значение — то, что он мог запомнить. */\nexport interface OptionLike {\n label: string\n value: unknown\n}\n\n/** Матчер по умолчанию: метка и значение опции. */\nexport function matchesOptionQuery(option: OptionLike, needle: string): boolean {\n return matchesQueryParts([option.label, String(option.value)], needle)\n}\n\n/**\n * Плоская фильтрация списка опций.\n *\n * Пустой запрос отдаёт **тот же массив** и матчер не зовёт вовсе: свой `filter`\n * потребителя не должен решать, что показывать «без запроса», а копия на\n * каждый пересчёт била бы по виртуализированным спискам на тысячи опций.\n *\n * Пользовательскому матчеру уходит **сырой** запрос (только `trim`), а не\n * нормализованный: это публичный контракт пропа `filter` у `GrAutocomplete`,\n * и чужая реализация вправе смотреть на регистр.\n */\nexport function filterOptions<TOption extends OptionLike>(\n options: TOption[],\n rawQuery: string,\n filter?: (option: TOption, query: string) => boolean,\n): TOption[] {\n const query = rawQuery.trim()\n if (!query)\n return options\n\n if (filter)\n return options.filter(option => filter(option, query))\n\n const needle = query.toLowerCase()\n return options.filter(option => matchesOptionQuery(option, needle))\n}\n\n/**\n * Значения выбора — в опции для чипов.\n *\n * Значение, которого нет среди опций, показывается как есть: так выглядит\n * произвольное значение (`allowCustomValue`) и значение, пришедшее из модели\n * раньше, чем подгрузились опции. Прятать его нельзя — пользователь видел бы\n * пустое место вместо своего выбора.\n *\n * Сравнение идёт через ключ, а не `===`: объектные значения приходят в модель\n * отдельной копией, и ссылочное равенство не нашло бы ничего.\n */\nexport function resolveSelectedOptions<TValue, TOption extends { label: string, value: TValue }>(\n values: readonly TValue[],\n options: readonly TOption[],\n keyOf: (value: TValue) => string = value => String(value),\n): (TOption | { value: TValue, label: string })[] {\n const byKey = new Map<string, TOption>()\n\n // Первый выигрывает: одно и то же значение в двух группах — это одна опция,\n // и подпись чипа не должна зависеть от порядка обхода.\n for (const option of options) {\n const key = keyOf(option.value)\n if (!byKey.has(key))\n byKey.set(key, option)\n }\n\n return values.map((value) => {\n const key = keyOf(value)\n return byKey.get(key) ?? { value, label: key }\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,SAAgB,qBAAqB,KAAqB;CACxD,OAAO,IAAI,KAAK,CAAC,CAAC,YAAY;AAChC;;;;;;;;AASA,SAAgB,kBAAkB,OAAwC,QAAyB;CACjG,IAAI,CAAC,QACH,OAAO;CAET,OAAO,MAAM,MAAK,SAAQ,SAAS,KAAA,KAAa,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,CAAC;AACrF;;AASA,SAAgB,mBAAmB,QAAoB,QAAyB;CAC9E,OAAO,kBAAkB,CAAC,OAAO,OAAO,OAAO,OAAO,KAAK,CAAC,GAAG,MAAM;AACvE;;;;;;;;;;;;AAaA,SAAgB,cACd,SACA,UACA,QACW;CACX,MAAM,QAAQ,SAAS,KAAK;CAC5B,IAAI,CAAC,OACH,OAAO;CAET,IAAI,QACF,OAAO,QAAQ,QAAO,WAAU,OAAO,QAAQ,KAAK,CAAC;CAEvD,MAAM,SAAS,MAAM,YAAY;CACjC,OAAO,QAAQ,QAAO,WAAU,mBAAmB,QAAQ,MAAM,CAAC;AACpE;;;;;;;;;;;;AAaA,SAAgB,uBACd,QACA,SACA,SAAmC,UAAS,OAAO,KAAK,GACR;CAChD,MAAM,wBAAQ,IAAI,IAAqB;CAIvC,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,MAAM,MAAM,OAAO,KAAK;EAC9B,IAAI,CAAC,MAAM,IAAI,GAAG,GAChB,MAAM,IAAI,KAAK,MAAM;CACzB;CAEA,OAAO,OAAO,KAAK,UAAU;EAC3B,MAAM,MAAM,MAAM,KAAK;EACvB,OAAO,MAAM,IAAI,GAAG,KAAK;GAAE;GAAO,OAAO;EAAI;CAC/C,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlayStack-C8cfsqXa.js","names":[],"sources":["../../src/composables/internal/overlayStack.ts"],"sourcesContent":["/**\n * Единый стек слоёв оверлеев: модалки, drawer'ы, просмотрщик, панели селектов,\n * дропдауны, подсказки — всё, что открывается поверх страницы.\n *\n * Список один, и порядок регистрации совпадает с порядком открытия, но из него\n * выводятся **две разные вершины**:\n *\n * - **Esc** → последний слой любого рода. Дропдаун, открытый внутри модалки,\n * обязан закрыться первым — пользователь видит верхним именно его.\n * - **`inert`** → ставится на модальные слои ниже последнего **модального**.\n * Немодальный слой сверху модалку не понижает: иначе окно ушло бы в `inert`\n * вместе со своим же открытым дропдауном и перестало отвечать.\n * - **высота** → `openModalCount()` поднимает floating-панель над окном, из\n * которого её открыли: телепортированная панель лежит рядом с корнем окна,\n * и статический `z-index` дропдауна оставлял её под ним.\n *\n * Вести эти две вершины в двух независимых реестрах нельзя: реестры описывают\n * один и тот же порядок и расходятся молча — достаточно оверлея, который\n * зарегистрировался в одном и забыл про другой.\n *\n * Ловушку фокуса стек не реализует сам, но питает её (`useFocusTrap`): он\n * видит слои из **разных деревьев рендера** (диалоги `useDialogService`\n * монтируются отдельным `render()` в `body`) и знает их корни.\n */\n\nimport { isComposingEvent } from '../../internal/keyboard'\n\nexport interface OverlayLayer {\n /** Уникальный id зарегистрированного слоя. */\n id: number\n /**\n * Модальный слой: блокирует страницу и участвует в вычислении `inert`.\n * Немодальные (поповеры, меню, подсказки) — только в очереди Esc.\n */\n modal: boolean\n /** Закрывать ли слой по Esc (реактивный геттер: `closeOnEsc` и подобные). */\n shouldClose: () => boolean\n /** Закрыть слой. */\n close: () => void\n /** Уведомление «этот модальный слой сейчас верхний». Только для `modal`. */\n setTopmost?: (isTopmost: boolean) => void\n /**\n * Корень слоя. Ловушка фокуса нижнего слоя обязана считать его своим:\n * панель селекта, открытого внутри модалки, телепортирована в `body` и лежит\n * вне DOM-поддерева окна, но для пользователя это тот же слой.\n */\n root?: () => HTMLElement | null\n}\n\nconst stack: OverlayLayer[] = []\nlet listening = false\nlet nextId = 1\n\n/** Пересчитывает верхний **модальный** слой и уведомляет все модальные. */\nfunction syncTopmost(): void {\n const modals = stack.filter(layer => layer.modal)\n const top = modals[modals.length - 1]\n for (const layer of modals) layer.setTopmost?.(layer === top)\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape')
|
|
1
|
+
{"version":3,"file":"overlayStack-C8cfsqXa.js","names":[],"sources":["../../src/composables/internal/overlayStack.ts"],"sourcesContent":["/**\n * Единый стек слоёв оверлеев: модалки, drawer'ы, просмотрщик, панели селектов,\n * дропдауны, подсказки — всё, что открывается поверх страницы.\n *\n * Список один, и порядок регистрации совпадает с порядком открытия, но из него\n * выводятся **две разные вершины**:\n *\n * - **Esc** → последний слой любого рода. Дропдаун, открытый внутри модалки,\n * обязан закрыться первым — пользователь видит верхним именно его.\n * - **`inert`** → ставится на модальные слои ниже последнего **модального**.\n * Немодальный слой сверху модалку не понижает: иначе окно ушло бы в `inert`\n * вместе со своим же открытым дропдауном и перестало отвечать.\n * - **высота** → `openModalCount()` поднимает floating-панель над окном, из\n * которого её открыли: телепортированная панель лежит рядом с корнем окна,\n * и статический `z-index` дропдауна оставлял её под ним.\n *\n * Вести эти две вершины в двух независимых реестрах нельзя: реестры описывают\n * один и тот же порядок и расходятся молча — достаточно оверлея, который\n * зарегистрировался в одном и забыл про другой.\n *\n * Ловушку фокуса стек не реализует сам, но питает её (`useFocusTrap`): он\n * видит слои из **разных деревьев рендера** (диалоги `useDialogService`\n * монтируются отдельным `render()` в `body`) и знает их корни.\n */\n\nimport { isComposingEvent } from '../../internal/keyboard'\n\nexport interface OverlayLayer {\n /** Уникальный id зарегистрированного слоя. */\n id: number\n /**\n * Модальный слой: блокирует страницу и участвует в вычислении `inert`.\n * Немодальные (поповеры, меню, подсказки) — только в очереди Esc.\n */\n modal: boolean\n /** Закрывать ли слой по Esc (реактивный геттер: `closeOnEsc` и подобные). */\n shouldClose: () => boolean\n /** Закрыть слой. */\n close: () => void\n /** Уведомление «этот модальный слой сейчас верхний». Только для `modal`. */\n setTopmost?: (isTopmost: boolean) => void\n /**\n * Корень слоя. Ловушка фокуса нижнего слоя обязана считать его своим:\n * панель селекта, открытого внутри модалки, телепортирована в `body` и лежит\n * вне DOM-поддерева окна, но для пользователя это тот же слой.\n */\n root?: () => HTMLElement | null\n}\n\nconst stack: OverlayLayer[] = []\nlet listening = false\nlet nextId = 1\n\n/** Пересчитывает верхний **модальный** слой и уведомляет все модальные. */\nfunction syncTopmost(): void {\n const modals = stack.filter(layer => layer.modal)\n const top = modals[modals.length - 1]\n for (const layer of modals) layer.setTopmost?.(layer === top)\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape')\n return\n // Esc во время IME-композиции отменяет её, а не закрывает слой.\n if (isComposingEvent(event))\n return\n if (stack.length === 0)\n return\n\n const top = stack[stack.length - 1]\n\n // Гасим всегда, даже если верхний слой не закрывается по Esc: иначе нажатие\n // «провалится» на слой ниже и закроет не то, что видит пользователь.\n event.preventDefault()\n event.stopPropagation()\n if (typeof event.stopImmediatePropagation === 'function')\n event.stopImmediatePropagation()\n\n if (top.shouldClose())\n top.close()\n}\n\nfunction startListening(): void {\n if (listening)\n return\n if (typeof window === 'undefined')\n return\n // Capture-фаза опережает любые локальные обработчики: Esc обязан достаться\n // верхнему слою, а не тому, кто первым подписался.\n window.addEventListener('keydown', handleKeydown, true)\n listening = true\n}\n\nfunction stopListening(): void {\n if (!listening)\n return\n if (typeof window === 'undefined')\n return\n window.removeEventListener('keydown', handleKeydown, true)\n listening = false\n}\n\n/** Регистрирует слой как верхний и возвращает его id. */\nexport function pushOverlayLayer(layer: Omit<OverlayLayer, 'id'>): number {\n const id = nextId++\n stack.push({ ...layer, id })\n startListening()\n syncTopmost()\n return id\n}\n\n/** Снимает слой со стека (при закрытии или размонтировании). */\nexport function removeOverlayLayer(id: number): void {\n const index = stack.findIndex(item => item.id === id)\n if (index >= 0)\n stack.splice(index, 1)\n if (stack.length === 0)\n stopListening()\n syncTopmost()\n}\n\n/**\n * Корни слоёв, открытых **поверх** данного.\n *\n * Ими ловушка фокуса дополняет свой контейнер: всё, что открыто изнутри слоя\n * (панель селекта, меню, подсказка), лежит в `body` отдельным поддеревом, и\n * без этого списка ловушка утаскивала бы из него фокус обратно в окно.\n */\nexport function layerRootsAbove(id: number): HTMLElement[] {\n const index = stack.findIndex(layer => layer.id === id)\n if (index < 0)\n return []\n\n return stack\n .slice(index + 1)\n .map(layer => layer.root?.() ?? null)\n .filter((root): root is HTMLElement => root !== null)\n}\n\n/** Корни всех зарегистрированных слоёв. */\nexport function allLayerRoots(): HTMLElement[] {\n return stack\n .map(layer => layer.root?.() ?? null)\n .filter((root): root is HTMLElement => root !== null)\n}\n\n/** Сколько слоёв открыто. Для тестов и диагностики. */\nexport function overlayStackSize(): number {\n return stack.length\n}\n\n/**\n * Сколько **модальных** слоёв открыто прямо сейчас.\n *\n * Читает `useFloating`, когда считает высоту панели: панель телепортирована в\n * общий портал и лежит рядом с корнем окна, а не внутри него, поэтому свой\n * стек-контекст её не спасает — высоту приходится брать из порядка слоёв.\n */\nexport function openModalCount(): number {\n return stack.reduce((count, layer) => count + (layer.modal ? 1 : 0), 0)\n}\n\n/** Тестовая/служебная очистка стека. */\nexport function resetOverlayStack(): void {\n stack.splice(0, stack.length)\n stopListening()\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,IAAM,QAAwB,CAAC;AAC/B,IAAI,YAAY;AAChB,IAAI,SAAS;;AAGb,SAAS,cAAoB;CAC3B,MAAM,SAAS,MAAM,QAAO,UAAS,MAAM,KAAK;CAChD,MAAM,MAAM,OAAO,OAAO,SAAS;CACnC,KAAK,MAAM,SAAS,QAAQ,MAAM,aAAa,UAAU,GAAG;AAC9D;AAEA,SAAS,cAAc,OAA4B;CACjD,IAAI,MAAM,QAAQ,UAChB;CAEF,IAAI,iBAAiB,KAAK,GACxB;CACF,IAAI,MAAM,WAAW,GACnB;CAEF,MAAM,MAAM,MAAM,MAAM,SAAS;CAIjC,MAAM,eAAe;CACrB,MAAM,gBAAgB;CACtB,IAAI,OAAO,MAAM,6BAA6B,YAC5C,MAAM,yBAAyB;CAEjC,IAAI,IAAI,YAAY,GAClB,IAAI,MAAM;AACd;AAEA,SAAS,iBAAuB;CAC9B,IAAI,WACF;CACF,IAAI,OAAO,WAAW,aACpB;CAGF,OAAO,iBAAiB,WAAW,eAAe,IAAI;CACtD,YAAY;AACd;AAEA,SAAS,gBAAsB;CAC7B,IAAI,CAAC,WACH;CACF,IAAI,OAAO,WAAW,aACpB;CACF,OAAO,oBAAoB,WAAW,eAAe,IAAI;CACzD,YAAY;AACd;;AAGA,SAAgB,iBAAiB,OAAyC;CACxE,MAAM,KAAK;CACX,MAAM,KAAK;EAAE,GAAG;EAAO;CAAG,CAAC;CAC3B,eAAe;CACf,YAAY;CACZ,OAAO;AACT;;AAGA,SAAgB,mBAAmB,IAAkB;CACnD,MAAM,QAAQ,MAAM,WAAU,SAAQ,KAAK,OAAO,EAAE;CACpD,IAAI,SAAS,GACX,MAAM,OAAO,OAAO,CAAC;CACvB,IAAI,MAAM,WAAW,GACnB,cAAc;CAChB,YAAY;AACd;;;;;;;;AASA,SAAgB,gBAAgB,IAA2B;CACzD,MAAM,QAAQ,MAAM,WAAU,UAAS,MAAM,OAAO,EAAE;CACtD,IAAI,QAAQ,GACV,OAAO,CAAC;CAEV,OAAO,MACJ,MAAM,QAAQ,CAAC,CAAC,CAChB,KAAI,UAAS,MAAM,OAAO,KAAK,IAAI,CAAC,CACpC,QAAQ,SAA8B,SAAS,IAAI;AACxD;;;;;;;;AAqBA,SAAgB,iBAAyB;CACvC,OAAO,MAAM,QAAQ,OAAO,UAAU,SAAS,MAAM,QAAQ,IAAI,IAAI,CAAC;AACxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portalRoot-CVhCgdG6.js","names":[],"sources":["../../src/composables/internal/portalRoot.ts"],"sourcesContent":["/**\n * Единая точка монтирования оверлеев.\n *\n * До неё каждый оверлей телепортировался в `body` сам по себе, и правило\n * `inert` держалось на договорённости «все оверлеи лежат ровно на один уровень\n * ниже `body`». Один контейнер даёт управляемую точку монтирования (своё\n * приложение в контейнере, микрофронтенд, shadow DOM), одну точку очистки и\n * порядок, совпадающий с порядком открытия.\n *\n * **У корня не должно быть ни стилей, ни классов.** `transform`, `filter`,\n * `contain`, `perspective` и `will-change` создают containing block для\n * `position: fixed`, и тогда панели `useFloating` начинают позиционироваться\n * относительно портала, а не вьюпорта — с виду это «панель прилипла не туда»,\n * и ищется такое долго.\n */\n\nexport const GR_PORTAL_ID = 'gr-portal'\n\n/**\n * Создаёт корень портала при первом обращении и возвращает его.\n *\n * `null` на сервере: DOM там трогать нечем, а телепорт всё равно выключен до\n * монтирования (см. `useTeleportEnabled`).\n */\nexport function ensurePortalRoot(): HTMLElement | null {\n if (typeof document === 'undefined')
|
|
1
|
+
{"version":3,"file":"portalRoot-CVhCgdG6.js","names":[],"sources":["../../src/composables/internal/portalRoot.ts"],"sourcesContent":["/**\n * Единая точка монтирования оверлеев.\n *\n * До неё каждый оверлей телепортировался в `body` сам по себе, и правило\n * `inert` держалось на договорённости «все оверлеи лежат ровно на один уровень\n * ниже `body`». Один контейнер даёт управляемую точку монтирования (своё\n * приложение в контейнере, микрофронтенд, shadow DOM), одну точку очистки и\n * порядок, совпадающий с порядком открытия.\n *\n * **У корня не должно быть ни стилей, ни классов.** `transform`, `filter`,\n * `contain`, `perspective` и `will-change` создают containing block для\n * `position: fixed`, и тогда панели `useFloating` начинают позиционироваться\n * относительно портала, а не вьюпорта — с виду это «панель прилипла не туда»,\n * и ищется такое долго.\n */\n\nexport const GR_PORTAL_ID = 'gr-portal'\n\n/**\n * Создаёт корень портала при первом обращении и возвращает его.\n *\n * `null` на сервере: DOM там трогать нечем, а телепорт всё равно выключен до\n * монтирования (см. `useTeleportEnabled`).\n */\nexport function ensurePortalRoot(): HTMLElement | null {\n if (typeof document === 'undefined')\n return null\n\n const existing = document.getElementById(GR_PORTAL_ID)\n if (existing)\n return existing\n\n const root = document.createElement('div')\n root.id = GR_PORTAL_ID\n root.setAttribute('data-gr-portal', '')\n document.body.appendChild(root)\n\n return root\n}\n\n/** Текущий корень портала, если он уже создан. */\nexport function getPortalRoot(): HTMLElement | null {\n if (typeof document === 'undefined')\n return null\n return document.getElementById(GR_PORTAL_ID)\n}\n\n/** Тестовая/служебная очистка: убирает корень из документа. */\nexport function resetPortalRoot(): void {\n getPortalRoot()?.remove()\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAa,eAAe;;;;;;;AAQ5B,SAAgB,mBAAuC;CACrD,IAAI,OAAO,aAAa,aACtB,OAAO;CAET,MAAM,WAAW,SAAS,eAAe,YAAY;CACrD,IAAI,UACF,OAAO;CAET,MAAM,OAAO,SAAS,cAAc,KAAK;CACzC,KAAK,KAAK;CACV,KAAK,aAAa,kBAAkB,EAAE;CACtC,SAAS,KAAK,YAAY,IAAI;CAE9B,OAAO;AACT;;AAGA,SAAgB,gBAAoC;CAClD,IAAI,OAAO,aAAa,aACtB,OAAO;CACT,OAAO,SAAS,eAAe,YAAY;AAC7C;;AAGA,SAAgB,kBAAwB;CACtC,cAAc,CAAC,EAAE,OAAO;AAC1B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renamedProp-BwcN-SJg.js","names":[],"sources":["../../src/components/shared/renamedProp.ts"],"sourcesContent":["import { watchEffect } from 'vue'\n\n/**\n * Предупреждение о пропе, который переименовали.\n *\n * Незнакомый атрибут Vue не отвергает: он уходит в `$attrs` и садится на\n * корневой узел. Значит переименование пропа проходит у потребителя **молча** —\n * не падают ни типы (`vue-tsc` не проверяет лишние атрибуты: принимать их\n * вправе любой компонент), ни рантайм. Компонент просто рисуется дефолтом, и\n * найти это можно только сверкой шаблона со списком пропов.\n *\n * Так и случилось со сменой `variant` на `tone` у `GrBadge` и `GrChip`: у\n * стороннего пакета тихо разъехались десять мест в шести файлах. Причём\n * `variant` в кольце остаётся законным именем у `GrButton`, `GrCard`, `GrAlert`\n * и десятка других — то есть по аналогии не угадать.\n *\n * Предупреждение живёт только в разработке и ничего не чинит за потребителя:\n * подставлять снятое имя значило бы завести алиас, который после 1.0 останется\n * навсегда.\n */\nexport function warnRenamedProp(\n component: string,\n attrs: Record<string, unknown>,\n renames: Readonly<Record<string, string>>,\n): void {\n if (!__GR_DEV__)
|
|
1
|
+
{"version":3,"file":"renamedProp-BwcN-SJg.js","names":[],"sources":["../../src/components/shared/renamedProp.ts"],"sourcesContent":["import { watchEffect } from 'vue'\n\n/**\n * Предупреждение о пропе, который переименовали.\n *\n * Незнакомый атрибут Vue не отвергает: он уходит в `$attrs` и садится на\n * корневой узел. Значит переименование пропа проходит у потребителя **молча** —\n * не падают ни типы (`vue-tsc` не проверяет лишние атрибуты: принимать их\n * вправе любой компонент), ни рантайм. Компонент просто рисуется дефолтом, и\n * найти это можно только сверкой шаблона со списком пропов.\n *\n * Так и случилось со сменой `variant` на `tone` у `GrBadge` и `GrChip`: у\n * стороннего пакета тихо разъехались десять мест в шести файлах. Причём\n * `variant` в кольце остаётся законным именем у `GrButton`, `GrCard`, `GrAlert`\n * и десятка других — то есть по аналогии не угадать.\n *\n * Предупреждение живёт только в разработке и ничего не чинит за потребителя:\n * подставлять снятое имя значило бы завести алиас, который после 1.0 останется\n * навсегда.\n */\nexport function warnRenamedProp(\n component: string,\n attrs: Record<string, unknown>,\n renames: Readonly<Record<string, string>>,\n): void {\n if (!__GR_DEV__)\n return\n\n const reported = new Set<string>()\n\n watchEffect(() => {\n for (const [from, to] of Object.entries(renames)) {\n if (attrs[from] === undefined || reported.has(from))\n continue\n\n reported.add(from)\n console.warn(\n `[granularity] ${component}: проп \\`${from}\\` переименован в \\`${to}\\`. `\n + `Сейчас \\`${from}\\` уходит в атрибуты корневого узла и на вид компонента не влияет.`,\n )\n }\n })\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,gBACd,WACA,OACA,SACM;CACN,IAAI,EAAA,OAAA,YAAA,eAAA,QAAA,IAAA,aAAA,eACF;CAEF,MAAM,2BAAW,IAAI,IAAY;CAEjC,kBAAkB;EAChB,KAAK,MAAM,CAAC,MAAM,OAAO,OAAO,QAAQ,OAAO,GAAG;GAChD,IAAI,MAAM,UAAU,KAAA,KAAa,SAAS,IAAI,IAAI,GAChD;GAEF,SAAS,IAAI,IAAI;GACjB,QAAQ,KACN,iBAAiB,UAAU,WAAW,KAAK,sBAAsB,GAAG,eACtD,KAAK,mEACrB;EACF;CACF,CAAC;AACH"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as splitClassTokens } from "./classTokens-8v2XC74x.js";
|
|
2
|
-
import { c as tones, l as variantClass, n as blockClass, o as sizes, r as disabledClassByVariant, s as squareSizes, t as base } from "./grButtonStyles-
|
|
2
|
+
import { c as tones, l as variantClass, n as blockClass, o as sizes, r as disabledClassByVariant, s as squareSizes, t as base } from "./grButtonStyles-B3wRWW_i.js";
|
|
3
3
|
//#region src/components/GrButton/safelist.ts
|
|
4
4
|
var variantTokens = Object.keys(tones).flatMap((tone) => [
|
|
5
5
|
"primary",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safelist-
|
|
1
|
+
{"version":3,"file":"safelist-DxkMj5IW.js","names":[],"sources":["../../src/components/GrButton/safelist.ts"],"sourcesContent":["import { splitClassTokens } from '../shared/classTokens'\nimport {\n base,\n blockClass,\n disabledClassByVariant,\n sizes,\n squareSizes,\n tones,\n variantClass,\n type GrButtonTone,\n type GrButtonVariant,\n} from './grButtonStyles'\n\nconst variantTokens = (Object.keys(tones) as GrButtonTone[]).flatMap(tone =>\n (['primary', 'secondary', 'outline', 'ghost', 'ghost-border'] as GrButtonVariant[])\n .flatMap(variant => splitClassTokens(variantClass(variant, tone))),\n)\n\nexport const grButtonClassTokens = {\n base: splitClassTokens(base),\n sizes: Object.values(sizes).flatMap(splitClassTokens),\n squareSizes: Object.values(squareSizes).flatMap(splitClassTokens),\n variants: variantTokens,\n states: [...splitClassTokens(blockClass), ...Object.values(disabledClassByVariant).flatMap(splitClassTokens)],\n} as const\n\nexport const grButtonSafelist = [...new Set([\n ...grButtonClassTokens.base,\n ...grButtonClassTokens.sizes,\n ...grButtonClassTokens.squareSizes,\n ...grButtonClassTokens.variants,\n ...grButtonClassTokens.states,\n])]\n"],"mappings":";;;AAaA,IAAM,gBAAiB,OAAO,KAAK,KAAK,CAAC,CAAoB,SAAQ,SAClE;CAAC;CAAW;CAAa;CAAW;CAAS;AAAc,CAAC,CAC1D,SAAQ,YAAW,iBAAiB,aAAa,SAAS,IAAI,CAAC,CAAC,CACrE;AAEA,IAAa,sBAAsB;CACjC,MAAM,iBAAiB,IAAI;CAC3B,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,QAAQ,gBAAgB;CACpD,aAAa,OAAO,OAAO,WAAW,CAAC,CAAC,QAAQ,gBAAgB;CAChE,UAAU;CACV,QAAQ,CAAC,GAAG,iBAAiB,UAAU,GAAG,GAAG,OAAO,OAAO,sBAAsB,CAAC,CAAC,QAAQ,gBAAgB,CAAC;AAC9G;AAEA,IAAa,mBAAmB,CAAC,mBAAG,IAAI,IAAI;CAC1C,GAAG,oBAAoB;CACvB,GAAG,oBAAoB;CACvB,GAAG,oBAAoB;CACvB,GAAG,oBAAoB;CACvB,GAAG,oBAAoB;AACzB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slotNodes-DxNXpC7v.js","names":[],"sources":["../../src/components/shared/slotNodes.ts"],"sourcesContent":["import { Comment, Fragment, Text, type VNode } from 'vue'\n\n/**\n * Разбор содержимого слота.\n *\n * Общий модуль, а не приватные функции внутри SFC: то же самое нужно и\n * `GrFileUpload` («слот с кастомным UI или просто текст»), и `GrList` («в\n * списке пусто»). Импорт из чужой компонентной директории на сборке дал бы\n * ребро между компонентами, которого в разметке нет.\n */\n\n/** `v-for` и `<template>` приходят фрагментами — разворачиваем до реальных узлов. */\nexport function flattenSlotNodes(nodes: VNode[]): VNode[] {\n const out: VNode[] = []\n\n for (const node of nodes) {\n if (node.type === Fragment && Array.isArray(node.children)) {\n out.push(...flattenSlotNodes(node.children as VNode[]))\n continue\n }\n\n out.push(node)\n }\n\n return out\n}\n\nexport function isWhitespaceTextNode(node: VNode): boolean {\n if (node.type !== Text)
|
|
1
|
+
{"version":3,"file":"slotNodes-DxNXpC7v.js","names":[],"sources":["../../src/components/shared/slotNodes.ts"],"sourcesContent":["import { Comment, Fragment, Text, type VNode } from 'vue'\n\n/**\n * Разбор содержимого слота.\n *\n * Общий модуль, а не приватные функции внутри SFC: то же самое нужно и\n * `GrFileUpload` («слот с кастомным UI или просто текст»), и `GrList` («в\n * списке пусто»). Импорт из чужой компонентной директории на сборке дал бы\n * ребро между компонентами, которого в разметке нет.\n */\n\n/** `v-for` и `<template>` приходят фрагментами — разворачиваем до реальных узлов. */\nexport function flattenSlotNodes(nodes: VNode[]): VNode[] {\n const out: VNode[] = []\n\n for (const node of nodes) {\n if (node.type === Fragment && Array.isArray(node.children)) {\n out.push(...flattenSlotNodes(node.children as VNode[]))\n continue\n }\n\n out.push(node)\n }\n\n return out\n}\n\nexport function isWhitespaceTextNode(node: VNode): boolean {\n if (node.type !== Text)\n return false\n return typeof node.children === 'string' && node.children.trim().length === 0\n}\n\n/**\n * Узлы, которые пользователь действительно видит: без комментариев (их\n * оставляет после себя каждый `v-if`) и без переносов строк из шаблона.\n */\nexport function meaningfulSlotNodes(nodes: VNode[]): VNode[] {\n return flattenSlotNodes(nodes).filter(node => node.type !== Comment && !isWhitespaceTextNode(node))\n}\n\n/** Есть ли в слоте хоть что-то, кроме комментариев и пробелов. */\nexport function hasMeaningfulSlotContent(nodes: VNode[]): boolean {\n return meaningfulSlotNodes(nodes).length > 0\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,iBAAiB,OAAyB;CACxD,MAAM,MAAe,CAAC;CAEtB,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,KAAK,SAAS,YAAY,MAAM,QAAQ,KAAK,QAAQ,GAAG;GAC1D,IAAI,KAAK,GAAG,iBAAiB,KAAK,QAAmB,CAAC;GACtD;EACF;EAEA,IAAI,KAAK,IAAI;CACf;CAEA,OAAO;AACT;AAEA,SAAgB,qBAAqB,MAAsB;CACzD,IAAI,KAAK,SAAS,MAChB,OAAO;CACT,OAAO,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,KAAK,CAAC,CAAC,WAAW;AAC9E;;;;;AAMA,SAAgB,oBAAoB,OAAyB;CAC3D,OAAO,iBAAiB,KAAK,CAAC,CAAC,QAAO,SAAQ,KAAK,SAAS,WAAW,CAAC,qBAAqB,IAAI,CAAC;AACpG;;AAGA,SAAgB,yBAAyB,OAAyB;CAChE,OAAO,oBAAoB,KAAK,CAAC,CAAC,SAAS;AAC7C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useControlAddons-asUr10Pf.js","names":[],"sources":["../../src/composables/internal/useAddonMeasurement.ts","../../src/composables/internal/useControlAddons.ts"],"sourcesContent":["import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref } from 'vue'\nimport type { Ref } from 'vue'\n\n/**\n * Измерение фактической ширины prefix/suffix add-on'ов через `ResizeObserver`.\n * Общая логика GrInput/GrNumberInput.\n *\n * Потребитель вешает возвращённые `prefixEl`/`suffixEl` на DOM-элементы аддонов\n * и передаёт реактивные `hasPrefix`/`hasSuffix` (обычно — наличие слотов).\n * Возвращает измеренные ширины (`\"<n>px\"` или `undefined`, если элемент\n * отсутствует/скрыт/нулевой — например, в jsdom).\n */\nexport function useAddonMeasurement(hasPrefix: Ref<boolean>, hasSuffix: Ref<boolean>) {\n const prefixEl = ref<HTMLElement | null>(null)\n const suffixEl = ref<HTMLElement | null>(null)\n\n const measuredPrefixWidth = ref<string | undefined>(undefined)\n const measuredSuffixWidth = ref<string | undefined>(undefined)\n\n let ro: ResizeObserver | null = null\n let scheduled = false\n\n function readWidthPx(el: HTMLElement | null): string | undefined {\n if (!el) return undefined\n\n const width = Math.ceil(el.getBoundingClientRect().width || 0)\n // jsdom часто отдаёт 0; не перезатираем fallback minWidth в этом случае.\n if (width <= 0) return undefined\n\n return `${width}px`\n }\n\n function measure(): void {\n measuredPrefixWidth.value = hasPrefix.value ? readWidthPx(prefixEl.value) : undefined\n measuredSuffixWidth.value = hasSuffix.value ? readWidthPx(suffixEl.value) : undefined\n }\n\n function refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') return\n if (!ro) ro = new ResizeObserver(() => measure())\n\n ro.disconnect()\n if (prefixEl.value) ro.observe(prefixEl.value)\n if (suffixEl.value) ro.observe(suffixEl.value)\n }\n\n function scheduleMeasure(): void {\n if (scheduled) return\n scheduled = true\n\n void nextTick(() => {\n scheduled = false\n measure()\n refreshObserver()\n })\n }\n\n onMounted(() => scheduleMeasure())\n onUpdated(() => scheduleMeasure())\n onBeforeUnmount(() => ro?.disconnect())\n\n return {\n prefixEl,\n suffixEl,\n measuredPrefixWidth,\n measuredSuffixWidth,\n }\n}\n\n/** Складывает две CSS-длины: числовые `px` — арифметически, иначе через `calc()`. */\nexport function addLen(a: string, b: string): string {\n if (a === '0px') return b\n if (b === '0px') return a\n\n const apx = a.endsWith('px') ? Number(a.slice(0, -2)) : null\n const bpx = b.endsWith('px') ? Number(b.slice(0, -2)) : null\n\n if (apx !== null && Number.isFinite(apx) && bpx !== null && Number.isFinite(bpx)) {\n return `${apx + bpx}px`\n }\n\n return `calc(${a} + ${b})`\n}\n","import type { ComputedRef, Ref } from 'vue'\nimport { computed, useSlots } from 'vue'\n\nimport { addLen, useAddonMeasurement } from './useAddonMeasurement'\n\n/**\n * Аддоны контрола: слоты `prefix`/`suffix` и геометрия вокруг них.\n *\n * Слот здесь — вершина айсберга. Контракт аддона состоит из шести пропов ширины,\n * замера фактического размера через `ResizeObserver` и пересчёта внутренних\n * отступов поля, чтобы текст не заезжал под аддон. Эта математика была написана\n * дважды — в `GrInput` и `GrNumberInput`, — и раскатать её ещё на четыре\n * контрола копипастой значило бы завести шесть копий одного расчёта.\n *\n * Правило, которое держит композабл: **аддон резервирует место, а не заменяет\n * отступ**. Поле с аддоном и без него выглядит одинаково отбитым от края —\n * ширина аддона прибавляется к базовому паддингу ступени, а не вытесняет его.\n *\n * Карта ширины по умолчанию остаётся у компонента (`defaultMinWidth`): у\n * `GrInput` она в `rem`, у `GrNumberInput` — в тех же значениях, но в пикселях,\n * потому что оттуда же считаются ширины степперов. Сводить их к одной строке\n * значило бы менять то, что уезжает в `style`, ради косметики.\n */\nexport interface GrControlAddonProps {\n /** Минимальная ширина префикса. По умолчанию — ширина аддона для ступени размера. */\n prefixMinWidth?: string\n /** Максимальная ширина префикса: содержимое шире — обрезается. */\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Жёсткая ширина префикса (из `prefixMaxWidth` → `prefixMinWidth` → дефолт).\n * Нужна, когда аддоны соседних полей обязаны совпасть по ширине: без этого\n * каждое поле меряет свой аддон и колонка «плывёт».\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface UseControlAddonsOptions {\n /** Ширина аддона по умолчанию для текущей ступени размера. */\n defaultMinWidth: () => string\n /** Базовый горизонтальный отступ поля из size-мапы. */\n paddingX: () => string\n /** Место, занятое собственными элементами контрола слева (степперы `GrNumberInput`). */\n leadingReserve?: () => string\n /** То же справа: кнопки очистки и показа пароля, спиннер, степперы. */\n trailingReserve?: () => string\n /** Прижать суффикс к правому краю — там, где аддон позиционируется абсолютно. */\n anchorSuffixRight?: boolean\n}\n\nexport interface ControlAddons {\n hasPrefix: ComputedRef<boolean>\n hasSuffix: ComputedRef<boolean>\n /** Вешаются на DOM-элементы аддонов: по ним идёт замер. */\n prefixEl: Ref<HTMLElement | null>\n suffixEl: Ref<HTMLElement | null>\n /** Занятое аддоном место (`0px`, если слота нет) — контролу нужно для своих кнопок. */\n prefixLen: ComputedRef<string>\n suffixLen: ComputedRef<string>\n prefixStyle: ComputedRef<Record<string, string | undefined>>\n suffixStyle: ComputedRef<Record<string, string | undefined>>\n /** `paddingLeft`/`paddingRight` поля с учётом аддонов и собственных кнопок. */\n fieldPadding: ComputedRef<Record<string, string | undefined>>\n}\n\nexport function useControlAddons(\n props: () => GrControlAddonProps,\n options: UseControlAddonsOptions,\n): ControlAddons {\n const slots = useSlots()\n\n const hasPrefix = computed(() => Boolean(slots.prefix))\n const hasSuffix = computed(() => Boolean(slots.suffix))\n\n const prefixMinWidth = computed(() => props().prefixMinWidth ?? options.defaultMinWidth())\n const suffixMinWidth = computed(() => props().suffixMinWidth ?? options.defaultMinWidth())\n\n // Жёсткая ширина для fixed-режима: max → min → дефолт.\n const prefixFixedWidth = computed(() => props().prefixMaxWidth ?? props().prefixMinWidth ?? options.defaultMinWidth())\n const suffixFixedWidth = computed(() => props().suffixMaxWidth ?? props().suffixMinWidth ?? options.defaultMinWidth())\n\n const { prefixEl, suffixEl, measuredPrefixWidth, measuredSuffixWidth } = useAddonMeasurement(hasPrefix, hasSuffix)\n\n const prefixLen = computed(() => {\n if (!hasPrefix.value) return '0px'\n return props().prefixFixed ? prefixFixedWidth.value : measuredPrefixWidth.value ?? prefixMinWidth.value\n })\n\n const suffixLen = computed(() => {\n if (!hasSuffix.value) return '0px'\n return props().suffixFixed ? suffixFixedWidth.value : measuredSuffixWidth.value ?? suffixMinWidth.value\n })\n\n const prefixStyle = computed<Record<string, string | undefined>>(() => {\n if (props().prefixFixed) {\n return {\n width: prefixFixedWidth.value,\n minWidth: prefixFixedWidth.value,\n maxWidth: prefixFixedWidth.value,\n }\n }\n\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props().prefixMaxWidth,\n }\n })\n\n const suffixStyle = computed<Record<string, string | undefined>>(() => {\n const anchor = options.anchorSuffixRight ? { right: '0px' } : {}\n\n if (props().suffixFixed) {\n return {\n ...anchor,\n width: suffixFixedWidth.value,\n minWidth: suffixFixedWidth.value,\n maxWidth: suffixFixedWidth.value,\n }\n }\n\n return {\n ...anchor,\n minWidth: suffixMinWidth.value,\n maxWidth: props().suffixMaxWidth,\n }\n })\n\n const fieldPadding = computed<Record<string, string | undefined>>(() => {\n const leading = options.leadingReserve?.() ?? '0px'\n const trailing = options.trailingReserve?.() ?? '0px'\n\n const left = addLen(prefixLen.value, leading)\n const right = addLen(suffixLen.value, trailing)\n\n return {\n // Отступ добавляется поверх зарезервированного места: поле с аддоном и без\n // него отбито от края одинаково.\n paddingLeft: hasPrefix.value || leading !== '0px' ? addLen(left, options.paddingX()) : undefined,\n paddingRight: hasSuffix.value || trailing !== '0px' ? addLen(right, options.paddingX()) : undefined,\n }\n })\n\n return {\n hasPrefix,\n hasSuffix,\n prefixEl,\n suffixEl,\n prefixLen,\n suffixLen,\n prefixStyle,\n suffixStyle,\n fieldPadding,\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useControlAddons-asUr10Pf.js","names":[],"sources":["../../src/composables/internal/useAddonMeasurement.ts","../../src/composables/internal/useControlAddons.ts"],"sourcesContent":["import { nextTick, onBeforeUnmount, onMounted, onUpdated, ref } from 'vue'\nimport type { Ref } from 'vue'\n\n/**\n * Измерение фактической ширины prefix/suffix add-on'ов через `ResizeObserver`.\n * Общая логика GrInput/GrNumberInput.\n *\n * Потребитель вешает возвращённые `prefixEl`/`suffixEl` на DOM-элементы аддонов\n * и передаёт реактивные `hasPrefix`/`hasSuffix` (обычно — наличие слотов).\n * Возвращает измеренные ширины (`\"<n>px\"` или `undefined`, если элемент\n * отсутствует/скрыт/нулевой — например, в jsdom).\n */\nexport function useAddonMeasurement(hasPrefix: Ref<boolean>, hasSuffix: Ref<boolean>) {\n const prefixEl = ref<HTMLElement | null>(null)\n const suffixEl = ref<HTMLElement | null>(null)\n\n const measuredPrefixWidth = ref<string | undefined>(undefined)\n const measuredSuffixWidth = ref<string | undefined>(undefined)\n\n let ro: ResizeObserver | null = null\n let scheduled = false\n\n function readWidthPx(el: HTMLElement | null): string | undefined {\n if (!el)\n return undefined\n\n const width = Math.ceil(el.getBoundingClientRect().width || 0)\n // jsdom часто отдаёт 0; не перезатираем fallback minWidth в этом случае.\n if (width <= 0)\n return undefined\n\n return `${width}px`\n }\n\n function measure(): void {\n measuredPrefixWidth.value = hasPrefix.value ? readWidthPx(prefixEl.value) : undefined\n measuredSuffixWidth.value = hasSuffix.value ? readWidthPx(suffixEl.value) : undefined\n }\n\n function refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined')\n return\n if (!ro)\n ro = new ResizeObserver(() => measure())\n\n ro.disconnect()\n if (prefixEl.value)\n ro.observe(prefixEl.value)\n if (suffixEl.value)\n ro.observe(suffixEl.value)\n }\n\n function scheduleMeasure(): void {\n if (scheduled)\n return\n scheduled = true\n\n void nextTick(() => {\n scheduled = false\n measure()\n refreshObserver()\n })\n }\n\n onMounted(() => scheduleMeasure())\n onUpdated(() => scheduleMeasure())\n onBeforeUnmount(() => ro?.disconnect())\n\n return {\n prefixEl,\n suffixEl,\n measuredPrefixWidth,\n measuredSuffixWidth,\n }\n}\n\n/** Складывает две CSS-длины: числовые `px` — арифметически, иначе через `calc()`. */\nexport function addLen(a: string, b: string): string {\n if (a === '0px')\n return b\n if (b === '0px')\n return a\n\n const apx = a.endsWith('px') ? Number(a.slice(0, -2)) : null\n const bpx = b.endsWith('px') ? Number(b.slice(0, -2)) : null\n\n if (apx !== null && Number.isFinite(apx) && bpx !== null && Number.isFinite(bpx)) {\n return `${apx + bpx}px`\n }\n\n return `calc(${a} + ${b})`\n}\n","import type { ComputedRef, Ref } from 'vue'\nimport { computed, useSlots } from 'vue'\n\nimport { addLen, useAddonMeasurement } from './useAddonMeasurement'\n\n/**\n * Аддоны контрола: слоты `prefix`/`suffix` и геометрия вокруг них.\n *\n * Слот здесь — вершина айсберга. Контракт аддона состоит из шести пропов ширины,\n * замера фактического размера через `ResizeObserver` и пересчёта внутренних\n * отступов поля, чтобы текст не заезжал под аддон. Эта математика была написана\n * дважды — в `GrInput` и `GrNumberInput`, — и раскатать её ещё на четыре\n * контрола копипастой значило бы завести шесть копий одного расчёта.\n *\n * Правило, которое держит композабл: **аддон резервирует место, а не заменяет\n * отступ**. Поле с аддоном и без него выглядит одинаково отбитым от края —\n * ширина аддона прибавляется к базовому паддингу ступени, а не вытесняет его.\n *\n * Карта ширины по умолчанию остаётся у компонента (`defaultMinWidth`): у\n * `GrInput` она в `rem`, у `GrNumberInput` — в тех же значениях, но в пикселях,\n * потому что оттуда же считаются ширины степперов. Сводить их к одной строке\n * значило бы менять то, что уезжает в `style`, ради косметики.\n */\nexport interface GrControlAddonProps {\n /** Минимальная ширина префикса. По умолчанию — ширина аддона для ступени размера. */\n prefixMinWidth?: string\n /** Максимальная ширина префикса: содержимое шире — обрезается. */\n prefixMaxWidth?: string\n suffixMinWidth?: string\n suffixMaxWidth?: string\n /**\n * Жёсткая ширина префикса (из `prefixMaxWidth` → `prefixMinWidth` → дефолт).\n * Нужна, когда аддоны соседних полей обязаны совпасть по ширине: без этого\n * каждое поле меряет свой аддон и колонка «плывёт».\n */\n prefixFixed?: boolean\n suffixFixed?: boolean\n}\n\nexport interface UseControlAddonsOptions {\n /** Ширина аддона по умолчанию для текущей ступени размера. */\n defaultMinWidth: () => string\n /** Базовый горизонтальный отступ поля из size-мапы. */\n paddingX: () => string\n /** Место, занятое собственными элементами контрола слева (степперы `GrNumberInput`). */\n leadingReserve?: () => string\n /** То же справа: кнопки очистки и показа пароля, спиннер, степперы. */\n trailingReserve?: () => string\n /** Прижать суффикс к правому краю — там, где аддон позиционируется абсолютно. */\n anchorSuffixRight?: boolean\n}\n\nexport interface ControlAddons {\n hasPrefix: ComputedRef<boolean>\n hasSuffix: ComputedRef<boolean>\n /** Вешаются на DOM-элементы аддонов: по ним идёт замер. */\n prefixEl: Ref<HTMLElement | null>\n suffixEl: Ref<HTMLElement | null>\n /** Занятое аддоном место (`0px`, если слота нет) — контролу нужно для своих кнопок. */\n prefixLen: ComputedRef<string>\n suffixLen: ComputedRef<string>\n prefixStyle: ComputedRef<Record<string, string | undefined>>\n suffixStyle: ComputedRef<Record<string, string | undefined>>\n /** `paddingLeft`/`paddingRight` поля с учётом аддонов и собственных кнопок. */\n fieldPadding: ComputedRef<Record<string, string | undefined>>\n}\n\nexport function useControlAddons(\n props: () => GrControlAddonProps,\n options: UseControlAddonsOptions,\n): ControlAddons {\n const slots = useSlots()\n\n const hasPrefix = computed(() => Boolean(slots.prefix))\n const hasSuffix = computed(() => Boolean(slots.suffix))\n\n const prefixMinWidth = computed(() => props().prefixMinWidth ?? options.defaultMinWidth())\n const suffixMinWidth = computed(() => props().suffixMinWidth ?? options.defaultMinWidth())\n\n // Жёсткая ширина для fixed-режима: max → min → дефолт.\n const prefixFixedWidth = computed(() => props().prefixMaxWidth ?? props().prefixMinWidth ?? options.defaultMinWidth())\n const suffixFixedWidth = computed(() => props().suffixMaxWidth ?? props().suffixMinWidth ?? options.defaultMinWidth())\n\n const { prefixEl, suffixEl, measuredPrefixWidth, measuredSuffixWidth } = useAddonMeasurement(hasPrefix, hasSuffix)\n\n const prefixLen = computed(() => {\n if (!hasPrefix.value)\n return '0px'\n return props().prefixFixed ? prefixFixedWidth.value : measuredPrefixWidth.value ?? prefixMinWidth.value\n })\n\n const suffixLen = computed(() => {\n if (!hasSuffix.value)\n return '0px'\n return props().suffixFixed ? suffixFixedWidth.value : measuredSuffixWidth.value ?? suffixMinWidth.value\n })\n\n const prefixStyle = computed<Record<string, string | undefined>>(() => {\n if (props().prefixFixed) {\n return {\n width: prefixFixedWidth.value,\n minWidth: prefixFixedWidth.value,\n maxWidth: prefixFixedWidth.value,\n }\n }\n\n return {\n minWidth: prefixMinWidth.value,\n maxWidth: props().prefixMaxWidth,\n }\n })\n\n const suffixStyle = computed<Record<string, string | undefined>>(() => {\n const anchor = options.anchorSuffixRight ? { right: '0px' } : {}\n\n if (props().suffixFixed) {\n return {\n ...anchor,\n width: suffixFixedWidth.value,\n minWidth: suffixFixedWidth.value,\n maxWidth: suffixFixedWidth.value,\n }\n }\n\n return {\n ...anchor,\n minWidth: suffixMinWidth.value,\n maxWidth: props().suffixMaxWidth,\n }\n })\n\n const fieldPadding = computed<Record<string, string | undefined>>(() => {\n const leading = options.leadingReserve?.() ?? '0px'\n const trailing = options.trailingReserve?.() ?? '0px'\n\n const left = addLen(prefixLen.value, leading)\n const right = addLen(suffixLen.value, trailing)\n\n return {\n // Отступ добавляется поверх зарезервированного места: поле с аддоном и без\n // него отбито от края одинаково.\n paddingLeft: hasPrefix.value || leading !== '0px' ? addLen(left, options.paddingX()) : undefined,\n paddingRight: hasSuffix.value || trailing !== '0px' ? addLen(right, options.paddingX()) : undefined,\n }\n })\n\n return {\n hasPrefix,\n hasSuffix,\n prefixEl,\n suffixEl,\n prefixLen,\n suffixLen,\n prefixStyle,\n suffixStyle,\n fieldPadding,\n }\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,oBAAoB,WAAyB,WAAyB;CACpF,MAAM,WAAW,IAAwB,IAAI;CAC7C,MAAM,WAAW,IAAwB,IAAI;CAE7C,MAAM,sBAAsB,IAAwB,KAAA,CAAS;CAC7D,MAAM,sBAAsB,IAAwB,KAAA,CAAS;CAE7D,IAAI,KAA4B;CAChC,IAAI,YAAY;CAEhB,SAAS,YAAY,IAA4C;EAC/D,IAAI,CAAC,IACH,OAAO,KAAA;EAET,MAAM,QAAQ,KAAK,KAAK,GAAG,sBAAsB,CAAC,CAAC,SAAS,CAAC;EAE7D,IAAI,SAAS,GACX,OAAO,KAAA;EAET,OAAO,GAAG,MAAM;CAClB;CAEA,SAAS,UAAgB;EACvB,oBAAoB,QAAQ,UAAU,QAAQ,YAAY,SAAS,KAAK,IAAI,KAAA;EAC5E,oBAAoB,QAAQ,UAAU,QAAQ,YAAY,SAAS,KAAK,IAAI,KAAA;CAC9E;CAEA,SAAS,kBAAwB;EAC/B,IAAI,OAAO,mBAAmB,aAC5B;EACF,IAAI,CAAC,IACH,KAAK,IAAI,qBAAqB,QAAQ,CAAC;EAEzC,GAAG,WAAW;EACd,IAAI,SAAS,OACX,GAAG,QAAQ,SAAS,KAAK;EAC3B,IAAI,SAAS,OACX,GAAG,QAAQ,SAAS,KAAK;CAC7B;CAEA,SAAS,kBAAwB;EAC/B,IAAI,WACF;EACF,YAAY;EAEZ,eAAoB;GAClB,YAAY;GACZ,QAAQ;GACR,gBAAgB;EAClB,CAAC;CACH;CAEA,gBAAgB,gBAAgB,CAAC;CACjC,gBAAgB,gBAAgB,CAAC;CACjC,sBAAsB,IAAI,WAAW,CAAC;CAEtC,OAAO;EACL;EACA;EACA;EACA;CACF;AACF;;AAGA,SAAgB,OAAO,GAAW,GAAmB;CACnD,IAAI,MAAM,OACR,OAAO;CACT,IAAI,MAAM,OACR,OAAO;CAET,MAAM,MAAM,EAAE,SAAS,IAAI,IAAI,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI;CACxD,MAAM,MAAM,EAAE,SAAS,IAAI,IAAI,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI;CAExD,IAAI,QAAQ,QAAQ,OAAO,SAAS,GAAG,KAAK,QAAQ,QAAQ,OAAO,SAAS,GAAG,GAC7E,OAAO,GAAG,MAAM,IAAI;CAGtB,OAAO,QAAQ,EAAE,KAAK,EAAE;AAC1B;;;ACxBA,SAAgB,iBACd,OACA,SACe;CACf,MAAM,QAAQ,SAAS;CAEvB,MAAM,YAAY,eAAe,QAAQ,MAAM,MAAM,CAAC;CACtD,MAAM,YAAY,eAAe,QAAQ,MAAM,MAAM,CAAC;CAEtD,MAAM,iBAAiB,eAAe,MAAM,CAAC,CAAC,kBAAkB,QAAQ,gBAAgB,CAAC;CACzF,MAAM,iBAAiB,eAAe,MAAM,CAAC,CAAC,kBAAkB,QAAQ,gBAAgB,CAAC;CAGzF,MAAM,mBAAmB,eAAe,MAAM,CAAC,CAAC,kBAAkB,MAAM,CAAC,CAAC,kBAAkB,QAAQ,gBAAgB,CAAC;CACrH,MAAM,mBAAmB,eAAe,MAAM,CAAC,CAAC,kBAAkB,MAAM,CAAC,CAAC,kBAAkB,QAAQ,gBAAgB,CAAC;CAErH,MAAM,EAAE,UAAU,UAAU,qBAAqB,wBAAwB,oBAAoB,WAAW,SAAS;CAEjH,MAAM,YAAY,eAAe;EAC/B,IAAI,CAAC,UAAU,OACb,OAAO;EACT,OAAO,MAAM,CAAC,CAAC,cAAc,iBAAiB,QAAQ,oBAAoB,SAAS,eAAe;CACpG,CAAC;CAED,MAAM,YAAY,eAAe;EAC/B,IAAI,CAAC,UAAU,OACb,OAAO;EACT,OAAO,MAAM,CAAC,CAAC,cAAc,iBAAiB,QAAQ,oBAAoB,SAAS,eAAe;CACpG,CAAC;CAmDD,OAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA,aAxDkB,eAAmD;GACrE,IAAI,MAAM,CAAC,CAAC,aACV,OAAO;IACL,OAAO,iBAAiB;IACxB,UAAU,iBAAiB;IAC3B,UAAU,iBAAiB;GAC7B;GAGF,OAAO;IACL,UAAU,eAAe;IACzB,UAAU,MAAM,CAAC,CAAC;GACpB;EACF,CA2CE;EACA,aA1CkB,eAAmD;GACrE,MAAM,SAAS,QAAQ,oBAAoB,EAAE,OAAO,MAAM,IAAI,CAAC;GAE/D,IAAI,MAAM,CAAC,CAAC,aACV,OAAO;IACL,GAAG;IACH,OAAO,iBAAiB;IACxB,UAAU,iBAAiB;IAC3B,UAAU,iBAAiB;GAC7B;GAGF,OAAO;IACL,GAAG;IACH,UAAU,eAAe;IACzB,UAAU,MAAM,CAAC,CAAC;GACpB;EACF,CAyBE;EACA,cAxBmB,eAAmD;GACtE,MAAM,UAAU,QAAQ,iBAAiB,KAAK;GAC9C,MAAM,WAAW,QAAQ,kBAAkB,KAAK;GAEhD,MAAM,OAAO,OAAO,UAAU,OAAO,OAAO;GAC5C,MAAM,QAAQ,OAAO,UAAU,OAAO,QAAQ;GAE9C,OAAO;IAGL,aAAa,UAAU,SAAS,YAAY,QAAQ,OAAO,MAAM,QAAQ,SAAS,CAAC,IAAI,KAAA;IACvF,cAAc,UAAU,SAAS,aAAa,QAAQ,OAAO,OAAO,QAAQ,SAAS,CAAC,IAAI,KAAA;GAC5F;EACF,CAWE;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useControlledOpen-DV3SqLTP.js","names":[],"sources":["../../src/composables/internal/useControlledOpen.ts"],"sourcesContent":["import type { ComputedRef } from 'vue'\nimport { computed, ref } from 'vue'\n\n/**\n * Контролируемое состояние открытия оверлея — единый контракт `v-model:open`\n * панельных компонентов (Popover, Select, TreeSelect, Autocomplete, Dropdown).\n *\n * Uncontrolled-состояние; в controlled-режиме перекрывается пропом `open`.\n * Без пропа `v-model` был бы обязателен, а поповер без модели — самый частый\n * случай использования. Гарды компонента (disabled/readonly) остаются на его\n * стороне — примитив ведёт только состояние и эмит.\n */\nexport function useControlledOpen(\n controlled: () => boolean | undefined,\n onChange: (value: boolean) => void,\n): { open: ComputedRef<boolean>, setOpen: (next: boolean) => void } {\n const internalOpen = ref(false)\n const isControlled = computed(() => controlled() !== undefined)\n const open = computed(() => controlled() ?? internalOpen.value)\n\n function setOpen(next: boolean): void {\n // Сравнение — ДО мутации: в uncontrolled-режиме запись в `internalOpen`\n // немедленно меняет `open`, и проверка после неё всегда была бы ложной,\n // то есть `onChange` не вызывался бы никогда.\n if (next === open.value)
|
|
1
|
+
{"version":3,"file":"useControlledOpen-DV3SqLTP.js","names":[],"sources":["../../src/composables/internal/useControlledOpen.ts"],"sourcesContent":["import type { ComputedRef } from 'vue'\nimport { computed, ref } from 'vue'\n\n/**\n * Контролируемое состояние открытия оверлея — единый контракт `v-model:open`\n * панельных компонентов (Popover, Select, TreeSelect, Autocomplete, Dropdown).\n *\n * Uncontrolled-состояние; в controlled-режиме перекрывается пропом `open`.\n * Без пропа `v-model` был бы обязателен, а поповер без модели — самый частый\n * случай использования. Гарды компонента (disabled/readonly) остаются на его\n * стороне — примитив ведёт только состояние и эмит.\n */\nexport function useControlledOpen(\n controlled: () => boolean | undefined,\n onChange: (value: boolean) => void,\n): { open: ComputedRef<boolean>, setOpen: (next: boolean) => void } {\n const internalOpen = ref(false)\n const isControlled = computed(() => controlled() !== undefined)\n const open = computed(() => controlled() ?? internalOpen.value)\n\n function setOpen(next: boolean): void {\n // Сравнение — ДО мутации: в uncontrolled-режиме запись в `internalOpen`\n // немедленно меняет `open`, и проверка после неё всегда была бы ложной,\n // то есть `onChange` не вызывался бы никогда.\n if (next === open.value)\n return\n\n if (!isControlled.value)\n internalOpen.value = next\n\n onChange(next)\n }\n\n return { open, setOpen }\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,kBACd,YACA,UACkE;CAClE,MAAM,eAAe,IAAI,KAAK;CAC9B,MAAM,eAAe,eAAe,WAAW,MAAM,KAAA,CAAS;CAC9D,MAAM,OAAO,eAAe,WAAW,KAAK,aAAa,KAAK;CAE9D,SAAS,QAAQ,MAAqB;EAIpC,IAAI,SAAS,KAAK,OAChB;EAEF,IAAI,CAAC,aAAa,OAChB,aAAa,QAAQ;EAEvB,SAAS,IAAI;CACf;CAEA,OAAO;EAAE;EAAM;CAAQ;AACzB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDragSort-0l47h4yb.js","names":[],"sources":["../../src/composables/internal/dragSortGeometry.ts","../../src/composables/useDragSort.ts"],"sourcesContent":["/**\n * Геометрия перестановки: чистая арифметика без DOM и без реактивности.\n *\n * Вынесена отдельно, потому что ошибка здесь проявляется не исключением, а\n * «элемент встал не туда» — поймать это можно только тестом на числах.\n * Композабл `useDragSort` сводит `DOMRect` к отрезкам вдоль оси и зовёт эти\n * функции; какой смысл у попадания, решает уже компонент.\n */\n\n/** Отрезок, занимаемый элементом вдоль оси переноса. */\nexport interface DragSortSpan {\n start: number\n end: number\n}\n\nexport interface DragSortHitResult {\n /** Индекс элемента в переданном наборе. */\n index: number\n /** Доля вдоль элемента: 0 — его начало, 1 — конец. */\n fraction: number\n}\n\n/**\n * Какой элемент под указателем и в каком месте его.\n *\n * Промах не отбрасывается: между строками есть зазоры, а курсор уезжает за\n * край списка — и в обоих случаях перенос обязан продолжать показывать\n * ближайшую цель, иначе индикатор мигает на каждом пикселе между строками.\n *\n * Вырожденный отрезок (нулевая высота строки) даёт `fraction: 0` — цель\n * остаётся детерминированной, а не зависящей от направления подхода.\n */\nexport function hitTest(spans: readonly DragSortSpan[], position: number): DragSortHitResult | null {\n if (spans.length === 0) return null\n\n let nearestIndex = 0\n let nearestDistance = Number.POSITIVE_INFINITY\n\n for (let index = 0; index < spans.length; index += 1) {\n const span = spans[index]\n\n if (position >= span.start && position <= span.end)\n return { index, fraction: fractionIn(span, position) }\n\n const distance = position < span.start ? span.start - position : position - span.end\n if (distance < nearestDistance) {\n nearestDistance = distance\n nearestIndex = index\n }\n }\n\n const nearest = spans[nearestIndex]\n\n return { index: nearestIndex, fraction: fractionIn(nearest, position) }\n}\n\nfunction fractionIn(span: DragSortSpan, position: number): number {\n const size = span.end - span.start\n if (size <= 0) return 0\n\n return clamp((position - span.start) / size, 0, 1)\n}\n\n/**\n * Куда встанет элемент, если отпустить сейчас.\n *\n * Верхняя половина строки — «до неё», нижняя — «после». Индекс возвращается\n * **в терминах итогового массива**, то есть уже с поправкой на то, что\n * перетаскиваемый элемент из него временно вынут: без поправки перенос вниз\n * на одну позицию не двигал бы ничего.\n */\nexport function insertionIndex(hit: DragSortHitResult, from: number, count: number): number {\n if (count <= 0) return 0\n\n const base = hit.fraction < 0.5 ? hit.index : hit.index + 1\n const adjusted = from < base ? base - 1 : base\n\n return clamp(adjusted, 0, count - 1)\n}\n\nexport interface AutoScrollOptions {\n /** Ширина чувствительной полосы у каждого края, px. По умолчанию 32. */\n zone?: number\n /** Скорость на самом краю, px за кадр. По умолчанию 12. */\n speed?: number\n}\n\n/**\n * На сколько прокрутить контейнер за кадр: отрицательное — к началу.\n *\n * Скорость растёт линейно по мере приближения к краю, а не включается\n * ступенькой: ступенька на длинном списке ощущается как рывок и промахивается\n * мимо цели.\n */\nexport function autoScrollDelta(viewport: DragSortSpan, position: number, options: AutoScrollOptions = {}): number {\n const zone = options.zone ?? 32\n const speed = options.speed ?? 12\n\n if (zone <= 0 || speed <= 0) return 0\n // Вьюпорт короче двух зон: полосы наложились бы и тянули список в обе стороны.\n if (viewport.end - viewport.start <= zone * 2) return 0\n\n if (position < viewport.start || position > viewport.end) return 0\n\n const fromStart = position - viewport.start\n if (fromStart < zone)\n return -Math.ceil(((zone - fromStart) / zone) * speed)\n\n const fromEnd = viewport.end - position\n if (fromEnd < zone)\n return Math.ceil(((zone - fromEnd) / zone) * speed)\n\n return 0\n}\n\n/**\n * Перестановка без мутации входа: массив потребителя остаётся нетронутым,\n * наружу уходит новый. `v-model` с мутацией исходного набора ломает и\n * сравнение «было — стало», и историю в сторе потребителя.\n */\nexport function moveItem<T>(items: readonly T[], from: number, to: number): T[] {\n const next = items.slice()\n if (from === to || from < 0 || from >= items.length) return next\n\n const [moved] = next.splice(from, 1)\n next.splice(clamp(to, 0, next.length), 0, moved)\n\n return next\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n","import type { Ref } from 'vue'\nimport { computed, onScopeDispose, shallowRef } from 'vue'\n\nimport type { DragSortSpan } from './internal/dragSortGeometry'\nimport { autoScrollDelta, hitTest } from './internal/dragSortGeometry'\nimport { useDragGesture } from './useDragGesture'\n\n/**\n * Модель перестановки: перенос элемента указателем и с клавиатуры.\n *\n * Композабл владеет **механикой** — сессией жеста поверх `useDragGesture`,\n * порогом начала, попаданием в элемент, автопрокруткой у краёв и состоянием\n * переноса. **Смысл** остаётся у компонента: что считать целью (индекс вставки\n * у списка, «до / внутрь / после» у дерева), какими клавишами это делается и\n * что сказать в живой регион.\n *\n * Граница проведена так не из вкуса: у списка цель переноса — число, у дерева —\n * пара «узел и зона», а `Space` в дереве уже занят чекбоксом. Общая у них\n * механика, а не семантика; обобщать семантику значило бы завести опцию на\n * каждое расхождение.\n *\n * Данные композабл не меняет: он сообщает «источник и цель», перестановку\n * делает потребитель (`moveItem` из `internal/dragSortGeometry` — если она\n * обычная).\n *\n * ```ts\n * const sort = useDragSort<string, number>({\n * items: () => keys.value,\n * elementFor: key => rowEls.get(key),\n * scroller: () => scrollerEl.value,\n * resolveTarget: (hit, source) => insertionIndex(hit, indexOf(source), keys.value.length),\n * onDrop: (source, to) => emit('move', indexOf(source), to),\n * })\n * ```\n */\n\nexport type DragSortOrientation = 'vertical' | 'horizontal'\n\n/** Чем ведётся перенос: указателем или с клавиатуры. */\nexport type DragSortMode = 'pointer' | 'keyboard'\n\nexport interface DragSortHit<TKey> {\n key: TKey\n index: number\n /** Доля вдоль элемента: 0 — начало, 1 — конец. Зоны нарезает компонент. */\n fraction: number\n}\n\nexport interface UseDragSortOptions<TKey, TTarget> {\n items: () => readonly TKey[]\n elementFor: (key: TKey) => HTMLElement | null | undefined\n /** По умолчанию `vertical`. */\n orientation?: () => DragSortOrientation\n /** Скроллер для автопрокрутки у краёв. Не задан — автопрокрутки нет. */\n scroller?: () => HTMLElement | null | undefined\n disabled?: () => boolean\n canDrag?: (key: TKey) => boolean\n /** Сдвиг, после которого нажатие становится переносом. По умолчанию 4px. */\n threshold?: () => number\n /**\n * Попадание указателя → цель переноса. `null` — сюда положить нельзя.\n *\n * Третьим аргументом приходит прежняя цель: цели сравниваются по ссылке, и\n * объектной цели (как «узел плюс зона» у дерева) стоит вернуть **ту же**\n * ссылку, пока смысл не изменился, — иначе подсветка и объявления будут\n * дёргаться на каждом движении указателя.\n */\n resolveTarget: (hit: DragSortHit<TKey>, source: TKey, previous: TTarget | null) => TTarget | null\n /** Перенос состоялся. */\n onDrop: (source: TKey, target: TTarget) => void\n /** Состояние изменилось: подсветка строк и объявления — на этом. */\n onUpdate?: (source: TKey | null, target: TTarget | null, mode: DragSortMode | null) => void\n}\n\nexport interface UseDragSortReturn<TKey, TTarget> {\n source: Ref<TKey | null>\n target: Ref<TTarget | null>\n mode: Ref<DragSortMode | null>\n isActive: Ref<boolean>\n /** Вешается на `@pointerdown` ручки или строки. */\n startFrom: (key: TKey) => (event: PointerEvent) => void\n /** Клавиатурный перенос: взять элемент. */\n grab: (key: TKey) => void\n /** Клавиатурный перенос: назначить цель (двигать ведёт компонент). */\n setTarget: (target: TTarget | null) => void\n /** Положить: если цель есть — состоится `onDrop`. */\n drop: () => void\n /** Отменить: ничего не переносится. */\n cancel: () => void\n}\n\nexport function useDragSort<TKey, TTarget>(\n options: UseDragSortOptions<TKey, TTarget>,\n): UseDragSortReturn<TKey, TTarget> {\n const source = shallowRef<TKey | null>(null)\n const target = shallowRef<TTarget | null>(null)\n const mode = shallowRef<DragSortMode | null>(null)\n const isActive = computed(() => mode.value !== null)\n\n // Нажатая, но ещё не потянутая строка: между `pointerdown` и порогом это\n // остаётся обычным кликом, и объявлять перенос рано.\n let pendingKey: TKey | null = null\n let originX = 0\n let originY = 0\n let lastPoint: { x: number, y: number } | null = null\n let scrollFrame: number | null = null\n\n const gesture = useDragGesture({\n disabled: () => options.disabled?.() ?? false,\n onStart: (event) => {\n if (pendingKey === null) return false\n if (options.canDrag?.(pendingKey) === false) return false\n\n originX = event.clientX\n originY = event.clientY\n\n return true\n },\n onMove: (event) => {\n if (mode.value === null) {\n const moved = Math.hypot(event.clientX - originX, event.clientY - originY)\n if (moved < (options.threshold?.() ?? 4)) return\n\n source.value = pendingKey\n mode.value = 'pointer'\n // Esc отменяет перенос, но `pointercancel` браузер при этом не шлёт —\n // клавишу приходится слушать самим, в capture, до слоёв приложения.\n window.addEventListener('keydown', onWindowKeydown, true)\n startScrolling()\n notify()\n }\n\n // Против выделения текста под курсором во время переноса.\n event.preventDefault()\n\n lastPoint = { x: event.clientX, y: event.clientY }\n updateTarget(lastPoint)\n },\n onEnd: () => {\n if (mode.value === 'pointer') commit()\n else reset()\n },\n onCancel: reset,\n })\n\n function isVertical(): boolean {\n return (options.orientation?.() ?? 'vertical') === 'vertical'\n }\n\n function spanOf(rect: DOMRect): DragSortSpan {\n return isVertical() ? { start: rect.top, end: rect.bottom } : { start: rect.left, end: rect.right }\n }\n\n function notify(): void {\n options.onUpdate?.(source.value, target.value, mode.value)\n }\n\n function updateTarget(point: { x: number, y: number }): void {\n const current = source.value\n if (current === null) return\n\n // Отрисованы не все: список может быть под окном отсечения, ветка дерева —\n // свёрнута. Попадание считается по тому, что на экране есть.\n const rendered: { key: TKey, index: number, span: DragSortSpan }[] = []\n\n options.items().forEach((key, index) => {\n const el = options.elementFor(key)\n if (el) rendered.push({ key, index, span: spanOf(el.getBoundingClientRect()) })\n })\n\n const hit = hitTest(rendered.map(entry => entry.span), isVertical() ? point.y : point.x)\n const entry = hit === null ? null : rendered[hit.index]\n const next = entry === null || hit === null\n ? null\n : options.resolveTarget({ key: entry.key, index: entry.index, fraction: hit.fraction }, current, target.value)\n\n if (next === target.value) return\n\n target.value = next\n notify()\n }\n\n /**\n * Автопрокрутка живёт в своём кадре, а не в обработчике движения: у края\n * указатель обычно стоит неподвижно, событий больше нет — а список должен\n * продолжать ехать.\n */\n function tickScroll(): void {\n scrollFrame = null\n\n const scroller = options.scroller?.()\n const point = lastPoint\n if (!scroller || !point || mode.value !== 'pointer') return\n\n const delta = autoScrollDelta(spanOf(scroller.getBoundingClientRect()), isVertical() ? point.y : point.x)\n\n if (delta !== 0) {\n if (isVertical()) scroller.scrollTop += delta\n else scroller.scrollLeft += delta\n\n updateTarget(point)\n }\n\n scrollFrame = requestAnimationFrame(tickScroll)\n }\n\n function startScrolling(): void {\n if (scrollFrame !== null || !options.scroller?.()) return\n\n scrollFrame = requestAnimationFrame(tickScroll)\n }\n\n function stopScrolling(): void {\n if (scrollFrame === null) return\n\n cancelAnimationFrame(scrollFrame)\n scrollFrame = null\n }\n\n function onWindowKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape') return\n\n event.preventDefault()\n gesture.stop(false)\n }\n\n function reset(): void {\n stopScrolling()\n window.removeEventListener('keydown', onWindowKeydown, true)\n\n pendingKey = null\n lastPoint = null\n source.value = null\n target.value = null\n mode.value = null\n notify()\n }\n\n function commit(): void {\n const from = source.value\n const to = target.value\n\n if (from !== null && to !== null) options.onDrop(from, to)\n\n reset()\n }\n\n function startFrom(key: TKey) {\n return (event: PointerEvent): void => {\n // Клавиатурный перенос идёт своим чередом: указатель его не перебивает.\n if (mode.value === 'keyboard') return\n\n pendingKey = key\n gesture.start(event)\n }\n }\n\n function grab(key: TKey): void {\n if (options.disabled?.() || options.canDrag?.(key) === false) return\n if (mode.value !== null) return\n\n source.value = key\n target.value = null\n mode.value = 'keyboard'\n notify()\n }\n\n function setTarget(next: TTarget | null): void {\n if (mode.value !== 'keyboard') return\n\n target.value = next\n notify()\n }\n\n function drop(): void {\n if (mode.value !== 'keyboard') return\n\n commit()\n }\n\n function cancel(): void {\n if (mode.value === 'pointer') {\n gesture.stop(false)\n return\n }\n\n if (mode.value === 'keyboard') reset()\n }\n\n onScopeDispose(() => {\n stopScrolling()\n window.removeEventListener('keydown', onWindowKeydown, true)\n })\n\n return { source, target, mode, isActive, startFrom, grab, setTarget, drop, cancel }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useDragSort-0l47h4yb.js","names":[],"sources":["../../src/composables/internal/dragSortGeometry.ts","../../src/composables/useDragSort.ts"],"sourcesContent":["/**\n * Геометрия перестановки: чистая арифметика без DOM и без реактивности.\n *\n * Вынесена отдельно, потому что ошибка здесь проявляется не исключением, а\n * «элемент встал не туда» — поймать это можно только тестом на числах.\n * Композабл `useDragSort` сводит `DOMRect` к отрезкам вдоль оси и зовёт эти\n * функции; какой смысл у попадания, решает уже компонент.\n */\n\n/** Отрезок, занимаемый элементом вдоль оси переноса. */\nexport interface DragSortSpan {\n start: number\n end: number\n}\n\nexport interface DragSortHitResult {\n /** Индекс элемента в переданном наборе. */\n index: number\n /** Доля вдоль элемента: 0 — его начало, 1 — конец. */\n fraction: number\n}\n\n/**\n * Какой элемент под указателем и в каком месте его.\n *\n * Промах не отбрасывается: между строками есть зазоры, а курсор уезжает за\n * край списка — и в обоих случаях перенос обязан продолжать показывать\n * ближайшую цель, иначе индикатор мигает на каждом пикселе между строками.\n *\n * Вырожденный отрезок (нулевая высота строки) даёт `fraction: 0` — цель\n * остаётся детерминированной, а не зависящей от направления подхода.\n */\nexport function hitTest(spans: readonly DragSortSpan[], position: number): DragSortHitResult | null {\n if (spans.length === 0)\n return null\n\n let nearestIndex = 0\n let nearestDistance = Number.POSITIVE_INFINITY\n\n for (let index = 0; index < spans.length; index += 1) {\n const span = spans[index]\n\n if (position >= span.start && position <= span.end)\n return { index, fraction: fractionIn(span, position) }\n\n const distance = position < span.start ? span.start - position : position - span.end\n if (distance < nearestDistance) {\n nearestDistance = distance\n nearestIndex = index\n }\n }\n\n const nearest = spans[nearestIndex]\n\n return { index: nearestIndex, fraction: fractionIn(nearest, position) }\n}\n\nfunction fractionIn(span: DragSortSpan, position: number): number {\n const size = span.end - span.start\n if (size <= 0)\n return 0\n\n return clamp((position - span.start) / size, 0, 1)\n}\n\n/**\n * Куда встанет элемент, если отпустить сейчас.\n *\n * Верхняя половина строки — «до неё», нижняя — «после». Индекс возвращается\n * **в терминах итогового массива**, то есть уже с поправкой на то, что\n * перетаскиваемый элемент из него временно вынут: без поправки перенос вниз\n * на одну позицию не двигал бы ничего.\n */\nexport function insertionIndex(hit: DragSortHitResult, from: number, count: number): number {\n if (count <= 0)\n return 0\n\n const base = hit.fraction < 0.5 ? hit.index : hit.index + 1\n const adjusted = from < base ? base - 1 : base\n\n return clamp(adjusted, 0, count - 1)\n}\n\nexport interface AutoScrollOptions {\n /** Ширина чувствительной полосы у каждого края, px. По умолчанию 32. */\n zone?: number\n /** Скорость на самом краю, px за кадр. По умолчанию 12. */\n speed?: number\n}\n\n/**\n * На сколько прокрутить контейнер за кадр: отрицательное — к началу.\n *\n * Скорость растёт линейно по мере приближения к краю, а не включается\n * ступенькой: ступенька на длинном списке ощущается как рывок и промахивается\n * мимо цели.\n */\nexport function autoScrollDelta(viewport: DragSortSpan, position: number, options: AutoScrollOptions = {}): number {\n const zone = options.zone ?? 32\n const speed = options.speed ?? 12\n\n if (zone <= 0 || speed <= 0)\n return 0\n // Вьюпорт короче двух зон: полосы наложились бы и тянули список в обе стороны.\n if (viewport.end - viewport.start <= zone * 2)\n return 0\n\n if (position < viewport.start || position > viewport.end)\n return 0\n\n const fromStart = position - viewport.start\n if (fromStart < zone)\n return -Math.ceil(((zone - fromStart) / zone) * speed)\n\n const fromEnd = viewport.end - position\n if (fromEnd < zone)\n return Math.ceil(((zone - fromEnd) / zone) * speed)\n\n return 0\n}\n\n/**\n * Перестановка без мутации входа: массив потребителя остаётся нетронутым,\n * наружу уходит новый. `v-model` с мутацией исходного набора ломает и\n * сравнение «было — стало», и историю в сторе потребителя.\n */\nexport function moveItem<T>(items: readonly T[], from: number, to: number): T[] {\n const next = items.slice()\n if (from === to || from < 0 || from >= items.length)\n return next\n\n const [moved] = next.splice(from, 1)\n next.splice(clamp(to, 0, next.length), 0, moved)\n\n return next\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n","import type { Ref } from 'vue'\nimport { computed, onScopeDispose, shallowRef } from 'vue'\n\nimport type { DragSortSpan } from './internal/dragSortGeometry'\nimport { autoScrollDelta, hitTest } from './internal/dragSortGeometry'\nimport { useDragGesture } from './useDragGesture'\n\n/**\n * Модель перестановки: перенос элемента указателем и с клавиатуры.\n *\n * Композабл владеет **механикой** — сессией жеста поверх `useDragGesture`,\n * порогом начала, попаданием в элемент, автопрокруткой у краёв и состоянием\n * переноса. **Смысл** остаётся у компонента: что считать целью (индекс вставки\n * у списка, «до / внутрь / после» у дерева), какими клавишами это делается и\n * что сказать в живой регион.\n *\n * Граница проведена так не из вкуса: у списка цель переноса — число, у дерева —\n * пара «узел и зона», а `Space` в дереве уже занят чекбоксом. Общая у них\n * механика, а не семантика; обобщать семантику значило бы завести опцию на\n * каждое расхождение.\n *\n * Данные композабл не меняет: он сообщает «источник и цель», перестановку\n * делает потребитель (`moveItem` из `internal/dragSortGeometry` — если она\n * обычная).\n *\n * ```ts\n * const sort = useDragSort<string, number>({\n * items: () => keys.value,\n * elementFor: key => rowEls.get(key),\n * scroller: () => scrollerEl.value,\n * resolveTarget: (hit, source) => insertionIndex(hit, indexOf(source), keys.value.length),\n * onDrop: (source, to) => emit('move', indexOf(source), to),\n * })\n * ```\n */\n\nexport type DragSortOrientation = 'vertical' | 'horizontal'\n\n/** Чем ведётся перенос: указателем или с клавиатуры. */\nexport type DragSortMode = 'pointer' | 'keyboard'\n\nexport interface DragSortHit<TKey> {\n key: TKey\n index: number\n /** Доля вдоль элемента: 0 — начало, 1 — конец. Зоны нарезает компонент. */\n fraction: number\n}\n\nexport interface UseDragSortOptions<TKey, TTarget> {\n items: () => readonly TKey[]\n elementFor: (key: TKey) => HTMLElement | null | undefined\n /** По умолчанию `vertical`. */\n orientation?: () => DragSortOrientation\n /** Скроллер для автопрокрутки у краёв. Не задан — автопрокрутки нет. */\n scroller?: () => HTMLElement | null | undefined\n disabled?: () => boolean\n canDrag?: (key: TKey) => boolean\n /** Сдвиг, после которого нажатие становится переносом. По умолчанию 4px. */\n threshold?: () => number\n /**\n * Попадание указателя → цель переноса. `null` — сюда положить нельзя.\n *\n * Третьим аргументом приходит прежняя цель: цели сравниваются по ссылке, и\n * объектной цели (как «узел плюс зона» у дерева) стоит вернуть **ту же**\n * ссылку, пока смысл не изменился, — иначе подсветка и объявления будут\n * дёргаться на каждом движении указателя.\n */\n resolveTarget: (hit: DragSortHit<TKey>, source: TKey, previous: TTarget | null) => TTarget | null\n /** Перенос состоялся. */\n onDrop: (source: TKey, target: TTarget) => void\n /** Состояние изменилось: подсветка строк и объявления — на этом. */\n onUpdate?: (source: TKey | null, target: TTarget | null, mode: DragSortMode | null) => void\n}\n\nexport interface UseDragSortReturn<TKey, TTarget> {\n source: Ref<TKey | null>\n target: Ref<TTarget | null>\n mode: Ref<DragSortMode | null>\n isActive: Ref<boolean>\n /** Вешается на `@pointerdown` ручки или строки. */\n startFrom: (key: TKey) => (event: PointerEvent) => void\n /** Клавиатурный перенос: взять элемент. */\n grab: (key: TKey) => void\n /** Клавиатурный перенос: назначить цель (двигать ведёт компонент). */\n setTarget: (target: TTarget | null) => void\n /** Положить: если цель есть — состоится `onDrop`. */\n drop: () => void\n /** Отменить: ничего не переносится. */\n cancel: () => void\n}\n\nexport function useDragSort<TKey, TTarget>(\n options: UseDragSortOptions<TKey, TTarget>,\n): UseDragSortReturn<TKey, TTarget> {\n const source = shallowRef<TKey | null>(null)\n const target = shallowRef<TTarget | null>(null)\n const mode = shallowRef<DragSortMode | null>(null)\n const isActive = computed(() => mode.value !== null)\n\n // Нажатая, но ещё не потянутая строка: между `pointerdown` и порогом это\n // остаётся обычным кликом, и объявлять перенос рано.\n let pendingKey: TKey | null = null\n let originX = 0\n let originY = 0\n let lastPoint: { x: number, y: number } | null = null\n let scrollFrame: number | null = null\n\n const gesture = useDragGesture({\n disabled: () => options.disabled?.() ?? false,\n onStart: (event) => {\n if (pendingKey === null)\n return false\n if (options.canDrag?.(pendingKey) === false)\n return false\n\n originX = event.clientX\n originY = event.clientY\n\n return true\n },\n onMove: (event) => {\n if (mode.value === null) {\n const moved = Math.hypot(event.clientX - originX, event.clientY - originY)\n if (moved < (options.threshold?.() ?? 4))\n return\n\n source.value = pendingKey\n mode.value = 'pointer'\n // Esc отменяет перенос, но `pointercancel` браузер при этом не шлёт —\n // клавишу приходится слушать самим, в capture, до слоёв приложения.\n window.addEventListener('keydown', onWindowKeydown, true)\n startScrolling()\n notify()\n }\n\n // Против выделения текста под курсором во время переноса.\n event.preventDefault()\n\n lastPoint = { x: event.clientX, y: event.clientY }\n updateTarget(lastPoint)\n },\n onEnd: () => {\n if (mode.value === 'pointer')\n commit()\n else reset()\n },\n onCancel: reset,\n })\n\n function isVertical(): boolean {\n return (options.orientation?.() ?? 'vertical') === 'vertical'\n }\n\n function spanOf(rect: DOMRect): DragSortSpan {\n return isVertical() ? { start: rect.top, end: rect.bottom } : { start: rect.left, end: rect.right }\n }\n\n function notify(): void {\n options.onUpdate?.(source.value, target.value, mode.value)\n }\n\n function updateTarget(point: { x: number, y: number }): void {\n const current = source.value\n if (current === null)\n return\n\n // Отрисованы не все: список может быть под окном отсечения, ветка дерева —\n // свёрнута. Попадание считается по тому, что на экране есть.\n const rendered: { key: TKey, index: number, span: DragSortSpan }[] = []\n\n options.items().forEach((key, index) => {\n const el = options.elementFor(key)\n if (el)\n rendered.push({ key, index, span: spanOf(el.getBoundingClientRect()) })\n })\n\n const hit = hitTest(rendered.map(entry => entry.span), isVertical() ? point.y : point.x)\n const entry = hit === null ? null : rendered[hit.index]\n const next = entry === null || hit === null\n ? null\n : options.resolveTarget({ key: entry.key, index: entry.index, fraction: hit.fraction }, current, target.value)\n\n if (next === target.value)\n return\n\n target.value = next\n notify()\n }\n\n /**\n * Автопрокрутка живёт в своём кадре, а не в обработчике движения: у края\n * указатель обычно стоит неподвижно, событий больше нет — а список должен\n * продолжать ехать.\n */\n function tickScroll(): void {\n scrollFrame = null\n\n const scroller = options.scroller?.()\n const point = lastPoint\n if (!scroller || !point || mode.value !== 'pointer')\n return\n\n const delta = autoScrollDelta(spanOf(scroller.getBoundingClientRect()), isVertical() ? point.y : point.x)\n\n if (delta !== 0) {\n if (isVertical())\n scroller.scrollTop += delta\n else scroller.scrollLeft += delta\n\n updateTarget(point)\n }\n\n scrollFrame = requestAnimationFrame(tickScroll)\n }\n\n function startScrolling(): void {\n if (scrollFrame !== null || !options.scroller?.())\n return\n\n scrollFrame = requestAnimationFrame(tickScroll)\n }\n\n function stopScrolling(): void {\n if (scrollFrame === null)\n return\n\n cancelAnimationFrame(scrollFrame)\n scrollFrame = null\n }\n\n function onWindowKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape')\n return\n\n event.preventDefault()\n gesture.stop(false)\n }\n\n function reset(): void {\n stopScrolling()\n window.removeEventListener('keydown', onWindowKeydown, true)\n\n pendingKey = null\n lastPoint = null\n source.value = null\n target.value = null\n mode.value = null\n notify()\n }\n\n function commit(): void {\n const from = source.value\n const to = target.value\n\n if (from !== null && to !== null)\n options.onDrop(from, to)\n\n reset()\n }\n\n function startFrom(key: TKey) {\n return (event: PointerEvent): void => {\n // Клавиатурный перенос идёт своим чередом: указатель его не перебивает.\n if (mode.value === 'keyboard')\n return\n\n pendingKey = key\n gesture.start(event)\n }\n }\n\n function grab(key: TKey): void {\n if (options.disabled?.() || options.canDrag?.(key) === false)\n return\n if (mode.value !== null)\n return\n\n source.value = key\n target.value = null\n mode.value = 'keyboard'\n notify()\n }\n\n function setTarget(next: TTarget | null): void {\n if (mode.value !== 'keyboard')\n return\n\n target.value = next\n notify()\n }\n\n function drop(): void {\n if (mode.value !== 'keyboard')\n return\n\n commit()\n }\n\n function cancel(): void {\n if (mode.value === 'pointer') {\n gesture.stop(false)\n return\n }\n\n if (mode.value === 'keyboard')\n reset()\n }\n\n onScopeDispose(() => {\n stopScrolling()\n window.removeEventListener('keydown', onWindowKeydown, true)\n })\n\n return { source, target, mode, isActive, startFrom, grab, setTarget, drop, cancel }\n}\n"],"mappings":";;;;;;;;;;;;;AAgCA,SAAgB,QAAQ,OAAgC,UAA4C;CAClG,IAAI,MAAM,WAAW,GACnB,OAAO;CAET,IAAI,eAAe;CACnB,IAAI,kBAAkB,OAAO;CAE7B,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;EACpD,MAAM,OAAO,MAAM;EAEnB,IAAI,YAAY,KAAK,SAAS,YAAY,KAAK,KAC7C,OAAO;GAAE;GAAO,UAAU,WAAW,MAAM,QAAQ;EAAE;EAEvD,MAAM,WAAW,WAAW,KAAK,QAAQ,KAAK,QAAQ,WAAW,WAAW,KAAK;EACjF,IAAI,WAAW,iBAAiB;GAC9B,kBAAkB;GAClB,eAAe;EACjB;CACF;CAEA,MAAM,UAAU,MAAM;CAEtB,OAAO;EAAE,OAAO;EAAc,UAAU,WAAW,SAAS,QAAQ;CAAE;AACxE;AAEA,SAAS,WAAW,MAAoB,UAA0B;CAChE,MAAM,OAAO,KAAK,MAAM,KAAK;CAC7B,IAAI,QAAQ,GACV,OAAO;CAET,OAAO,OAAO,WAAW,KAAK,SAAS,MAAM,GAAG,CAAC;AACnD;;;;;;;;;AAUA,SAAgB,eAAe,KAAwB,MAAc,OAAuB;CAC1F,IAAI,SAAS,GACX,OAAO;CAET,MAAM,OAAO,IAAI,WAAW,KAAM,IAAI,QAAQ,IAAI,QAAQ;CAG1D,OAAO,MAFU,OAAO,OAAO,OAAO,IAAI,MAEnB,GAAG,QAAQ,CAAC;AACrC;;;;;;;;AAgBA,SAAgB,gBAAgB,UAAwB,UAAkB,UAA6B,CAAC,GAAW;CACjH,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,QAAQ,QAAQ,SAAS;CAE/B,IAAI,QAAQ,KAAK,SAAS,GACxB,OAAO;CAET,IAAI,SAAS,MAAM,SAAS,SAAS,OAAO,GAC1C,OAAO;CAET,IAAI,WAAW,SAAS,SAAS,WAAW,SAAS,KACnD,OAAO;CAET,MAAM,YAAY,WAAW,SAAS;CACtC,IAAI,YAAY,MACd,OAAO,CAAC,KAAK,MAAO,OAAO,aAAa,OAAQ,KAAK;CAEvD,MAAM,UAAU,SAAS,MAAM;CAC/B,IAAI,UAAU,MACZ,OAAO,KAAK,MAAO,OAAO,WAAW,OAAQ,KAAK;CAEpD,OAAO;AACT;;;;;;AAOA,SAAgB,SAAY,OAAqB,MAAc,IAAiB;CAC9E,MAAM,OAAO,MAAM,MAAM;CACzB,IAAI,SAAS,MAAM,OAAO,KAAK,QAAQ,MAAM,QAC3C,OAAO;CAET,MAAM,CAAC,SAAS,KAAK,OAAO,MAAM,CAAC;CACnC,KAAK,OAAO,MAAM,IAAI,GAAG,KAAK,MAAM,GAAG,GAAG,KAAK;CAE/C,OAAO;AACT;AAEA,SAAS,MAAM,OAAe,KAAa,KAAqB;CAC9D,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAC3C;;;AChDA,SAAgB,YACd,SACkC;CAClC,MAAM,SAAS,WAAwB,IAAI;CAC3C,MAAM,SAAS,WAA2B,IAAI;CAC9C,MAAM,OAAO,WAAgC,IAAI;CACjD,MAAM,WAAW,eAAe,KAAK,UAAU,IAAI;CAInD,IAAI,aAA0B;CAC9B,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,YAA6C;CACjD,IAAI,cAA6B;CAEjC,MAAM,UAAU,eAAe;EAC7B,gBAAgB,QAAQ,WAAW,KAAK;EACxC,UAAU,UAAU;GAClB,IAAI,eAAe,MACjB,OAAO;GACT,IAAI,QAAQ,UAAU,UAAU,MAAM,OACpC,OAAO;GAET,UAAU,MAAM;GAChB,UAAU,MAAM;GAEhB,OAAO;EACT;EACA,SAAS,UAAU;GACjB,IAAI,KAAK,UAAU,MAAM;IAEvB,IADc,KAAK,MAAM,MAAM,UAAU,SAAS,MAAM,UAAU,OAC9D,KAAS,QAAQ,YAAY,KAAK,IACpC;IAEF,OAAO,QAAQ;IACf,KAAK,QAAQ;IAGb,OAAO,iBAAiB,WAAW,iBAAiB,IAAI;IACxD,eAAe;IACf,OAAO;GACT;GAGA,MAAM,eAAe;GAErB,YAAY;IAAE,GAAG,MAAM;IAAS,GAAG,MAAM;GAAQ;GACjD,aAAa,SAAS;EACxB;EACA,aAAa;GACX,IAAI,KAAK,UAAU,WACjB,OAAO;QACJ,MAAM;EACb;EACA,UAAU;CACZ,CAAC;CAED,SAAS,aAAsB;EAC7B,QAAQ,QAAQ,cAAc,KAAK,gBAAgB;CACrD;CAEA,SAAS,OAAO,MAA6B;EAC3C,OAAO,WAAW,IAAI;GAAE,OAAO,KAAK;GAAK,KAAK,KAAK;EAAO,IAAI;GAAE,OAAO,KAAK;GAAM,KAAK,KAAK;EAAM;CACpG;CAEA,SAAS,SAAe;EACtB,QAAQ,WAAW,OAAO,OAAO,OAAO,OAAO,KAAK,KAAK;CAC3D;CAEA,SAAS,aAAa,OAAuC;EAC3D,MAAM,UAAU,OAAO;EACvB,IAAI,YAAY,MACd;EAIF,MAAM,WAA+D,CAAC;EAEtE,QAAQ,MAAM,CAAC,CAAC,SAAS,KAAK,UAAU;GACtC,MAAM,KAAK,QAAQ,WAAW,GAAG;GACjC,IAAI,IACF,SAAS,KAAK;IAAE;IAAK;IAAO,MAAM,OAAO,GAAG,sBAAsB,CAAC;GAAE,CAAC;EAC1E,CAAC;EAED,MAAM,MAAM,QAAQ,SAAS,KAAI,UAAS,MAAM,IAAI,GAAG,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC;EACvF,MAAM,QAAQ,QAAQ,OAAO,OAAO,SAAS,IAAI;EACjD,MAAM,OAAO,UAAU,QAAQ,QAAQ,OACnC,OACA,QAAQ,cAAc;GAAE,KAAK,MAAM;GAAK,OAAO,MAAM;GAAO,UAAU,IAAI;EAAS,GAAG,SAAS,OAAO,KAAK;EAE/G,IAAI,SAAS,OAAO,OAClB;EAEF,OAAO,QAAQ;EACf,OAAO;CACT;;;;;;CAOA,SAAS,aAAmB;EAC1B,cAAc;EAEd,MAAM,WAAW,QAAQ,WAAW;EACpC,MAAM,QAAQ;EACd,IAAI,CAAC,YAAY,CAAC,SAAS,KAAK,UAAU,WACxC;EAEF,MAAM,QAAQ,gBAAgB,OAAO,SAAS,sBAAsB,CAAC,GAAG,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC;EAExG,IAAI,UAAU,GAAG;GACf,IAAI,WAAW,GACb,SAAS,aAAa;QACnB,SAAS,cAAc;GAE5B,aAAa,KAAK;EACpB;EAEA,cAAc,sBAAsB,UAAU;CAChD;CAEA,SAAS,iBAAuB;EAC9B,IAAI,gBAAgB,QAAQ,CAAC,QAAQ,WAAW,GAC9C;EAEF,cAAc,sBAAsB,UAAU;CAChD;CAEA,SAAS,gBAAsB;EAC7B,IAAI,gBAAgB,MAClB;EAEF,qBAAqB,WAAW;EAChC,cAAc;CAChB;CAEA,SAAS,gBAAgB,OAA4B;EACnD,IAAI,MAAM,QAAQ,UAChB;EAEF,MAAM,eAAe;EACrB,QAAQ,KAAK,KAAK;CACpB;CAEA,SAAS,QAAc;EACrB,cAAc;EACd,OAAO,oBAAoB,WAAW,iBAAiB,IAAI;EAE3D,aAAa;EACb,YAAY;EACZ,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,KAAK,QAAQ;EACb,OAAO;CACT;CAEA,SAAS,SAAe;EACtB,MAAM,OAAO,OAAO;EACpB,MAAM,KAAK,OAAO;EAElB,IAAI,SAAS,QAAQ,OAAO,MAC1B,QAAQ,OAAO,MAAM,EAAE;EAEzB,MAAM;CACR;CAEA,SAAS,UAAU,KAAW;EAC5B,QAAQ,UAA8B;GAEpC,IAAI,KAAK,UAAU,YACjB;GAEF,aAAa;GACb,QAAQ,MAAM,KAAK;EACrB;CACF;CAEA,SAAS,KAAK,KAAiB;EAC7B,IAAI,QAAQ,WAAW,KAAK,QAAQ,UAAU,GAAG,MAAM,OACrD;EACF,IAAI,KAAK,UAAU,MACjB;EAEF,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,KAAK,QAAQ;EACb,OAAO;CACT;CAEA,SAAS,UAAU,MAA4B;EAC7C,IAAI,KAAK,UAAU,YACjB;EAEF,OAAO,QAAQ;EACf,OAAO;CACT;CAEA,SAAS,OAAa;EACpB,IAAI,KAAK,UAAU,YACjB;EAEF,OAAO;CACT;CAEA,SAAS,SAAe;EACtB,IAAI,KAAK,UAAU,WAAW;GAC5B,QAAQ,KAAK,KAAK;GAClB;EACF;EAEA,IAAI,KAAK,UAAU,YACjB,MAAM;CACV;CAEA,qBAAqB;EACnB,cAAc;EACd,OAAO,oBAAoB,WAAW,iBAAiB,IAAI;CAC7D,CAAC;CAED,OAAO;EAAE;EAAQ;EAAQ;EAAM;EAAU;EAAW;EAAM;EAAW;EAAM;CAAO;AACpF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFilePreviews-DnwgdyBs.js","names":[],"sources":["../../src/composables/internal/useFilePreviews.ts"],"sourcesContent":["export interface UseFilePreviewsOptions {\n /** Включены ли миниатюры. Геттер — проп реактивен. */\n enabled: () => boolean\n}\n\nexport interface UseFilePreviewsReturn {\n /** Стабильный ключ файла для `v-for`. */\n fileKey: (file: File) => string\n /** `object URL` миниатюры или `undefined`, если файл не картинка. */\n previewUrl: (file: File) => string | undefined\n revokePreview: (file: File) => void\n revokeAllPreviews: () => void\n}\n\n/**\n * Ключи списка и миниатюры превью. Общий на файловые компоненты пакета:\n * `GrFileUpload` (отправка) и `GrFormFile` (поле формы) показывают один и тот\n * же набор одинаково, и правила «что считать картинкой» у них обязаны совпадать.\n *\n * Ключ считается от самого файла, а не от имени: два одноимённых файла из\n * разных папок делили бы один `<li>`, и кнопка удаления в строке снимала бы\n * не тот файл.\n *\n * `object URL` живёт ровно столько, сколько файл в наборе: без отзыва blob\n * висит в памяти вкладки до перезагрузки — утечка тем заметнее, чем чаще\n * пользователь перевыбирает файлы.\n */\nexport function useFilePreviews(options: UseFilePreviewsOptions): UseFilePreviewsReturn {\n const keys = new WeakMap<File, string>()\n const urls = new Map<File, string>()\n let counter = 0\n\n function fileKey(file: File): string {\n const existing = keys.get(file)\n if (existing !== undefined)
|
|
1
|
+
{"version":3,"file":"useFilePreviews-DnwgdyBs.js","names":[],"sources":["../../src/composables/internal/useFilePreviews.ts"],"sourcesContent":["export interface UseFilePreviewsOptions {\n /** Включены ли миниатюры. Геттер — проп реактивен. */\n enabled: () => boolean\n}\n\nexport interface UseFilePreviewsReturn {\n /** Стабильный ключ файла для `v-for`. */\n fileKey: (file: File) => string\n /** `object URL` миниатюры или `undefined`, если файл не картинка. */\n previewUrl: (file: File) => string | undefined\n revokePreview: (file: File) => void\n revokeAllPreviews: () => void\n}\n\n/**\n * Ключи списка и миниатюры превью. Общий на файловые компоненты пакета:\n * `GrFileUpload` (отправка) и `GrFormFile` (поле формы) показывают один и тот\n * же набор одинаково, и правила «что считать картинкой» у них обязаны совпадать.\n *\n * Ключ считается от самого файла, а не от имени: два одноимённых файла из\n * разных папок делили бы один `<li>`, и кнопка удаления в строке снимала бы\n * не тот файл.\n *\n * `object URL` живёт ровно столько, сколько файл в наборе: без отзыва blob\n * висит в памяти вкладки до перезагрузки — утечка тем заметнее, чем чаще\n * пользователь перевыбирает файлы.\n */\nexport function useFilePreviews(options: UseFilePreviewsOptions): UseFilePreviewsReturn {\n const keys = new WeakMap<File, string>()\n const urls = new Map<File, string>()\n let counter = 0\n\n function fileKey(file: File): string {\n const existing = keys.get(file)\n if (existing !== undefined)\n return existing\n\n counter += 1\n const key = `gr-file-${counter}`\n keys.set(file, key)\n return key\n }\n\n function previewUrl(file: File): string | undefined {\n if (!options.enabled() || !file.type.startsWith('image/'))\n return undefined\n\n const existing = urls.get(file)\n if (existing)\n return existing\n\n if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function')\n return undefined\n\n const url = URL.createObjectURL(file)\n urls.set(file, url)\n return url\n }\n\n function revokePreview(file: File): void {\n const url = urls.get(file)\n if (!url)\n return\n\n URL.revokeObjectURL(url)\n urls.delete(file)\n }\n\n function revokeAllPreviews(): void {\n for (const file of [...urls.keys()]) revokePreview(file)\n }\n\n return { fileKey, previewUrl, revokePreview, revokeAllPreviews }\n}\n"],"mappings":";;;;;;;;;;;;;;AA2BA,SAAgB,gBAAgB,SAAwD;CACtF,MAAM,uBAAO,IAAI,QAAsB;CACvC,MAAM,uBAAO,IAAI,IAAkB;CACnC,IAAI,UAAU;CAEd,SAAS,QAAQ,MAAoB;EACnC,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,IAAI,aAAa,KAAA,GACf,OAAO;EAET,WAAW;EACX,MAAM,MAAM,WAAW;EACvB,KAAK,IAAI,MAAM,GAAG;EAClB,OAAO;CACT;CAEA,SAAS,WAAW,MAAgC;EAClD,IAAI,CAAC,QAAQ,QAAQ,KAAK,CAAC,KAAK,KAAK,WAAW,QAAQ,GACtD,OAAO,KAAA;EAET,MAAM,WAAW,KAAK,IAAI,IAAI;EAC9B,IAAI,UACF,OAAO;EAET,IAAI,OAAO,QAAQ,eAAe,OAAO,IAAI,oBAAoB,YAC/D,OAAO,KAAA;EAET,MAAM,MAAM,IAAI,gBAAgB,IAAI;EACpC,KAAK,IAAI,MAAM,GAAG;EAClB,OAAO;CACT;CAEA,SAAS,cAAc,MAAkB;EACvC,MAAM,MAAM,KAAK,IAAI,IAAI;EACzB,IAAI,CAAC,KACH;EAEF,IAAI,gBAAgB,GAAG;EACvB,KAAK,OAAO,IAAI;CAClB;CAEA,SAAS,oBAA0B;EACjC,KAAK,MAAM,QAAQ,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,cAAc,IAAI;CACzD;CAEA,OAAO;EAAE;EAAS;EAAY;EAAe;CAAkB;AACjE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFocusWithin-BwHE0rzm.js","names":[],"sources":["../../src/composables/internal/useFocusWithin.ts"],"sourcesContent":["import type { Ref } from 'vue'\n\nexport interface UseFocusWithinOptions {\n /** Фокус пришёл снаружи внутрь. */\n enter?: (event: FocusEvent) => void\n /** Фокус ушёл изнутри наружу. */\n leave?: (event: FocusEvent) => void\n /**\n * Узлы, которые считаются частью контрола, хотя лежат вне корня. Нужны там,\n * где панель телепортирована в `body`, а фокус в неё уходит по-настоящему:\n * без них переход в поле фильтра открытой панели выглядел бы уходом наружу.\n */\n containers?: () => (HTMLElement | null | undefined)[]\n}\n\nexport interface UseFocusWithinHandlers {\n onFocusIn: (event: FocusEvent) => void\n onFocusOut: (event: FocusEvent) => void\n}\n\n/**\n * Граница фокуса составного контрола.\n *\n * Нативные `focus`/`blur` не всплывают, поэтому у группы (радиогруппа,\n * сегменты, набор чекбоксов) их не поймать на корне вовсе — всплывают только\n * `focusin`/`focusout`. Но всплывают они и когда фокус переезжает **внутри**\n * группы: между её пунктами, с поля на его же кнопку очистки. Для потребителя\n * это не уход и не приход — контрол всё это время в фокусе, — а необработанный\n * переход дал бы `blur` + `focus` на каждую стрелку клавиатуры.\n *\n * Поэтому событие считается состоявшимся, только если `relatedTarget` лежит вне\n * корня. `relatedTarget: null` (клик по body, уход из окна, программный фокус)\n * — тоже пересечение границы.\n */\nexport function useFocusWithin(\n root: Ref<HTMLElement | null>,\n options: UseFocusWithinOptions,\n): UseFocusWithinHandlers {\n function crossedBoundary(event: FocusEvent): boolean {\n const other = event.relatedTarget as Node | null\n if (!other)
|
|
1
|
+
{"version":3,"file":"useFocusWithin-BwHE0rzm.js","names":[],"sources":["../../src/composables/internal/useFocusWithin.ts"],"sourcesContent":["import type { Ref } from 'vue'\n\nexport interface UseFocusWithinOptions {\n /** Фокус пришёл снаружи внутрь. */\n enter?: (event: FocusEvent) => void\n /** Фокус ушёл изнутри наружу. */\n leave?: (event: FocusEvent) => void\n /**\n * Узлы, которые считаются частью контрола, хотя лежат вне корня. Нужны там,\n * где панель телепортирована в `body`, а фокус в неё уходит по-настоящему:\n * без них переход в поле фильтра открытой панели выглядел бы уходом наружу.\n */\n containers?: () => (HTMLElement | null | undefined)[]\n}\n\nexport interface UseFocusWithinHandlers {\n onFocusIn: (event: FocusEvent) => void\n onFocusOut: (event: FocusEvent) => void\n}\n\n/**\n * Граница фокуса составного контрола.\n *\n * Нативные `focus`/`blur` не всплывают, поэтому у группы (радиогруппа,\n * сегменты, набор чекбоксов) их не поймать на корне вовсе — всплывают только\n * `focusin`/`focusout`. Но всплывают они и когда фокус переезжает **внутри**\n * группы: между её пунктами, с поля на его же кнопку очистки. Для потребителя\n * это не уход и не приход — контрол всё это время в фокусе, — а необработанный\n * переход дал бы `blur` + `focus` на каждую стрелку клавиатуры.\n *\n * Поэтому событие считается состоявшимся, только если `relatedTarget` лежит вне\n * корня. `relatedTarget: null` (клик по body, уход из окна, программный фокус)\n * — тоже пересечение границы.\n */\nexport function useFocusWithin(\n root: Ref<HTMLElement | null>,\n options: UseFocusWithinOptions,\n): UseFocusWithinHandlers {\n function crossedBoundary(event: FocusEvent): boolean {\n const other = event.relatedTarget as Node | null\n if (!other)\n return true\n\n if (root.value?.contains(other))\n return false\n\n return !options.containers?.().some(node => node?.contains(other))\n }\n\n return {\n onFocusIn(event) {\n if (crossedBoundary(event))\n options.enter?.(event)\n },\n onFocusOut(event) {\n if (crossedBoundary(event))\n options.leave?.(event)\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;AAkCA,SAAgB,eACd,MACA,SACwB;CACxB,SAAS,gBAAgB,OAA4B;EACnD,MAAM,QAAQ,MAAM;EACpB,IAAI,CAAC,OACH,OAAO;EAET,IAAI,KAAK,OAAO,SAAS,KAAK,GAC5B,OAAO;EAET,OAAO,CAAC,QAAQ,aAAa,CAAC,CAAC,MAAK,SAAQ,MAAM,SAAS,KAAK,CAAC;CACnE;CAEA,OAAO;EACL,UAAU,OAAO;GACf,IAAI,gBAAgB,KAAK,GACvB,QAAQ,QAAQ,KAAK;EACzB;EACA,WAAW,OAAO;GAChB,IAAI,gBAAgB,KAAK,GACvB,QAAQ,QAAQ,KAAK;EACzB;CACF;AACF"}
|