@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":"hotkeySequence-BTHIM_5A.js","names":[],"sources":["../../src/internal/hotkeySequence.ts"],"sourcesContent":["import type { ParsedHotkeyCombo } from './keyboard'\nimport { matchesHotkeyCombo } from './keyboard'\n\n/**\n * Сопоставление последовательностей клавиш («G, затем I»).\n *\n * Хранится буфер недавних нажатий, а хвост буфера сверяется с шагами хоткея.\n * Буфер, а не автомат на каждый хоткей: цепочки с общим началом (`g i` и `g p`)\n * буфер разводит сам, тогда как набор автоматов пришлось бы синхронизировать\n * между собой — и первый же сброс одного из них ронял бы второй.\n */\n\n/** Пауза, после которой набранное забывается. */\nexport const HOTKEY_SEQUENCE_TIMEOUT_MS = 1000\n\nconst MODIFIER_KEYS = new Set(['Shift', 'Control', 'Alt', 'Meta', 'AltGraph', 'CapsLock'])\n\nexport interface HotkeySequenceTracker {\n /**\n * Принять нажатие. Возвращает `false`, если событие в буфер не пошло —\n * потребителю это говорит, что состояние не изменилось.\n */\n push: (event: KeyboardEvent, now: number) => boolean\n /** Совпадает ли хвост буфера со всеми шагами. */\n matches: (steps: readonly ParsedHotkeyCombo[], apple?: boolean) => boolean\n reset: () => void\n}\n\nexport function createHotkeySequenceTracker(\n timeoutMs: number = HOTKEY_SEQUENCE_TIMEOUT_MS,\n): HotkeySequenceTracker {\n let buffer: KeyboardEvent[] = []\n let lastAt = 0\n\n function push(event: KeyboardEvent, now: number): boolean {\n /*\n * Нажатия одних модификаторов в буфер не идут: `Shift` перед `I` — часть\n * самого `I`, а не отдельный шаг, и попав в буфер он рвал бы цепочку.\n */\n if (MODIFIER_KEYS.has(event.key))\n return false\n\n // Пауза стирает буфер целиком, а не по одному событию: «полшага назад» —\n // состояние, которого пользователь не видит и предсказать не может.\n if (buffer.length > 0 && now - lastAt > timeoutMs)\n buffer = []\n\n buffer.push(event)\n lastAt = now\n\n // Длиннее самой длинной мыслимой цепочки буфер держать незачем.\n if (buffer.length > 8)\n buffer = buffer.slice(-8)\n\n return true\n }\n\n function matches(steps: readonly ParsedHotkeyCombo[], apple?: boolean): boolean {\n if (steps.length === 0 || buffer.length < steps.length)\n return false\n\n const tail = buffer.slice(-steps.length)\n\n return tail.every((event, index) => matchesHotkeyCombo(event, steps[index], apple))\n }\n\n return {\n push,\n matches,\n reset: () => {\n buffer = []\n lastAt = 0\n },\n }\n}\n"],"mappings":";;;;;;;;;;;AAaA,IAAa,6BAA6B;AAE1C,IAAM,gCAAgB,IAAI,IAAI;CAAC;CAAS;CAAW;CAAO;CAAQ;CAAY;AAAU,CAAC;AAazF,SAAgB,4BACd,YAAoB,4BACG;CACvB,IAAI,SAA0B,CAAC;CAC/B,IAAI,SAAS;CAEb,SAAS,KAAK,OAAsB,KAAsB;EAKxD,IAAI,cAAc,IAAI,MAAM,GAAG,GAC7B,OAAO;EAIT,IAAI,OAAO,SAAS,KAAK,MAAM,SAAS,WACtC,SAAS,CAAC;EAEZ,OAAO,KAAK,KAAK;EACjB,SAAS;EAGT,IAAI,OAAO,SAAS,GAClB,SAAS,OAAO,MAAM,EAAE;EAE1B,OAAO;CACT;CAEA,SAAS,QAAQ,OAAqC,OAA0B;EAC9E,IAAI,MAAM,WAAW,KAAK,OAAO,SAAS,MAAM,QAC9C,OAAO;EAIT,OAFa,OAAO,MAAM,CAAC,MAAM,MAE1B,CAAA,CAAK,OAAO,OAAO,UAAU,mBAAmB,OAAO,MAAM,QAAQ,KAAK,CAAC;CACpF;CAEA,OAAO;EACL;EACA;EACA,aAAa;GACX,SAAS,CAAC;GACV,SAAS;EACX;CACF;AACF"}
|
|
@@ -66,5 +66,74 @@ function shiftSatisfied(event, expectedKey, expectShift) {
|
|
|
66
66
|
if (!event.shiftKey) return true;
|
|
67
67
|
return expectedKey.length === 1 && !/\p{L}/u.test(expectedKey) && event.key === expectedKey;
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Имя клавиши в том виде, в каком его даёт `KeyboardEvent.key`.
|
|
71
|
+
*
|
|
72
|
+
* Регистр многобуквенных имён сохраняется (`ArrowUp`, `F6`): их сравнивают
|
|
73
|
+
* посимвольно. Одиночные символы приводятся к нижнему регистру — верхний у них
|
|
74
|
+
* означает Shift, а он объявляется отдельным токеном.
|
|
75
|
+
*/
|
|
76
|
+
function normalizeHotkeyToken(token) {
|
|
77
|
+
const t = token.trim().toLowerCase();
|
|
78
|
+
if (t === "esc" || t === "escape") return "Escape";
|
|
79
|
+
if (t === "space") return " ";
|
|
80
|
+
if (t.length === 1) return t;
|
|
81
|
+
return token.trim();
|
|
82
|
+
}
|
|
83
|
+
function parseHotkeyCombo(combo) {
|
|
84
|
+
const parts = combo.split("+").map((part) => part.trim()).filter(Boolean);
|
|
85
|
+
const keyToken = parts.at(-1);
|
|
86
|
+
if (!keyToken) return null;
|
|
87
|
+
const parsed = {
|
|
88
|
+
key: normalizeHotkeyToken(keyToken),
|
|
89
|
+
ctrl: false,
|
|
90
|
+
meta: false,
|
|
91
|
+
alt: false,
|
|
92
|
+
shift: false,
|
|
93
|
+
mod: false
|
|
94
|
+
};
|
|
95
|
+
for (const part of parts.slice(0, -1)) {
|
|
96
|
+
const token = part.toLowerCase();
|
|
97
|
+
if (token === "mod") parsed.mod = true;
|
|
98
|
+
else if (token === "ctrl" || token === "control") parsed.ctrl = true;
|
|
99
|
+
else if (token === "meta" || token === "cmd" || token === "command" || token === "⌘") parsed.meta = true;
|
|
100
|
+
else if (token === "alt" || token === "option") parsed.alt = true;
|
|
101
|
+
else if (token === "shift") parsed.shift = true;
|
|
102
|
+
}
|
|
103
|
+
return parsed;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Последовательность шагов: `'g i'` — «G, затем I», `'mod+k p'` — «⌘K, затем P».
|
|
107
|
+
*
|
|
108
|
+
* Шаги делятся пробелом, аккорд внутри шага — плюсом. Аккорд оказывается
|
|
109
|
+
* частным случаем из одного шага, поэтому отдельного пути для него нет: `'mod+k'`
|
|
110
|
+
* разбирается тем же вызовом и даёт массив длины один.
|
|
111
|
+
*
|
|
112
|
+
* Сам пробел как клавиша пишется словом `space` и с разделителем не спорит.
|
|
113
|
+
*/
|
|
114
|
+
function parseHotkeySequence(value) {
|
|
115
|
+
const steps = [];
|
|
116
|
+
for (const part of value.split(/\s+/)) {
|
|
117
|
+
const combo = parseHotkeyCombo(part);
|
|
118
|
+
if (combo) steps.push(combo);
|
|
119
|
+
}
|
|
120
|
+
return steps;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Совпадает ли событие с шагом сочетания.
|
|
124
|
+
*
|
|
125
|
+
* Проверки IME-композиции здесь намеренно нет: у потребителей разный момент,
|
|
126
|
+
* когда её задавать. Директива спрашивает один раз на событие, до перебора всех
|
|
127
|
+
* своих хоткеев, — зашитая внутрь проверка повторялась бы на каждом.
|
|
128
|
+
*/
|
|
129
|
+
function matchesHotkeyCombo(event, combo, apple = isAppleDevice()) {
|
|
130
|
+
const expectMeta = combo.meta || combo.mod && apple;
|
|
131
|
+
const expectCtrl = combo.ctrl || combo.mod && !apple;
|
|
132
|
+
if (event.metaKey !== expectMeta) return false;
|
|
133
|
+
if (event.ctrlKey !== expectCtrl) return false;
|
|
134
|
+
if (event.altKey !== combo.alt) return false;
|
|
135
|
+
if (!shiftSatisfied(event, combo.key, combo.shift)) return false;
|
|
136
|
+
return eventMatchesKey(event, combo.key, { codeFallback: expectMeta || expectCtrl || combo.alt });
|
|
137
|
+
}
|
|
69
138
|
//#endregion
|
|
70
|
-
export {
|
|
139
|
+
export { parseHotkeySequence as a, matchesHotkeyCombo as i, isComposingEvent as n, isEditableTarget as r, isAppleDevice as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard-BiFSgWLt.js","names":[],"sources":["../../src/internal/keyboard.ts"],"sourcesContent":["/**\n * Общие примитивы разбора клавиатурных событий: IME-композиция и матчинг\n * клавиш, не зависящий от раскладки.\n *\n * Живут в `internal/`, а не рядом с потребителем: одно и то же нужно стеку\n * оверлеев (`composables/internal/overlayStack.ts`), директиве `v-hotkey` и\n * матчеру палитры (`components/shared/hotkey.ts`) — три слоя, у которых нет\n * общего предка ближе этого модуля.\n */\n\n/**\n * Событие пришло во время IME-композиции — обрабатывать его как команду нельзя:\n * Esc здесь отменяет композицию, Enter — коммитит её, стрелки ходят по\n * кандидатам. `keyCode === 229` — страховка для движков, которые не выставляют\n * `isComposing` (исторически WebKit/старый Chromium).\n */\nexport function isComposingEvent(event: KeyboardEvent): boolean {\n return event.isComposing || event.keyCode === 229\n}\n\n/**\n * Фокус на контроле, который сам распоряжается печатными клавишами.\n *\n * Нужен всем, кто вешает клавиатурные команды на контейнер: горячая клавиша и\n * typeahead меню обязаны молчать, пока пользователь печатает в поле внутри них.\n */\nexport function isEditableTarget(target: EventTarget | null): boolean {\n if (!(target instanceof HTMLElement))\n return false\n if (target.isContentEditable)\n return true\n\n const tag = target.tagName\n return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT'\n}\n\n/**\n * macOS определяем по платформе: от неё зависит, чем является токен `mod` —\n * Cmd или Ctrl. Живёт здесь, а не рядом с одним потребителем: то же нужно\n * `GrKbd` (что показать), палитре (что слушать) и директиве `v-hotkey`.\n */\nexport function isAppleDevice(): boolean {\n if (typeof navigator === 'undefined')\n return false\n return /Mac|iPhone|iPad|iPod/.test(navigator.platform || navigator.userAgent)\n}\n\n/** Физический код клавиши для латинской буквы или цифры; иначе `null`. */\nexport function codeForChar(ch: string): string | null {\n if (/^[a-z]$/i.test(ch))\n return `Key${ch.toUpperCase()}`\n if (/^\\d$/.test(ch))\n return `Digit${ch}`\n return null\n}\n\nexport interface EventMatchesKeyOptions {\n /**\n * Дополнительно матчить по физическому коду (`event.code`), когда `event.key`\n * не совпал. Нужен комбинациям с модификаторами: на нелатинской раскладке\n * Ctrl/Cmd+K приходит как `key: 'л'`, и без кода сочетание мертво. Для\n * одиночных клавиш без модификаторов фолбэк не включается — там семантика\n * печатная, а не позиционная.\n */\n codeFallback?: boolean\n}\n\n/** Совпадает ли нажатая клавиша с ожидаемой (`'k'`, `'Escape'`, `' '`). */\nexport function eventMatchesKey(\n event: KeyboardEvent,\n expected: string,\n options: EventMatchesKeyOptions = {},\n): boolean {\n if (expected === ' ')\n return event.key === ' ' || event.key === 'Spacebar'\n\n if (expected.length === 1) {\n if (event.key.toLowerCase() === expected.toLowerCase())\n return true\n return Boolean(options.codeFallback) && codeForChar(expected) === event.code\n }\n\n return event.key === expected\n}\n\n/**\n * Удовлетворяет ли состояние Shift ожиданиям комбинации.\n *\n * Объявленный `shift` требует нажатого Shift. Необъявленный — запрещает его\n * с одним исключением: символ, который без Shift не набрать (`?`, `+`, `:`),\n * легален вместе с ним, когда `event.key` уже равен ожидаемому символу.\n * Буквы под исключение не попадают: `'a'` и Shift+A — разные намерения.\n */\nexport function shiftSatisfied(event: KeyboardEvent, expectedKey: string, expectShift: boolean): boolean {\n if (expectShift)\n return event.shiftKey\n if (!event.shiftKey)\n return true\n\n return expectedKey.length === 1 && !/\\p{L}/u.test(expectedKey) && event.key === expectedKey\n}\n\n/**\n * Разбор строки сочетания — один на пакет.\n *\n * Синтаксис `mod+shift+k` читают и директива `v-hotkey`, и `GrCommandPalette`\n * со своим слушателем на `window`. Разборов было два, почти одинаковых, и они\n * молча разошлись: `esc` и `space` директива приводила к `Escape` и пробелу, а\n * палитра оставляла как есть — то есть сравнивала `event.key === 'esc'`, что не\n * бывает истиной никогда, и такой хоткей не срабатывал вовсе.\n */\nexport interface ParsedHotkeyCombo {\n key: string\n ctrl: boolean\n meta: boolean\n alt: boolean\n shift: boolean\n /** `mod` — Cmd на macOS, Ctrl на остальных платформах. */\n mod: boolean\n}\n\n/**\n * Имя клавиши в том виде, в каком его даёт `KeyboardEvent.key`.\n *\n * Регистр многобуквенных имён сохраняется (`ArrowUp`, `F6`): их сравнивают\n * посимвольно. Одиночные символы приводятся к нижнему регистру — верхний у них\n * означает Shift, а он объявляется отдельным токеном.\n */\nexport function normalizeHotkeyToken(token: string): string {\n const t = token.trim().toLowerCase()\n\n if (t === 'esc' || t === 'escape')\n return 'Escape'\n if (t === 'space')\n return ' '\n if (t.length === 1)\n return t\n\n return token.trim()\n}\n\nexport function parseHotkeyCombo(combo: string): ParsedHotkeyCombo | null {\n const parts = combo.split('+').map(part => part.trim()).filter(Boolean)\n\n const keyToken = parts.at(-1)\n if (!keyToken)\n return null\n\n const parsed: ParsedHotkeyCombo = {\n key: normalizeHotkeyToken(keyToken),\n ctrl: false,\n meta: false,\n alt: false,\n shift: false,\n mod: false,\n }\n\n for (const part of parts.slice(0, -1)) {\n const token = part.toLowerCase()\n\n if (token === 'mod')\n parsed.mod = true\n else if (token === 'ctrl' || token === 'control')\n parsed.ctrl = true\n else if (token === 'meta' || token === 'cmd' || token === 'command' || token === '⌘')\n parsed.meta = true\n else if (token === 'alt' || token === 'option')\n parsed.alt = true\n else if (token === 'shift')\n parsed.shift = true\n }\n\n return parsed\n}\n\n/**\n * Последовательность шагов: `'g i'` — «G, затем I», `'mod+k p'` — «⌘K, затем P».\n *\n * Шаги делятся пробелом, аккорд внутри шага — плюсом. Аккорд оказывается\n * частным случаем из одного шага, поэтому отдельного пути для него нет: `'mod+k'`\n * разбирается тем же вызовом и даёт массив длины один.\n *\n * Сам пробел как клавиша пишется словом `space` и с разделителем не спорит.\n */\nexport function parseHotkeySequence(value: string): ParsedHotkeyCombo[] {\n const steps: ParsedHotkeyCombo[] = []\n\n for (const part of value.split(/\\s+/)) {\n const combo = parseHotkeyCombo(part)\n if (combo)\n steps.push(combo)\n }\n\n return steps\n}\n\n/**\n * Совпадает ли событие с шагом сочетания.\n *\n * Проверки IME-композиции здесь намеренно нет: у потребителей разный момент,\n * когда её задавать. Директива спрашивает один раз на событие, до перебора всех\n * своих хоткеев, — зашитая внутрь проверка повторялась бы на каждом.\n */\nexport function matchesHotkeyCombo(\n event: KeyboardEvent,\n combo: ParsedHotkeyCombo,\n apple = isAppleDevice(),\n): boolean {\n const expectMeta = combo.meta || (combo.mod && apple)\n const expectCtrl = combo.ctrl || (combo.mod && !apple)\n\n if (event.metaKey !== expectMeta)\n return false\n if (event.ctrlKey !== expectCtrl)\n return false\n if (event.altKey !== combo.alt)\n return false\n if (!shiftSatisfied(event, combo.key, combo.shift))\n return false\n\n /*\n * Комбинация с модификаторами матчится и по физическому коду: на нелатинской\n * раскладке `mod+K` приходит как `key: 'л'`, и без кода сочетание мертво.\n * Одиночная клавиша — печатная, её раскладка и определяет.\n */\n return eventMatchesKey(event, combo.key, {\n codeFallback: expectMeta || expectCtrl || combo.alt,\n })\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { useVirtualList } from "../composables/useVirtualList.js";
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
//#region src/components/shared/optionPanel.ts
|
|
4
|
+
/**
|
|
5
|
+
* Плоский набор → строки для рендера: заголовок группы не сосед опций, а их
|
|
6
|
+
* контейнер.
|
|
7
|
+
*
|
|
8
|
+
* Прямыми потомками `role="listbox"` обязаны быть только опции, иначе диктор
|
|
9
|
+
* считает заголовок таким же элементом выбора. Имя группе даёт `aria-labelledby`
|
|
10
|
+
* на её заголовке.
|
|
11
|
+
*
|
|
12
|
+
* `windowed` описывает срез: при виртуализации окно может начаться серединой
|
|
13
|
+
* группы, и такая группа открывается **без** видимого заголовка — иначе её
|
|
14
|
+
* опции оказались бы прямыми детьми listbox'а и потеряли имя набора.
|
|
15
|
+
*/
|
|
16
|
+
function buildOptionPanelRows(items, options = {}) {
|
|
17
|
+
const offset = options.offset ?? 0;
|
|
18
|
+
const rows = [];
|
|
19
|
+
let currentGroup;
|
|
20
|
+
items.forEach((item, position) => {
|
|
21
|
+
const index = position + offset;
|
|
22
|
+
if (item.kind === "group") {
|
|
23
|
+
currentGroup = {
|
|
24
|
+
kind: "group",
|
|
25
|
+
label: item.label,
|
|
26
|
+
key: item.key,
|
|
27
|
+
options: [],
|
|
28
|
+
...options.windowed ? {
|
|
29
|
+
labelVisible: true,
|
|
30
|
+
labelIndex: index
|
|
31
|
+
} : {}
|
|
32
|
+
};
|
|
33
|
+
rows.push(currentGroup);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const row = {
|
|
37
|
+
option: item.option,
|
|
38
|
+
key: item.key,
|
|
39
|
+
index
|
|
40
|
+
};
|
|
41
|
+
if (item.groupKey) {
|
|
42
|
+
if (!currentGroup || currentGroup.key !== item.groupKey) {
|
|
43
|
+
if (!options.windowed) {
|
|
44
|
+
currentGroup = void 0;
|
|
45
|
+
rows.push({
|
|
46
|
+
kind: "option",
|
|
47
|
+
...row
|
|
48
|
+
});
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
currentGroup = {
|
|
52
|
+
kind: "group",
|
|
53
|
+
label: options.groupLabels?.get(item.groupKey) ?? "",
|
|
54
|
+
key: item.groupKey,
|
|
55
|
+
options: [],
|
|
56
|
+
labelVisible: false
|
|
57
|
+
};
|
|
58
|
+
rows.push(currentGroup);
|
|
59
|
+
}
|
|
60
|
+
currentGroup.options.push(row);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
currentGroup = void 0;
|
|
64
|
+
rows.push({
|
|
65
|
+
kind: "option",
|
|
66
|
+
...row
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
return rows;
|
|
70
|
+
}
|
|
71
|
+
/** Оценки высоты строк: опция крупнее заголовка группы. Уточняются замером. */
|
|
72
|
+
var OPTION_SIZE_ESTIMATE = 36;
|
|
73
|
+
var GROUP_LABEL_SIZE_ESTIMATE = 28;
|
|
74
|
+
/**
|
|
75
|
+
* Виртуализация панели с группами.
|
|
76
|
+
*
|
|
77
|
+
* Набор — `[ведущая строка?] + panelItems`, то есть заголовки групп идут в нём
|
|
78
|
+
* наравне с опциями: на экране они занимают такую же строку. Вложенную
|
|
79
|
+
* структуру групп рендер пересобирает уже из окна.
|
|
80
|
+
*/
|
|
81
|
+
function useOptionPanelVirtualization(options) {
|
|
82
|
+
const virtualEnabled = computed(() => options.enabled());
|
|
83
|
+
const addOffset = computed(() => options.hasLeadingRow.value ? 1 : 0);
|
|
84
|
+
const virtualCount = computed(() => options.panelItems.value.length + addOffset.value);
|
|
85
|
+
const virtualizer = useVirtualList({
|
|
86
|
+
container: options.listboxEl,
|
|
87
|
+
count: () => virtualEnabled.value ? virtualCount.value : 0,
|
|
88
|
+
source: () => options.panelItems.value,
|
|
89
|
+
itemSize: (index) => {
|
|
90
|
+
return options.panelItems.value[index - addOffset.value]?.kind === "group" ? GROUP_LABEL_SIZE_ESTIMATE : OPTION_SIZE_ESTIMATE;
|
|
91
|
+
},
|
|
92
|
+
viewportSize: () => options.maxHeight()
|
|
93
|
+
});
|
|
94
|
+
/**
|
|
95
|
+
* Позиция и размер набора для каждой опции.
|
|
96
|
+
*
|
|
97
|
+
* Набор — не весь список: опция внутри `role="group"` принадлежит набору своей
|
|
98
|
+
* группы, и `aria-posinset` отсчитывается от неё. Опции вне групп вместе с
|
|
99
|
+
* ведущей строкой образуют набор уровня listbox'а.
|
|
100
|
+
*/
|
|
101
|
+
const optionSets = computed(() => {
|
|
102
|
+
const sizes = /* @__PURE__ */ new Map();
|
|
103
|
+
const positions = /* @__PURE__ */ new Map();
|
|
104
|
+
const ROOT = "__root__";
|
|
105
|
+
let rootCount = options.hasLeadingRow.value ? 1 : 0;
|
|
106
|
+
options.panelItems.value.forEach((item, index) => {
|
|
107
|
+
if (item.kind === "group") return;
|
|
108
|
+
const setKey = item.groupKey ?? ROOT;
|
|
109
|
+
const next = (sizes.get(setKey) ?? (setKey === ROOT ? rootCount : 0)) + 1;
|
|
110
|
+
sizes.set(setKey, next);
|
|
111
|
+
positions.set(index, {
|
|
112
|
+
setKey,
|
|
113
|
+
posInSet: next
|
|
114
|
+
});
|
|
115
|
+
if (setKey === ROOT) rootCount = next;
|
|
116
|
+
});
|
|
117
|
+
if (options.hasLeadingRow.value && !sizes.has(ROOT)) sizes.set(ROOT, rootCount);
|
|
118
|
+
return {
|
|
119
|
+
sizes,
|
|
120
|
+
positions,
|
|
121
|
+
ROOT
|
|
122
|
+
};
|
|
123
|
+
});
|
|
124
|
+
/** ARIA набора: объявляем только при виртуализации — иначе набор виден по DOM. */
|
|
125
|
+
function optionSetProps(index) {
|
|
126
|
+
if (!virtualEnabled.value) return void 0;
|
|
127
|
+
const position = optionSets.value.positions.get(index);
|
|
128
|
+
if (!position) return void 0;
|
|
129
|
+
return {
|
|
130
|
+
"aria-setsize": optionSets.value.sizes.get(position.setKey) ?? 1,
|
|
131
|
+
"aria-posinset": position.posInSet
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/** ARIA ведущей строки: она первая в наборе уровня listbox'а. */
|
|
135
|
+
const leadingRowSetProps = computed(() => {
|
|
136
|
+
if (!virtualEnabled.value) return void 0;
|
|
137
|
+
return {
|
|
138
|
+
"aria-setsize": optionSets.value.sizes.get(optionSets.value.ROOT) ?? 1,
|
|
139
|
+
"aria-posinset": 1
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
/** Метка группы по её ключу: окно может начаться ниже заголовка. */
|
|
143
|
+
const groupLabels = computed(() => {
|
|
144
|
+
const labels = /* @__PURE__ */ new Map();
|
|
145
|
+
for (const item of options.panelItems.value) if (item.kind === "group") labels.set(item.key, item.label);
|
|
146
|
+
return labels;
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
virtualEnabled,
|
|
150
|
+
addOffset,
|
|
151
|
+
scrollToIndex: (index) => virtualizer.scrollToIndex(index),
|
|
152
|
+
measure: (index, el) => virtualizer.measure(index, el),
|
|
153
|
+
optionSetProps,
|
|
154
|
+
leadingRowSetProps,
|
|
155
|
+
showLeadingRow: computed(() => {
|
|
156
|
+
if (!options.hasLeadingRow.value) return false;
|
|
157
|
+
return !virtualEnabled.value || virtualizer.range.value.start === 0;
|
|
158
|
+
}),
|
|
159
|
+
renderedPanelRows: computed(() => {
|
|
160
|
+
if (!virtualEnabled.value) return options.panelRows.value;
|
|
161
|
+
const items = options.panelItems.value;
|
|
162
|
+
const { start, end } = virtualizer.range.value;
|
|
163
|
+
const from = Math.max(0, start - addOffset.value);
|
|
164
|
+
const to = Math.min(items.length, Math.max(0, end - addOffset.value));
|
|
165
|
+
return buildOptionPanelRows(items.slice(from, to), {
|
|
166
|
+
offset: from,
|
|
167
|
+
windowed: true,
|
|
168
|
+
groupLabels: groupLabels.value
|
|
169
|
+
});
|
|
170
|
+
}),
|
|
171
|
+
listboxStyle: computed(() => {
|
|
172
|
+
const base = { maxHeight: `${options.maxHeight()}px` };
|
|
173
|
+
if (!virtualEnabled.value) return base;
|
|
174
|
+
return {
|
|
175
|
+
...base,
|
|
176
|
+
...virtualizer.spacerStyle.value
|
|
177
|
+
};
|
|
178
|
+
})
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
export { useOptionPanelVirtualization as n, buildOptionPanelRows as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optionPanel-DvY7Tv_4.js","names":[],"sources":["../../src/components/shared/optionPanel.ts"],"sourcesContent":["import type { ComputedRef, Ref } from 'vue'\nimport { computed } from 'vue'\n\nimport { useVirtualList } from '../../composables/useVirtualList'\n\n/**\n * Панель списка опций с группами — общая машинерия `GrSelect` и `GrAutocomplete`.\n *\n * Модуль ничего не знает об опции: ему нужны только её место в наборе и\n * принадлежность группе. Поэтому он обобщён по типу опции, а не по типу\n * значения — иначе у двух компонентов с разными шкалами значений получилось бы\n * две копии одного алгоритма, и разошлись бы они молча.\n *\n * Разделение ответственности: **что показывать** решает компонент (у каждого\n * своя фильтрация — локальный матчер, серверный ответ, «добавить своё»), а\n * **как это разложить** — здесь.\n */\n\n/** Элемент панели: заголовок группы либо опция. Плоский, в порядке отрисовки. */\nexport type OptionPanelItem<TOption>\n = | { kind: 'group', label: string, key: string }\n /** `groupKey` связывает опцию с заголовком её группы для скринридера. */\n | { kind: 'option', option: TOption, key: string, groupKey?: string }\n\nexport type OptionPanelOptionRow<TOption> = {\n option: TOption\n key: string\n /** Позиция в плоском наборе: из неё строится `id`, поэтому она не от значения. */\n index: number\n}\n\nexport type OptionPanelGroupRow<TOption> = {\n kind: 'group'\n label: string\n key: string\n options: OptionPanelOptionRow<TOption>[]\n /**\n * Есть ли в этой отрисовке видимый заголовок. При виртуализации окно может\n * начаться серединой группы: обёртка нужна всё равно, а заголовка нет —\n * имя тогда идёт в `aria-label`.\n */\n labelVisible?: boolean\n /** Позиция заголовка в наборе — по ней его замеряет виртуализатор. */\n labelIndex?: number\n}\n\nexport type OptionPanelRow<TOption>\n = | OptionPanelGroupRow<TOption>\n | ({ kind: 'option' } & OptionPanelOptionRow<TOption>)\n\n/**\n * Плоский набор → строки для рендера: заголовок группы не сосед опций, а их\n * контейнер.\n *\n * Прямыми потомками `role=\"listbox\"` обязаны быть только опции, иначе диктор\n * считает заголовок таким же элементом выбора. Имя группе даёт `aria-labelledby`\n * на её заголовке.\n *\n * `windowed` описывает срез: при виртуализации окно может начаться серединой\n * группы, и такая группа открывается **без** видимого заголовка — иначе её\n * опции оказались бы прямыми детьми listbox'а и потеряли имя набора.\n */\nexport function buildOptionPanelRows<TOption>(\n items: OptionPanelItem<TOption>[],\n options: {\n /** Смещение индексов, если срез начат не с нуля. */\n offset?: number\n windowed?: boolean\n /** Метки групп по ключу — нужны группе, чей заголовок остался выше окна. */\n groupLabels?: Map<string, string>\n } = {},\n): OptionPanelRow<TOption>[] {\n const offset = options.offset ?? 0\n const rows: OptionPanelRow<TOption>[] = []\n let currentGroup: OptionPanelGroupRow<TOption> | undefined\n\n items.forEach((item, position) => {\n const index = position + offset\n\n if (item.kind === 'group') {\n currentGroup = {\n kind: 'group',\n label: item.label,\n key: item.key,\n options: [],\n ...(options.windowed ? { labelVisible: true, labelIndex: index } : {}),\n }\n rows.push(currentGroup)\n return\n }\n\n const row: OptionPanelOptionRow<TOption> = { option: item.option, key: item.key, index }\n\n if (item.groupKey) {\n if (!currentGroup || currentGroup.key !== item.groupKey) {\n // Заголовок остался выше окна: группу всё равно открываем, имя берём из\n // словаря и объявляем через `aria-label`.\n if (!options.windowed) {\n currentGroup = undefined\n rows.push({ kind: 'option', ...row })\n return\n }\n\n currentGroup = {\n kind: 'group',\n label: options.groupLabels?.get(item.groupKey) ?? '',\n key: item.groupKey,\n options: [],\n labelVisible: false,\n }\n rows.push(currentGroup)\n }\n\n currentGroup.options.push(row)\n return\n }\n\n currentGroup = undefined\n rows.push({ kind: 'option', ...row })\n })\n\n return rows\n}\n\n/** Оценки высоты строк: опция крупнее заголовка группы. Уточняются замером. */\nconst OPTION_SIZE_ESTIMATE = 36\nconst GROUP_LABEL_SIZE_ESTIMATE = 28\n\nexport interface UseOptionPanelVirtualizationOptions<TOption> {\n panelItems: ComputedRef<OptionPanelItem<TOption>[]>\n panelRows: ComputedRef<OptionPanelRow<TOption>[]>\n /** Есть ли перед опциями строка «добавить своё»: она сдвигает весь набор. */\n hasLeadingRow: ComputedRef<boolean>\n /** Контейнер списка: по нему считается окно и замеряются строки. */\n listboxEl: Ref<HTMLElement | null>\n enabled: () => boolean\n maxHeight: () => number\n}\n\nexport interface OptionPanelVirtualization<TOption> {\n virtualEnabled: ComputedRef<boolean>\n /** Сдвиг набора: ведущая строка стоит перед опциями. */\n addOffset: ComputedRef<number>\n scrollToIndex: (index: number) => void\n /** Замер строки: оценка высоты уточняется по реальному узлу. */\n measure: (index: number, el: Element | null) => void\n /** ARIA набора для опции; вне виртуализации набор виден по DOM. */\n optionSetProps: (index: number) => Record<string, number> | undefined\n leadingRowSetProps: ComputedRef<Record<string, number> | undefined>\n showLeadingRow: ComputedRef<boolean>\n renderedPanelRows: ComputedRef<OptionPanelRow<TOption>[]>\n listboxStyle: ComputedRef<Record<string, string>>\n}\n\n/**\n * Виртуализация панели с группами.\n *\n * Набор — `[ведущая строка?] + panelItems`, то есть заголовки групп идут в нём\n * наравне с опциями: на экране они занимают такую же строку. Вложенную\n * структуру групп рендер пересобирает уже из окна.\n */\nexport function useOptionPanelVirtualization<TOption>(\n options: UseOptionPanelVirtualizationOptions<TOption>,\n): OptionPanelVirtualization<TOption> {\n const virtualEnabled = computed(() => options.enabled())\n const addOffset = computed(() => (options.hasLeadingRow.value ? 1 : 0))\n const virtualCount = computed(() => options.panelItems.value.length + addOffset.value)\n\n const virtualizer = useVirtualList({\n container: options.listboxEl,\n count: () => (virtualEnabled.value ? virtualCount.value : 0),\n // Фильтрация пересобирает набор строк — замеры прошлого набора невалидны.\n source: () => options.panelItems.value,\n itemSize: (index) => {\n const item = options.panelItems.value[index - addOffset.value]\n return item?.kind === 'group' ? GROUP_LABEL_SIZE_ESTIMATE : OPTION_SIZE_ESTIMATE\n },\n // Панель закрыта — контейнера в раскладке нет, `clientHeight` нулевой.\n viewportSize: () => options.maxHeight(),\n })\n\n /**\n * Позиция и размер набора для каждой опции.\n *\n * Набор — не весь список: опция внутри `role=\"group\"` принадлежит набору своей\n * группы, и `aria-posinset` отсчитывается от неё. Опции вне групп вместе с\n * ведущей строкой образуют набор уровня listbox'а.\n */\n const optionSets = computed(() => {\n const sizes = new Map<string, number>()\n const positions = new Map<number, { setKey: string, posInSet: number }>()\n const ROOT = '__root__'\n\n let rootCount = options.hasLeadingRow.value ? 1 : 0\n\n options.panelItems.value.forEach((item, index) => {\n if (item.kind === 'group')\n return\n\n const setKey = item.groupKey ?? ROOT\n const next = (sizes.get(setKey) ?? (setKey === ROOT ? rootCount : 0)) + 1\n sizes.set(setKey, next)\n positions.set(index, { setKey, posInSet: next })\n if (setKey === ROOT)\n rootCount = next\n })\n\n if (options.hasLeadingRow.value && !sizes.has(ROOT))\n sizes.set(ROOT, rootCount)\n\n return { sizes, positions, ROOT }\n })\n\n /** ARIA набора: объявляем только при виртуализации — иначе набор виден по DOM. */\n function optionSetProps(index: number): Record<string, number> | undefined {\n if (!virtualEnabled.value)\n return undefined\n\n const position = optionSets.value.positions.get(index)\n if (!position)\n return undefined\n\n return {\n 'aria-setsize': optionSets.value.sizes.get(position.setKey) ?? 1,\n 'aria-posinset': position.posInSet,\n }\n }\n\n /** ARIA ведущей строки: она первая в наборе уровня listbox'а. */\n const leadingRowSetProps = computed<Record<string, number> | undefined>(() => {\n if (!virtualEnabled.value)\n return undefined\n return {\n 'aria-setsize': optionSets.value.sizes.get(optionSets.value.ROOT) ?? 1,\n 'aria-posinset': 1,\n }\n })\n\n /** Метка группы по её ключу: окно может начаться ниже заголовка. */\n const groupLabels = computed(() => {\n const labels = new Map<string, string>()\n for (const item of options.panelItems.value) {\n if (item.kind === 'group')\n labels.set(item.key, item.label)\n }\n return labels\n })\n\n /** Видна ли ведущая строка: вне окна её рисовать нельзя — она элемент набора. */\n const showLeadingRow = computed(() => {\n if (!options.hasLeadingRow.value)\n return false\n return !virtualEnabled.value || virtualizer.range.value.start === 0\n })\n\n const renderedPanelRows = computed<OptionPanelRow<TOption>[]>(() => {\n if (!virtualEnabled.value)\n return options.panelRows.value\n\n const items = options.panelItems.value\n const { start, end } = virtualizer.range.value\n const from = Math.max(0, start - addOffset.value)\n const to = Math.min(items.length, Math.max(0, end - addOffset.value))\n\n return buildOptionPanelRows(items.slice(from, to), {\n offset: from,\n windowed: true,\n groupLabels: groupLabels.value,\n })\n })\n\n const listboxStyle = computed(() => {\n const base: Record<string, string> = { maxHeight: `${options.maxHeight()}px` }\n if (!virtualEnabled.value)\n return base\n\n return { ...base, ...virtualizer.spacerStyle.value }\n })\n\n return {\n virtualEnabled,\n addOffset,\n scrollToIndex: index => virtualizer.scrollToIndex(index),\n measure: (index, el) => virtualizer.measure(index, el),\n optionSetProps,\n leadingRowSetProps,\n showLeadingRow,\n renderedPanelRows,\n listboxStyle,\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//#region src/components/shared/overlayListPanel.ts
|
|
2
|
+
/**
|
|
3
|
+
* Панель списка, ужимающаяся до места, оставшегося до края вьюпорта.
|
|
4
|
+
*
|
|
5
|
+
* Слой позиционирования публикует замер в `--gr-floating-available-height`
|
|
6
|
+
* (`composables/useFloating.ts`), и панель берёт его потолком: собственный
|
|
7
|
+
* `dropdownMaxHeight` на коротком экране больше доступного места, а сжать
|
|
8
|
+
* панель не умеют ни `flip`, ни `shift` — низ списка просто уходит за край.
|
|
9
|
+
*
|
|
10
|
+
* Колонка обязательна вместе с потолком. Панель держит не только список, но и
|
|
11
|
+
* поле поиска, строку «добавить своё» и строки состояний; без `flex-col` и
|
|
12
|
+
* `min-h-0` у списка ужимался бы не он, а панель целиком — то есть вместо
|
|
13
|
+
* одного скролла вышло бы два.
|
|
14
|
+
*
|
|
15
|
+
* Модуль отдельный от `overlayPanelSurface` намеренно: ту поверхность берёт и
|
|
16
|
+
* `GrPopover` со своим потолком, а класс из общего модуля обязан объявить в
|
|
17
|
+
* safelist **каждый** импортёр, даже не пользующийся им.
|
|
18
|
+
*/
|
|
19
|
+
var overlayListPanelClass = "flex flex-col max-h-[var(--gr-floating-available-height,100vh)]";
|
|
20
|
+
//#endregion
|
|
21
|
+
export { overlayListPanelClass as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlayListPanel-D91jBCHU.js","names":[],"sources":["../../src/components/shared/overlayListPanel.ts"],"sourcesContent":["/**\n * Панель списка, ужимающаяся до места, оставшегося до края вьюпорта.\n *\n * Слой позиционирования публикует замер в `--gr-floating-available-height`\n * (`composables/useFloating.ts`), и панель берёт его потолком: собственный\n * `dropdownMaxHeight` на коротком экране больше доступного места, а сжать\n * панель не умеют ни `flip`, ни `shift` — низ списка просто уходит за край.\n *\n * Колонка обязательна вместе с потолком. Панель держит не только список, но и\n * поле поиска, строку «добавить своё» и строки состояний; без `flex-col` и\n * `min-h-0` у списка ужимался бы не он, а панель целиком — то есть вместо\n * одного скролла вышло бы два.\n *\n * Модуль отдельный от `overlayPanelSurface` намеренно: ту поверхность берёт и\n * `GrPopover` со своим потолком, а класс из общего модуля обязан объявить в\n * safelist **каждый** импортёр, даже не пользующийся им.\n */\nexport const overlayListPanelClass = 'flex flex-col max-h-[var(--gr-floating-available-height,100vh)]'\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,IAAa,wBAAwB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as isComposingEvent } from "./keyboard-BiFSgWLt.js";
|
|
2
2
|
import { n as provideGrDevLayers, t as emitGrDevEvent } from "./devHook-CiqijpNM.js";
|
|
3
3
|
//#region src/composables/internal/overlayStack.ts
|
|
4
4
|
var stack = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlayStack-DH4Z7am1.js","names":[],"sources":["../../src/composables/internal/overlayStack.ts"],"sourcesContent":["/**\n * Единый стек слоёв оверлеев: модалки, drawer'ы, просмотрщик, панели селектов,\n * дропдауны, подсказки — всё, что открывается поверх страницы.\n *\n * Список один, и порядок регистрации совпадает с порядком открытия, но из него\n * выводятся **две разные вершины**:\n *\n * - **Esc** → последний слой любого рода. Дропдаун, открытый внутри модалки,\n * обязан закрыться первым — пользователь видит верхним именно его.\n * - **`inert`** → ставится на модальные слои ниже последнего **модального**.\n * Немодальный слой сверху модалку не понижает: иначе окно ушло бы в `inert`\n * вместе со своим же открытым дропдауном и перестало отвечать.\n * - **высота** → `openModalCount()` поднимает floating-панель над окном, из\n * которого её открыли: телепортированная панель лежит рядом с корнем окна,\n * и статический `z-index` дропдауна оставлял её под ним.\n *\n * Вести эти две вершины в двух независимых реестрах нельзя: реестры описывают\n * один и тот же порядок и расходятся молча — достаточно оверлея, который\n * зарегистрировался в одном и забыл про другой.\n *\n * Ловушку фокуса стек не реализует сам, но питает её (`useFocusTrap`): он\n * видит слои из **разных деревьев рендера** (диалоги `useDialogService`\n * монтируются отдельным `render()` в `body`) и знает их корни.\n */\n\nimport type { GrLayerFocus, GrOverlaySnapshot } from '../../internal/devHook'\nimport { emitGrDevEvent, provideGrDevLayers } from '../../internal/devHook'\nimport { isComposingEvent } from '../../internal/keyboard'\n\nexport interface OverlayLayer {\n /** Уникальный id зарегистрированного слоя. */\n id: number\n /**\n * Модальный слой: блокирует страницу и участвует в вычислении `inert`.\n * Немодальные (поповеры, меню, подсказки) — только в очереди Esc.\n */\n modal: boolean\n /** Закрывать ли слой по Esc (реактивный геттер: `closeOnEsc` и подобные). */\n shouldClose: () => boolean\n /** Закрыть слой. */\n close: () => void\n /** Уведомление «этот модальный слой сейчас верхний». Только для `modal`. */\n setTopmost?: (isTopmost: boolean) => void\n /**\n * Позиция слоя среди модальных, от нуля. Только для `modal`.\n *\n * От неё считается высота: у всех модальных слоёв один токен\n * (`--gr-z-modal`), и без этой прибавки порядок отрисовки решал бы порядок\n * узлов в портале, а он задаётся **созданием** телепорта, а не открытием\n * слоя. Диалог, объявленный в шаблоне статически, вставал бы под окном,\n * открытым позже, — и получал бы `inert` от стека, оставаясь видимым.\n */\n setDepth?: (depth: number) => void\n /**\n * Корень слоя. Ловушка фокуса нижнего слоя обязана считать его своим:\n * панель селекта, открытого внутри модалки, телепортирована в `body` и лежит\n * вне DOM-поддерева окна, но для пользователя это тот же слой.\n */\n root?: () => HTMLElement | null\n /**\n * Компонент, открывший слой. Только для наблюдателя: стеку он не нужен, но\n * без него слой в панели — безымянный номер.\n */\n owner?: string | null\n /**\n * Картина фокуса глазами самого слоя. Геттер, а не значение: она меняется от\n * каждого клика, а спрашивают её только в момент снимка.\n */\n describeFocus?: () => GrLayerFocus | null\n}\n\nconst stack: OverlayLayer[] = []\nlet listening = false\nlet nextId = 1\n\n/**\n * Пересчитывает состояние модальных слоёв: кто верхний и на какой высоте.\n *\n * Обе величины выводятся из одного порядка, поэтому и считаются в одном месте:\n * разъедься они — «верхний» для `inert` и «верхний» для отрисовки перестали бы\n * совпадать, а это ровно тот дефект, из-за которого высота и заведена.\n */\nfunction syncModalLayers(): void {\n const modals = stack.filter(layer => layer.modal)\n const top = modals[modals.length - 1]\n\n modals.forEach((layer, index) => {\n layer.setTopmost?.(layer === top)\n layer.setDepth?.(index)\n })\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:sync', layers: snapshotLayers() })\n}\n\n/**\n * Снимок стека для dev-канала.\n *\n * Считается здесь же, где `syncModalLayers` раздаёт то же самое слоям, и по тем\n * же выражениям: разъедься они — наблюдатель показывал бы картину, которой в\n * пакете нет, и отладка по ней уводила бы в сторону.\n */\nfunction snapshotLayers(): GrOverlaySnapshot[] {\n const modals = stack.filter(layer => layer.modal)\n const topModal = modals[modals.length - 1]\n const top = stack[stack.length - 1]\n\n return stack.map(layer => ({\n id: layer.id,\n owner: layer.owner ?? null,\n focus: layer.describeFocus?.() ?? null,\n modal: layer.modal,\n topmostForEscape: layer === top,\n inert: layer.modal && layer !== topModal,\n depth: layer.modal ? modals.indexOf(layer) : null,\n closesOnEscape: layer.shouldClose(),\n }))\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape')\n return\n // Esc во время IME-композиции отменяет её, а не закрывает слой.\n if (isComposingEvent(event))\n return\n if (stack.length === 0)\n return\n\n const top = stack[stack.length - 1]\n\n // Гасим всегда, даже если верхний слой не закрывается по Esc: иначе нажатие\n // «провалится» на слой ниже и закроет не то, что видит пользователь.\n event.preventDefault()\n event.stopPropagation()\n if (typeof event.stopImmediatePropagation === 'function')\n event.stopImmediatePropagation()\n\n const closes = top.shouldClose()\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:escape', id: top.id, closed: closes })\n\n if (closes)\n top.close()\n}\n\nfunction startListening(): void {\n if (listening)\n return\n if (typeof window === 'undefined')\n return\n // Capture-фаза опережает любые локальные обработчики: Esc обязан достаться\n // верхнему слою, а не тому, кто первым подписался.\n window.addEventListener('keydown', handleKeydown, true)\n listening = true\n}\n\nfunction stopListening(): void {\n if (!listening)\n return\n if (typeof window === 'undefined')\n return\n window.removeEventListener('keydown', handleKeydown, true)\n listening = false\n}\n\n/** Регистрирует слой как верхний и возвращает его id. */\nexport function pushOverlayLayer(layer: Omit<OverlayLayer, 'id'>): number {\n const id = nextId++\n stack.push({ ...layer, id })\n startListening()\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:push', id, modal: layer.modal, owner: layer.owner ?? null })\n\n syncModalLayers()\n return id\n}\n\n/** Снимает слой со стека (при закрытии или размонтировании). */\nexport function removeOverlayLayer(id: number): void {\n const index = stack.findIndex(item => item.id === id)\n if (index >= 0)\n stack.splice(index, 1)\n if (stack.length === 0)\n stopListening()\n\n if (__GR_DEV__ && index >= 0)\n emitGrDevEvent({ type: 'overlay:remove', id })\n\n syncModalLayers()\n}\n\n/**\n * Корни слоёв, открытых **поверх** данного.\n *\n * Ими ловушка фокуса дополняет свой контейнер: всё, что открыто изнутри слоя\n * (панель селекта, меню, подсказка), лежит в `body` отдельным поддеревом, и\n * без этого списка ловушка утаскивала бы из него фокус обратно в окно.\n */\nexport function layerRootsAbove(id: number): HTMLElement[] {\n const index = stack.findIndex(layer => layer.id === id)\n if (index < 0)\n return []\n\n return stack\n .slice(index + 1)\n .map(layer => layer.root?.() ?? null)\n .filter((root): root is HTMLElement => root !== null)\n}\n\n/** Корни всех зарегистрированных слоёв. */\nexport function allLayerRoots(): HTMLElement[] {\n return stack\n .map(layer => layer.root?.() ?? null)\n .filter((root): root is HTMLElement => root !== null)\n}\n\n/** Сколько слоёв открыто. Для тестов и диагностики. */\nexport function overlayStackSize(): number {\n return stack.length\n}\n\n/**\n * Сколько **модальных** слоёв открыто прямо сейчас.\n *\n * Читает `useFloating`, когда считает высоту панели: панель телепортирована в\n * общий портал и лежит рядом с корнем окна, а не внутри него, поэтому свой\n * стек-контекст её не спасает — высоту приходится брать из порядка слоёв.\n */\nexport function openModalCount(): number {\n return stack.reduce((count, layer) => count + (layer.modal ? 1 : 0), 0)\n}\n\n/**\n * Высота слоя, лежащего внутри открытого окна.\n *\n * Панель телепортирована в общий портал и становится **соседом** корня окна, а\n * не его потомком: собственный stacking-контекст окна её не накрывает, и\n * статический `--gr-z-dropdown` (1000) оставлял её под окном (1100). Слагаемое —\n * число модальных слоёв, поэтому панель встаёт над самым верхним из них, а\n * полноэкранная загрузка (1150) и тосты (1200) остаются сверху.\n *\n * Обратный случай — «поповер снаружи не должен перекрывать окно» — не страдает:\n * пока окно открыто, страница под ним в `inert`, и открыть там поповер нечем.\n */\nexport function floatingLayerZIndex(zIndexVar: string): string {\n const modals = openModalCount()\n\n return modals > 0 ? `calc(var(--gr-z-modal) + ${modals})` : `var(${zIndexVar})`\n}\n\n/**\n * Высота самого модального слоя: базовый токен плюс его позиция в стеке.\n *\n * Нулевая глубина оставляет чистый `var(...)` — у единственного окна на экране\n * в стилях не появляется лишней арифметики.\n */\nexport function modalLayerZIndex(depth: number, zIndexVar = '--gr-z-modal'): string {\n return depth > 0 ? `calc(var(${zIndexVar}) + ${depth})` : `var(${zIndexVar})`\n}\n\n// Читалка регистрируется на загрузке модуля: наблюдатель может подключиться\n// раньше первого открытого слоя, и тогда спросить ему было бы нечего.\nif (__GR_DEV__)\n provideGrDevLayers(snapshotLayers)\n\n/** Тестовая/служебная очистка стека. */\nexport function resetOverlayStack(): void {\n stack.splice(0, stack.length)\n stopListening()\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:sync', layers: [] })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"overlayStack-DCkCgXZP.js","names":[],"sources":["../../src/composables/internal/overlayStack.ts"],"sourcesContent":["/**\n * Единый стек слоёв оверлеев: модалки, drawer'ы, просмотрщик, панели селектов,\n * дропдауны, подсказки — всё, что открывается поверх страницы.\n *\n * Список один, и порядок регистрации совпадает с порядком открытия, но из него\n * выводятся **две разные вершины**:\n *\n * - **Esc** → последний слой любого рода. Дропдаун, открытый внутри модалки,\n * обязан закрыться первым — пользователь видит верхним именно его.\n * - **`inert`** → ставится на модальные слои ниже последнего **модального**.\n * Немодальный слой сверху модалку не понижает: иначе окно ушло бы в `inert`\n * вместе со своим же открытым дропдауном и перестало отвечать.\n * - **высота** → `openModalCount()` поднимает floating-панель над окном, из\n * которого её открыли: телепортированная панель лежит рядом с корнем окна,\n * и статический `z-index` дропдауна оставлял её под ним.\n *\n * Вести эти две вершины в двух независимых реестрах нельзя: реестры описывают\n * один и тот же порядок и расходятся молча — достаточно оверлея, который\n * зарегистрировался в одном и забыл про другой.\n *\n * Ловушку фокуса стек не реализует сам, но питает её (`useFocusTrap`): он\n * видит слои из **разных деревьев рендера** (диалоги `useDialogService`\n * монтируются отдельным `render()` в `body`) и знает их корни.\n */\n\nimport type { GrLayerFocus, GrOverlaySnapshot } from '../../internal/devHook'\nimport { emitGrDevEvent, provideGrDevLayers } from '../../internal/devHook'\nimport { isComposingEvent } from '../../internal/keyboard'\n\nexport interface OverlayLayer {\n /** Уникальный id зарегистрированного слоя. */\n id: number\n /**\n * Модальный слой: блокирует страницу и участвует в вычислении `inert`.\n * Немодальные (поповеры, меню, подсказки) — только в очереди Esc.\n */\n modal: boolean\n /** Закрывать ли слой по Esc (реактивный геттер: `closeOnEsc` и подобные). */\n shouldClose: () => boolean\n /** Закрыть слой. */\n close: () => void\n /** Уведомление «этот модальный слой сейчас верхний». Только для `modal`. */\n setTopmost?: (isTopmost: boolean) => void\n /**\n * Позиция слоя среди модальных, от нуля. Только для `modal`.\n *\n * От неё считается высота: у всех модальных слоёв один токен\n * (`--gr-z-modal`), и без этой прибавки порядок отрисовки решал бы порядок\n * узлов в портале, а он задаётся **созданием** телепорта, а не открытием\n * слоя. Диалог, объявленный в шаблоне статически, вставал бы под окном,\n * открытым позже, — и получал бы `inert` от стека, оставаясь видимым.\n */\n setDepth?: (depth: number) => void\n /**\n * Корень слоя. Ловушка фокуса нижнего слоя обязана считать его своим:\n * панель селекта, открытого внутри модалки, телепортирована в `body` и лежит\n * вне DOM-поддерева окна, но для пользователя это тот же слой.\n */\n root?: () => HTMLElement | null\n /**\n * Компонент, открывший слой. Только для наблюдателя: стеку он не нужен, но\n * без него слой в панели — безымянный номер.\n */\n owner?: string | null\n /**\n * Картина фокуса глазами самого слоя. Геттер, а не значение: она меняется от\n * каждого клика, а спрашивают её только в момент снимка.\n */\n describeFocus?: () => GrLayerFocus | null\n}\n\nconst stack: OverlayLayer[] = []\nlet listening = false\nlet nextId = 1\n\n/**\n * Пересчитывает состояние модальных слоёв: кто верхний и на какой высоте.\n *\n * Обе величины выводятся из одного порядка, поэтому и считаются в одном месте:\n * разъедься они — «верхний» для `inert` и «верхний» для отрисовки перестали бы\n * совпадать, а это ровно тот дефект, из-за которого высота и заведена.\n */\nfunction syncModalLayers(): void {\n const modals = stack.filter(layer => layer.modal)\n const top = modals[modals.length - 1]\n\n modals.forEach((layer, index) => {\n layer.setTopmost?.(layer === top)\n layer.setDepth?.(index)\n })\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:sync', layers: snapshotLayers() })\n}\n\n/**\n * Снимок стека для dev-канала.\n *\n * Считается здесь же, где `syncModalLayers` раздаёт то же самое слоям, и по тем\n * же выражениям: разъедься они — наблюдатель показывал бы картину, которой в\n * пакете нет, и отладка по ней уводила бы в сторону.\n */\nfunction snapshotLayers(): GrOverlaySnapshot[] {\n const modals = stack.filter(layer => layer.modal)\n const topModal = modals[modals.length - 1]\n const top = stack[stack.length - 1]\n\n return stack.map(layer => ({\n id: layer.id,\n owner: layer.owner ?? null,\n focus: layer.describeFocus?.() ?? null,\n modal: layer.modal,\n topmostForEscape: layer === top,\n inert: layer.modal && layer !== topModal,\n depth: layer.modal ? modals.indexOf(layer) : null,\n closesOnEscape: layer.shouldClose(),\n }))\n}\n\nfunction handleKeydown(event: KeyboardEvent): void {\n if (event.key !== 'Escape')\n return\n // Esc во время IME-композиции отменяет её, а не закрывает слой.\n if (isComposingEvent(event))\n return\n if (stack.length === 0)\n return\n\n const top = stack[stack.length - 1]\n\n // Гасим всегда, даже если верхний слой не закрывается по Esc: иначе нажатие\n // «провалится» на слой ниже и закроет не то, что видит пользователь.\n event.preventDefault()\n event.stopPropagation()\n if (typeof event.stopImmediatePropagation === 'function')\n event.stopImmediatePropagation()\n\n const closes = top.shouldClose()\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:escape', id: top.id, closed: closes })\n\n if (closes)\n top.close()\n}\n\nfunction startListening(): void {\n if (listening)\n return\n if (typeof window === 'undefined')\n return\n // Capture-фаза опережает любые локальные обработчики: Esc обязан достаться\n // верхнему слою, а не тому, кто первым подписался.\n window.addEventListener('keydown', handleKeydown, true)\n listening = true\n}\n\nfunction stopListening(): void {\n if (!listening)\n return\n if (typeof window === 'undefined')\n return\n window.removeEventListener('keydown', handleKeydown, true)\n listening = false\n}\n\n/** Регистрирует слой как верхний и возвращает его id. */\nexport function pushOverlayLayer(layer: Omit<OverlayLayer, 'id'>): number {\n const id = nextId++\n stack.push({ ...layer, id })\n startListening()\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:push', id, modal: layer.modal, owner: layer.owner ?? null })\n\n syncModalLayers()\n return id\n}\n\n/** Снимает слой со стека (при закрытии или размонтировании). */\nexport function removeOverlayLayer(id: number): void {\n const index = stack.findIndex(item => item.id === id)\n if (index >= 0)\n stack.splice(index, 1)\n if (stack.length === 0)\n stopListening()\n\n if (__GR_DEV__ && index >= 0)\n emitGrDevEvent({ type: 'overlay:remove', id })\n\n syncModalLayers()\n}\n\n/**\n * Корни слоёв, открытых **поверх** данного.\n *\n * Ими ловушка фокуса дополняет свой контейнер: всё, что открыто изнутри слоя\n * (панель селекта, меню, подсказка), лежит в `body` отдельным поддеревом, и\n * без этого списка ловушка утаскивала бы из него фокус обратно в окно.\n */\nexport function layerRootsAbove(id: number): HTMLElement[] {\n const index = stack.findIndex(layer => layer.id === id)\n if (index < 0)\n return []\n\n return stack\n .slice(index + 1)\n .map(layer => layer.root?.() ?? null)\n .filter((root): root is HTMLElement => root !== null)\n}\n\n/** Корни всех зарегистрированных слоёв. */\nexport function allLayerRoots(): HTMLElement[] {\n return stack\n .map(layer => layer.root?.() ?? null)\n .filter((root): root is HTMLElement => root !== null)\n}\n\n/** Сколько слоёв открыто. Для тестов и диагностики. */\nexport function overlayStackSize(): number {\n return stack.length\n}\n\n/**\n * Сколько **модальных** слоёв открыто прямо сейчас.\n *\n * Читает `useFloating`, когда считает высоту панели: панель телепортирована в\n * общий портал и лежит рядом с корнем окна, а не внутри него, поэтому свой\n * стек-контекст её не спасает — высоту приходится брать из порядка слоёв.\n */\nexport function openModalCount(): number {\n return stack.reduce((count, layer) => count + (layer.modal ? 1 : 0), 0)\n}\n\n/**\n * Высота слоя, лежащего внутри открытого окна.\n *\n * Панель телепортирована в общий портал и становится **соседом** корня окна, а\n * не его потомком: собственный stacking-контекст окна её не накрывает, и\n * статический `--gr-z-dropdown` (1000) оставлял её под окном (1100). Слагаемое —\n * число модальных слоёв, поэтому панель встаёт над самым верхним из них, а\n * полноэкранная загрузка (1150) и тосты (1200) остаются сверху.\n *\n * Обратный случай — «поповер снаружи не должен перекрывать окно» — не страдает:\n * пока окно открыто, страница под ним в `inert`, и открыть там поповер нечем.\n */\nexport function floatingLayerZIndex(zIndexVar: string): string {\n const modals = openModalCount()\n\n return modals > 0 ? `calc(var(--gr-z-modal) + ${modals})` : `var(${zIndexVar})`\n}\n\n/**\n * Высота самого модального слоя: базовый токен плюс его позиция в стеке.\n *\n * Нулевая глубина оставляет чистый `var(...)` — у единственного окна на экране\n * в стилях не появляется лишней арифметики.\n */\nexport function modalLayerZIndex(depth: number, zIndexVar = '--gr-z-modal'): string {\n return depth > 0 ? `calc(var(${zIndexVar}) + ${depth})` : `var(${zIndexVar})`\n}\n\n// Читалка регистрируется на загрузке модуля: наблюдатель может подключиться\n// раньше первого открытого слоя, и тогда спросить ему было бы нечего.\nif (__GR_DEV__)\n provideGrDevLayers(snapshotLayers)\n\n/** Тестовая/служебная очистка стека. */\nexport function resetOverlayStack(): void {\n stack.splice(0, stack.length)\n stopListening()\n\n if (__GR_DEV__)\n emitGrDevEvent({ type: 'overlay:sync', layers: [] })\n}\n"],"mappings":";;;AAuEA,IAAM,QAAwB,CAAC;AAC/B,IAAI,YAAY;AAChB,IAAI,SAAS;;;;;;;;AASb,SAAS,kBAAwB;CAC/B,MAAM,SAAS,MAAM,QAAO,UAAS,MAAM,KAAK;CAChD,MAAM,MAAM,OAAO,OAAO,SAAS;CAEnC,OAAO,SAAS,OAAO,UAAU;EAC/B,MAAM,aAAa,UAAU,GAAG;EAChC,MAAM,WAAW,KAAK;CACxB,CAAC;CAED,IAAA,QAAA,IAAA,aAAA,cACE,eAAe;EAAE,MAAM;EAAgB,QAAQ,eAAe;CAAE,CAAC;AACrE;;;;;;;;AASA,SAAS,iBAAsC;CAC7C,MAAM,SAAS,MAAM,QAAO,UAAS,MAAM,KAAK;CAChD,MAAM,WAAW,OAAO,OAAO,SAAS;CACxC,MAAM,MAAM,MAAM,MAAM,SAAS;CAEjC,OAAO,MAAM,KAAI,WAAU;EACzB,IAAI,MAAM;EACV,OAAO,MAAM,SAAS;EACtB,OAAO,MAAM,gBAAgB,KAAK;EAClC,OAAO,MAAM;EACb,kBAAkB,UAAU;EAC5B,OAAO,MAAM,SAAS,UAAU;EAChC,OAAO,MAAM,QAAQ,OAAO,QAAQ,KAAK,IAAI;EAC7C,gBAAgB,MAAM,YAAY;CACpC,EAAE;AACJ;AAEA,SAAS,cAAc,OAA4B;CACjD,IAAI,MAAM,QAAQ,UAChB;CAEF,IAAI,iBAAiB,KAAK,GACxB;CACF,IAAI,MAAM,WAAW,GACnB;CAEF,MAAM,MAAM,MAAM,MAAM,SAAS;CAIjC,MAAM,eAAe;CACrB,MAAM,gBAAgB;CACtB,IAAI,OAAO,MAAM,6BAA6B,YAC5C,MAAM,yBAAyB;CAEjC,MAAM,SAAS,IAAI,YAAY;CAE/B,IAAA,QAAA,IAAA,aAAA,cACE,eAAe;EAAE,MAAM;EAAkB,IAAI,IAAI;EAAI,QAAQ;CAAO,CAAC;CAEvE,IAAI,QACF,IAAI,MAAM;AACd;AAEA,SAAS,iBAAuB;CAC9B,IAAI,WACF;CACF,IAAI,OAAO,WAAW,aACpB;CAGF,OAAO,iBAAiB,WAAW,eAAe,IAAI;CACtD,YAAY;AACd;AAEA,SAAS,gBAAsB;CAC7B,IAAI,CAAC,WACH;CACF,IAAI,OAAO,WAAW,aACpB;CACF,OAAO,oBAAoB,WAAW,eAAe,IAAI;CACzD,YAAY;AACd;;AAGA,SAAgB,iBAAiB,OAAyC;CACxE,MAAM,KAAK;CACX,MAAM,KAAK;EAAE,GAAG;EAAO;CAAG,CAAC;CAC3B,eAAe;CAEf,IAAA,QAAA,IAAA,aAAA,cACE,eAAe;EAAE,MAAM;EAAgB;EAAI,OAAO,MAAM;EAAO,OAAO,MAAM,SAAS;CAAK,CAAC;CAE7F,gBAAgB;CAChB,OAAO;AACT;;AAGA,SAAgB,mBAAmB,IAAkB;CACnD,MAAM,QAAQ,MAAM,WAAU,SAAQ,KAAK,OAAO,EAAE;CACpD,IAAI,SAAS,GACX,MAAM,OAAO,OAAO,CAAC;CACvB,IAAI,MAAM,WAAW,GACnB,cAAc;CAEhB,IAAA,QAAA,IAAA,aAAA,gBAAkB,SAAS,GACzB,eAAe;EAAE,MAAM;EAAkB;CAAG,CAAC;CAE/C,gBAAgB;AAClB;;;;;;;;AASA,SAAgB,gBAAgB,IAA2B;CACzD,MAAM,QAAQ,MAAM,WAAU,UAAS,MAAM,OAAO,EAAE;CACtD,IAAI,QAAQ,GACV,OAAO,CAAC;CAEV,OAAO,MACJ,MAAM,QAAQ,CAAC,CAAC,CAChB,KAAI,UAAS,MAAM,OAAO,KAAK,IAAI,CAAC,CACpC,QAAQ,SAA8B,SAAS,IAAI;AACxD;;;;;;;;AAqBA,SAAgB,iBAAyB;CACvC,OAAO,MAAM,QAAQ,OAAO,UAAU,SAAS,MAAM,QAAQ,IAAI,IAAI,CAAC;AACxE;;;;;;;;;;;;;AAcA,SAAgB,oBAAoB,WAA2B;CAC7D,MAAM,SAAS,eAAe;CAE9B,OAAO,SAAS,IAAI,4BAA4B,OAAO,KAAK,OAAO,UAAU;AAC/E;;;;;;;AAQA,SAAgB,iBAAiB,OAAe,YAAY,gBAAwB;CAClF,OAAO,QAAQ,IAAI,YAAY,UAAU,MAAM,MAAM,KAAK,OAAO,UAAU;AAC7E;AAIA,IAAA,QAAA,IAAA,aAAA,cACE,mBAAmB,cAAc"}
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
//#region src/components/shared/overlayPanelSurface.ts
|
|
2
|
+
/**
|
|
3
|
+
* Общая поверхность всплывающей панели: радиус, рамка, фон, тень.
|
|
4
|
+
*
|
|
5
|
+
* До выноса шесть этих утилит были скопированы пять раз — у поповера,
|
|
6
|
+
* выпадающего меню и трёх списков выбора. Расходились бы молча: разница в одном
|
|
7
|
+
* токене видна только рядом с эталоном, а эталоны у компонентов свои.
|
|
8
|
+
*
|
|
9
|
+
* Фон — `--gr-popover`, и это не выбор из двух равных. Описание токена в
|
|
10
|
+
* `tokens/themes/*.json` — «Фон popover-, dropdown- и overlay-поверхностей», то
|
|
11
|
+
* есть он заведён ровно под этот случай; `--gr-card` — фон карточки. Четверо из
|
|
12
|
+
* пяти брали карточный, и значения совпадали, поэтому расхождение не
|
|
13
|
+
* проявлялось. Проявилось бы у первого, кто перекрасит `--gr-popover`, чтобы
|
|
14
|
+
* отличать всплывающие поверхности от карточек.
|
|
15
|
+
*
|
|
16
|
+
* Всё, что специфично для компонента, — поле, потолок ширины, `overflow` — он
|
|
17
|
+
* дописывает сам: это уже не поверхность, а его собственная раскладка.
|
|
18
|
+
*
|
|
19
|
+
* Модуль безадресный: в `dist` он уезжает в общий чанк, который пресет не
|
|
20
|
+
* сканирует, поэтому каждый потребитель объявляет эти классы в своём `safelist.ts`.
|
|
21
|
+
*/
|
|
22
|
+
var overlayPanelSurfaceClass = "rounded-[var(--gr-radius-xl)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-2)]";
|
|
23
|
+
//#endregion
|
|
1
24
|
//#region src/components/shared/overlayTransition.ts
|
|
2
25
|
/**
|
|
3
26
|
* Набор перехода всплывающей панели.
|
|
@@ -33,27 +56,4 @@ var panelPopTransition = {
|
|
|
33
56
|
leaveTo: "transform opacity-0 scale-95"
|
|
34
57
|
};
|
|
35
58
|
//#endregion
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Общая поверхность всплывающей панели: радиус, рамка, фон, тень.
|
|
39
|
-
*
|
|
40
|
-
* До выноса шесть этих утилит были скопированы пять раз — у поповера,
|
|
41
|
-
* выпадающего меню и трёх списков выбора. Расходились бы молча: разница в одном
|
|
42
|
-
* токене видна только рядом с эталоном, а эталоны у компонентов свои.
|
|
43
|
-
*
|
|
44
|
-
* Фон — `--gr-popover`, и это не выбор из двух равных. Описание токена в
|
|
45
|
-
* `tokens/themes/*.json` — «Фон popover-, dropdown- и overlay-поверхностей», то
|
|
46
|
-
* есть он заведён ровно под этот случай; `--gr-card` — фон карточки. Четверо из
|
|
47
|
-
* пяти брали карточный, и значения совпадали, поэтому расхождение не
|
|
48
|
-
* проявлялось. Проявилось бы у первого, кто перекрасит `--gr-popover`, чтобы
|
|
49
|
-
* отличать всплывающие поверхности от карточек.
|
|
50
|
-
*
|
|
51
|
-
* Всё, что специфично для компонента, — поле, потолок ширины, `overflow` — он
|
|
52
|
-
* дописывает сам: это уже не поверхность, а его собственная раскладка.
|
|
53
|
-
*
|
|
54
|
-
* Модуль безадресный: в `dist` он уезжает в общий чанк, который пресет не
|
|
55
|
-
* сканирует, поэтому каждый потребитель объявляет эти классы в своём `safelist.ts`.
|
|
56
|
-
*/
|
|
57
|
-
var overlayPanelSurfaceClass = "rounded-[var(--gr-radius-xl)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-2)]";
|
|
58
|
-
//#endregion
|
|
59
|
-
export { panelPopTransition as n, overlayPanelSurfaceClass as t };
|
|
59
|
+
export { overlayPanelSurfaceClass as n, panelPopTransition as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlayTransition-C0FDMx7Q.js","names":[],"sources":["../../src/components/shared/overlayPanelSurface.ts","../../src/components/shared/overlayTransition.ts"],"sourcesContent":["/**\n * Общая поверхность всплывающей панели: радиус, рамка, фон, тень.\n *\n * До выноса шесть этих утилит были скопированы пять раз — у поповера,\n * выпадающего меню и трёх списков выбора. Расходились бы молча: разница в одном\n * токене видна только рядом с эталоном, а эталоны у компонентов свои.\n *\n * Фон — `--gr-popover`, и это не выбор из двух равных. Описание токена в\n * `tokens/themes/*.json` — «Фон popover-, dropdown- и overlay-поверхностей», то\n * есть он заведён ровно под этот случай; `--gr-card` — фон карточки. Четверо из\n * пяти брали карточный, и значения совпадали, поэтому расхождение не\n * проявлялось. Проявилось бы у первого, кто перекрасит `--gr-popover`, чтобы\n * отличать всплывающие поверхности от карточек.\n *\n * Всё, что специфично для компонента, — поле, потолок ширины, `overflow` — он\n * дописывает сам: это уже не поверхность, а его собственная раскладка.\n *\n * Модуль безадресный: в `dist` он уезжает в общий чанк, который пресет не\n * сканирует, поэтому каждый потребитель объявляет эти классы в своём `safelist.ts`.\n */\nexport const overlayPanelSurfaceClass = 'rounded-[var(--gr-radius-xl)] border border-[var(--gr-brd)] bg-[var(--gr-popover)] text-[var(--gr-popover-fg)] shadow-[var(--gr-shadow-2)]'\n","/**\n * Набор перехода всплывающей панели.\n *\n * Пять панелей — выпадающее меню, поповер, списки выбора — появляются\n * одинаково, и до выноса держали этот набор литералами в своих шаблонах:\n * шесть строк, повторённых дословно пять раз. Разъезжались они молча, потому\n * что визуально расхождение в одной фазе заметно только рядом с эталоном.\n *\n * Форма — объект по фазам, как `overlayTransition`/`panelTransition` у\n * `GrModal`: так набор биндится в `<transition>` по одному имени на фазу и\n * разворачивается в safelist одним вызовом, не превращаясь в «мешок токенов».\n *\n * Два правила, оба следуют из того, что модуль безадресный (в `dist` он лежит\n * в общем чанке, который пресет не сканирует):\n *\n * 1. **Каждый потребитель обязан объявить эти классы в своём `safelist.ts`** —\n * через `flattenTransitionTokens`. Гейт `src/__tests__/safelist.test.ts`\n * проверяет это обходом импортов; забывшего он назовёт поимённо.\n * 2. **Один пресет — один файл.** Второй набор, положенный сюда же, обложил бы\n * налогом потребителей первого: safelist считается по модулю целиком, и\n * `GrDropdown` понёс бы классы, которых не рендерит.\n *\n * Набор с единственным потребителем сюда не переезжает вовсе — литералы в его\n * `.vue` находит скан, и это дешевле. Поэтому здесь нет фейда `GrTooltip`.\n */\nexport const panelPopTransition = {\n enter: 'transition ease-[var(--gr-ease-out)] duration-[var(--gr-duration-fast)]',\n enterFrom: 'transform opacity-0 scale-95',\n enterTo: 'transform opacity-100 scale-100',\n leave: 'transition ease-[var(--gr-ease-in)] duration-[var(--gr-duration-fast)]',\n leaveFrom: 'transform opacity-100 scale-100',\n leaveTo: 'transform opacity-0 scale-95',\n} as const\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoBA,IAAa,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACKxC,IAAa,qBAAqB;CAChC,OAAO;CACP,WAAW;CACX,SAAS;CACT,OAAO;CACP,WAAW;CACX,SAAS;AACX"}
|
|
@@ -15,7 +15,8 @@ var transfer = {
|
|
|
15
15
|
"moved": "{count} перенесено в «{list}»",
|
|
16
16
|
"reordered": "Позиция {position} из {count}",
|
|
17
17
|
"reorderFiltered": "Пока включён поиск, порядок не меняется",
|
|
18
|
-
"cancelled": "Перенос отменён"
|
|
18
|
+
"cancelled": "Перенос отменён",
|
|
19
|
+
"loading": "Ищем…"
|
|
19
20
|
};
|
|
20
21
|
var carousel = {
|
|
21
22
|
"roledescription": "карусель",
|
|
@@ -51,7 +52,8 @@ var steps = {
|
|
|
51
52
|
var treeSelect = {
|
|
52
53
|
"filterPlaceholder": "Поиск…",
|
|
53
54
|
"empty": "Нет данных",
|
|
54
|
-
"loading": "Загрузка…"
|
|
55
|
+
"loading": "Загрузка…",
|
|
56
|
+
"removeTag": "Удалить {label}"
|
|
55
57
|
};
|
|
56
58
|
var badgeWrap = { "count": "{count} новых" };
|
|
57
59
|
var avatar = {
|
|
@@ -98,7 +100,14 @@ var dataTable = {
|
|
|
98
100
|
"detailRetry": "Повторить",
|
|
99
101
|
"detailExpanded": "Подробности строки раскрыты",
|
|
100
102
|
"detailCollapsed": "Подробности строки свёрнуты",
|
|
101
|
-
"detailReady": "Подробности строки загружены"
|
|
103
|
+
"detailReady": "Подробности строки загружены",
|
|
104
|
+
"columnMenu": "Действия с колонкой {label}",
|
|
105
|
+
"pinLeft": "Закрепить слева",
|
|
106
|
+
"pinRight": "Закрепить справа",
|
|
107
|
+
"unpin": "Открепить",
|
|
108
|
+
"columnPinnedLeft": "Колонка {label} закреплена слева",
|
|
109
|
+
"columnPinnedRight": "Колонка {label} закреплена справа",
|
|
110
|
+
"columnUnpinned": "Колонка {label} откреплена"
|
|
102
111
|
};
|
|
103
112
|
var breadcrumbs = {
|
|
104
113
|
"label": "Навигационная цепочка",
|
|
@@ -211,7 +220,9 @@ var inputTag = {
|
|
|
211
220
|
"addedMany": "Добавлено тегов: {count}",
|
|
212
221
|
"removed": "Удалён тег: {tag}",
|
|
213
222
|
"cleared": "Все теги удалены",
|
|
214
|
-
"limitReached": "Достигнут предел тегов"
|
|
223
|
+
"limitReached": "Достигнут предел тегов",
|
|
224
|
+
"editTag": "Правка тега {tag}",
|
|
225
|
+
"edited": "Тег изменён: {tag}"
|
|
215
226
|
};
|
|
216
227
|
var fileUpload = {
|
|
217
228
|
"placeholder": "Перетащите файлы сюда или нажмите для выбора",
|
|
@@ -329,7 +340,9 @@ var colorPicker = {
|
|
|
329
340
|
"opacity": "Непрозрачность",
|
|
330
341
|
"hexLabel": "Значение hex",
|
|
331
342
|
"presetsLabel": "Палитра",
|
|
332
|
-
"swatchLabel": "Цвет {color}"
|
|
343
|
+
"swatchLabel": "Цвет {color}",
|
|
344
|
+
"area": "Насыщенность и светлота",
|
|
345
|
+
"eyedropper": "Взять цвет с экрана"
|
|
333
346
|
};
|
|
334
347
|
var collapse = { "empty": "Пока пусто" };
|
|
335
348
|
var list = { "empty": "Пока пусто" };
|
|
@@ -344,7 +357,10 @@ var bottomNav = {
|
|
|
344
357
|
"label": "Нижняя навигация",
|
|
345
358
|
"badge": "{count} новых"
|
|
346
359
|
};
|
|
347
|
-
var emptyState = {
|
|
360
|
+
var emptyState = {
|
|
361
|
+
"title": "Пока пусто",
|
|
362
|
+
"searchTitle": "Ничего не найдено"
|
|
363
|
+
};
|
|
348
364
|
var sortable = {
|
|
349
365
|
"label": "Сортируемый список",
|
|
350
366
|
"handle": "Переставить",
|