@feugene/granularity 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/chunks/chevron-up-BhW6hi8L.js +23 -0
- package/dist/chunks/chevron-up-BhW6hi8L.js.map +1 -0
- package/dist/chunks/{config-E1cTyRVV.js → config-8n1VR0ju.js} +1 -1
- package/dist/chunks/{config-E1cTyRVV.js.map → config-8n1VR0ju.js.map} +1 -1
- package/dist/chunks/{config-DIDz_E5m.js → config-B34mkd53.js} +66 -1
- package/dist/chunks/config-B34mkd53.js.map +1 -0
- package/dist/chunks/{config-B5SBUa6u.js → config-B7qZqiUB.js} +28 -1
- package/dist/chunks/config-B7qZqiUB.js.map +1 -0
- package/dist/chunks/config-BBrPUEye.js +8 -0
- package/dist/chunks/config-BBrPUEye.js.map +1 -0
- package/dist/chunks/{config-CT4lP6S4.js → config-BOCzHqWh.js} +2 -2
- package/dist/chunks/{config-CT4lP6S4.js.map → config-BOCzHqWh.js.map} +1 -1
- package/dist/chunks/{config-iemgzS4x.js → config-BP1ktzwh.js} +2 -24
- package/dist/chunks/config-BP1ktzwh.js.map +1 -0
- package/dist/chunks/config-BTxmdRzT.js +28 -0
- package/dist/chunks/config-BTxmdRzT.js.map +1 -0
- package/dist/chunks/{config-CjWOTgLL.js → config-Bk52xd5l.js} +12 -5
- package/dist/chunks/config-Bk52xd5l.js.map +1 -0
- package/dist/chunks/{config-D3HOuHDe.js → config-C3SqBqSQ.js} +3 -1
- package/dist/chunks/config-C3SqBqSQ.js.map +1 -0
- package/dist/chunks/{config-2ZHRsdOc.js → config-C3uqd-sW.js} +1 -1
- package/dist/chunks/config-C3uqd-sW.js.map +1 -0
- package/dist/chunks/{config-BXRmsn3t.js → config-CPhcZzHi.js} +72 -10
- package/dist/chunks/config-CPhcZzHi.js.map +1 -0
- package/dist/chunks/config-CWtfxkM9.js.map +1 -1
- package/dist/chunks/{config--t2dxj0t.js → config-CmLTCmY9.js} +2 -2
- package/dist/chunks/{config--t2dxj0t.js.map → config-CmLTCmY9.js.map} +1 -1
- package/dist/chunks/{config-BAse0laT.js → config-CvtqB4ki.js} +8 -3
- package/dist/chunks/config-CvtqB4ki.js.map +1 -0
- package/dist/chunks/{config-BcXX7S7B.js → config-D-JgqV0J.js} +5 -1
- package/dist/chunks/{config-BcXX7S7B.js.map → config-D-JgqV0J.js.map} +1 -1
- package/dist/chunks/{config-BHLMTJaR.js → config-D1oVht-E.js} +8 -3
- package/dist/chunks/config-D1oVht-E.js.map +1 -0
- package/dist/chunks/{config-B42iJ48O.js → config-DG41PD9i.js} +13 -1
- package/dist/chunks/config-DG41PD9i.js.map +1 -0
- package/dist/chunks/{config-Bs1HwUcx.js → config-DPJ3jtiB.js} +12 -2
- package/dist/chunks/config-DPJ3jtiB.js.map +1 -0
- package/dist/chunks/{config-tNqtapcT.js → config-DpqjlCfB.js} +2 -2
- package/dist/chunks/{config-tNqtapcT.js.map → config-DpqjlCfB.js.map} +1 -1
- package/dist/chunks/{config-DNDgp9bB.js → config-DpzFoSIh.js} +64 -2
- package/dist/chunks/config-DpzFoSIh.js.map +1 -0
- package/dist/chunks/config-SPugeaHT.js +37 -0
- package/dist/chunks/config-SPugeaHT.js.map +1 -0
- package/dist/chunks/{en-CsQUoBS2.js → en-mIFywnV2.js} +22 -6
- package/dist/chunks/en-mIFywnV2.js.map +1 -0
- package/dist/chunks/{es-9nbkPvdA.js → es-CvthYwtB.js} +22 -6
- package/dist/chunks/es-CvthYwtB.js.map +1 -0
- package/dist/chunks/{es-DrUz5J6I.js → es-DlGd6Mrj.js} +3 -3
- package/dist/chunks/{es-DrUz5J6I.js.map → es-DlGd6Mrj.js.map} +1 -1
- package/dist/chunks/{generated-CxvC5O0V.js → generated-Cx4C7GRN.js} +84 -0
- package/dist/chunks/{generated-CxvC5O0V.js.map → generated-Cx4C7GRN.js.map} +1 -1
- package/dist/chunks/{grPopoverStyles-CYAeO3Ov.js → grPopoverStyles-CncEkiPu.js} +1 -1
- package/dist/chunks/{grPopoverStyles-CYAeO3Ov.js.map → grPopoverStyles-CncEkiPu.js.map} +1 -1
- package/dist/chunks/{grSelectStyles-D9EQ-pgK.js → grSelectStyles-Cam-IG9s.js} +3 -2
- package/dist/chunks/grSelectStyles-Cam-IG9s.js.map +1 -0
- package/dist/chunks/{grSliderStyles-kaz6VXZ3.js → grSliderStyles-CqzV0hll.js} +1 -1
- package/dist/chunks/grSliderStyles-CqzV0hll.js.map +1 -0
- package/dist/chunks/{config-CbEbLyDE.js → grSortableListStyles-CxieDmpe.js} +1 -27
- package/dist/chunks/grSortableListStyles-CxieDmpe.js.map +1 -0
- package/dist/chunks/grTableStyles-CPSxDTOW.js +66 -0
- package/dist/chunks/grTableStyles-CPSxDTOW.js.map +1 -0
- package/dist/chunks/{config-CMNfZO7S.js → grTabsStyles-DIz2VdIm.js} +1 -36
- package/dist/chunks/grTabsStyles-DIz2VdIm.js.map +1 -0
- package/dist/chunks/grTooltipStyles-CtbGEEGV.js +24 -0
- package/dist/chunks/grTooltipStyles-CtbGEEGV.js.map +1 -0
- package/dist/chunks/{granular-provider-B6xomapW.js → granular-provider-cv5vaM1a.js} +21 -19
- package/dist/chunks/granular-provider-cv5vaM1a.js.map +1 -0
- package/dist/chunks/hotkeySequence-BTHIM_5A.js +46 -0
- package/dist/chunks/hotkeySequence-BTHIM_5A.js.map +1 -0
- package/dist/chunks/{keyboard-DE2yhz_b.js → keyboard-BiFSgWLt.js} +70 -1
- package/dist/chunks/keyboard-BiFSgWLt.js.map +1 -0
- package/dist/chunks/optionPanel-DvY7Tv_4.js +182 -0
- package/dist/chunks/optionPanel-DvY7Tv_4.js.map +1 -0
- package/dist/chunks/overlayListPanel-D91jBCHU.js +21 -0
- package/dist/chunks/overlayListPanel-D91jBCHU.js.map +1 -0
- package/dist/chunks/{overlayStack-DH4Z7am1.js → overlayStack-DCkCgXZP.js} +1 -1
- package/dist/chunks/{overlayStack-DH4Z7am1.js.map → overlayStack-DCkCgXZP.js.map} +1 -1
- package/dist/chunks/{overlayPanelSurface-BKeMhRx4.js → overlayTransition-C0FDMx7Q.js} +24 -24
- package/dist/chunks/overlayTransition-C0FDMx7Q.js.map +1 -0
- package/dist/chunks/{ru-88i1WG0b.js → ru-CQKDR-kD.js} +22 -6
- package/dist/chunks/ru-CQKDR-kD.js.map +1 -0
- package/dist/chunks/{useMenuItemsFocus-B7Bl_aFA.js → useMenuItemsFocus-C031blYS.js} +1 -1
- package/dist/chunks/{useMenuItemsFocus-B7Bl_aFA.js.map → useMenuItemsFocus-C031blYS.js.map} +1 -1
- package/dist/chunks/{useModalOverlay-ACRkflzx.js → useModalOverlay-CKCQkGdB.js} +1 -1
- package/dist/chunks/{useModalOverlay-ACRkflzx.js.map → useModalOverlay-CKCQkGdB.js.map} +1 -1
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-Chhw65pI.js → GrAutocomplete-Cyr1-ENw.js} +181 -123
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-Cyr1-ENw.js.map +1 -0
- package/dist/components/GrAutocomplete/index.js +2 -2
- package/dist/components/GrAutocomplete/styles.css +1 -1
- package/dist/components/GrCarousel/chunks/{GrCarousel-ByuughtI.js → GrCarousel-CBj-PEPB.js} +186 -37
- package/dist/components/GrCarousel/chunks/GrCarousel-CBj-PEPB.js.map +1 -0
- package/dist/components/GrCarousel/index.js +2 -2
- package/dist/components/GrCarousel/styles.css +1 -1
- package/dist/components/GrCheckbox/chunks/{GrCheckbox-Dp201frb.js → GrCheckbox-B4NJc7Ur.js} +2 -1
- package/dist/components/GrCheckbox/chunks/GrCheckbox-B4NJc7Ur.js.map +1 -0
- package/dist/components/GrCheckbox/index.js +1 -1
- package/dist/components/GrCheckboxGroup/chunks/{GrCheckboxGroup-IpqG5qwm.js → GrCheckboxGroup-qZHpNFCX.js} +1 -1
- package/dist/components/GrCheckboxGroup/chunks/{GrCheckboxGroup-IpqG5qwm.js.map → GrCheckboxGroup-qZHpNFCX.js.map} +1 -1
- package/dist/components/GrCheckboxGroup/index.js +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-BRM_4lyI.js → GrColorPicker-vGSgpUV9.js} +353 -38
- package/dist/components/GrColorPicker/chunks/GrColorPicker-vGSgpUV9.js.map +1 -0
- package/dist/components/GrColorPicker/index.js +2 -2
- package/dist/components/GrColorPicker/styles.css +1 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-BJHmkDQv.js → GrCommandPalette-CAHgj0Aa.js} +23 -7
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-CAHgj0Aa.js.map +1 -0
- package/dist/components/GrCommandPalette/index.js +1 -1
- package/dist/components/GrCommandPalette/styles.css +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-BzUQwbec.js → GrConfirmDialog-dg021BMd.js} +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-BzUQwbec.js.map → GrConfirmDialog-dg021BMd.js.map} +1 -1
- package/dist/components/GrConfirmDialog/index.js +1 -1
- package/dist/components/GrContextMenu/chunks/{GrContextMenu-RpxMHQXH.js → GrContextMenu-bQ1Vvy2c.js} +3 -3
- package/dist/components/GrContextMenu/chunks/{GrContextMenu-RpxMHQXH.js.map → GrContextMenu-bQ1Vvy2c.js.map} +1 -1
- package/dist/components/GrContextMenu/index.js +1 -1
- package/dist/components/GrDataTable/chunks/{GrDataTable-DQxYYgQk.js → GrDataTable-Bb2jWnRG.js} +576 -136
- package/dist/components/GrDataTable/chunks/GrDataTable-Bb2jWnRG.js.map +1 -0
- package/dist/components/GrDataTable/index.js +2 -2
- package/dist/components/GrDelta/chunks/{GrDelta-CgTvfoIN.js → GrDelta-BSv1KdMy.js} +0 -0
- package/dist/components/GrDelta/chunks/{GrDelta-CgTvfoIN.js.map → GrDelta-BSv1KdMy.js.map} +1 -1
- package/dist/components/GrDelta/index.js +2 -2
- package/dist/components/GrDialog/chunks/{GrDialog-DDz_OEhk.js → GrDialog-C9Zu72jB.js} +1 -1
- package/dist/components/GrDialog/chunks/{GrDialog-DDz_OEhk.js.map → GrDialog-C9Zu72jB.js.map} +1 -1
- package/dist/components/GrDialog/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-Dtq0ZCN5.js → GrDialogService-DLBifO61.js} +2 -2
- package/dist/components/GrDialogService/chunks/{GrDialogService-Dtq0ZCN5.js.map → GrDialogService-DLBifO61.js.map} +1 -1
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-CNpSMuN-.js → GrDrawer-DOcRTE8G.js} +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-CNpSMuN-.js.map → GrDrawer-DOcRTE8G.js.map} +1 -1
- package/dist/components/GrDrawer/index.js +1 -1
- package/dist/components/GrDropdown/chunks/{GrDropdown-cLZuZMEY.js → GrDropdown-kxwmgfSe.js} +2 -2
- package/dist/components/GrDropdown/chunks/{GrDropdown-cLZuZMEY.js.map → GrDropdown-kxwmgfSe.js.map} +1 -1
- package/dist/components/GrDropdown/index.js +2 -2
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-Dxkz_ry4.js → GrDropdownMenu--VoK3VY0.js} +3 -34
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu--VoK3VY0.js.map +1 -0
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-CkM3BFPL.js +34 -0
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-CkM3BFPL.js.map +1 -0
- package/dist/components/GrDropdownMenu/index.js +2 -1
- package/dist/components/GrEmptyState/chunks/{GrEmptyState-BbvwE-E0.js → GrEmptyState-Bz6-_ED4.js} +42 -6
- package/dist/components/GrEmptyState/chunks/GrEmptyState-Bz6-_ED4.js.map +1 -0
- package/dist/components/GrEmptyState/index.js +1 -1
- package/dist/components/GrFileUpload/chunks/{GrFileUpload-BwMR71zN.js → GrFileUpload-Bgdfnkd8.js} +5 -2
- package/dist/components/GrFileUpload/chunks/GrFileUpload-Bgdfnkd8.js.map +1 -0
- package/dist/components/GrFileUpload/index.js +1 -1
- package/dist/components/GrFormFile/chunks/{GrFormFile-Dlxx-9zi.js → GrFormFile-Ctq1isz-.js} +78 -11
- package/dist/components/GrFormFile/chunks/GrFormFile-Ctq1isz-.js.map +1 -0
- package/dist/components/GrFormFile/index.js +2 -2
- package/dist/components/GrFormFile/styles.css +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-DK-iVHHC.js → GrImageViewer-Bnql_dNP.js} +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-DK-iVHHC.js.map → GrImageViewer-Bnql_dNP.js.map} +1 -1
- package/dist/components/GrImageViewer/index.js +1 -1
- package/dist/components/GrInput/chunks/{GrInput-DV4gMWPR.js → GrInput-CoaFLpaU.js} +43 -22
- package/dist/components/GrInput/chunks/GrInput-CoaFLpaU.js.map +1 -0
- package/dist/components/GrInput/index.js +1 -1
- package/dist/components/GrInputTag/chunks/{GrInputTag-qnRdAZnn.js → GrInputTag-BzzNRBe3.js} +245 -18
- package/dist/components/GrInputTag/chunks/GrInputTag-BzzNRBe3.js.map +1 -0
- package/dist/components/GrInputTag/index.js +2 -2
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-D7CyWzhT.js → GrJsonViewer-B6ZJ3GFH.js} +2 -2
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-D7CyWzhT.js.map → GrJsonViewer-B6ZJ3GFH.js.map} +1 -1
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrKbd/chunks/{GrKbd-BNMeoaLV.js → GrKbd-D2rHDkQv.js} +42 -37
- package/dist/components/GrKbd/chunks/GrKbd-D2rHDkQv.js.map +1 -0
- package/dist/components/GrKbd/index.js +1 -1
- package/dist/components/GrModal/chunks/{GrModal-Cm0mtSXk.js → GrModal-SHb1U2qx.js} +1 -1
- package/dist/components/GrModal/chunks/{GrModal-Cm0mtSXk.js.map → GrModal-SHb1U2qx.js.map} +1 -1
- package/dist/components/GrModal/index.js +1 -1
- package/dist/components/GrNavbar/chunks/{GrNavbar-BRGuksiy.js → GrNavbar-IEIHC5Vt.js} +10 -4
- package/dist/components/GrNavbar/chunks/GrNavbar-IEIHC5Vt.js.map +1 -0
- package/dist/components/GrNavbar/index.js +2 -2
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-BI6-vkcs.js → GrNumberInput-CmnUDaKi.js} +2 -22
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-BI6-vkcs.js.map → GrNumberInput-CmnUDaKi.js.map} +1 -1
- package/dist/components/GrNumberInput/index.js +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-CMrLPzvL.js → GrPagination-AMbn5D8I.js} +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-CMrLPzvL.js.map → GrPagination-AMbn5D8I.js.map} +1 -1
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrPopover/chunks/{GrPopover-CcWvr1ZA.js → GrPopover-DjVxt8bf.js} +3 -3
- package/dist/components/GrPopover/chunks/{GrPopover-CcWvr1ZA.js.map → GrPopover-DjVxt8bf.js.map} +1 -1
- package/dist/components/GrPopover/index.js +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-BEL-QgwE.js → GrPromptDialog-BhbIZP1f.js} +3 -3
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-BEL-QgwE.js.map → GrPromptDialog-BhbIZP1f.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-DvSq-nV-.js → GrSelect-B-ykrHpL.js} +24 -180
- package/dist/components/GrSelect/chunks/GrSelect-B-ykrHpL.js.map +1 -0
- package/dist/components/GrSelect/index.js +2 -2
- package/dist/components/GrSelect/styles.css +1 -1
- package/dist/components/GrSidebar/chunks/GrSidebar-jVvoLGDO.js +543 -0
- package/dist/components/GrSidebar/chunks/GrSidebar-jVvoLGDO.js.map +1 -0
- package/dist/components/GrSidebar/index.js +2 -2
- package/dist/components/GrSlider/chunks/{GrSlider-CO8N9SF6.js → GrSlider-DF2Cq0rV.js} +29 -4
- package/dist/components/GrSlider/chunks/GrSlider-DF2Cq0rV.js.map +1 -0
- package/dist/components/GrSlider/index.js +2 -2
- package/dist/components/GrSortableList/chunks/{GrSortableList-NKriuGm_.js → GrSortableList-ChH9nQBv.js} +1 -1
- package/dist/components/GrSortableList/chunks/{GrSortableList-NKriuGm_.js.map → GrSortableList-ChH9nQBv.js.map} +1 -1
- package/dist/components/GrSortableList/index.js +2 -2
- package/dist/components/GrTabPanels/chunks/GrTabPanels-BDt5oVdd.js +30 -0
- package/dist/components/GrTabPanels/chunks/GrTabPanels-BDt5oVdd.js.map +1 -0
- package/dist/components/GrTabPanels/chunks/GrTabPanels-BJvAflnA.js +83 -0
- package/dist/components/GrTabPanels/chunks/GrTabPanels-BJvAflnA.js.map +1 -0
- package/dist/components/GrTabPanels/index.js +2 -1
- package/dist/components/GrTable/chunks/{GrTable-CKJ-HrsN.js → GrTable-BQitSDep.js} +52 -16
- package/dist/components/GrTable/chunks/GrTable-BQitSDep.js.map +1 -0
- package/dist/components/GrTable/index.js +2 -2
- package/dist/components/GrTabs/chunks/{GrTabs-s9RzC1WX.js → GrTabs-DVC6nfWB.js} +1 -1
- package/dist/components/GrTabs/chunks/{GrTabs-s9RzC1WX.js.map → GrTabs-DVC6nfWB.js.map} +1 -1
- package/dist/components/GrTabs/index.js +2 -2
- package/dist/components/GrTabsWithPanels/chunks/GrTabsWithPanels-BYtuzouj.js +98 -0
- package/dist/components/GrTabsWithPanels/chunks/GrTabsWithPanels-BYtuzouj.js.map +1 -0
- package/dist/components/GrTabsWithPanels/index.js +3 -0
- package/dist/components/GrTextarea/chunks/{GrTextarea-Bfgc0DD2.js → GrTextarea-BFwyqcHB.js} +22 -5
- package/dist/components/GrTextarea/chunks/GrTextarea-BFwyqcHB.js.map +1 -0
- package/dist/components/GrTextarea/index.js +1 -1
- package/dist/components/GrToaster/chunks/{GrToaster-CZixNk0l.js → GrToaster-OzT1VKcp.js} +163 -32
- package/dist/components/GrToaster/chunks/GrToaster-OzT1VKcp.js.map +1 -0
- package/dist/components/GrToaster/index.js +2 -2
- package/dist/components/GrToaster/styles.css +1 -1
- package/dist/components/GrTooltip/chunks/{GrTooltip-DkMMM0uq.js → GrTooltip-BDcFycvW.js} +26 -13
- package/dist/components/GrTooltip/chunks/GrTooltip-BDcFycvW.js.map +1 -0
- package/dist/components/GrTooltip/index.js +2 -2
- package/dist/components/GrTransfer/chunks/{GrTransfer-DdfBUf35.js → GrTransfer-CV7BL5UK.js} +114 -12
- package/dist/components/GrTransfer/chunks/GrTransfer-CV7BL5UK.js.map +1 -0
- package/dist/components/GrTransfer/index.js +1 -1
- package/dist/components/GrTransfer/styles.css +1 -1
- package/dist/components/GrTree/chunks/{GrTree-6yZrncJE.js → GrTree-DpgaP8hk.js} +39 -1
- package/dist/components/GrTree/chunks/GrTree-DpgaP8hk.js.map +1 -0
- package/dist/components/GrTree/index.js +1 -1
- package/dist/components/GrTree/styles.css +1 -1
- package/dist/components/GrTreeSections/chunks/{GrTreeSections-DcmDcLwj.js → GrTreeSections-BeOj__44.js} +1 -1
- package/dist/components/GrTreeSections/chunks/{GrTreeSections-DcmDcLwj.js.map → GrTreeSections-BeOj__44.js.map} +1 -1
- package/dist/components/GrTreeSections/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-B6KoDYiZ.js → GrTreeSelect-BHq-T8wH.js} +113 -14
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-BHq-T8wH.js.map +1 -0
- package/dist/components/GrTreeSelect/index.js +2 -2
- package/dist/composables/useFloating.js +1 -1
- package/dist/composables/useHotkeys.js +89 -0
- package/dist/composables/useHotkeys.js.map +1 -0
- package/dist/composables/useOverlayLayer.js +1 -1
- package/dist/directives/hotkey.js +19 -46
- package/dist/directives/hotkey.js.map +1 -1
- package/dist/granular-provider-node.js +1 -1
- package/dist/granular-provider.js +1 -1
- package/dist/i18n/all.js +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/index.js +71 -63
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrAutocomplete/GrAutocomplete.vue.d.ts +4 -4
- package/dist/types/components/GrAutocomplete/composables/useAutocompleteNavigation.d.ts +12 -3
- package/dist/types/components/GrAutocomplete/composables/useAutocompletePanel.d.ts +12 -2
- package/dist/types/components/GrAutocomplete/composables/useAutocompleteValues.d.ts +7 -4
- package/dist/types/components/GrAutocomplete/composables/useRemoteOptions.d.ts +4 -4
- package/dist/types/components/GrAutocomplete/grAutocompleteStyles.d.ts +15 -1
- package/dist/types/components/GrAutocomplete/index.d.ts +1 -1
- package/dist/types/components/GrCarousel/GrCarousel.vue.d.ts +29 -1
- package/dist/types/components/GrCarousel/composables/useCarouselSwipe.d.ts +5 -0
- package/dist/types/components/GrCarousel/grCarouselContext.d.ts +21 -0
- package/dist/types/components/GrCarousel/grCarouselStyles.d.ts +39 -9
- package/dist/types/components/GrCheckbox/GrCheckbox.vue.d.ts +13 -0
- package/dist/types/components/GrColorPicker/GrColorPicker.vue.d.ts +18 -2
- package/dist/types/components/GrColorPicker/color.d.ts +13 -1
- package/dist/types/components/GrColorPicker/colorArea.d.ts +59 -0
- package/dist/types/components/GrColorPicker/defaults.d.ts +3 -1
- package/dist/types/components/GrColorPicker/eyeDropper.d.ts +21 -0
- package/dist/types/components/GrColorPicker/grColorPickerStyles.d.ts +42 -0
- package/dist/types/components/GrCommandPalette/GrCommandPalette.vue.d.ts +10 -1
- package/dist/types/components/GrDataTable/GrDataTable.vue.d.ts +41 -1
- package/dist/types/components/GrDataTable/composables/useDataTableGridNavigation.d.ts +53 -0
- package/dist/types/components/GrDataTable/grDataTableStyles.d.ts +8 -0
- package/dist/types/components/GrDataTable/index.d.ts +1 -1
- package/dist/types/components/GrDelta/grDeltaStyles.d.ts +10 -3
- package/dist/types/components/GrEmptyState/GrEmptyState.vue.d.ts +11 -2
- package/dist/types/components/GrEmptyState/grEmptyStateStyles.d.ts +10 -0
- package/dist/types/components/GrEmptyState/index.d.ts +1 -1
- package/dist/types/components/GrFileUpload/GrFileUpload.vue.d.ts +2 -1
- package/dist/types/components/GrFormFile/GrFormFile.vue.d.ts +10 -0
- package/dist/types/components/GrInput/GrInput.vue.d.ts +15 -0
- package/dist/types/components/GrInputTag/GrInputTag.vue.d.ts +16 -0
- package/dist/types/components/GrInputTag/grInputTagStyles.d.ts +11 -0
- package/dist/types/components/GrKbd/GrKbd.vue.d.ts +14 -0
- package/dist/types/components/GrNavbar/GrNavbar.vue.d.ts +18 -0
- package/dist/types/components/GrNavbar/grNavbarStyles.d.ts +10 -1
- package/dist/types/components/GrNavbar/index.d.ts +1 -0
- package/dist/types/components/GrSelect/composables/useSelectPanelItems.d.ts +8 -34
- package/dist/types/components/GrSelect/composables/useSelectVirtualization.d.ts +7 -0
- package/dist/types/components/GrSelect/grSelectStyles.d.ts +1 -1
- package/dist/types/components/GrSidebar/GrSidebar.vue.d.ts +20 -0
- package/dist/types/components/GrSidebar/GrSidebarItem.vue.d.ts +39 -5
- package/dist/types/components/GrSidebar/grSidebarStyles.d.ts +32 -0
- package/dist/types/components/GrSidebar/index.d.ts +1 -1
- package/dist/types/components/GrSidebar/sidebarContext.d.ts +23 -1
- package/dist/types/components/GrSlider/GrSlider.vue.d.ts +39 -1
- package/dist/types/components/GrSlider/grSliderStyles.d.ts +1 -1
- package/dist/types/components/GrTabPanels/context.d.ts +6 -0
- package/dist/types/components/GrTable/GrTable.vue.d.ts +37 -0
- package/dist/types/components/GrTable/grTableStyles.d.ts +16 -7
- package/dist/types/components/GrTabsWithPanels/GrTabsWithPanels.vue.d.ts +77 -0
- package/dist/types/components/GrTabsWithPanels/config.d.ts +1 -0
- package/dist/types/components/GrTabsWithPanels/index.d.ts +4 -0
- package/dist/types/components/GrTextarea/GrTextarea.vue.d.ts +19 -1
- package/dist/types/components/GrToaster/GrToaster.vue.d.ts +12 -0
- package/dist/types/components/GrToaster/grToasterStyles.d.ts +26 -0
- package/dist/types/components/GrTooltip/GrTooltip.vue.d.ts +22 -0
- package/dist/types/components/GrTransfer/GrTransfer.vue.d.ts +23 -0
- package/dist/types/components/GrTreeSelect/grTreeSelectStyles.d.ts +1 -1
- package/dist/types/components/GrTreeSelect/grTreeSelectTypes.d.ts +20 -0
- package/dist/types/components/shared/hotkey.d.ts +8 -9
- package/dist/types/components/shared/optionPanel.d.ts +100 -0
- package/dist/types/components/shared/overlayListPanel.d.ts +18 -0
- package/dist/types/composables/useHotkeys.d.ts +43 -0
- package/dist/types/granular-provider/shared.d.ts +1 -0
- package/dist/types/i18n/messages/en.d.ts +28 -0
- package/dist/types/i18n/messages/es.d.ts +28 -0
- package/dist/types/i18n/messages/ru.d.ts +28 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/internal/hotkeySequence.d.ts +22 -0
- package/dist/types/internal/keyboard.d.ts +45 -0
- package/dist/web-types.json +463 -26
- package/package.json +9 -1
- package/dist/chunks/config-2ZHRsdOc.js.map +0 -1
- package/dist/chunks/config-B42iJ48O.js.map +0 -1
- package/dist/chunks/config-B5SBUa6u.js.map +0 -1
- package/dist/chunks/config-BAse0laT.js.map +0 -1
- package/dist/chunks/config-BHLMTJaR.js.map +0 -1
- package/dist/chunks/config-BXRmsn3t.js.map +0 -1
- package/dist/chunks/config-Bs1HwUcx.js.map +0 -1
- package/dist/chunks/config-CMNfZO7S.js.map +0 -1
- package/dist/chunks/config-CbEbLyDE.js.map +0 -1
- package/dist/chunks/config-CjWOTgLL.js.map +0 -1
- package/dist/chunks/config-D3HOuHDe.js.map +0 -1
- package/dist/chunks/config-DIDz_E5m.js.map +0 -1
- package/dist/chunks/config-DNDgp9bB.js.map +0 -1
- package/dist/chunks/config-iemgzS4x.js.map +0 -1
- package/dist/chunks/en-CsQUoBS2.js.map +0 -1
- package/dist/chunks/es-9nbkPvdA.js.map +0 -1
- package/dist/chunks/grSelectStyles-D9EQ-pgK.js.map +0 -1
- package/dist/chunks/grSliderStyles-kaz6VXZ3.js.map +0 -1
- package/dist/chunks/grTableStyles-DkQPMfx9.js +0 -39
- package/dist/chunks/grTableStyles-DkQPMfx9.js.map +0 -1
- package/dist/chunks/granular-provider-B6xomapW.js.map +0 -1
- package/dist/chunks/keyboard-DE2yhz_b.js.map +0 -1
- package/dist/chunks/overlayPanelSurface-BKeMhRx4.js.map +0 -1
- package/dist/chunks/ru-88i1WG0b.js.map +0 -1
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-Chhw65pI.js.map +0 -1
- package/dist/components/GrCarousel/chunks/GrCarousel-ByuughtI.js.map +0 -1
- package/dist/components/GrCheckbox/chunks/GrCheckbox-Dp201frb.js.map +0 -1
- package/dist/components/GrColorPicker/chunks/GrColorPicker-BRM_4lyI.js.map +0 -1
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BJHmkDQv.js.map +0 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-DQxYYgQk.js.map +0 -1
- package/dist/components/GrDropdownMenu/chunks/GrDropdownMenu-Dxkz_ry4.js.map +0 -1
- package/dist/components/GrEmptyState/chunks/GrEmptyState-BbvwE-E0.js.map +0 -1
- package/dist/components/GrFileUpload/chunks/GrFileUpload-BwMR71zN.js.map +0 -1
- package/dist/components/GrFormFile/chunks/GrFormFile-Dlxx-9zi.js.map +0 -1
- package/dist/components/GrInput/chunks/GrInput-DV4gMWPR.js.map +0 -1
- package/dist/components/GrInputTag/chunks/GrInputTag-qnRdAZnn.js.map +0 -1
- package/dist/components/GrKbd/chunks/GrKbd-BNMeoaLV.js.map +0 -1
- package/dist/components/GrNavbar/chunks/GrNavbar-BRGuksiy.js.map +0 -1
- package/dist/components/GrSelect/chunks/GrSelect-DvSq-nV-.js.map +0 -1
- package/dist/components/GrSidebar/chunks/GrSidebar-DoKLlHSw.js +0 -264
- package/dist/components/GrSidebar/chunks/GrSidebar-DoKLlHSw.js.map +0 -1
- package/dist/components/GrSlider/chunks/GrSlider-CO8N9SF6.js.map +0 -1
- package/dist/components/GrTabPanels/chunks/GrTabPanels-5zsbqS1v.js +0 -92
- package/dist/components/GrTabPanels/chunks/GrTabPanels-5zsbqS1v.js.map +0 -1
- package/dist/components/GrTable/chunks/GrTable-CKJ-HrsN.js.map +0 -1
- package/dist/components/GrTextarea/chunks/GrTextarea-Bfgc0DD2.js.map +0 -1
- package/dist/components/GrToaster/chunks/GrToaster-CZixNk0l.js.map +0 -1
- package/dist/components/GrTooltip/chunks/GrTooltip-DkMMM0uq.js.map +0 -1
- package/dist/components/GrTransfer/chunks/GrTransfer-DdfBUf35.js.map +0 -1
- package/dist/components/GrTree/chunks/GrTree-6yZrncJE.js.map +0 -1
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-B6KoDYiZ.js.map +0 -1
- package/dist/types/components/GrAutocomplete/composables/useAutocompleteVirtual.d.ts +0 -33
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrSidebar-jVvoLGDO.js","names":["$attrs","$slots"],"sources":["../../../../src/components/GrSidebar/sidebarContext.ts","../../../../src/components/GrSidebar/GrSidebar.vue","../../../../src/components/GrSidebar/GrSidebar.vue","../../../../src/components/GrSidebar/GrSidebarItem.vue","../../../../src/components/GrSidebar/GrSidebarItem.vue","../../../../src/components/GrSidebar/GrSidebarGroup.vue","../../../../src/components/GrSidebar/GrSidebarGroup.vue"],"sourcesContent":["import type { ComputedRef, InjectionKey, Ref } from 'vue'\n\nimport type { GrSidebarPosition } from './grSidebarStyles'\n\n/**\n * Контекст `GrSidebar`, который потребляют `GrSidebarItem`'ы: свёрнута ли панель.\n * В свёрнутом состоянии пункты показывают только иконку (или первую букву метки).\n */\nexport interface GrSidebarContext {\n collapsed: Ref<boolean>\n /**\n * Сторона панели. Пункту она нужна ради подсказки в свёрнутом режиме: та\n * обязана уходить **от** панели, иначе накроет саму себя.\n */\n position: ComputedRef<GrSidebarPosition> | Ref<GrSidebarPosition>\n /**\n * Развернуть панель. Нужна пункту с подпунктами: в свёрнутом рейле ветке\n * негде показаться, и нажатие на неё обязано сперва вернуть панели ширину —\n * иначе это кнопка, от которой ничего не происходит.\n *\n * Отдельным методом, а не записью в `collapsed`: состояние может быть\n * контролируемым, и без эмита родитель разошёлся бы с панелью.\n */\n expand: () => void\n}\n\nexport const GR_SIDEBAR_KEY: InjectionKey<GrSidebarContext> = Symbol.for('@feugene/granularity/sidebar')\n\n/**\n * Уровень вложенности пункта. Раздаётся не панелью, а самими пунктами: каждый\n * объявляет своим детям `level + 1`, поэтому глубина считается разметкой, а не\n * пропом, который потребителю пришлось бы проставлять руками и держать в\n * синхроне при перестановке ветки.\n */\nexport const GR_SIDEBAR_LEVEL_KEY: InjectionKey<number> = Symbol.for('@feugene/granularity/sidebar-level')\n","<script setup lang=\"ts\">\n/**\n * GrSidebar — боковая панель приложения с опциональным заголовком, кнопкой\n * сворачивания и слотом контента (обычно — список `GrSidebarItem`).\n *\n * - Свёрнутое состояние (`collapsed`, поддерживает `v-model:collapsed`) сужает\n * панель до иконочной ширины; `GrSidebarItem`'ы через inject показывают только\n * иконку (или первую букву метки).\n * - Кнопка тогла (`show-toggle-button`) переключает состояние.\n * - Хедер рендерится только если есть `title`/`subtitle` (или кнопка тогла) —\n * иначе панель начинается сразу с контента.\n * - Слоты `#title` / `#subtitle` имеют приоритет над одноимёнными пропами.\n */\nimport { computed, provide, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconChevronLeft from '~icons/lucide/chevron-left'\nimport IconChevronRight from '~icons/lucide/chevron-right'\nimport { useModalOverlay } from '../../composables/internal/useModalOverlay'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport IconClose from '~icons/lucide/x'\n\nimport {\n contentBase,\n grSidebarCollapseDirection,\n grSidebarRootClass,\n headerBase,\n layerBackdropClass,\n layerPanelEnterFrom,\n layerPanelPositions,\n layerPassThroughClass,\n layerRootClass,\n subtitleClass,\n titleClass,\n type GrSidebarLandmark,\n type GrSidebarPosition,\n} from './grSidebarStyles'\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nexport type { GrSidebarLandmark, GrSidebarPosition } from './grSidebarStyles'\n\nexport interface GrSidebarProps {\n title?: string\n subtitle?: string\n /** Свёрнутое состояние. Поддерживает `v-model:collapsed`. */\n collapsed?: boolean\n /** Показать кнопку сворачивания/разворачивания в хедере. */\n showToggleButton?: boolean\n /** Ширина в развёрнутом состоянии. */\n width?: string\n /** Ширина в свёрнутом состоянии. */\n collapsedWidth?: string\n /** A11y-лейбл кнопки тогла. Не задан — берётся из локали (`gr.sidebar.*`). */\n toggleLabel?: string\n /** Сторона экрана: граница и направление шеврона зеркалятся. */\n position?: GrSidebarPosition\n /**\n * Лендмарк корня. `complementary` (по умолчанию) — `<aside>`; `navigation` —\n * `<nav>` для панели, которая действительно является навигацией. Вложенный\n * `<nav>` внутрь `<aside>` не заводим: два лендмарка на одну панель засоряют\n * обзор, а панель фильтров навигацией не является вовсе.\n */\n landmark?: GrSidebarLandmark\n /** Имя лендмарка: без него две панели на странице неразличимы. */\n ariaLabel?: string\n /**\n * Панель как модальный слой поверх страницы: подложка, `Esc`, ловушка фокуса,\n * блокировка прокрутки. Для узкого экрана, где колонке места нет.\n *\n * Когда включать — решает приложение: своей системы брейкпоинтов у пакета нет,\n * а спрашивать среду в `setup` нельзя — первый клиентский рендер разошёлся бы\n * с серверным и сломал гидрацию.\n */\n overlay?: boolean\n /** Открыт ли слой. Поддерживает `v-model:open`; без `overlay` не значит ничего. */\n open?: boolean\n}\n\nexport interface GrSidebarEmits {\n (e: 'update:collapsed', value: boolean): void\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrSidebarProps>(), {\n title: undefined,\n subtitle: undefined,\n collapsed: false,\n showToggleButton: false,\n width: '240px',\n collapsedWidth: '64px',\n toggleLabel: undefined,\n position: 'left',\n landmark: 'complementary',\n ariaLabel: undefined,\n overlay: false,\n open: false,\n})\n\nconst { t } = useGranularityTranslations()\n\ndefineOptions({\n // Корень компонента — обёртка слоя, а она в обычном режиме `display: contents`\n // и из раскладки исчезает: класс потребителя, севший на неё, не сделал бы\n // ничего. Атрибуты уходят на саму панель явным `v-bind`.\n inheritAttrs: false,\n})\n\nconst emit = defineEmits<GrSidebarEmits>()\n\n// Локальное состояние с синхронизацией из пропа — поддерживает и controlled\n// (`v-model:collapsed`), и uncontrolled (панель сама помнит состояние).\nconst collapsedState = ref(props.collapsed)\nwatch(() => props.collapsed, value => (collapsedState.value = value))\n\nfunction toggle(): void {\n collapsedState.value = !collapsedState.value\n emit('update:collapsed', collapsedState.value)\n}\n\nfunction expand(): void {\n if (!collapsedState.value)\n return\n\n collapsedState.value = false\n emit('update:collapsed', false)\n}\n\n// ————— Модальный слой (узкий экран).\n\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst openState = ref(props.open)\nwatch(() => props.open, value => (openState.value = value))\n\nfunction closeOverlay(): void {\n openState.value = false\n emit('update:open', false)\n}\n\n/**\n * Слой активен только вместе с `overlay`: композабл зовётся всегда — условным\n * его сделать нельзя, — но при выключенном режиме открытым не бывает никогда, и\n * ни портал, ни ловушка фокуса не включаются.\n */\nconst layerOpen = computed(() => props.overlay && openState.value)\n\nconst {\n rootEl,\n isMounted,\n isVisible,\n inertAttr,\n layerZIndex,\n portalTarget,\n teleportEnabled,\n themeAttrs,\n backdrop,\n onPanelAfterLeave,\n} = useModalOverlay(layerOpen, closeOverlay, { panel: panelEl })\n\n/**\n * Портал включается только под слой. В обычном режиме панель обязана остаться\n * там, где её поставил потребитель: она часть его раскладки, а не всплывающий\n * элемент.\n *\n * До монтирования слой не рисуется вовсе — ни панели, ни подложки. Иначе первый\n * клиентский рендер показал бы модальную панель прямо в потоке страницы: телепорт\n * включается только после монтирования, чтобы совпасть с серверным рендером.\n *\n * `isMounted` здесь обязателен: корень слоя растянут на весь экран, и оставь мы\n * его при закрытой панели, страница получила бы невидимую плёнку, перехватывающую\n * все клики.\n */\nconst layerActive = computed(() => props.overlay && teleportEnabled.value && isMounted.value)\n\n/**\n * В слое панель показывается целиком: рейл шириной в иконку внутри модального\n * окна — это половина экрана, потраченная на пустоту. Поэтому свёрнутость там\n * игнорируется.\n */\nconst effectiveCollapsed = computed(() => collapsedState.value && !props.overlay)\n\n/**\n * Кнопка в шапке меняет смысл вместе с режимом: в колонке она сворачивает\n * панель, в слое — закрывает его. Сворачивать там нечего, а закрыть иначе можно\n * только `Esc` или подложкой.\n */\nfunction onHeaderButton(): void {\n if (props.overlay)\n closeOverlay()\n else toggle()\n}\n\n// Даём `GrSidebarItem`'ам знать про свёрнутость.\nprovide(GR_SIDEBAR_KEY, {\n collapsed: effectiveCollapsed,\n position: computed(() => props.position),\n expand,\n})\n\nconst hasTitle = computed(() => Boolean(props.title))\nconst hasSubtitle = computed(() => Boolean(props.subtitle))\nconst showHeader = computed(() => Boolean(hasTitle.value || hasSubtitle.value || props.showToggleButton))\n\nconst asideStyle = computed(() => ({\n width: collapsedState.value ? props.collapsedWidth : props.width,\n}))\n\nconst resolvedToggleLabel = computed(() => props.toggleLabel ?? (collapsedState.value\n ? t('gr.sidebar.expand', 'Expand sidebar')\n : t('gr.sidebar.collapse', 'Collapse sidebar')))\n\nconst rootTag = computed(() => (props.landmark === 'navigation' ? 'nav' : 'aside'))\nconst rootClass = computed(() => grSidebarRootClass(props.position))\n\nconst collapseDirection = computed(() => grSidebarCollapseDirection(props.position, collapsedState.value))\nconst collapseIcon = computed(() => (collapseDirection.value === 'right' ? IconChevronRight : IconChevronLeft))\n\ndefineSlots<{\n /** Содержимое панели: навигация, группы, произвольная разметка. */\n default?: () => any\n /** Заголовок шапки вместо пропа `title`. */\n title?: () => any\n /** Подзаголовок под заголовком. */\n subtitle?: () => any\n}>()\n</script>\n\n<template>\n <teleport :to=\"portalTarget\" :disabled=\"!layerActive\">\n <!--\n Обёртка слоя рисуется всегда, но вне режима `overlay` она `display:\n contents` и из раскладки исчезает. Так панель со своей шапкой описана один\n раз на оба режима: продублируй мы её, две копии разошлись бы молча.\n -->\n <div\n ref=\"rootEl\"\n :data-gr-sidebar-layer=\"layerActive ? '' : undefined\"\n :data-gr-overlay-root=\"layerActive ? '' : undefined\"\n :role=\"layerActive ? 'dialog' : undefined\"\n :aria-modal=\"layerActive ? 'true' : undefined\"\n :aria-label=\"layerActive ? ariaLabel : undefined\"\n :class=\"layerActive ? layerRootClass : layerPassThroughClass\"\n :style=\"layerActive ? { zIndex: layerZIndex } : undefined\"\n :inert=\"inertAttr\"\n >\n <Transition\n enter-active-class=\"transition-opacity duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-[var(--gr-duration-fast)] ease-[var(--gr-ease-in)]\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"layerActive && isVisible\"\n data-gr-sidebar-backdrop\n :class=\"layerBackdropClass\"\n aria-hidden=\"true\"\n v-on=\"backdrop\"\n />\n </Transition>\n\n <Transition\n enter-active-class=\"transition-transform duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n :enter-from-class=\"layerPanelEnterFrom[position]\"\n leave-active-class=\"transition-transform duration-[var(--gr-duration-fast)] ease-[var(--gr-ease-in)]\"\n :leave-to-class=\"layerPanelEnterFrom[position]\"\n @after-leave=\"onPanelAfterLeave\"\n >\n <component\n :is=\"rootTag\"\n v-if=\"overlay ? (layerActive && isVisible) : true\"\n ref=\"panelEl\"\n v-bind=\"{ ...$attrs, ...(layerActive ? themeAttrs : {}) }\"\n data-gr-sidebar\n :data-collapsed=\"effectiveCollapsed ? 'true' : undefined\"\n :data-position=\"position\"\n :aria-label=\"ariaLabel\"\n :class=\"[rootClass, layerActive ? layerPanelPositions[position] : '']\"\n :style=\"asideStyle\"\n >\n <div\n v-if=\"showHeader\"\n data-gr-sidebar-header\n :class=\"[headerBase, collapsedState ? 'justify-center' : 'justify-between']\"\n >\n <div\n v-if=\"!collapsedState && (hasTitle || hasSubtitle || $slots.title || $slots.subtitle)\"\n class=\"min-w-0\"\n >\n <div\n v-if=\"$slots.subtitle || hasSubtitle\"\n data-gr-sidebar-subtitle\n :class=\"subtitleClass\"\n >\n <slot name=\"subtitle\">\n {{ subtitle }}\n </slot>\n </div>\n <div\n v-if=\"$slots.title || hasTitle\"\n data-gr-sidebar-title\n :class=\"titleClass\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <GrButton\n v-if=\"showToggleButton\"\n data-gr-sidebar-toggle\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedToggleLabel\"\n :aria-expanded=\"overlay ? undefined : (collapsedState ? 'false' : 'true')\"\n :data-direction=\"overlay ? undefined : collapseDirection\"\n @click=\"onHeaderButton\"\n >\n <GrIcon :size=\"16\">\n <component :is=\"overlay ? IconClose : collapseIcon\" />\n </GrIcon>\n </GrButton>\n </div>\n\n <div\n data-gr-sidebar-content\n tabindex=\"0\"\n :class=\"[contentBase, effectiveCollapsed ? 'p-2' : 'p-3']\"\n >\n <slot />\n </div>\n </component>\n </Transition>\n </div>\n </teleport>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebar — боковая панель приложения с опциональным заголовком, кнопкой\n * сворачивания и слотом контента (обычно — список `GrSidebarItem`).\n *\n * - Свёрнутое состояние (`collapsed`, поддерживает `v-model:collapsed`) сужает\n * панель до иконочной ширины; `GrSidebarItem`'ы через inject показывают только\n * иконку (или первую букву метки).\n * - Кнопка тогла (`show-toggle-button`) переключает состояние.\n * - Хедер рендерится только если есть `title`/`subtitle` (или кнопка тогла) —\n * иначе панель начинается сразу с контента.\n * - Слоты `#title` / `#subtitle` имеют приоритет над одноимёнными пропами.\n */\nimport { computed, provide, ref, watch } from 'vue'\n\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrIcon from '../GrIcon/GrIcon.vue'\nimport IconChevronLeft from '~icons/lucide/chevron-left'\nimport IconChevronRight from '~icons/lucide/chevron-right'\nimport { useModalOverlay } from '../../composables/internal/useModalOverlay'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport IconClose from '~icons/lucide/x'\n\nimport {\n contentBase,\n grSidebarCollapseDirection,\n grSidebarRootClass,\n headerBase,\n layerBackdropClass,\n layerPanelEnterFrom,\n layerPanelPositions,\n layerPassThroughClass,\n layerRootClass,\n subtitleClass,\n titleClass,\n type GrSidebarLandmark,\n type GrSidebarPosition,\n} from './grSidebarStyles'\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nexport type { GrSidebarLandmark, GrSidebarPosition } from './grSidebarStyles'\n\nexport interface GrSidebarProps {\n title?: string\n subtitle?: string\n /** Свёрнутое состояние. Поддерживает `v-model:collapsed`. */\n collapsed?: boolean\n /** Показать кнопку сворачивания/разворачивания в хедере. */\n showToggleButton?: boolean\n /** Ширина в развёрнутом состоянии. */\n width?: string\n /** Ширина в свёрнутом состоянии. */\n collapsedWidth?: string\n /** A11y-лейбл кнопки тогла. Не задан — берётся из локали (`gr.sidebar.*`). */\n toggleLabel?: string\n /** Сторона экрана: граница и направление шеврона зеркалятся. */\n position?: GrSidebarPosition\n /**\n * Лендмарк корня. `complementary` (по умолчанию) — `<aside>`; `navigation` —\n * `<nav>` для панели, которая действительно является навигацией. Вложенный\n * `<nav>` внутрь `<aside>` не заводим: два лендмарка на одну панель засоряют\n * обзор, а панель фильтров навигацией не является вовсе.\n */\n landmark?: GrSidebarLandmark\n /** Имя лендмарка: без него две панели на странице неразличимы. */\n ariaLabel?: string\n /**\n * Панель как модальный слой поверх страницы: подложка, `Esc`, ловушка фокуса,\n * блокировка прокрутки. Для узкого экрана, где колонке места нет.\n *\n * Когда включать — решает приложение: своей системы брейкпоинтов у пакета нет,\n * а спрашивать среду в `setup` нельзя — первый клиентский рендер разошёлся бы\n * с серверным и сломал гидрацию.\n */\n overlay?: boolean\n /** Открыт ли слой. Поддерживает `v-model:open`; без `overlay` не значит ничего. */\n open?: boolean\n}\n\nexport interface GrSidebarEmits {\n (e: 'update:collapsed', value: boolean): void\n (e: 'update:open', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrSidebarProps>(), {\n title: undefined,\n subtitle: undefined,\n collapsed: false,\n showToggleButton: false,\n width: '240px',\n collapsedWidth: '64px',\n toggleLabel: undefined,\n position: 'left',\n landmark: 'complementary',\n ariaLabel: undefined,\n overlay: false,\n open: false,\n})\n\nconst { t } = useGranularityTranslations()\n\ndefineOptions({\n // Корень компонента — обёртка слоя, а она в обычном режиме `display: contents`\n // и из раскладки исчезает: класс потребителя, севший на неё, не сделал бы\n // ничего. Атрибуты уходят на саму панель явным `v-bind`.\n inheritAttrs: false,\n})\n\nconst emit = defineEmits<GrSidebarEmits>()\n\n// Локальное состояние с синхронизацией из пропа — поддерживает и controlled\n// (`v-model:collapsed`), и uncontrolled (панель сама помнит состояние).\nconst collapsedState = ref(props.collapsed)\nwatch(() => props.collapsed, value => (collapsedState.value = value))\n\nfunction toggle(): void {\n collapsedState.value = !collapsedState.value\n emit('update:collapsed', collapsedState.value)\n}\n\nfunction expand(): void {\n if (!collapsedState.value)\n return\n\n collapsedState.value = false\n emit('update:collapsed', false)\n}\n\n// ————— Модальный слой (узкий экран).\n\nconst panelEl = ref<HTMLElement | null>(null)\n\nconst openState = ref(props.open)\nwatch(() => props.open, value => (openState.value = value))\n\nfunction closeOverlay(): void {\n openState.value = false\n emit('update:open', false)\n}\n\n/**\n * Слой активен только вместе с `overlay`: композабл зовётся всегда — условным\n * его сделать нельзя, — но при выключенном режиме открытым не бывает никогда, и\n * ни портал, ни ловушка фокуса не включаются.\n */\nconst layerOpen = computed(() => props.overlay && openState.value)\n\nconst {\n rootEl,\n isMounted,\n isVisible,\n inertAttr,\n layerZIndex,\n portalTarget,\n teleportEnabled,\n themeAttrs,\n backdrop,\n onPanelAfterLeave,\n} = useModalOverlay(layerOpen, closeOverlay, { panel: panelEl })\n\n/**\n * Портал включается только под слой. В обычном режиме панель обязана остаться\n * там, где её поставил потребитель: она часть его раскладки, а не всплывающий\n * элемент.\n *\n * До монтирования слой не рисуется вовсе — ни панели, ни подложки. Иначе первый\n * клиентский рендер показал бы модальную панель прямо в потоке страницы: телепорт\n * включается только после монтирования, чтобы совпасть с серверным рендером.\n *\n * `isMounted` здесь обязателен: корень слоя растянут на весь экран, и оставь мы\n * его при закрытой панели, страница получила бы невидимую плёнку, перехватывающую\n * все клики.\n */\nconst layerActive = computed(() => props.overlay && teleportEnabled.value && isMounted.value)\n\n/**\n * В слое панель показывается целиком: рейл шириной в иконку внутри модального\n * окна — это половина экрана, потраченная на пустоту. Поэтому свёрнутость там\n * игнорируется.\n */\nconst effectiveCollapsed = computed(() => collapsedState.value && !props.overlay)\n\n/**\n * Кнопка в шапке меняет смысл вместе с режимом: в колонке она сворачивает\n * панель, в слое — закрывает его. Сворачивать там нечего, а закрыть иначе можно\n * только `Esc` или подложкой.\n */\nfunction onHeaderButton(): void {\n if (props.overlay)\n closeOverlay()\n else toggle()\n}\n\n// Даём `GrSidebarItem`'ам знать про свёрнутость.\nprovide(GR_SIDEBAR_KEY, {\n collapsed: effectiveCollapsed,\n position: computed(() => props.position),\n expand,\n})\n\nconst hasTitle = computed(() => Boolean(props.title))\nconst hasSubtitle = computed(() => Boolean(props.subtitle))\nconst showHeader = computed(() => Boolean(hasTitle.value || hasSubtitle.value || props.showToggleButton))\n\nconst asideStyle = computed(() => ({\n width: collapsedState.value ? props.collapsedWidth : props.width,\n}))\n\nconst resolvedToggleLabel = computed(() => props.toggleLabel ?? (collapsedState.value\n ? t('gr.sidebar.expand', 'Expand sidebar')\n : t('gr.sidebar.collapse', 'Collapse sidebar')))\n\nconst rootTag = computed(() => (props.landmark === 'navigation' ? 'nav' : 'aside'))\nconst rootClass = computed(() => grSidebarRootClass(props.position))\n\nconst collapseDirection = computed(() => grSidebarCollapseDirection(props.position, collapsedState.value))\nconst collapseIcon = computed(() => (collapseDirection.value === 'right' ? IconChevronRight : IconChevronLeft))\n\ndefineSlots<{\n /** Содержимое панели: навигация, группы, произвольная разметка. */\n default?: () => any\n /** Заголовок шапки вместо пропа `title`. */\n title?: () => any\n /** Подзаголовок под заголовком. */\n subtitle?: () => any\n}>()\n</script>\n\n<template>\n <teleport :to=\"portalTarget\" :disabled=\"!layerActive\">\n <!--\n Обёртка слоя рисуется всегда, но вне режима `overlay` она `display:\n contents` и из раскладки исчезает. Так панель со своей шапкой описана один\n раз на оба режима: продублируй мы её, две копии разошлись бы молча.\n -->\n <div\n ref=\"rootEl\"\n :data-gr-sidebar-layer=\"layerActive ? '' : undefined\"\n :data-gr-overlay-root=\"layerActive ? '' : undefined\"\n :role=\"layerActive ? 'dialog' : undefined\"\n :aria-modal=\"layerActive ? 'true' : undefined\"\n :aria-label=\"layerActive ? ariaLabel : undefined\"\n :class=\"layerActive ? layerRootClass : layerPassThroughClass\"\n :style=\"layerActive ? { zIndex: layerZIndex } : undefined\"\n :inert=\"inertAttr\"\n >\n <Transition\n enter-active-class=\"transition-opacity duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n enter-from-class=\"opacity-0\"\n leave-active-class=\"transition-opacity duration-[var(--gr-duration-fast)] ease-[var(--gr-ease-in)]\"\n leave-to-class=\"opacity-0\"\n >\n <div\n v-if=\"layerActive && isVisible\"\n data-gr-sidebar-backdrop\n :class=\"layerBackdropClass\"\n aria-hidden=\"true\"\n v-on=\"backdrop\"\n />\n </Transition>\n\n <Transition\n enter-active-class=\"transition-transform duration-[var(--gr-duration-base)] ease-[var(--gr-ease-out)]\"\n :enter-from-class=\"layerPanelEnterFrom[position]\"\n leave-active-class=\"transition-transform duration-[var(--gr-duration-fast)] ease-[var(--gr-ease-in)]\"\n :leave-to-class=\"layerPanelEnterFrom[position]\"\n @after-leave=\"onPanelAfterLeave\"\n >\n <component\n :is=\"rootTag\"\n v-if=\"overlay ? (layerActive && isVisible) : true\"\n ref=\"panelEl\"\n v-bind=\"{ ...$attrs, ...(layerActive ? themeAttrs : {}) }\"\n data-gr-sidebar\n :data-collapsed=\"effectiveCollapsed ? 'true' : undefined\"\n :data-position=\"position\"\n :aria-label=\"ariaLabel\"\n :class=\"[rootClass, layerActive ? layerPanelPositions[position] : '']\"\n :style=\"asideStyle\"\n >\n <div\n v-if=\"showHeader\"\n data-gr-sidebar-header\n :class=\"[headerBase, collapsedState ? 'justify-center' : 'justify-between']\"\n >\n <div\n v-if=\"!collapsedState && (hasTitle || hasSubtitle || $slots.title || $slots.subtitle)\"\n class=\"min-w-0\"\n >\n <div\n v-if=\"$slots.subtitle || hasSubtitle\"\n data-gr-sidebar-subtitle\n :class=\"subtitleClass\"\n >\n <slot name=\"subtitle\">\n {{ subtitle }}\n </slot>\n </div>\n <div\n v-if=\"$slots.title || hasTitle\"\n data-gr-sidebar-title\n :class=\"titleClass\"\n >\n <slot name=\"title\">\n {{ title }}\n </slot>\n </div>\n </div>\n\n <GrButton\n v-if=\"showToggleButton\"\n data-gr-sidebar-toggle\n variant=\"ghost\"\n size=\"sm\"\n square\n :aria-label=\"resolvedToggleLabel\"\n :aria-expanded=\"overlay ? undefined : (collapsedState ? 'false' : 'true')\"\n :data-direction=\"overlay ? undefined : collapseDirection\"\n @click=\"onHeaderButton\"\n >\n <GrIcon :size=\"16\">\n <component :is=\"overlay ? IconClose : collapseIcon\" />\n </GrIcon>\n </GrButton>\n </div>\n\n <div\n data-gr-sidebar-content\n tabindex=\"0\"\n :class=\"[contentBase, effectiveCollapsed ? 'p-2' : 'p-3']\"\n >\n <slot />\n </div>\n </component>\n </Transition>\n </div>\n </teleport>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebarItem — пункт навигации боковой панели `GrSidebar`.\n *\n * Контракт:\n * - развёрнутая панель — `[иконка] метка [бейдж]`;\n * - свёрнутая панель — только иконка; если иконки нет, показывается ПЕРВАЯ БУКВА\n * метки (uppercase). Имя пункту даёт `aria-label`, а подпись показывает\n * `GrTooltip`: нативный `title` появлялся только по наведению, то есть с\n * клавиатуры свёрнутую панель было не прочесть.\n *\n * Корневой тег: `as` → `<a href>` → `<button>` (в этом порядке).\n */\nimport { computed, inject, markRaw, provide, ref, useId, watch, type Component } from 'vue'\n\nimport GrTooltip from '../GrTooltip/GrTooltip.vue'\n\nimport IconChevronRight from '~icons/lucide/chevron-right'\n\nimport {\n grSidebarItemClass,\n itemBadgeClass,\n itemChevronClass,\n itemChevronExpandedClass,\n itemChildrenClass,\n itemLetterClass,\n itemNestClass,\n itemNestIndent,\n} from './grSidebarStyles'\nimport { GR_SIDEBAR_KEY, GR_SIDEBAR_LEVEL_KEY } from './sidebarContext'\n\nexport interface GrSidebarItemProps {\n label: string\n /** Иконка: класс UnoCSS-иконки (`'i-lucide-home'`) или Vue-компонент. */\n icon?: string | Component\n href?: string\n as?: string | Component\n active?: boolean\n disabled?: boolean\n badge?: string | number\n /**\n * Раскрыто ли поддерево. Поддерживает `v-model:expanded`; без пропа пункт\n * ведёт себя сам.\n */\n expanded?: boolean\n /** Начальное состояние поддерева, когда `expanded` не контролируется. */\n defaultExpanded?: boolean\n}\n\nexport interface GrSidebarItemEmits {\n (e: 'update:expanded', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrSidebarItemProps>(), {\n icon: undefined,\n href: undefined,\n as: undefined,\n active: false,\n disabled: false,\n badge: undefined,\n expanded: undefined,\n defaultExpanded: false,\n})\n\nconst emit = defineEmits<GrSidebarItemEmits>()\n\nconst slots = defineSlots<{\n /**\n * Вложенные пункты. Их наличие превращает пункт в раскрывающийся: он теряет\n * роль ссылки и становится переключателем поддерева.\n */\n default?: () => unknown\n}>()\n\nconst sidebar = inject(GR_SIDEBAR_KEY, null)\nconst collapsed = computed(() => sidebar?.collapsed.value ?? false)\n\n/**\n * Подсказка уходит **от** панели: у левой — вправо, у правой — влево. Иначе\n * она легла бы поверх самой панели и закрыла соседние пункты.\n */\nconst tooltipPlacement = computed(() => (sidebar?.position.value === 'right' ? 'left' : 'right'))\n\n/*\n * Обёртка тултипом появляется только в свёрнутом виде: в развёрнутом подпись\n * видна текстом, и лишний узел в разметке ни к чему.\n *\n * В шаблоне это `v-if`/`v-else`, и комментарию там не место: узел-комментарий\n * перед корнем делает компонент многокорневым, а у такого не работает проброс\n * атрибутов и `wrapper.element` указывает не туда.\n *\n * `describe-trigger=\"false\"` обязателен — имя пункту уже даёт `aria-label` с\n * той же подписью, и описание тем же текстом диктор прочитал бы дважды.\n * `block` — потому что пункт тянется на всю ширину рейла, а обёртка по\n * умолчанию `inline-flex` схлопнула бы его вместе с подсветкой и кольцом\n * фокуса.\n */\n\n// ————— Вложенность.\n\nconst level = inject(GR_SIDEBAR_LEVEL_KEY, 0)\nprovide(GR_SIDEBAR_LEVEL_KEY, level + 1)\n\nconst hasChildren = computed(() => Boolean(slots.default))\nconst childrenId = useId()\n\n/**\n * Раскрытие живёт локально, пока проп не задан: панель навигации чаще всего\n * управляет собой сама, и требовать `v-model` на каждую ветку значило бы\n * заставить потребителя держать дерево состояний ради поведения по умолчанию.\n */\nconst uncontrolledExpanded = ref(props.defaultExpanded)\nconst isExpanded = computed(() => props.expanded ?? uncontrolledExpanded.value)\n\nfunction toggleChildren(): void {\n const next = !isExpanded.value\n\n uncontrolledExpanded.value = next\n emit('update:expanded', next)\n}\n\n/**\n * Нажатие на ветку в свёрнутом рейле сперва возвращает панели ширину: показать\n * подпункты в шестидесяти четырёх пикселях негде, и без этого кнопка ничего бы\n * не делала.\n */\nfunction onCollapsedActivate(): void {\n if (!hasChildren.value)\n return\n\n sidebar?.expand()\n\n if (!isExpanded.value)\n toggleChildren()\n}\n\n/*\n * `href` вместе с подпунктами — противоречие: одно нажатие не может и увести на\n * страницу, и раскрыть ветку. Побеждают подпункты, потому что иначе раскрыть\n * ветку было бы нечем вовсе, — но молчать об этом нельзя: тихо снятая\n * навигация по поведению неотличима от забытой.\n */\nif (__GR_DEV__) {\n watch(\n () => hasChildren.value && (props.href !== undefined || props.as !== undefined),\n (conflict) => {\n if (conflict) {\n console.warn(\n `[GrSidebarItem] У пункта \"${props.label}\" есть подпункты, поэтому он стал переключателем ветки: `\n + '`href` и `as` не используются. Ссылкой сделайте отдельный подпункт.',\n )\n }\n },\n { immediate: true },\n )\n}\n\nconst isStringIcon = computed(() => typeof props.icon === 'string')\nconst iconComponent = computed(() => (props.icon && typeof props.icon !== 'string' ? markRaw(props.icon as Component) : null))\nconst firstLetter = computed(() => props.label.trim().charAt(0).toUpperCase() || '•')\n\nconst rootTag = computed<string | Component>(() => {\n if (props.disabled)\n return 'span'\n // Пункт с подпунктами — переключатель, а кнопка это и есть переключатель.\n if (hasChildren.value)\n return 'button'\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n return props.href ? 'a' : 'button'\n})\n\n/**\n * Компонент-ссылка (`Link` от Inertia, `RouterLink`) рендерит `<a>` сам, и без\n * `href` он ведёт в никуда. Строковый тег, кроме `a`, атрибут не понимает —\n * там он и гасится.\n */\nconst rootHref = computed(() => (\n typeof rootTag.value === 'string' && rootTag.value !== 'a' ? undefined : props.href\n))\n\nconst rootClass = computed(() => grSidebarItemClass({\n collapsed: collapsed.value,\n disabled: props.disabled,\n active: props.active,\n}))\n\n/**\n * Отступ уровня — в свёрнутом рейле его нет: там строка шириной с иконку, и\n * сдвигать её некуда.\n */\nconst rootStyle = computed(() => {\n const indent = collapsed.value ? undefined : itemNestIndent(level)\n\n return indent ? { paddingInlineStart: indent } : undefined\n})\n\n/**\n * Поддерево исчезает из DOM, а не прячется: невидимая ветка ловила бы `Tab` и\n * читалась бы диктором вопреки `aria-expanded=\"false\"`.\n *\n * Свёрнутость здесь не проверяется — её перехватывает `v-if=\"collapsed\"` раньше:\n * в рейле пункт рисуется иконкой с подсказкой, и до ветки дело не доходит.\n */\nconst showChildren = computed(() => hasChildren.value && isExpanded.value)\n</script>\n\n<template>\n <GrTooltip\n v-if=\"collapsed\"\n :text=\"label\"\n :placement=\"tooltipPlacement\"\n block\n :describe-trigger=\"false\"\n data-gr-sidebar-item-tooltip\n >\n <component\n :is=\"rootTag\"\n data-gr-sidebar-item\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootHref\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :aria-label=\"label\"\n :aria-expanded=\"hasChildren ? 'false' : undefined\"\n :class=\"rootClass\"\n @click=\"onCollapsedActivate\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n <span v-else :class=\"itemLetterClass\" aria-hidden=\"true\">{{ firstLetter }}</span>\n </span>\n </component>\n </GrTooltip>\n\n <div v-else-if=\"hasChildren\" data-gr-sidebar-item-nest :class=\"itemNestClass\">\n <button\n data-gr-sidebar-item\n type=\"button\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :aria-expanded=\"isExpanded ? 'true' : 'false'\"\n :aria-controls=\"showChildren ? childrenId : undefined\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n @click=\"toggleChildren\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n </span>\n\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ label }}</span>\n <span v-if=\"badge != null\" :class=\"itemBadgeClass\">{{ badge }}</span>\n\n <!-- Состояние ветки несёт `aria-expanded`; шеврон говорит то же самое глазам. -->\n <IconChevronRight\n aria-hidden=\"true\"\n :class=\"[itemChevronClass, isExpanded ? itemChevronExpandedClass : '']\"\n />\n </button>\n\n <div v-if=\"showChildren\" :id=\"childrenId\" data-gr-sidebar-item-children :class=\"itemChildrenClass\">\n <slot />\n </div>\n </div>\n\n <component\n :is=\"rootTag\"\n v-else\n data-gr-sidebar-item\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootHref\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n </span>\n\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ label }}</span>\n <span v-if=\"badge != null\" :class=\"itemBadgeClass\">{{ badge }}</span>\n </component>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebarItem — пункт навигации боковой панели `GrSidebar`.\n *\n * Контракт:\n * - развёрнутая панель — `[иконка] метка [бейдж]`;\n * - свёрнутая панель — только иконка; если иконки нет, показывается ПЕРВАЯ БУКВА\n * метки (uppercase). Имя пункту даёт `aria-label`, а подпись показывает\n * `GrTooltip`: нативный `title` появлялся только по наведению, то есть с\n * клавиатуры свёрнутую панель было не прочесть.\n *\n * Корневой тег: `as` → `<a href>` → `<button>` (в этом порядке).\n */\nimport { computed, inject, markRaw, provide, ref, useId, watch, type Component } from 'vue'\n\nimport GrTooltip from '../GrTooltip/GrTooltip.vue'\n\nimport IconChevronRight from '~icons/lucide/chevron-right'\n\nimport {\n grSidebarItemClass,\n itemBadgeClass,\n itemChevronClass,\n itemChevronExpandedClass,\n itemChildrenClass,\n itemLetterClass,\n itemNestClass,\n itemNestIndent,\n} from './grSidebarStyles'\nimport { GR_SIDEBAR_KEY, GR_SIDEBAR_LEVEL_KEY } from './sidebarContext'\n\nexport interface GrSidebarItemProps {\n label: string\n /** Иконка: класс UnoCSS-иконки (`'i-lucide-home'`) или Vue-компонент. */\n icon?: string | Component\n href?: string\n as?: string | Component\n active?: boolean\n disabled?: boolean\n badge?: string | number\n /**\n * Раскрыто ли поддерево. Поддерживает `v-model:expanded`; без пропа пункт\n * ведёт себя сам.\n */\n expanded?: boolean\n /** Начальное состояние поддерева, когда `expanded` не контролируется. */\n defaultExpanded?: boolean\n}\n\nexport interface GrSidebarItemEmits {\n (e: 'update:expanded', value: boolean): void\n}\n\nconst props = withDefaults(defineProps<GrSidebarItemProps>(), {\n icon: undefined,\n href: undefined,\n as: undefined,\n active: false,\n disabled: false,\n badge: undefined,\n expanded: undefined,\n defaultExpanded: false,\n})\n\nconst emit = defineEmits<GrSidebarItemEmits>()\n\nconst slots = defineSlots<{\n /**\n * Вложенные пункты. Их наличие превращает пункт в раскрывающийся: он теряет\n * роль ссылки и становится переключателем поддерева.\n */\n default?: () => unknown\n}>()\n\nconst sidebar = inject(GR_SIDEBAR_KEY, null)\nconst collapsed = computed(() => sidebar?.collapsed.value ?? false)\n\n/**\n * Подсказка уходит **от** панели: у левой — вправо, у правой — влево. Иначе\n * она легла бы поверх самой панели и закрыла соседние пункты.\n */\nconst tooltipPlacement = computed(() => (sidebar?.position.value === 'right' ? 'left' : 'right'))\n\n/*\n * Обёртка тултипом появляется только в свёрнутом виде: в развёрнутом подпись\n * видна текстом, и лишний узел в разметке ни к чему.\n *\n * В шаблоне это `v-if`/`v-else`, и комментарию там не место: узел-комментарий\n * перед корнем делает компонент многокорневым, а у такого не работает проброс\n * атрибутов и `wrapper.element` указывает не туда.\n *\n * `describe-trigger=\"false\"` обязателен — имя пункту уже даёт `aria-label` с\n * той же подписью, и описание тем же текстом диктор прочитал бы дважды.\n * `block` — потому что пункт тянется на всю ширину рейла, а обёртка по\n * умолчанию `inline-flex` схлопнула бы его вместе с подсветкой и кольцом\n * фокуса.\n */\n\n// ————— Вложенность.\n\nconst level = inject(GR_SIDEBAR_LEVEL_KEY, 0)\nprovide(GR_SIDEBAR_LEVEL_KEY, level + 1)\n\nconst hasChildren = computed(() => Boolean(slots.default))\nconst childrenId = useId()\n\n/**\n * Раскрытие живёт локально, пока проп не задан: панель навигации чаще всего\n * управляет собой сама, и требовать `v-model` на каждую ветку значило бы\n * заставить потребителя держать дерево состояний ради поведения по умолчанию.\n */\nconst uncontrolledExpanded = ref(props.defaultExpanded)\nconst isExpanded = computed(() => props.expanded ?? uncontrolledExpanded.value)\n\nfunction toggleChildren(): void {\n const next = !isExpanded.value\n\n uncontrolledExpanded.value = next\n emit('update:expanded', next)\n}\n\n/**\n * Нажатие на ветку в свёрнутом рейле сперва возвращает панели ширину: показать\n * подпункты в шестидесяти четырёх пикселях негде, и без этого кнопка ничего бы\n * не делала.\n */\nfunction onCollapsedActivate(): void {\n if (!hasChildren.value)\n return\n\n sidebar?.expand()\n\n if (!isExpanded.value)\n toggleChildren()\n}\n\n/*\n * `href` вместе с подпунктами — противоречие: одно нажатие не может и увести на\n * страницу, и раскрыть ветку. Побеждают подпункты, потому что иначе раскрыть\n * ветку было бы нечем вовсе, — но молчать об этом нельзя: тихо снятая\n * навигация по поведению неотличима от забытой.\n */\nif (__GR_DEV__) {\n watch(\n () => hasChildren.value && (props.href !== undefined || props.as !== undefined),\n (conflict) => {\n if (conflict) {\n console.warn(\n `[GrSidebarItem] У пункта \"${props.label}\" есть подпункты, поэтому он стал переключателем ветки: `\n + '`href` и `as` не используются. Ссылкой сделайте отдельный подпункт.',\n )\n }\n },\n { immediate: true },\n )\n}\n\nconst isStringIcon = computed(() => typeof props.icon === 'string')\nconst iconComponent = computed(() => (props.icon && typeof props.icon !== 'string' ? markRaw(props.icon as Component) : null))\nconst firstLetter = computed(() => props.label.trim().charAt(0).toUpperCase() || '•')\n\nconst rootTag = computed<string | Component>(() => {\n if (props.disabled)\n return 'span'\n // Пункт с подпунктами — переключатель, а кнопка это и есть переключатель.\n if (hasChildren.value)\n return 'button'\n if (props.as)\n return typeof props.as === 'string' ? props.as : markRaw(props.as)\n return props.href ? 'a' : 'button'\n})\n\n/**\n * Компонент-ссылка (`Link` от Inertia, `RouterLink`) рендерит `<a>` сам, и без\n * `href` он ведёт в никуда. Строковый тег, кроме `a`, атрибут не понимает —\n * там он и гасится.\n */\nconst rootHref = computed(() => (\n typeof rootTag.value === 'string' && rootTag.value !== 'a' ? undefined : props.href\n))\n\nconst rootClass = computed(() => grSidebarItemClass({\n collapsed: collapsed.value,\n disabled: props.disabled,\n active: props.active,\n}))\n\n/**\n * Отступ уровня — в свёрнутом рейле его нет: там строка шириной с иконку, и\n * сдвигать её некуда.\n */\nconst rootStyle = computed(() => {\n const indent = collapsed.value ? undefined : itemNestIndent(level)\n\n return indent ? { paddingInlineStart: indent } : undefined\n})\n\n/**\n * Поддерево исчезает из DOM, а не прячется: невидимая ветка ловила бы `Tab` и\n * читалась бы диктором вопреки `aria-expanded=\"false\"`.\n *\n * Свёрнутость здесь не проверяется — её перехватывает `v-if=\"collapsed\"` раньше:\n * в рейле пункт рисуется иконкой с подсказкой, и до ветки дело не доходит.\n */\nconst showChildren = computed(() => hasChildren.value && isExpanded.value)\n</script>\n\n<template>\n <GrTooltip\n v-if=\"collapsed\"\n :text=\"label\"\n :placement=\"tooltipPlacement\"\n block\n :describe-trigger=\"false\"\n data-gr-sidebar-item-tooltip\n >\n <component\n :is=\"rootTag\"\n data-gr-sidebar-item\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootHref\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :aria-label=\"label\"\n :aria-expanded=\"hasChildren ? 'false' : undefined\"\n :class=\"rootClass\"\n @click=\"onCollapsedActivate\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n <span v-else :class=\"itemLetterClass\" aria-hidden=\"true\">{{ firstLetter }}</span>\n </span>\n </component>\n </GrTooltip>\n\n <div v-else-if=\"hasChildren\" data-gr-sidebar-item-nest :class=\"itemNestClass\">\n <button\n data-gr-sidebar-item\n type=\"button\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :aria-expanded=\"isExpanded ? 'true' : 'false'\"\n :aria-controls=\"showChildren ? childrenId : undefined\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n @click=\"toggleChildren\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n </span>\n\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ label }}</span>\n <span v-if=\"badge != null\" :class=\"itemBadgeClass\">{{ badge }}</span>\n\n <!-- Состояние ветки несёт `aria-expanded`; шеврон говорит то же самое глазам. -->\n <IconChevronRight\n aria-hidden=\"true\"\n :class=\"[itemChevronClass, isExpanded ? itemChevronExpandedClass : '']\"\n />\n </button>\n\n <div v-if=\"showChildren\" :id=\"childrenId\" data-gr-sidebar-item-children :class=\"itemChildrenClass\">\n <slot />\n </div>\n </div>\n\n <component\n :is=\"rootTag\"\n v-else\n data-gr-sidebar-item\n :type=\"rootTag === 'button' ? 'button' : undefined\"\n :href=\"rootHref\"\n :aria-current=\"active ? 'page' : undefined\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"rootClass\"\n :style=\"rootStyle\"\n >\n <span class=\"flex h-5 w-5 shrink-0 items-center justify-center\">\n <component :is=\"iconComponent\" v-if=\"iconComponent\" class=\"h-5 w-5\" aria-hidden=\"true\" />\n <span v-else-if=\"isStringIcon\" :class=\"icon\" class=\"block h-5 w-5\" aria-hidden=\"true\" />\n </span>\n\n <span class=\"min-w-0 flex-1 truncate text-left\">{{ label }}</span>\n <span v-if=\"badge != null\" :class=\"itemBadgeClass\">{{ badge }}</span>\n </component>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebarGroup — секция пунктов `GrSidebar` с заголовком.\n *\n * В свёрнутой панели заголовку негде поместиться: он скрывается, а секции\n * разделяет линия — без неё иконки соседних секций сливаются в один столбец.\n */\nimport { computed, inject, useId } from 'vue'\n\nimport { groupCollapsedClass, groupLabelClass } from './grSidebarStyles'\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nexport interface GrSidebarGroupProps {\n /** Заголовок секции. Слот `#label` сильнее. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<GrSidebarGroupProps>(), {\n label: undefined,\n})\n\nconst slots = defineSlots<{\n /** Пункты секции. */\n default?: () => unknown\n /** Заголовок целиком — вместо строки `label`. */\n label?: () => unknown\n}>()\n\nconst sidebar = inject(GR_SIDEBAR_KEY, null)\nconst collapsed = computed(() => sidebar?.collapsed.value ?? false)\n\nconst labelId = useId()\nconst hasLabel = computed(() => Boolean(props.label || slots.label))\n\n// Имя группе даёт только видимый заголовок: в свёрнутом виде его нет, и\n// `aria-labelledby` указывал бы в пустоту.\nconst showLabel = computed(() => hasLabel.value && !collapsed.value)\n</script>\n\n<template>\n <div\n data-gr-sidebar-group\n role=\"group\"\n :aria-labelledby=\"showLabel ? labelId : undefined\"\n :class=\"collapsed ? groupCollapsedClass : undefined\"\n >\n <div\n v-if=\"showLabel\"\n :id=\"labelId\"\n data-gr-sidebar-group-label\n :class=\"groupLabelClass\"\n >\n <slot name=\"label\">\n {{ label }}\n </slot>\n </div>\n\n <slot />\n </div>\n</template>\n","<script setup lang=\"ts\">\n/**\n * GrSidebarGroup — секция пунктов `GrSidebar` с заголовком.\n *\n * В свёрнутой панели заголовку негде поместиться: он скрывается, а секции\n * разделяет линия — без неё иконки соседних секций сливаются в один столбец.\n */\nimport { computed, inject, useId } from 'vue'\n\nimport { groupCollapsedClass, groupLabelClass } from './grSidebarStyles'\nimport { GR_SIDEBAR_KEY } from './sidebarContext'\n\nexport interface GrSidebarGroupProps {\n /** Заголовок секции. Слот `#label` сильнее. */\n label?: string\n}\n\nconst props = withDefaults(defineProps<GrSidebarGroupProps>(), {\n label: undefined,\n})\n\nconst slots = defineSlots<{\n /** Пункты секции. */\n default?: () => unknown\n /** Заголовок целиком — вместо строки `label`. */\n label?: () => unknown\n}>()\n\nconst sidebar = inject(GR_SIDEBAR_KEY, null)\nconst collapsed = computed(() => sidebar?.collapsed.value ?? false)\n\nconst labelId = useId()\nconst hasLabel = computed(() => Boolean(props.label || slots.label))\n\n// Имя группе даёт только видимый заголовок: в свёрнутом виде его нет, и\n// `aria-labelledby` указывал бы в пустоту.\nconst showLabel = computed(() => hasLabel.value && !collapsed.value)\n</script>\n\n<template>\n <div\n data-gr-sidebar-group\n role=\"group\"\n :aria-labelledby=\"showLabel ? labelId : undefined\"\n :class=\"collapsed ? groupCollapsedClass : undefined\"\n >\n <div\n v-if=\"showLabel\"\n :id=\"labelId\"\n data-gr-sidebar-group-label\n :class=\"groupLabelClass\"\n >\n <slot name=\"label\">\n {{ label }}\n </slot>\n </div>\n\n <slot />\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { n as grSidebarSafelist, t as grSidebarConfig } from "../../chunks/config-
|
|
2
|
-
import { n as GrSidebarItem_default, r as GrSidebar_default, t as GrSidebarGroup_default } from "./chunks/GrSidebar-
|
|
1
|
+
import { n as grSidebarSafelist, t as grSidebarConfig } from "../../chunks/config-DpzFoSIh.js";
|
|
2
|
+
import { n as GrSidebarItem_default, r as GrSidebar_default, t as GrSidebarGroup_default } from "./chunks/GrSidebar-jVvoLGDO.js";
|
|
3
3
|
export { GrSidebar_default as GrSidebar, GrSidebar_default as default, GrSidebarGroup_default as GrSidebarGroup, GrSidebarItem_default as GrSidebarItem, grSidebarConfig, grSidebarSafelist };
|
|
@@ -5,8 +5,8 @@ import { useGrFormControl } from "../../../composables/useGrFormControl.js";
|
|
|
5
5
|
import { t as useFocusWithin } from "../../../chunks/useFocusWithin-BwHE0rzm.js";
|
|
6
6
|
import { useDragGesture } from "../../../composables/useDragGesture.js";
|
|
7
7
|
import { t as bigStep } from "../../../chunks/numericStep-Bq2lj4ew.js";
|
|
8
|
-
import { A as sliderTrackVerticalLengthClass, D as sliderTooltipClass, a as sliderFillOrientationClass, f as sliderMarkTickClass, g as sliderRailClass, j as sliderTrackWidthBySize, k as sliderTrackHeightBySize, n as sliderFillClass, s as sliderMarkLabelClassFor, v as sliderRootClass, x as sliderThumbClass } from "../../../chunks/grSliderStyles-
|
|
9
|
-
import { Fragment, computed, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, ref, renderList, toDisplayString, unref, watchEffect } from "vue";
|
|
8
|
+
import { A as sliderTrackVerticalLengthClass, D as sliderTooltipClass, a as sliderFillOrientationClass, f as sliderMarkTickClass, g as sliderRailClass, j as sliderTrackWidthBySize, k as sliderTrackHeightBySize, n as sliderFillClass, s as sliderMarkLabelClassFor, v as sliderRootClass, x as sliderThumbClass } from "../../../chunks/grSliderStyles-CqzV0hll.js";
|
|
9
|
+
import { Fragment, computed, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, normalizeClass, normalizeStyle, openBlock, ref, renderList, renderSlot, toDisplayString, unref, watchEffect } from "vue";
|
|
10
10
|
//#region src/components/GrSlider/GrSlider.vue?vue&type=script&setup=true&lang.ts
|
|
11
11
|
var _hoisted_1 = ["data-orientation"];
|
|
12
12
|
var _hoisted_2 = ["name", "value"];
|
|
@@ -199,6 +199,20 @@ var GrSlider_default = /* @__PURE__ */ defineComponent({
|
|
|
199
199
|
}
|
|
200
200
|
const activeThumb = ref(null);
|
|
201
201
|
const hoveredThumb = ref(null);
|
|
202
|
+
/**
|
|
203
|
+
* Попало ли деление в закрашенную часть дорожки.
|
|
204
|
+
*
|
|
205
|
+
* Считается тем же способом, что и сама заливка: у диапазона закрашен участок
|
|
206
|
+
* **между** бегунками, а не от нуля, и деление слева от первого бегунка
|
|
207
|
+
* пройденным не является.
|
|
208
|
+
*/
|
|
209
|
+
function isMarkActive(value) {
|
|
210
|
+
const position = percent(value);
|
|
211
|
+
const first = percent(values.value[0]);
|
|
212
|
+
if (!props.range) return position <= first;
|
|
213
|
+
const second = percent(values.value[1]);
|
|
214
|
+
return position >= Math.min(first, second) && position <= Math.max(first, second);
|
|
215
|
+
}
|
|
202
216
|
function valueFromPointer(event) {
|
|
203
217
|
const rect = trackEl.value?.getBoundingClientRect();
|
|
204
218
|
if (!rect) return props.min;
|
|
@@ -338,7 +352,12 @@ var GrSlider_default = /* @__PURE__ */ defineComponent({
|
|
|
338
352
|
})),
|
|
339
353
|
style: normalizeStyle(offsetStyle(mark.value)),
|
|
340
354
|
"aria-hidden": "true"
|
|
341
|
-
},
|
|
355
|
+
}, [renderSlot(_ctx.$slots, "mark", {
|
|
356
|
+
value: mark.value,
|
|
357
|
+
label: mark.label,
|
|
358
|
+
percent: percent(mark.value),
|
|
359
|
+
active: isMarkActive(mark.value)
|
|
360
|
+
}, () => [createTextVNode(toDisplayString(mark.label), 1)])], 6)], 64);
|
|
342
361
|
}), 128)),
|
|
343
362
|
(openBlock(true), createElementBlock(Fragment, null, renderList(values.value, (value, index) => {
|
|
344
363
|
return openBlock(), createElementBlock("div", {
|
|
@@ -374,7 +393,13 @@ var GrSlider_default = /* @__PURE__ */ defineComponent({
|
|
|
374
393
|
onMouseleave: _cache[0] || (_cache[0] = ($event) => hoveredThumb.value = null),
|
|
375
394
|
onFocus: ($event) => hoveredThumb.value = index,
|
|
376
395
|
onBlur: _cache[1] || (_cache[1] = ($event) => hoveredThumb.value = null)
|
|
377
|
-
}, [
|
|
396
|
+
}, [renderSlot(_ctx.$slots, "thumb", {
|
|
397
|
+
index,
|
|
398
|
+
value,
|
|
399
|
+
percent: percent(value),
|
|
400
|
+
active: hoveredThumb.value === index,
|
|
401
|
+
disabled: unref(isDisabled)
|
|
402
|
+
}), showTooltipFor(index) ? (openBlock(), createElementBlock("span", {
|
|
378
403
|
key: 0,
|
|
379
404
|
"data-gr-slider-tooltip": "",
|
|
380
405
|
class: normalizeClass(unref(sliderTooltipClass)(__props.orientation))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GrSlider-DF2Cq0rV.js","names":[],"sources":["../../../../src/components/GrSlider/GrSlider.vue","../../../../src/components/GrSlider/GrSlider.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watchEffect } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { bigStep } from '../shared/numericStep'\nimport { useDragGesture } from '../../composables/useDragGesture'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n sliderFillClass,\n sliderMarkLabelClassFor,\n sliderMarkTickClass,\n sliderRailClass,\n sliderRootClass,\n sliderThumbClass,\n sliderTooltipClass,\n sliderTrackHeightBySize,\n sliderTrackVerticalLengthClass,\n sliderTrackWidthBySize,\n sliderFillOrientationClass,\n type GrSliderMarks,\n type GrSliderModelValue,\n type GrSliderOrientation,\n type GrSliderSize,\n} from './grSliderStyles'\n\nexport type {\n GrSliderMarks,\n GrSliderModelValue,\n GrSliderOrientation,\n GrSliderSize,\n} from './grSliderStyles'\n\n/**\n * Публичный GR-примитив «Slider» (WAI-ARIA slider pattern).\n *\n * Поддерживает одиночное значение и диапазон (`range` — два бегунка), шаг, метки\n * делений, всплывающее значение (tooltip), полную клавиатурную навигацию\n * (стрелки / PageUp/Down / Home / End) и `role=\"slider\"` с\n * `aria-valuemin`/`max`/`now` на каждом бегунке.\n */\nexport interface GrSliderProps {\n /** `number` — одиночное значение; `[lo, hi]` — диапазон (при `range=true`). */\n modelValue: GrSliderModelValue\n min?: number\n max?: number\n step?: number\n /** Диапазон с двумя бегунками; модель — кортеж `[lo, hi]`. */\n range?: boolean\n disabled?: boolean\n /** Только для чтения: значение видно, но не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrSliderSize\n /** Метки делений: `{ [value]: label }` или массив значений. */\n marks?: GrSliderMarks\n /** Всплывающее значение над бегунком: `true`/`'hover'` — при hover/drag/focus, `'always'` — всегда. */\n showTooltip?: boolean | 'always' | 'hover'\n /** Форматирование значения в tooltip. Оно же уходит в `aria-valuetext`. */\n formatTooltip?: (value: number) => string\n /** Ориентация дорожки. В вертикальной минимум внизу. */\n orientation?: GrSliderOrientation\n /**\n * Эмитить `update:modelValue` только по завершении жеста: во время\n * перетаскивания значение ведёт сам слайдер. Клавиатура коммитит сразу —\n * нажатие клавиши дискретно, придерживать его нечего.\n */\n lazy?: boolean\n ariaLabel?: string\n /** Имя для нативной формы: hidden input на значение, при `range` — два с одним именем. */\n name?: string\n}\n\nexport interface GrSliderEmits {\n (e: 'update:modelValue', value: GrSliderModelValue): void\n /** Значение зафиксировано (отпущен бегунок / клавиша). */\n (e: 'change', value: GrSliderModelValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrSliderProps>(),\n {\n min: 0,\n max: 100,\n step: 1,\n range: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n marks: undefined,\n showTooltip: false,\n formatTooltip: undefined,\n orientation: 'horizontal',\n lazy: false,\n ariaLabel: undefined,\n name: undefined,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst isVertical = computed(() => props.orientation === 'vertical')\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrSlider',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrSliderEmits>()\n\n// Контекст `GrFormField`: id/aria-describedby/invalid/required как fallback.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\nconst trackEl = ref<HTMLElement | null>(null)\nconst rootEl = ref<HTMLElement | null>(null)\n\n// У диапазона два бегунка, и фокус ходит между ними: без границы переход с\n// одного на другой давал бы потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nconst thumbEls = ref<HTMLElement[]>([])\n\nfunction focus(): void {\n thumbEls.value[0]?.focus()\n}\n\nfunction blur(): void {\n thumbEls.value[0]?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n// Нормализованные границы (гард от max<=min).\nconst span = computed(() => (props.max > props.min ? props.max - props.min : 1))\n\nconst stepDecimals = computed(() => {\n const s = String(props.step)\n const dot = s.indexOf('.')\n return dot >= 0 ? s.length - dot - 1 : 0\n})\n\nfunction clamp(value: number): number {\n return Math.min(props.max, Math.max(props.min, value))\n}\n\nfunction snap(value: number): number {\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\n return Number(clamp(stepped).toFixed(stepDecimals.value))\n}\n\nfunction percent(value: number): number {\n return ((clamp(value) - props.min) / span.value) * 100\n}\n\n// Черновик жеста: при `lazy` значение до отпускания живёт здесь, а наружу не\n// уходит — иначе смысл режима теряется.\nconst draftValues = ref<number[] | null>(null)\n\n// Не доехавшее значение (промах вызова, асинхронный стор) увело бы в `NaN`\n// и смещение бегунка, и `aria-valuenow`; нижняя граница — честный старт.\nfunction toFiniteValue(value: number | undefined): number {\n return Number.isFinite(value) ? (value as number) : props.min\n}\n\n// ————— Значения бегунков как массив (single = один бегунок).\nconst modelValues = computed<number[]>(() => {\n if (props.range) {\n const v = Array.isArray(props.modelValue) ? props.modelValue : [props.min, props.min]\n return [snap(toFiniteValue(v[0])), snap(toFiniteValue(v[1]))]\n }\n const v = Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue\n return [snap(toFiniteValue(v))]\n})\n\nif (__GR_DEV__) {\n watchEffect(() => {\n const missing = props.range\n ? !Array.isArray(props.modelValue)\n : !Number.isFinite(props.modelValue)\n\n if (missing) {\n console.warn(\n `[granularity] GrSlider: обязательный проп \\`modelValue\\` должен быть ${props.range ? 'парой чисел' : 'числом'} — получено ${String(props.modelValue)}.`,\n )\n }\n })\n}\n\nconst values = computed<number[]>(() => draftValues.value ?? modelValues.value)\n\n// `[touch-action:none]` обязателен: без него вертикальный свайп по бегунку\n// уходит в скролл страницы, браузер шлёт `pointercancel`, и слайдер пальцем\n// неуправляем. Утилиты `touch-none` нет ни в `presetMini`, ни в extra-rules —\n// поэтому arbitrary-значение.\nconst trackClass = computed(() => (isVertical.value\n ? `[touch-action:none] ${sliderTrackWidthBySize[resolvedSize.value]} ${sliderTrackVerticalLengthClass}`\n : `[touch-action:none] w-full ${sliderTrackHeightBySize[resolvedSize.value]}`))\n\n/** Смещение вдоль дорожки: по горизонтали слева, по вертикали снизу. */\nfunction offsetStyle(value: number): Record<string, string> {\n return isVertical.value ? { bottom: `${percent(value)}%` } : { left: `${percent(value)}%` }\n}\n\nconst fillStyle = computed(() => {\n const start = props.range\n ? Math.min(percent(values.value[0]), percent(values.value[1]))\n : 0\n const end = props.range\n ? Math.max(percent(values.value[0]), percent(values.value[1]))\n : percent(values.value[0])\n\n return isVertical.value\n ? { bottom: `${start}%`, top: `${100 - end}%` }\n : { left: `${start}%`, right: `${100 - end}%` }\n})\n\n// ————— Метки делений.\ntype SliderMark = { value: number, label: string }\nconst normalizedMarks = computed<SliderMark[]>(() => {\n if (!props.marks)\n return []\n if (Array.isArray(props.marks))\n return props.marks.map(v => ({ value: v, label: String(v) }))\n return Object.entries(props.marks).map(([value, label]) => ({ value: Number(value), label }))\n})\n\nfunction tooltipText(value: number): string {\n return props.formatTooltip ? props.formatTooltip(value) : String(value)\n}\n\n// ————— Обновление значения конкретного бегунка.\nfunction emitValue(next: number[], commit: boolean): void {\n const payload: GrSliderModelValue = props.range ? [next[0], next[1]] : next[0]\n emit('update:modelValue', payload)\n if (commit)\n emit('change', payload)\n}\n\nfunction setThumb(index: number, value: number, commit: boolean): void {\n const next = values.value.slice()\n next[index] = snap(value)\n\n // range: не даём бегункам «перепрыгнуть» друг друга.\n if (props.range) {\n if (index === 0)\n next[0] = Math.min(next[0], next[1])\n else next[1] = Math.max(next[1], next[0])\n }\n\n // `lazy` придерживает только непрерывный жест: `commit` приходит от клавиатуры\n // и от отпускания, и там значение уходит наружу как обычно.\n if (props.lazy && !commit) {\n draftValues.value = next\n return\n }\n\n draftValues.value = null\n emitValue(next, commit)\n}\n\n// ————— Pointer-драг.\nconst activeThumb = ref<number | null>(null)\nconst hoveredThumb = ref<number | null>(null)\n\ndefineSlots<{\n /**\n * Подпись деления шкалы. Слот наполняет её узел, а не заменяет: позиция,\n * `aria-hidden` и дублирование значения диктору остаются за компонентом.\n */\n mark?: (props: {\n value: number\n /** Строка из `marks`; у массива это само значение. */\n label: string\n /** Положение деления на дорожке, проценты. */\n percent: number\n /** Деление попало в закрашенную часть — по нему красят пройденное. */\n active: boolean\n }) => unknown\n /**\n * Содержимое бегунка.\n *\n * Слот рендерится **внутрь** ручки, а не вместо неё: на самом узле висят\n * `role=\"slider\"`, `tabindex`, восемь `aria-*` и позиционирование, и отдать\n * его разметке потребителя значит отдать вместе с ним весь виджетный\n * контракт. Вид самой ручки меняется токенами `--gr-slider-thumb-*`.\n */\n thumb?: (props: {\n index: number\n value: number\n /** Положение бегунка на дорожке, проценты. */\n percent: number\n /** Бегунок под курсором или в фокусе — та же величина, что у тултипа. */\n active: boolean\n disabled: boolean\n }) => unknown\n}>()\n\n/**\n * Попало ли деление в закрашенную часть дорожки.\n *\n * Считается тем же способом, что и сама заливка: у диапазона закрашен участок\n * **между** бегунками, а не от нуля, и деление слева от первого бегунка\n * пройденным не является.\n */\nfunction isMarkActive(value: number): boolean {\n const position = percent(value)\n const first = percent(values.value[0])\n\n if (!props.range)\n return position <= first\n\n const second = percent(values.value[1])\n return position >= Math.min(first, second) && position <= Math.max(first, second)\n}\n\nfunction valueFromPointer(event: { clientX: number, clientY: number }): number {\n const rect = trackEl.value?.getBoundingClientRect()\n if (!rect)\n return props.min\n\n // В вертикали минимум внизу, поэтому доля отсчитывается от нижнего края.\n const ratio = isVertical.value\n ? (rect.height === 0 ? 0 : 1 - (event.clientY - rect.top) / rect.height)\n : (rect.width === 0 ? 0 : (event.clientX - rect.left) / rect.width)\n\n return props.min + Math.min(1, Math.max(0, ratio)) * span.value\n}\n\n/**\n * Какой бегунок повезёт клик. При равенстве расстояний берём верхний: иначе\n * схлопнувшийся в точку диапазон не развести мышью — нижний бегунок упирается\n * в верхний и оба стоят на месте.\n */\nfunction nearestThumb(value: number): number {\n if (!props.range)\n return 0\n\n const [lo, hi] = values.value\n if (value < lo)\n return 0\n if (value > hi)\n return 1\n\n return Math.abs(value - lo) < Math.abs(value - hi) ? 0 : 1\n}\n\nfunction endDrag(commit: boolean): void {\n if (activeThumb.value !== null && commit) {\n // Финальный commit значения: у `lazy` он же и единственный `update:modelValue`.\n const next = values.value.slice()\n draftValues.value = null\n emitValue(next, true)\n }\n // Оборванный жест (`pointercancel`: браузер забрал указатель) значения не\n // коммитит — черновик просто отбрасывается, бегунок возвращается к модели.\n draftValues.value = null\n activeThumb.value = null\n}\n\nconst drag = useDragGesture({\n disabled: () => isDisabled.value || isReadonly.value,\n onStart: (event) => {\n const value = valueFromPointer(event)\n const index = nearestThumb(value)\n activeThumb.value = index\n // Фокусируем активный бегунок сразу — чтобы клавиатура работала после клика по\n // дорожке (div не получает фокус по клику автоматически, только через `.focus()`).\n thumbEls.value[index]?.focus()\n setThumb(index, value, false)\n },\n onMove: (event) => {\n if (activeThumb.value === null)\n return\n event.preventDefault()\n setThumb(activeThumb.value, valueFromPointer(event), false)\n },\n onEnd: () => endDrag(true),\n onCancel: () => endDrag(false),\n})\n\n// ————— Клавиатура.\nfunction keyboardBigStep(): number {\n // Границы передаются от нормализованного размаха, а не от сырых пропов:\n // `span` уже подставил единицу вместо вырожденного `max <= min`.\n return bigStep(props.step, props.min, props.min + span.value)\n}\n\nfunction onThumbKeydown(event: KeyboardEvent, index: number): void {\n if (isDisabled.value || isReadonly.value)\n return\n const current = values.value[index]\n let next: number | null = null\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = current + props.step\n break\n case 'ArrowLeft':\n case 'ArrowDown':\n next = current - props.step\n break\n case 'PageUp':\n next = current + keyboardBigStep()\n break\n case 'PageDown':\n next = current - keyboardBigStep()\n break\n case 'Home':\n next = props.min\n break\n case 'End':\n next = props.max\n break\n default:\n return\n }\n\n event.preventDefault()\n setThumb(index, next, true)\n}\n\nfunction showTooltipFor(index: number): boolean {\n if (props.showTooltip === 'always')\n return true\n if (!props.showTooltip)\n return false\n return activeThumb.value === index || hoveredThumb.value === index\n}\n\nfunction thumbAriaLabel(index: number): string | undefined {\n if (!props.ariaLabel)\n return undefined\n if (!props.range)\n return props.ariaLabel\n\n const bound = index === 0 ? t('gr.slider.min', 'min') : t('gr.slider.max', 'max')\n return `${props.ariaLabel} (${bound})`\n}\n\n// `aria-valuetext` нужен, только когда число само по себе не читается: «1200»\n// против «$1 200». Без своего формата атрибут не добавляем.\nfunction thumbValueText(value: number): string | undefined {\n return props.formatTooltip ? props.formatTooltip(value) : undefined\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n data-gr-slider\n :data-orientation=\"orientation\"\n :class=\"sliderRootClass({ size: resolvedSize, disabled: isDisabled, hasMarks: normalizedMarks.length > 0, orientation })\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Нативная форма: роль-виджет не labelable и в submit не попадает.\n Сериализуется модель (snapped), а не черновик жеста. -->\n <template v-if=\"name\">\n <input\n v-for=\"(value, index) in modelValues\"\n :key=\"index\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\n <div\n ref=\"trackEl\"\n data-gr-slider-track\n class=\"relative\"\n :class=\"trackClass\"\n @pointerdown=\"drag.start\"\n >\n <div :class=\"sliderRailClass\" />\n <div\n data-gr-slider-fill\n :class=\"[sliderFillClass(isDisabled), sliderFillOrientationClass[orientation]]\"\n :style=\"fillStyle\"\n />\n\n <!-- Метки делений. -->\n <template v-for=\"mark in normalizedMarks\" :key=\"mark.value\">\n <span\n data-gr-slider-mark\n :class=\"sliderMarkTickClass(orientation)\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n />\n <!-- Подпись метки дублирует значение, которое диктор читает с бегунка:\n как отдельный текст внутри слайдера она только мешает. -->\n <span\n data-gr-slider-mark-label\n :class=\"sliderMarkLabelClassFor(percent(mark.value), { orientation, disabled: isDisabled })\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n >\n <slot\n name=\"mark\"\n :value=\"mark.value\"\n :label=\"mark.label\"\n :percent=\"percent(mark.value)\"\n :active=\"isMarkActive(mark.value)\"\n >\n {{ mark.label }}\n </slot>\n </span>\n </template>\n\n <!-- Бегунки. -->\n <div\n v-for=\"(value, index) in values\"\n :id=\"index === 0 ? resolvedId : undefined\"\n :key=\"index\"\n :ref=\"(el) => { if (el) thumbEls[index] = el as HTMLElement }\"\n data-gr-slider-thumb\n :data-testid=\"`gr-slider-thumb-${index}`\"\n :class=\"sliderThumbClass({ size: resolvedSize, disabled: isDisabled, orientation })\"\n :style=\"offsetStyle(value)\"\n role=\"slider\"\n :tabindex=\"isDisabled ? -1 : 0\"\n :aria-valuemin=\"range && index === 1 ? values[0] : min\"\n :aria-valuemax=\"range && index === 0 ? values[1] : max\"\n :aria-valuenow=\"value\"\n :aria-valuetext=\"thumbValueText(value)\"\n :aria-orientation=\"orientation\"\n :aria-label=\"thumbAriaLabel(index)\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"index === 0 ? describedBy : undefined\"\n :aria-required=\"isRequired && index === 0 ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n @keydown=\"onThumbKeydown($event, index)\"\n @mouseenter=\"hoveredThumb = index\"\n @mouseleave=\"hoveredThumb = null\"\n @focus=\"hoveredThumb = index\"\n @blur=\"hoveredThumb = null\"\n >\n <!-- Тултип остаётся рядом со слотом, а не вытесняется им: значок в\n ручке не должен стоить потребителю подсказки со значением. -->\n <slot\n name=\"thumb\"\n :index=\"index\"\n :value=\"value\"\n :percent=\"percent(value)\"\n :active=\"hoveredThumb === index\"\n :disabled=\"isDisabled\"\n />\n\n <span\n v-if=\"showTooltipFor(index)\"\n data-gr-slider-tooltip\n :class=\"sliderTooltipClass(orientation)\"\n >\n {{ tooltipText(value) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, watchEffect } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { bigStep } from '../shared/numericStep'\nimport { useDragGesture } from '../../composables/useDragGesture'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport {\n sliderFillClass,\n sliderMarkLabelClassFor,\n sliderMarkTickClass,\n sliderRailClass,\n sliderRootClass,\n sliderThumbClass,\n sliderTooltipClass,\n sliderTrackHeightBySize,\n sliderTrackVerticalLengthClass,\n sliderTrackWidthBySize,\n sliderFillOrientationClass,\n type GrSliderMarks,\n type GrSliderModelValue,\n type GrSliderOrientation,\n type GrSliderSize,\n} from './grSliderStyles'\n\nexport type {\n GrSliderMarks,\n GrSliderModelValue,\n GrSliderOrientation,\n GrSliderSize,\n} from './grSliderStyles'\n\n/**\n * Публичный GR-примитив «Slider» (WAI-ARIA slider pattern).\n *\n * Поддерживает одиночное значение и диапазон (`range` — два бегунка), шаг, метки\n * делений, всплывающее значение (tooltip), полную клавиатурную навигацию\n * (стрелки / PageUp/Down / Home / End) и `role=\"slider\"` с\n * `aria-valuemin`/`max`/`now` на каждом бегунке.\n */\nexport interface GrSliderProps {\n /** `number` — одиночное значение; `[lo, hi]` — диапазон (при `range=true`). */\n modelValue: GrSliderModelValue\n min?: number\n max?: number\n step?: number\n /** Диапазон с двумя бегунками; модель — кортеж `[lo, hi]`. */\n range?: boolean\n disabled?: boolean\n /** Только для чтения: значение видно, но не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n size?: GrSliderSize\n /** Метки делений: `{ [value]: label }` или массив значений. */\n marks?: GrSliderMarks\n /** Всплывающее значение над бегунком: `true`/`'hover'` — при hover/drag/focus, `'always'` — всегда. */\n showTooltip?: boolean | 'always' | 'hover'\n /** Форматирование значения в tooltip. Оно же уходит в `aria-valuetext`. */\n formatTooltip?: (value: number) => string\n /** Ориентация дорожки. В вертикальной минимум внизу. */\n orientation?: GrSliderOrientation\n /**\n * Эмитить `update:modelValue` только по завершении жеста: во время\n * перетаскивания значение ведёт сам слайдер. Клавиатура коммитит сразу —\n * нажатие клавиши дискретно, придерживать его нечего.\n */\n lazy?: boolean\n ariaLabel?: string\n /** Имя для нативной формы: hidden input на значение, при `range` — два с одним именем. */\n name?: string\n}\n\nexport interface GrSliderEmits {\n (e: 'update:modelValue', value: GrSliderModelValue): void\n /** Значение зафиксировано (отпущен бегунок / клавиша). */\n (e: 'change', value: GrSliderModelValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrSliderProps>(),\n {\n min: 0,\n max: 100,\n step: 1,\n range: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n size: undefined,\n marks: undefined,\n showTooltip: false,\n formatTooltip: undefined,\n orientation: 'horizontal',\n lazy: false,\n ariaLabel: undefined,\n name: undefined,\n },\n)\n\nconst { t } = useGranularityTranslations()\n\nconst isVertical = computed(() => props.orientation === 'vertical')\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrSlider',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrSliderEmits>()\n\n// Контекст `GrFormField`: id/aria-describedby/invalid/required как fallback.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid,\n required: isRequired,\n readonly: isReadonly,\n} = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\nconst trackEl = ref<HTMLElement | null>(null)\nconst rootEl = ref<HTMLElement | null>(null)\n\n// У диапазона два бегунка, и фокус ходит между ними: без границы переход с\n// одного на другой давал бы потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nconst thumbEls = ref<HTMLElement[]>([])\n\nfunction focus(): void {\n thumbEls.value[0]?.focus()\n}\n\nfunction blur(): void {\n thumbEls.value[0]?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n// Нормализованные границы (гард от max<=min).\nconst span = computed(() => (props.max > props.min ? props.max - props.min : 1))\n\nconst stepDecimals = computed(() => {\n const s = String(props.step)\n const dot = s.indexOf('.')\n return dot >= 0 ? s.length - dot - 1 : 0\n})\n\nfunction clamp(value: number): number {\n return Math.min(props.max, Math.max(props.min, value))\n}\n\nfunction snap(value: number): number {\n const stepped = Math.round((value - props.min) / props.step) * props.step + props.min\n return Number(clamp(stepped).toFixed(stepDecimals.value))\n}\n\nfunction percent(value: number): number {\n return ((clamp(value) - props.min) / span.value) * 100\n}\n\n// Черновик жеста: при `lazy` значение до отпускания живёт здесь, а наружу не\n// уходит — иначе смысл режима теряется.\nconst draftValues = ref<number[] | null>(null)\n\n// Не доехавшее значение (промах вызова, асинхронный стор) увело бы в `NaN`\n// и смещение бегунка, и `aria-valuenow`; нижняя граница — честный старт.\nfunction toFiniteValue(value: number | undefined): number {\n return Number.isFinite(value) ? (value as number) : props.min\n}\n\n// ————— Значения бегунков как массив (single = один бегунок).\nconst modelValues = computed<number[]>(() => {\n if (props.range) {\n const v = Array.isArray(props.modelValue) ? props.modelValue : [props.min, props.min]\n return [snap(toFiniteValue(v[0])), snap(toFiniteValue(v[1]))]\n }\n const v = Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue\n return [snap(toFiniteValue(v))]\n})\n\nif (__GR_DEV__) {\n watchEffect(() => {\n const missing = props.range\n ? !Array.isArray(props.modelValue)\n : !Number.isFinite(props.modelValue)\n\n if (missing) {\n console.warn(\n `[granularity] GrSlider: обязательный проп \\`modelValue\\` должен быть ${props.range ? 'парой чисел' : 'числом'} — получено ${String(props.modelValue)}.`,\n )\n }\n })\n}\n\nconst values = computed<number[]>(() => draftValues.value ?? modelValues.value)\n\n// `[touch-action:none]` обязателен: без него вертикальный свайп по бегунку\n// уходит в скролл страницы, браузер шлёт `pointercancel`, и слайдер пальцем\n// неуправляем. Утилиты `touch-none` нет ни в `presetMini`, ни в extra-rules —\n// поэтому arbitrary-значение.\nconst trackClass = computed(() => (isVertical.value\n ? `[touch-action:none] ${sliderTrackWidthBySize[resolvedSize.value]} ${sliderTrackVerticalLengthClass}`\n : `[touch-action:none] w-full ${sliderTrackHeightBySize[resolvedSize.value]}`))\n\n/** Смещение вдоль дорожки: по горизонтали слева, по вертикали снизу. */\nfunction offsetStyle(value: number): Record<string, string> {\n return isVertical.value ? { bottom: `${percent(value)}%` } : { left: `${percent(value)}%` }\n}\n\nconst fillStyle = computed(() => {\n const start = props.range\n ? Math.min(percent(values.value[0]), percent(values.value[1]))\n : 0\n const end = props.range\n ? Math.max(percent(values.value[0]), percent(values.value[1]))\n : percent(values.value[0])\n\n return isVertical.value\n ? { bottom: `${start}%`, top: `${100 - end}%` }\n : { left: `${start}%`, right: `${100 - end}%` }\n})\n\n// ————— Метки делений.\ntype SliderMark = { value: number, label: string }\nconst normalizedMarks = computed<SliderMark[]>(() => {\n if (!props.marks)\n return []\n if (Array.isArray(props.marks))\n return props.marks.map(v => ({ value: v, label: String(v) }))\n return Object.entries(props.marks).map(([value, label]) => ({ value: Number(value), label }))\n})\n\nfunction tooltipText(value: number): string {\n return props.formatTooltip ? props.formatTooltip(value) : String(value)\n}\n\n// ————— Обновление значения конкретного бегунка.\nfunction emitValue(next: number[], commit: boolean): void {\n const payload: GrSliderModelValue = props.range ? [next[0], next[1]] : next[0]\n emit('update:modelValue', payload)\n if (commit)\n emit('change', payload)\n}\n\nfunction setThumb(index: number, value: number, commit: boolean): void {\n const next = values.value.slice()\n next[index] = snap(value)\n\n // range: не даём бегункам «перепрыгнуть» друг друга.\n if (props.range) {\n if (index === 0)\n next[0] = Math.min(next[0], next[1])\n else next[1] = Math.max(next[1], next[0])\n }\n\n // `lazy` придерживает только непрерывный жест: `commit` приходит от клавиатуры\n // и от отпускания, и там значение уходит наружу как обычно.\n if (props.lazy && !commit) {\n draftValues.value = next\n return\n }\n\n draftValues.value = null\n emitValue(next, commit)\n}\n\n// ————— Pointer-драг.\nconst activeThumb = ref<number | null>(null)\nconst hoveredThumb = ref<number | null>(null)\n\ndefineSlots<{\n /**\n * Подпись деления шкалы. Слот наполняет её узел, а не заменяет: позиция,\n * `aria-hidden` и дублирование значения диктору остаются за компонентом.\n */\n mark?: (props: {\n value: number\n /** Строка из `marks`; у массива это само значение. */\n label: string\n /** Положение деления на дорожке, проценты. */\n percent: number\n /** Деление попало в закрашенную часть — по нему красят пройденное. */\n active: boolean\n }) => unknown\n /**\n * Содержимое бегунка.\n *\n * Слот рендерится **внутрь** ручки, а не вместо неё: на самом узле висят\n * `role=\"slider\"`, `tabindex`, восемь `aria-*` и позиционирование, и отдать\n * его разметке потребителя значит отдать вместе с ним весь виджетный\n * контракт. Вид самой ручки меняется токенами `--gr-slider-thumb-*`.\n */\n thumb?: (props: {\n index: number\n value: number\n /** Положение бегунка на дорожке, проценты. */\n percent: number\n /** Бегунок под курсором или в фокусе — та же величина, что у тултипа. */\n active: boolean\n disabled: boolean\n }) => unknown\n}>()\n\n/**\n * Попало ли деление в закрашенную часть дорожки.\n *\n * Считается тем же способом, что и сама заливка: у диапазона закрашен участок\n * **между** бегунками, а не от нуля, и деление слева от первого бегунка\n * пройденным не является.\n */\nfunction isMarkActive(value: number): boolean {\n const position = percent(value)\n const first = percent(values.value[0])\n\n if (!props.range)\n return position <= first\n\n const second = percent(values.value[1])\n return position >= Math.min(first, second) && position <= Math.max(first, second)\n}\n\nfunction valueFromPointer(event: { clientX: number, clientY: number }): number {\n const rect = trackEl.value?.getBoundingClientRect()\n if (!rect)\n return props.min\n\n // В вертикали минимум внизу, поэтому доля отсчитывается от нижнего края.\n const ratio = isVertical.value\n ? (rect.height === 0 ? 0 : 1 - (event.clientY - rect.top) / rect.height)\n : (rect.width === 0 ? 0 : (event.clientX - rect.left) / rect.width)\n\n return props.min + Math.min(1, Math.max(0, ratio)) * span.value\n}\n\n/**\n * Какой бегунок повезёт клик. При равенстве расстояний берём верхний: иначе\n * схлопнувшийся в точку диапазон не развести мышью — нижний бегунок упирается\n * в верхний и оба стоят на месте.\n */\nfunction nearestThumb(value: number): number {\n if (!props.range)\n return 0\n\n const [lo, hi] = values.value\n if (value < lo)\n return 0\n if (value > hi)\n return 1\n\n return Math.abs(value - lo) < Math.abs(value - hi) ? 0 : 1\n}\n\nfunction endDrag(commit: boolean): void {\n if (activeThumb.value !== null && commit) {\n // Финальный commit значения: у `lazy` он же и единственный `update:modelValue`.\n const next = values.value.slice()\n draftValues.value = null\n emitValue(next, true)\n }\n // Оборванный жест (`pointercancel`: браузер забрал указатель) значения не\n // коммитит — черновик просто отбрасывается, бегунок возвращается к модели.\n draftValues.value = null\n activeThumb.value = null\n}\n\nconst drag = useDragGesture({\n disabled: () => isDisabled.value || isReadonly.value,\n onStart: (event) => {\n const value = valueFromPointer(event)\n const index = nearestThumb(value)\n activeThumb.value = index\n // Фокусируем активный бегунок сразу — чтобы клавиатура работала после клика по\n // дорожке (div не получает фокус по клику автоматически, только через `.focus()`).\n thumbEls.value[index]?.focus()\n setThumb(index, value, false)\n },\n onMove: (event) => {\n if (activeThumb.value === null)\n return\n event.preventDefault()\n setThumb(activeThumb.value, valueFromPointer(event), false)\n },\n onEnd: () => endDrag(true),\n onCancel: () => endDrag(false),\n})\n\n// ————— Клавиатура.\nfunction keyboardBigStep(): number {\n // Границы передаются от нормализованного размаха, а не от сырых пропов:\n // `span` уже подставил единицу вместо вырожденного `max <= min`.\n return bigStep(props.step, props.min, props.min + span.value)\n}\n\nfunction onThumbKeydown(event: KeyboardEvent, index: number): void {\n if (isDisabled.value || isReadonly.value)\n return\n const current = values.value[index]\n let next: number | null = null\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = current + props.step\n break\n case 'ArrowLeft':\n case 'ArrowDown':\n next = current - props.step\n break\n case 'PageUp':\n next = current + keyboardBigStep()\n break\n case 'PageDown':\n next = current - keyboardBigStep()\n break\n case 'Home':\n next = props.min\n break\n case 'End':\n next = props.max\n break\n default:\n return\n }\n\n event.preventDefault()\n setThumb(index, next, true)\n}\n\nfunction showTooltipFor(index: number): boolean {\n if (props.showTooltip === 'always')\n return true\n if (!props.showTooltip)\n return false\n return activeThumb.value === index || hoveredThumb.value === index\n}\n\nfunction thumbAriaLabel(index: number): string | undefined {\n if (!props.ariaLabel)\n return undefined\n if (!props.range)\n return props.ariaLabel\n\n const bound = index === 0 ? t('gr.slider.min', 'min') : t('gr.slider.max', 'max')\n return `${props.ariaLabel} (${bound})`\n}\n\n// `aria-valuetext` нужен, только когда число само по себе не читается: «1200»\n// против «$1 200». Без своего формата атрибут не добавляем.\nfunction thumbValueText(value: number): string | undefined {\n return props.formatTooltip ? props.formatTooltip(value) : undefined\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n data-gr-slider\n :data-orientation=\"orientation\"\n :class=\"sliderRootClass({ size: resolvedSize, disabled: isDisabled, hasMarks: normalizedMarks.length > 0, orientation })\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Нативная форма: роль-виджет не labelable и в submit не попадает.\n Сериализуется модель (snapped), а не черновик жеста. -->\n <template v-if=\"name\">\n <input\n v-for=\"(value, index) in modelValues\"\n :key=\"index\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(value)\"\n >\n </template>\n <div\n ref=\"trackEl\"\n data-gr-slider-track\n class=\"relative\"\n :class=\"trackClass\"\n @pointerdown=\"drag.start\"\n >\n <div :class=\"sliderRailClass\" />\n <div\n data-gr-slider-fill\n :class=\"[sliderFillClass(isDisabled), sliderFillOrientationClass[orientation]]\"\n :style=\"fillStyle\"\n />\n\n <!-- Метки делений. -->\n <template v-for=\"mark in normalizedMarks\" :key=\"mark.value\">\n <span\n data-gr-slider-mark\n :class=\"sliderMarkTickClass(orientation)\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n />\n <!-- Подпись метки дублирует значение, которое диктор читает с бегунка:\n как отдельный текст внутри слайдера она только мешает. -->\n <span\n data-gr-slider-mark-label\n :class=\"sliderMarkLabelClassFor(percent(mark.value), { orientation, disabled: isDisabled })\"\n :style=\"offsetStyle(mark.value)\"\n aria-hidden=\"true\"\n >\n <slot\n name=\"mark\"\n :value=\"mark.value\"\n :label=\"mark.label\"\n :percent=\"percent(mark.value)\"\n :active=\"isMarkActive(mark.value)\"\n >\n {{ mark.label }}\n </slot>\n </span>\n </template>\n\n <!-- Бегунки. -->\n <div\n v-for=\"(value, index) in values\"\n :id=\"index === 0 ? resolvedId : undefined\"\n :key=\"index\"\n :ref=\"(el) => { if (el) thumbEls[index] = el as HTMLElement }\"\n data-gr-slider-thumb\n :data-testid=\"`gr-slider-thumb-${index}`\"\n :class=\"sliderThumbClass({ size: resolvedSize, disabled: isDisabled, orientation })\"\n :style=\"offsetStyle(value)\"\n role=\"slider\"\n :tabindex=\"isDisabled ? -1 : 0\"\n :aria-valuemin=\"range && index === 1 ? values[0] : min\"\n :aria-valuemax=\"range && index === 0 ? values[1] : max\"\n :aria-valuenow=\"value\"\n :aria-valuetext=\"thumbValueText(value)\"\n :aria-orientation=\"orientation\"\n :aria-label=\"thumbAriaLabel(index)\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-describedby=\"index === 0 ? describedBy : undefined\"\n :aria-required=\"isRequired && index === 0 ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n @keydown=\"onThumbKeydown($event, index)\"\n @mouseenter=\"hoveredThumb = index\"\n @mouseleave=\"hoveredThumb = null\"\n @focus=\"hoveredThumb = index\"\n @blur=\"hoveredThumb = null\"\n >\n <!-- Тултип остаётся рядом со слотом, а не вытесняется им: значок в\n ручке не должен стоить потребителю подсказки со значением. -->\n <slot\n name=\"thumb\"\n :index=\"index\"\n :value=\"value\"\n :percent=\"percent(value)\"\n :active=\"hoveredThumb === index\"\n :disabled=\"isDisabled\"\n />\n\n <span\n v-if=\"showTooltipFor(index)\"\n data-gr-slider-tooltip\n :class=\"sliderTooltipClass(orientation)\"\n >\n {{ tooltipText(value) }}\n </span>\n </div>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as GrSlider_default } from "./chunks/GrSlider-
|
|
2
|
-
import { n as grSliderSafelist, t as grSliderConfig } from "../../chunks/config-
|
|
1
|
+
import { t as GrSlider_default } from "./chunks/GrSlider-DF2Cq0rV.js";
|
|
2
|
+
import { n as grSliderSafelist, t as grSliderConfig } from "../../chunks/config-8n1VR0ju.js";
|
|
3
3
|
export { GrSlider_default as GrSlider, GrSlider_default as default, grSliderConfig, grSliderSafelist };
|
|
@@ -5,7 +5,7 @@ import { useAnnouncer } from "../../../composables/useAnnouncer.js";
|
|
|
5
5
|
import { i as moveItem, r as insertionIndex } from "../../../chunks/dragSortGeometry-CY-llfrQ.js";
|
|
6
6
|
import { useDragSort } from "../../../composables/useDragSort.js";
|
|
7
7
|
import { t as grip_vertical_default } from "../../../chunks/grip-vertical-lRha2S7q.js";
|
|
8
|
-
import { a as
|
|
8
|
+
import { a as handleDisabledClass, c as listClass, d as rowFocusClass, f as rowGrabbedClass, i as handleClass, l as rowDisabledClass, n as dividedClass, o as indicatorAfterClass, p as rowLayoutClass, r as emptyClass, s as indicatorBeforeClass, t as contentClass, u as rowDraggingClass } from "../../../chunks/grSortableListStyles-CxieDmpe.js";
|
|
9
9
|
import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, nextTick, normalizeClass, normalizeStyle, openBlock, ref, renderList, renderSlot, toDisplayString, unref, watchEffect, withCtx, withModifiers } from "vue";
|
|
10
10
|
//#region src/components/GrSortableList/GrSortableList.vue?vue&type=script&setup=true&lang.ts
|
|
11
11
|
var _hoisted_1 = ["aria-label", "aria-disabled"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GrSortableList-NKriuGm_.js","names":[],"sources":["../../../../src/components/GrSortableList/GrSortableList.vue","../../../../src/components/GrSortableList/GrSortableList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T\">\nimport { computed, nextTick, ref, watchEffect } from 'vue'\n\nimport GrCard, { type GrCardVariant } from '../GrCard'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useDragSort } from '../../composables/useDragSort'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { insertionIndex, moveItem } from '../../composables/internal/dragSortGeometry'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport type { GrSortableOrientation } from './grSortableListStyles'\nimport {\n contentClass,\n dividedClass,\n emptyClass,\n handleClass,\n handleDisabledClass,\n indicatorAfterClass,\n indicatorBeforeClass,\n listClass,\n rowDisabledClass,\n rowDraggingClass,\n rowFocusClass,\n rowGrabbedClass,\n rowLayoutClass,\n} from './grSortableListStyles'\n\nimport IconGripVertical from '~icons/lucide/grip-vertical'\n\nexport interface GrSortableListProps<TItem = unknown> {\n /** Набор в текущем порядке. `v-model`: наружу уходит новый массив, вход не мутируется. */\n modelValue: TItem[]\n /** Ключ элемента: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Список только для чтения: ни указателем, ни с клавиатуры. */\n disabled?: boolean\n /** Ось переноса. `horizontal` — ряд с переносом по ширине. */\n orientation?: GrSortableOrientation\n /** Поверхность под списком — вариант карточки. */\n variant?: GrCardVariant\n /** Разделители между строками. */\n divided?: boolean\n /** Тянуть можно только за ручку. Выключено — тянется вся строка. */\n handleOnly?: boolean\n /** Высота видимой части: список становится скроллером с автопрокруткой при переносе. */\n maxHeight?: number | string\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /** Имя списка для скринридера. Не задано — берётся из локали. */\n ariaLabel?: string\n}\n\nexport interface GrSortableListEmits<TItem = unknown> {\n (e: 'update:modelValue', value: TItem[]): void\n (e: 'move', from: number, to: number): void\n (e: 'change', value: TItem[]): void\n}\n\nconst props = withDefaults(defineProps<GrSortableListProps<T>>(), {\n itemKey: undefined,\n disabled: false,\n orientation: 'vertical',\n variant: undefined,\n divided: true,\n handleOnly: true,\n maxHeight: undefined,\n emptyText: undefined,\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrSortableListEmits<T>>()\n\ndefineSlots<{\n item?: (props: { item: T, index: number, dragging: boolean, grabbed: boolean }) => any\n handle?: (props: { item: T, index: number, disabled: boolean }) => any\n empty?: () => any\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst listEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<string, HTMLElement>()\n\nconst count = computed(() => props.modelValue.length)\nconst isEmpty = computed(() => count.value === 0)\nconst isVertical = computed(() => props.orientation === 'vertical')\n\nfunction keyOf(item: T, index: number): string {\n if (typeof props.itemKey === 'function')\n return String(props.itemKey(item, index))\n if (typeof props.itemKey === 'string')\n return String((item as Record<string, unknown>)[props.itemKey])\n\n return String(index)\n}\n\nconst keys = computed(() => props.modelValue.map((item, index) => keyOf(item, index)))\n\nfunction indexOfKey(key: string): number {\n return keys.value.indexOf(key)\n}\n\nfunction registerRow(key: string, el: unknown): void {\n if (el instanceof HTMLElement) {\n rowEls.set(key, el)\n return\n }\n\n // Перестановка размонтирует старый узел уже после того, как встал новый:\n // снимаем запись, только если она протухла (приём из `GrTree`).\n const previous = rowEls.get(key)\n if (previous && !previous.isConnected)\n rowEls.delete(key)\n}\n\nconst roving = useRovingFocus<string>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => (isVertical.value ? 'vertical' : 'horizontal'),\n wrap: () => false,\n})\n\nfunction applyMove(from: number, to: number): void {\n if (from < 0 || to < 0 || from === to)\n return\n\n const next = moveItem(props.modelValue, from, to)\n emit('update:modelValue', next)\n emit('move', from, to)\n emit('change', next)\n}\n\nconst sort = useDragSort<string, number>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => props.orientation,\n scroller: () => (props.maxHeight === undefined ? null : listEl.value),\n disabled: () => props.disabled,\n resolveTarget: (hit, source) => insertionIndex(hit, indexOfKey(source), count.value),\n onDrop: (source, to) => applyMove(indexOfKey(source), to),\n onUpdate: (source, target, mode) => {\n // Объявляется только клавиатурный перенос: у мыши всё видно на экране, а\n // поток объявлений на каждом движении указателя делает диктор бесполезным.\n if (mode !== 'keyboard' || source === null)\n return\n\n const position = (target ?? indexOfKey(source)) + 1\n announce(t('gr.sortable.moved', 'Position {position} of {count}', { position, count: count.value }))\n },\n})\n\nconst activeIndex = computed(() => (sort.source.value === null ? -1 : indexOfKey(sort.source.value)))\n\n/** Куда встанет строка: индикатор рисуется на строке-соседе, а не отдельным узлом. */\nfunction indicatorFor(index: number): 'before' | 'after' | null {\n const target = sort.target.value\n if (target === null || !sort.isActive.value || index === activeIndex.value)\n return null\n\n if (target === index)\n return target < activeIndex.value || activeIndex.value === -1 ? 'before' : 'after'\n\n return null\n}\n\nfunction rowClass(index: number, key: string): string[] {\n const grabbed = sort.mode.value === 'keyboard' && sort.source.value === key\n const dragging = sort.mode.value === 'pointer' && sort.source.value === key\n const indicator = indicatorFor(index)\n\n return [\n rowLayoutClass,\n rowFocusClass,\n props.disabled ? rowDisabledClass : '',\n grabbed ? rowGrabbedClass : '',\n dragging ? rowDraggingClass : '',\n indicator === 'before' ? indicatorBeforeClass : '',\n indicator === 'after' ? indicatorAfterClass : '',\n ]\n}\n\nfunction startPointer(key: string, event: PointerEvent): void {\n if (props.disabled)\n return\n\n sort.startFrom(key)(event)\n}\n\nfunction grab(key: string): void {\n sort.grab(key)\n if (sort.mode.value !== 'keyboard')\n return\n\n // Цель ставится до объявления: `setTarget` сам объявляет позицию, и обратный\n // порядок стёр бы более полное сообщение о захвате.\n sort.setTarget(indexOfKey(key))\n announce(t('gr.sortable.grabbed', 'Grabbed. Position {position} of {count}', {\n position: indexOfKey(key) + 1,\n count: count.value,\n }))\n}\n\nasync function drop(key: string): Promise<void> {\n const target = sort.target.value\n sort.drop()\n\n announce(t('gr.sortable.dropped', 'Dropped at position {position} of {count}', {\n position: (target ?? 0) + 1,\n count: count.value,\n }))\n\n // Фокус остаётся на перенесённой строке: иначе после переноса он падает на\n // тело документа и клавиатурный сценарий обрывается на первом же шаге.\n await nextTick()\n await roving.focusKey(key)\n}\n\nfunction cancel(): void {\n if (!sort.isActive.value)\n return\n\n sort.cancel()\n announce(t('gr.sortable.cancelled', 'Move cancelled'))\n}\n\nfunction moveTargetBy(delta: number): void {\n const current = sort.target.value ?? activeIndex.value\n const next = Math.min(count.value - 1, Math.max(0, current + delta))\n\n if (next !== current)\n sort.setTarget(next)\n}\n\nfunction onRowKeydown(event: KeyboardEvent, key: string): void {\n if (props.disabled)\n return\n\n const forward = isVertical.value ? 'ArrowDown' : 'ArrowRight'\n const backward = isVertical.value ? 'ArrowUp' : 'ArrowLeft'\n const grabbed = sort.mode.value === 'keyboard'\n\n if (event.key === 'Escape') {\n if (!grabbed)\n return\n\n event.preventDefault()\n cancel()\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n if (grabbed)\n void drop(key)\n else grab(key)\n return\n }\n\n if (grabbed) {\n // Взятая строка забирает стрелки себе: двигается она, а не фокус.\n if (event.key === forward || event.key === backward) {\n event.preventDefault()\n moveTargetBy(event.key === forward ? 1 : -1)\n }\n return\n }\n\n roving.handleNavigationKeys(event)\n}\n\n/** Фокус ушёл из списка — взятая строка не может остаться взятой навсегда. */\nfunction onFocusout(event: FocusEvent): void {\n if (sort.mode.value !== 'keyboard')\n return\n\n const next = event.relatedTarget as Node | null\n if (next && listEl.value?.contains(next))\n return\n\n cancel()\n}\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.sortable.empty', 'Nothing to sort yet'))\nconst resolvedLabel = computed(() => props.ariaLabel ?? t('gr.sortable.label', 'Sortable list'))\nconst handleLabel = computed(() => t('gr.sortable.handle', 'Reorder'))\n\nconst listStyle = computed(() => (props.maxHeight === undefined\n ? undefined\n : { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight, overflow: 'auto' }))\n\ndefineExpose({\n /** Программная перестановка — та же, что делает перенос. */\n move: (from: number, to: number) => applyMove(from, to),\n focusItem: (index: number) => roving.focusKey(keys.value[index]),\n})\n\nif (__GR_DEV__) {\n watchEffect(() => {\n if (!Array.isArray(props.modelValue)) {\n console.warn(\n `[granularity] GrSortableList: обязательный проп \\`modelValue\\` должен быть массивом — получено ${String(props.modelValue)}.`,\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n role=\"list\"\n data-gr-sortable\n :aria-label=\"resolvedLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"[listClass[orientation], divided ? dividedClass : '']\"\n :style=\"listStyle\"\n @focusout=\"onFocusout\"\n >\n <div\n v-if=\"isEmpty\"\n data-gr-sortable-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <div\n v-for=\"(item, index) in modelValue\"\n v-else\n :key=\"keys[index]\"\n :ref=\"el => registerRow(keys[index], el)\"\n role=\"listitem\"\n data-gr-sortable-item\n :tabindex=\"disabled ? -1 : roving.tabindexFor(keys[index])\"\n :aria-roledescription=\"handleLabel\"\n :aria-posinset=\"index + 1\"\n :aria-setsize=\"count\"\n :aria-grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index] ? 'true' : undefined\"\n :class=\"rowClass(index, keys[index])\"\n @keydown=\"onRowKeydown($event, keys[index])\"\n @focus=\"roving.setActive(keys[index])\"\n @pointerdown=\"handleOnly ? undefined : startPointer(keys[index], $event)\"\n >\n <button\n type=\"button\"\n tabindex=\"-1\"\n data-gr-sortable-handle\n :class=\"[handleClass, disabled ? handleDisabledClass : '']\"\n :aria-label=\"handleLabel\"\n :disabled=\"disabled\"\n @pointerdown=\"startPointer(keys[index], $event)\"\n @click.stop\n >\n <slot\n name=\"handle\"\n :item=\"item\"\n :index=\"index\"\n :disabled=\"disabled\"\n >\n <IconGripVertical class=\"block\" aria-hidden=\"true\" />\n </slot>\n </button>\n\n <div :class=\"contentClass\">\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :dragging=\"sort.mode.value === 'pointer' && sort.source.value === keys[index]\"\n :grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index]\"\n >\n {{ item }}\n </slot>\n </div>\n </div>\n </div>\n </GrCard>\n</template>\n","<script setup lang=\"ts\" generic=\"T\">\nimport { computed, nextTick, ref, watchEffect } from 'vue'\n\nimport GrCard, { type GrCardVariant } from '../GrCard'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useDragSort } from '../../composables/useDragSort'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { insertionIndex, moveItem } from '../../composables/internal/dragSortGeometry'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport type { GrSortableOrientation } from './grSortableListStyles'\nimport {\n contentClass,\n dividedClass,\n emptyClass,\n handleClass,\n handleDisabledClass,\n indicatorAfterClass,\n indicatorBeforeClass,\n listClass,\n rowDisabledClass,\n rowDraggingClass,\n rowFocusClass,\n rowGrabbedClass,\n rowLayoutClass,\n} from './grSortableListStyles'\n\nimport IconGripVertical from '~icons/lucide/grip-vertical'\n\nexport interface GrSortableListProps<TItem = unknown> {\n /** Набор в текущем порядке. `v-model`: наружу уходит новый массив, вход не мутируется. */\n modelValue: TItem[]\n /** Ключ элемента: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Список только для чтения: ни указателем, ни с клавиатуры. */\n disabled?: boolean\n /** Ось переноса. `horizontal` — ряд с переносом по ширине. */\n orientation?: GrSortableOrientation\n /** Поверхность под списком — вариант карточки. */\n variant?: GrCardVariant\n /** Разделители между строками. */\n divided?: boolean\n /** Тянуть можно только за ручку. Выключено — тянется вся строка. */\n handleOnly?: boolean\n /** Высота видимой части: список становится скроллером с автопрокруткой при переносе. */\n maxHeight?: number | string\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /** Имя списка для скринридера. Не задано — берётся из локали. */\n ariaLabel?: string\n}\n\nexport interface GrSortableListEmits<TItem = unknown> {\n (e: 'update:modelValue', value: TItem[]): void\n (e: 'move', from: number, to: number): void\n (e: 'change', value: TItem[]): void\n}\n\nconst props = withDefaults(defineProps<GrSortableListProps<T>>(), {\n itemKey: undefined,\n disabled: false,\n orientation: 'vertical',\n variant: undefined,\n divided: true,\n handleOnly: true,\n maxHeight: undefined,\n emptyText: undefined,\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrSortableListEmits<T>>()\n\ndefineSlots<{\n item?: (props: { item: T, index: number, dragging: boolean, grabbed: boolean }) => any\n handle?: (props: { item: T, index: number, disabled: boolean }) => any\n empty?: () => any\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst listEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<string, HTMLElement>()\n\nconst count = computed(() => props.modelValue.length)\nconst isEmpty = computed(() => count.value === 0)\nconst isVertical = computed(() => props.orientation === 'vertical')\n\nfunction keyOf(item: T, index: number): string {\n if (typeof props.itemKey === 'function')\n return String(props.itemKey(item, index))\n if (typeof props.itemKey === 'string')\n return String((item as Record<string, unknown>)[props.itemKey])\n\n return String(index)\n}\n\nconst keys = computed(() => props.modelValue.map((item, index) => keyOf(item, index)))\n\nfunction indexOfKey(key: string): number {\n return keys.value.indexOf(key)\n}\n\nfunction registerRow(key: string, el: unknown): void {\n if (el instanceof HTMLElement) {\n rowEls.set(key, el)\n return\n }\n\n // Перестановка размонтирует старый узел уже после того, как встал новый:\n // снимаем запись, только если она протухла (приём из `GrTree`).\n const previous = rowEls.get(key)\n if (previous && !previous.isConnected)\n rowEls.delete(key)\n}\n\nconst roving = useRovingFocus<string>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => (isVertical.value ? 'vertical' : 'horizontal'),\n wrap: () => false,\n})\n\nfunction applyMove(from: number, to: number): void {\n if (from < 0 || to < 0 || from === to)\n return\n\n const next = moveItem(props.modelValue, from, to)\n emit('update:modelValue', next)\n emit('move', from, to)\n emit('change', next)\n}\n\nconst sort = useDragSort<string, number>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => props.orientation,\n scroller: () => (props.maxHeight === undefined ? null : listEl.value),\n disabled: () => props.disabled,\n resolveTarget: (hit, source) => insertionIndex(hit, indexOfKey(source), count.value),\n onDrop: (source, to) => applyMove(indexOfKey(source), to),\n onUpdate: (source, target, mode) => {\n // Объявляется только клавиатурный перенос: у мыши всё видно на экране, а\n // поток объявлений на каждом движении указателя делает диктор бесполезным.\n if (mode !== 'keyboard' || source === null)\n return\n\n const position = (target ?? indexOfKey(source)) + 1\n announce(t('gr.sortable.moved', 'Position {position} of {count}', { position, count: count.value }))\n },\n})\n\nconst activeIndex = computed(() => (sort.source.value === null ? -1 : indexOfKey(sort.source.value)))\n\n/** Куда встанет строка: индикатор рисуется на строке-соседе, а не отдельным узлом. */\nfunction indicatorFor(index: number): 'before' | 'after' | null {\n const target = sort.target.value\n if (target === null || !sort.isActive.value || index === activeIndex.value)\n return null\n\n if (target === index)\n return target < activeIndex.value || activeIndex.value === -1 ? 'before' : 'after'\n\n return null\n}\n\nfunction rowClass(index: number, key: string): string[] {\n const grabbed = sort.mode.value === 'keyboard' && sort.source.value === key\n const dragging = sort.mode.value === 'pointer' && sort.source.value === key\n const indicator = indicatorFor(index)\n\n return [\n rowLayoutClass,\n rowFocusClass,\n props.disabled ? rowDisabledClass : '',\n grabbed ? rowGrabbedClass : '',\n dragging ? rowDraggingClass : '',\n indicator === 'before' ? indicatorBeforeClass : '',\n indicator === 'after' ? indicatorAfterClass : '',\n ]\n}\n\nfunction startPointer(key: string, event: PointerEvent): void {\n if (props.disabled)\n return\n\n sort.startFrom(key)(event)\n}\n\nfunction grab(key: string): void {\n sort.grab(key)\n if (sort.mode.value !== 'keyboard')\n return\n\n // Цель ставится до объявления: `setTarget` сам объявляет позицию, и обратный\n // порядок стёр бы более полное сообщение о захвате.\n sort.setTarget(indexOfKey(key))\n announce(t('gr.sortable.grabbed', 'Grabbed. Position {position} of {count}', {\n position: indexOfKey(key) + 1,\n count: count.value,\n }))\n}\n\nasync function drop(key: string): Promise<void> {\n const target = sort.target.value\n sort.drop()\n\n announce(t('gr.sortable.dropped', 'Dropped at position {position} of {count}', {\n position: (target ?? 0) + 1,\n count: count.value,\n }))\n\n // Фокус остаётся на перенесённой строке: иначе после переноса он падает на\n // тело документа и клавиатурный сценарий обрывается на первом же шаге.\n await nextTick()\n await roving.focusKey(key)\n}\n\nfunction cancel(): void {\n if (!sort.isActive.value)\n return\n\n sort.cancel()\n announce(t('gr.sortable.cancelled', 'Move cancelled'))\n}\n\nfunction moveTargetBy(delta: number): void {\n const current = sort.target.value ?? activeIndex.value\n const next = Math.min(count.value - 1, Math.max(0, current + delta))\n\n if (next !== current)\n sort.setTarget(next)\n}\n\nfunction onRowKeydown(event: KeyboardEvent, key: string): void {\n if (props.disabled)\n return\n\n const forward = isVertical.value ? 'ArrowDown' : 'ArrowRight'\n const backward = isVertical.value ? 'ArrowUp' : 'ArrowLeft'\n const grabbed = sort.mode.value === 'keyboard'\n\n if (event.key === 'Escape') {\n if (!grabbed)\n return\n\n event.preventDefault()\n cancel()\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n if (grabbed)\n void drop(key)\n else grab(key)\n return\n }\n\n if (grabbed) {\n // Взятая строка забирает стрелки себе: двигается она, а не фокус.\n if (event.key === forward || event.key === backward) {\n event.preventDefault()\n moveTargetBy(event.key === forward ? 1 : -1)\n }\n return\n }\n\n roving.handleNavigationKeys(event)\n}\n\n/** Фокус ушёл из списка — взятая строка не может остаться взятой навсегда. */\nfunction onFocusout(event: FocusEvent): void {\n if (sort.mode.value !== 'keyboard')\n return\n\n const next = event.relatedTarget as Node | null\n if (next && listEl.value?.contains(next))\n return\n\n cancel()\n}\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.sortable.empty', 'Nothing to sort yet'))\nconst resolvedLabel = computed(() => props.ariaLabel ?? t('gr.sortable.label', 'Sortable list'))\nconst handleLabel = computed(() => t('gr.sortable.handle', 'Reorder'))\n\nconst listStyle = computed(() => (props.maxHeight === undefined\n ? undefined\n : { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight, overflow: 'auto' }))\n\ndefineExpose({\n /** Программная перестановка — та же, что делает перенос. */\n move: (from: number, to: number) => applyMove(from, to),\n focusItem: (index: number) => roving.focusKey(keys.value[index]),\n})\n\nif (__GR_DEV__) {\n watchEffect(() => {\n if (!Array.isArray(props.modelValue)) {\n console.warn(\n `[granularity] GrSortableList: обязательный проп \\`modelValue\\` должен быть массивом — получено ${String(props.modelValue)}.`,\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n role=\"list\"\n data-gr-sortable\n :aria-label=\"resolvedLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"[listClass[orientation], divided ? dividedClass : '']\"\n :style=\"listStyle\"\n @focusout=\"onFocusout\"\n >\n <div\n v-if=\"isEmpty\"\n data-gr-sortable-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <div\n v-for=\"(item, index) in modelValue\"\n v-else\n :key=\"keys[index]\"\n :ref=\"el => registerRow(keys[index], el)\"\n role=\"listitem\"\n data-gr-sortable-item\n :tabindex=\"disabled ? -1 : roving.tabindexFor(keys[index])\"\n :aria-roledescription=\"handleLabel\"\n :aria-posinset=\"index + 1\"\n :aria-setsize=\"count\"\n :aria-grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index] ? 'true' : undefined\"\n :class=\"rowClass(index, keys[index])\"\n @keydown=\"onRowKeydown($event, keys[index])\"\n @focus=\"roving.setActive(keys[index])\"\n @pointerdown=\"handleOnly ? undefined : startPointer(keys[index], $event)\"\n >\n <button\n type=\"button\"\n tabindex=\"-1\"\n data-gr-sortable-handle\n :class=\"[handleClass, disabled ? handleDisabledClass : '']\"\n :aria-label=\"handleLabel\"\n :disabled=\"disabled\"\n @pointerdown=\"startPointer(keys[index], $event)\"\n @click.stop\n >\n <slot\n name=\"handle\"\n :item=\"item\"\n :index=\"index\"\n :disabled=\"disabled\"\n >\n <IconGripVertical class=\"block\" aria-hidden=\"true\" />\n </slot>\n </button>\n\n <div :class=\"contentClass\">\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :dragging=\"sort.mode.value === 'pointer' && sort.source.value === keys[index]\"\n :grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index]\"\n >\n {{ item }}\n </slot>\n </div>\n </div>\n </div>\n </GrCard>\n</template>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GrSortableList-ChH9nQBv.js","names":[],"sources":["../../../../src/components/GrSortableList/GrSortableList.vue","../../../../src/components/GrSortableList/GrSortableList.vue"],"sourcesContent":["<script setup lang=\"ts\" generic=\"T\">\nimport { computed, nextTick, ref, watchEffect } from 'vue'\n\nimport GrCard, { type GrCardVariant } from '../GrCard'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useDragSort } from '../../composables/useDragSort'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { insertionIndex, moveItem } from '../../composables/internal/dragSortGeometry'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport type { GrSortableOrientation } from './grSortableListStyles'\nimport {\n contentClass,\n dividedClass,\n emptyClass,\n handleClass,\n handleDisabledClass,\n indicatorAfterClass,\n indicatorBeforeClass,\n listClass,\n rowDisabledClass,\n rowDraggingClass,\n rowFocusClass,\n rowGrabbedClass,\n rowLayoutClass,\n} from './grSortableListStyles'\n\nimport IconGripVertical from '~icons/lucide/grip-vertical'\n\nexport interface GrSortableListProps<TItem = unknown> {\n /** Набор в текущем порядке. `v-model`: наружу уходит новый массив, вход не мутируется. */\n modelValue: TItem[]\n /** Ключ элемента: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Список только для чтения: ни указателем, ни с клавиатуры. */\n disabled?: boolean\n /** Ось переноса. `horizontal` — ряд с переносом по ширине. */\n orientation?: GrSortableOrientation\n /** Поверхность под списком — вариант карточки. */\n variant?: GrCardVariant\n /** Разделители между строками. */\n divided?: boolean\n /** Тянуть можно только за ручку. Выключено — тянется вся строка. */\n handleOnly?: boolean\n /** Высота видимой части: список становится скроллером с автопрокруткой при переносе. */\n maxHeight?: number | string\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /** Имя списка для скринридера. Не задано — берётся из локали. */\n ariaLabel?: string\n}\n\nexport interface GrSortableListEmits<TItem = unknown> {\n (e: 'update:modelValue', value: TItem[]): void\n (e: 'move', from: number, to: number): void\n (e: 'change', value: TItem[]): void\n}\n\nconst props = withDefaults(defineProps<GrSortableListProps<T>>(), {\n itemKey: undefined,\n disabled: false,\n orientation: 'vertical',\n variant: undefined,\n divided: true,\n handleOnly: true,\n maxHeight: undefined,\n emptyText: undefined,\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrSortableListEmits<T>>()\n\ndefineSlots<{\n item?: (props: { item: T, index: number, dragging: boolean, grabbed: boolean }) => any\n handle?: (props: { item: T, index: number, disabled: boolean }) => any\n empty?: () => any\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst listEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<string, HTMLElement>()\n\nconst count = computed(() => props.modelValue.length)\nconst isEmpty = computed(() => count.value === 0)\nconst isVertical = computed(() => props.orientation === 'vertical')\n\nfunction keyOf(item: T, index: number): string {\n if (typeof props.itemKey === 'function')\n return String(props.itemKey(item, index))\n if (typeof props.itemKey === 'string')\n return String((item as Record<string, unknown>)[props.itemKey])\n\n return String(index)\n}\n\nconst keys = computed(() => props.modelValue.map((item, index) => keyOf(item, index)))\n\nfunction indexOfKey(key: string): number {\n return keys.value.indexOf(key)\n}\n\nfunction registerRow(key: string, el: unknown): void {\n if (el instanceof HTMLElement) {\n rowEls.set(key, el)\n return\n }\n\n // Перестановка размонтирует старый узел уже после того, как встал новый:\n // снимаем запись, только если она протухла (приём из `GrTree`).\n const previous = rowEls.get(key)\n if (previous && !previous.isConnected)\n rowEls.delete(key)\n}\n\nconst roving = useRovingFocus<string>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => (isVertical.value ? 'vertical' : 'horizontal'),\n wrap: () => false,\n})\n\nfunction applyMove(from: number, to: number): void {\n if (from < 0 || to < 0 || from === to)\n return\n\n const next = moveItem(props.modelValue, from, to)\n emit('update:modelValue', next)\n emit('move', from, to)\n emit('change', next)\n}\n\nconst sort = useDragSort<string, number>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => props.orientation,\n scroller: () => (props.maxHeight === undefined ? null : listEl.value),\n disabled: () => props.disabled,\n resolveTarget: (hit, source) => insertionIndex(hit, indexOfKey(source), count.value),\n onDrop: (source, to) => applyMove(indexOfKey(source), to),\n onUpdate: (source, target, mode) => {\n // Объявляется только клавиатурный перенос: у мыши всё видно на экране, а\n // поток объявлений на каждом движении указателя делает диктор бесполезным.\n if (mode !== 'keyboard' || source === null)\n return\n\n const position = (target ?? indexOfKey(source)) + 1\n announce(t('gr.sortable.moved', 'Position {position} of {count}', { position, count: count.value }))\n },\n})\n\nconst activeIndex = computed(() => (sort.source.value === null ? -1 : indexOfKey(sort.source.value)))\n\n/** Куда встанет строка: индикатор рисуется на строке-соседе, а не отдельным узлом. */\nfunction indicatorFor(index: number): 'before' | 'after' | null {\n const target = sort.target.value\n if (target === null || !sort.isActive.value || index === activeIndex.value)\n return null\n\n if (target === index)\n return target < activeIndex.value || activeIndex.value === -1 ? 'before' : 'after'\n\n return null\n}\n\nfunction rowClass(index: number, key: string): string[] {\n const grabbed = sort.mode.value === 'keyboard' && sort.source.value === key\n const dragging = sort.mode.value === 'pointer' && sort.source.value === key\n const indicator = indicatorFor(index)\n\n return [\n rowLayoutClass,\n rowFocusClass,\n props.disabled ? rowDisabledClass : '',\n grabbed ? rowGrabbedClass : '',\n dragging ? rowDraggingClass : '',\n indicator === 'before' ? indicatorBeforeClass : '',\n indicator === 'after' ? indicatorAfterClass : '',\n ]\n}\n\nfunction startPointer(key: string, event: PointerEvent): void {\n if (props.disabled)\n return\n\n sort.startFrom(key)(event)\n}\n\nfunction grab(key: string): void {\n sort.grab(key)\n if (sort.mode.value !== 'keyboard')\n return\n\n // Цель ставится до объявления: `setTarget` сам объявляет позицию, и обратный\n // порядок стёр бы более полное сообщение о захвате.\n sort.setTarget(indexOfKey(key))\n announce(t('gr.sortable.grabbed', 'Grabbed. Position {position} of {count}', {\n position: indexOfKey(key) + 1,\n count: count.value,\n }))\n}\n\nasync function drop(key: string): Promise<void> {\n const target = sort.target.value\n sort.drop()\n\n announce(t('gr.sortable.dropped', 'Dropped at position {position} of {count}', {\n position: (target ?? 0) + 1,\n count: count.value,\n }))\n\n // Фокус остаётся на перенесённой строке: иначе после переноса он падает на\n // тело документа и клавиатурный сценарий обрывается на первом же шаге.\n await nextTick()\n await roving.focusKey(key)\n}\n\nfunction cancel(): void {\n if (!sort.isActive.value)\n return\n\n sort.cancel()\n announce(t('gr.sortable.cancelled', 'Move cancelled'))\n}\n\nfunction moveTargetBy(delta: number): void {\n const current = sort.target.value ?? activeIndex.value\n const next = Math.min(count.value - 1, Math.max(0, current + delta))\n\n if (next !== current)\n sort.setTarget(next)\n}\n\nfunction onRowKeydown(event: KeyboardEvent, key: string): void {\n if (props.disabled)\n return\n\n const forward = isVertical.value ? 'ArrowDown' : 'ArrowRight'\n const backward = isVertical.value ? 'ArrowUp' : 'ArrowLeft'\n const grabbed = sort.mode.value === 'keyboard'\n\n if (event.key === 'Escape') {\n if (!grabbed)\n return\n\n event.preventDefault()\n cancel()\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n if (grabbed)\n void drop(key)\n else grab(key)\n return\n }\n\n if (grabbed) {\n // Взятая строка забирает стрелки себе: двигается она, а не фокус.\n if (event.key === forward || event.key === backward) {\n event.preventDefault()\n moveTargetBy(event.key === forward ? 1 : -1)\n }\n return\n }\n\n roving.handleNavigationKeys(event)\n}\n\n/** Фокус ушёл из списка — взятая строка не может остаться взятой навсегда. */\nfunction onFocusout(event: FocusEvent): void {\n if (sort.mode.value !== 'keyboard')\n return\n\n const next = event.relatedTarget as Node | null\n if (next && listEl.value?.contains(next))\n return\n\n cancel()\n}\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.sortable.empty', 'Nothing to sort yet'))\nconst resolvedLabel = computed(() => props.ariaLabel ?? t('gr.sortable.label', 'Sortable list'))\nconst handleLabel = computed(() => t('gr.sortable.handle', 'Reorder'))\n\nconst listStyle = computed(() => (props.maxHeight === undefined\n ? undefined\n : { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight, overflow: 'auto' }))\n\ndefineExpose({\n /** Программная перестановка — та же, что делает перенос. */\n move: (from: number, to: number) => applyMove(from, to),\n focusItem: (index: number) => roving.focusKey(keys.value[index]),\n})\n\nif (__GR_DEV__) {\n watchEffect(() => {\n if (!Array.isArray(props.modelValue)) {\n console.warn(\n `[granularity] GrSortableList: обязательный проп \\`modelValue\\` должен быть массивом — получено ${String(props.modelValue)}.`,\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n role=\"list\"\n data-gr-sortable\n :aria-label=\"resolvedLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"[listClass[orientation], divided ? dividedClass : '']\"\n :style=\"listStyle\"\n @focusout=\"onFocusout\"\n >\n <div\n v-if=\"isEmpty\"\n data-gr-sortable-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <div\n v-for=\"(item, index) in modelValue\"\n v-else\n :key=\"keys[index]\"\n :ref=\"el => registerRow(keys[index], el)\"\n role=\"listitem\"\n data-gr-sortable-item\n :tabindex=\"disabled ? -1 : roving.tabindexFor(keys[index])\"\n :aria-roledescription=\"handleLabel\"\n :aria-posinset=\"index + 1\"\n :aria-setsize=\"count\"\n :aria-grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index] ? 'true' : undefined\"\n :class=\"rowClass(index, keys[index])\"\n @keydown=\"onRowKeydown($event, keys[index])\"\n @focus=\"roving.setActive(keys[index])\"\n @pointerdown=\"handleOnly ? undefined : startPointer(keys[index], $event)\"\n >\n <button\n type=\"button\"\n tabindex=\"-1\"\n data-gr-sortable-handle\n :class=\"[handleClass, disabled ? handleDisabledClass : '']\"\n :aria-label=\"handleLabel\"\n :disabled=\"disabled\"\n @pointerdown=\"startPointer(keys[index], $event)\"\n @click.stop\n >\n <slot\n name=\"handle\"\n :item=\"item\"\n :index=\"index\"\n :disabled=\"disabled\"\n >\n <IconGripVertical class=\"block\" aria-hidden=\"true\" />\n </slot>\n </button>\n\n <div :class=\"contentClass\">\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :dragging=\"sort.mode.value === 'pointer' && sort.source.value === keys[index]\"\n :grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index]\"\n >\n {{ item }}\n </slot>\n </div>\n </div>\n </div>\n </GrCard>\n</template>\n","<script setup lang=\"ts\" generic=\"T\">\nimport { computed, nextTick, ref, watchEffect } from 'vue'\n\nimport GrCard, { type GrCardVariant } from '../GrCard'\nimport { useAnnouncer } from '../../composables/useAnnouncer'\nimport { useDragSort } from '../../composables/useDragSort'\nimport { useRovingFocus } from '../../composables/useRovingFocus'\nimport { insertionIndex, moveItem } from '../../composables/internal/dragSortGeometry'\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\n\nimport type { GrSortableOrientation } from './grSortableListStyles'\nimport {\n contentClass,\n dividedClass,\n emptyClass,\n handleClass,\n handleDisabledClass,\n indicatorAfterClass,\n indicatorBeforeClass,\n listClass,\n rowDisabledClass,\n rowDraggingClass,\n rowFocusClass,\n rowGrabbedClass,\n rowLayoutClass,\n} from './grSortableListStyles'\n\nimport IconGripVertical from '~icons/lucide/grip-vertical'\n\nexport interface GrSortableListProps<TItem = unknown> {\n /** Набор в текущем порядке. `v-model`: наружу уходит новый массив, вход не мутируется. */\n modelValue: TItem[]\n /** Ключ элемента: имя поля или функция. Без него — индекс. */\n itemKey?: string | ((item: TItem, index: number) => string | number)\n /** Список только для чтения: ни указателем, ни с клавиатуры. */\n disabled?: boolean\n /** Ось переноса. `horizontal` — ряд с переносом по ширине. */\n orientation?: GrSortableOrientation\n /** Поверхность под списком — вариант карточки. */\n variant?: GrCardVariant\n /** Разделители между строками. */\n divided?: boolean\n /** Тянуть можно только за ручку. Выключено — тянется вся строка. */\n handleOnly?: boolean\n /** Высота видимой части: список становится скроллером с автопрокруткой при переносе. */\n maxHeight?: number | string\n /** Текст пустого состояния. Слот `#empty` сильнее. */\n emptyText?: string\n /** Имя списка для скринридера. Не задано — берётся из локали. */\n ariaLabel?: string\n}\n\nexport interface GrSortableListEmits<TItem = unknown> {\n (e: 'update:modelValue', value: TItem[]): void\n (e: 'move', from: number, to: number): void\n (e: 'change', value: TItem[]): void\n}\n\nconst props = withDefaults(defineProps<GrSortableListProps<T>>(), {\n itemKey: undefined,\n disabled: false,\n orientation: 'vertical',\n variant: undefined,\n divided: true,\n handleOnly: true,\n maxHeight: undefined,\n emptyText: undefined,\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrSortableListEmits<T>>()\n\ndefineSlots<{\n item?: (props: { item: T, index: number, dragging: boolean, grabbed: boolean }) => any\n handle?: (props: { item: T, index: number, disabled: boolean }) => any\n empty?: () => any\n}>()\n\nconst { t } = useGranularityTranslations()\nconst { announce } = useAnnouncer()\n\nconst listEl = ref<HTMLElement | null>(null)\nconst rowEls = new Map<string, HTMLElement>()\n\nconst count = computed(() => props.modelValue.length)\nconst isEmpty = computed(() => count.value === 0)\nconst isVertical = computed(() => props.orientation === 'vertical')\n\nfunction keyOf(item: T, index: number): string {\n if (typeof props.itemKey === 'function')\n return String(props.itemKey(item, index))\n if (typeof props.itemKey === 'string')\n return String((item as Record<string, unknown>)[props.itemKey])\n\n return String(index)\n}\n\nconst keys = computed(() => props.modelValue.map((item, index) => keyOf(item, index)))\n\nfunction indexOfKey(key: string): number {\n return keys.value.indexOf(key)\n}\n\nfunction registerRow(key: string, el: unknown): void {\n if (el instanceof HTMLElement) {\n rowEls.set(key, el)\n return\n }\n\n // Перестановка размонтирует старый узел уже после того, как встал новый:\n // снимаем запись, только если она протухла (приём из `GrTree`).\n const previous = rowEls.get(key)\n if (previous && !previous.isConnected)\n rowEls.delete(key)\n}\n\nconst roving = useRovingFocus<string>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => (isVertical.value ? 'vertical' : 'horizontal'),\n wrap: () => false,\n})\n\nfunction applyMove(from: number, to: number): void {\n if (from < 0 || to < 0 || from === to)\n return\n\n const next = moveItem(props.modelValue, from, to)\n emit('update:modelValue', next)\n emit('move', from, to)\n emit('change', next)\n}\n\nconst sort = useDragSort<string, number>({\n items: () => keys.value,\n elementFor: key => rowEls.get(key) ?? null,\n orientation: () => props.orientation,\n scroller: () => (props.maxHeight === undefined ? null : listEl.value),\n disabled: () => props.disabled,\n resolveTarget: (hit, source) => insertionIndex(hit, indexOfKey(source), count.value),\n onDrop: (source, to) => applyMove(indexOfKey(source), to),\n onUpdate: (source, target, mode) => {\n // Объявляется только клавиатурный перенос: у мыши всё видно на экране, а\n // поток объявлений на каждом движении указателя делает диктор бесполезным.\n if (mode !== 'keyboard' || source === null)\n return\n\n const position = (target ?? indexOfKey(source)) + 1\n announce(t('gr.sortable.moved', 'Position {position} of {count}', { position, count: count.value }))\n },\n})\n\nconst activeIndex = computed(() => (sort.source.value === null ? -1 : indexOfKey(sort.source.value)))\n\n/** Куда встанет строка: индикатор рисуется на строке-соседе, а не отдельным узлом. */\nfunction indicatorFor(index: number): 'before' | 'after' | null {\n const target = sort.target.value\n if (target === null || !sort.isActive.value || index === activeIndex.value)\n return null\n\n if (target === index)\n return target < activeIndex.value || activeIndex.value === -1 ? 'before' : 'after'\n\n return null\n}\n\nfunction rowClass(index: number, key: string): string[] {\n const grabbed = sort.mode.value === 'keyboard' && sort.source.value === key\n const dragging = sort.mode.value === 'pointer' && sort.source.value === key\n const indicator = indicatorFor(index)\n\n return [\n rowLayoutClass,\n rowFocusClass,\n props.disabled ? rowDisabledClass : '',\n grabbed ? rowGrabbedClass : '',\n dragging ? rowDraggingClass : '',\n indicator === 'before' ? indicatorBeforeClass : '',\n indicator === 'after' ? indicatorAfterClass : '',\n ]\n}\n\nfunction startPointer(key: string, event: PointerEvent): void {\n if (props.disabled)\n return\n\n sort.startFrom(key)(event)\n}\n\nfunction grab(key: string): void {\n sort.grab(key)\n if (sort.mode.value !== 'keyboard')\n return\n\n // Цель ставится до объявления: `setTarget` сам объявляет позицию, и обратный\n // порядок стёр бы более полное сообщение о захвате.\n sort.setTarget(indexOfKey(key))\n announce(t('gr.sortable.grabbed', 'Grabbed. Position {position} of {count}', {\n position: indexOfKey(key) + 1,\n count: count.value,\n }))\n}\n\nasync function drop(key: string): Promise<void> {\n const target = sort.target.value\n sort.drop()\n\n announce(t('gr.sortable.dropped', 'Dropped at position {position} of {count}', {\n position: (target ?? 0) + 1,\n count: count.value,\n }))\n\n // Фокус остаётся на перенесённой строке: иначе после переноса он падает на\n // тело документа и клавиатурный сценарий обрывается на первом же шаге.\n await nextTick()\n await roving.focusKey(key)\n}\n\nfunction cancel(): void {\n if (!sort.isActive.value)\n return\n\n sort.cancel()\n announce(t('gr.sortable.cancelled', 'Move cancelled'))\n}\n\nfunction moveTargetBy(delta: number): void {\n const current = sort.target.value ?? activeIndex.value\n const next = Math.min(count.value - 1, Math.max(0, current + delta))\n\n if (next !== current)\n sort.setTarget(next)\n}\n\nfunction onRowKeydown(event: KeyboardEvent, key: string): void {\n if (props.disabled)\n return\n\n const forward = isVertical.value ? 'ArrowDown' : 'ArrowRight'\n const backward = isVertical.value ? 'ArrowUp' : 'ArrowLeft'\n const grabbed = sort.mode.value === 'keyboard'\n\n if (event.key === 'Escape') {\n if (!grabbed)\n return\n\n event.preventDefault()\n cancel()\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n if (grabbed)\n void drop(key)\n else grab(key)\n return\n }\n\n if (grabbed) {\n // Взятая строка забирает стрелки себе: двигается она, а не фокус.\n if (event.key === forward || event.key === backward) {\n event.preventDefault()\n moveTargetBy(event.key === forward ? 1 : -1)\n }\n return\n }\n\n roving.handleNavigationKeys(event)\n}\n\n/** Фокус ушёл из списка — взятая строка не может остаться взятой навсегда. */\nfunction onFocusout(event: FocusEvent): void {\n if (sort.mode.value !== 'keyboard')\n return\n\n const next = event.relatedTarget as Node | null\n if (next && listEl.value?.contains(next))\n return\n\n cancel()\n}\n\nconst resolvedEmptyText = computed(() => props.emptyText ?? t('gr.sortable.empty', 'Nothing to sort yet'))\nconst resolvedLabel = computed(() => props.ariaLabel ?? t('gr.sortable.label', 'Sortable list'))\nconst handleLabel = computed(() => t('gr.sortable.handle', 'Reorder'))\n\nconst listStyle = computed(() => (props.maxHeight === undefined\n ? undefined\n : { maxHeight: typeof props.maxHeight === 'number' ? `${props.maxHeight}px` : props.maxHeight, overflow: 'auto' }))\n\ndefineExpose({\n /** Программная перестановка — та же, что делает перенос. */\n move: (from: number, to: number) => applyMove(from, to),\n focusItem: (index: number) => roving.focusKey(keys.value[index]),\n})\n\nif (__GR_DEV__) {\n watchEffect(() => {\n if (!Array.isArray(props.modelValue)) {\n console.warn(\n `[granularity] GrSortableList: обязательный проп \\`modelValue\\` должен быть массивом — получено ${String(props.modelValue)}.`,\n )\n }\n })\n}\n</script>\n\n<template>\n <GrCard :variant=\"variant\">\n <div\n ref=\"listEl\"\n role=\"list\"\n data-gr-sortable\n :aria-label=\"resolvedLabel\"\n :aria-disabled=\"disabled ? 'true' : undefined\"\n :class=\"[listClass[orientation], divided ? dividedClass : '']\"\n :style=\"listStyle\"\n @focusout=\"onFocusout\"\n >\n <div\n v-if=\"isEmpty\"\n data-gr-sortable-empty\n :class=\"emptyClass\"\n >\n <slot name=\"empty\">\n {{ resolvedEmptyText }}\n </slot>\n </div>\n\n <div\n v-for=\"(item, index) in modelValue\"\n v-else\n :key=\"keys[index]\"\n :ref=\"el => registerRow(keys[index], el)\"\n role=\"listitem\"\n data-gr-sortable-item\n :tabindex=\"disabled ? -1 : roving.tabindexFor(keys[index])\"\n :aria-roledescription=\"handleLabel\"\n :aria-posinset=\"index + 1\"\n :aria-setsize=\"count\"\n :aria-grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index] ? 'true' : undefined\"\n :class=\"rowClass(index, keys[index])\"\n @keydown=\"onRowKeydown($event, keys[index])\"\n @focus=\"roving.setActive(keys[index])\"\n @pointerdown=\"handleOnly ? undefined : startPointer(keys[index], $event)\"\n >\n <button\n type=\"button\"\n tabindex=\"-1\"\n data-gr-sortable-handle\n :class=\"[handleClass, disabled ? handleDisabledClass : '']\"\n :aria-label=\"handleLabel\"\n :disabled=\"disabled\"\n @pointerdown=\"startPointer(keys[index], $event)\"\n @click.stop\n >\n <slot\n name=\"handle\"\n :item=\"item\"\n :index=\"index\"\n :disabled=\"disabled\"\n >\n <IconGripVertical class=\"block\" aria-hidden=\"true\" />\n </slot>\n </button>\n\n <div :class=\"contentClass\">\n <slot\n name=\"item\"\n :item=\"item\"\n :index=\"index\"\n :dragging=\"sort.mode.value === 'pointer' && sort.source.value === keys[index]\"\n :grabbed=\"sort.mode.value === 'keyboard' && sort.source.value === keys[index]\"\n >\n {{ item }}\n </slot>\n </div>\n </div>\n </div>\n </GrCard>\n</template>\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
1
|
+
import { t as GrSortableList_default } from "./chunks/GrSortableList-ChH9nQBv.js";
|
|
2
|
+
import { n as grSortableListSafelist, t as grSortableListConfig } from "../../chunks/config-BTxmdRzT.js";
|
|
3
3
|
export { GrSortableList_default as GrSortableList, GrSortableList_default as default, grSortableListConfig, grSortableListSafelist };
|