@feugene/granularity 0.16.0 → 0.19.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/dist/chunks/chevron-left-D8AUBAM7.js +23 -0
- package/dist/chunks/chevron-left-D8AUBAM7.js.map +1 -0
- package/dist/chunks/{chevron-right-BfqMVpbB.js → chevron-right-C6Z01h0X.js} +1 -22
- package/dist/chunks/chevron-right-C6Z01h0X.js.map +1 -0
- package/dist/chunks/config-8Pcutu1j2.js.map +1 -0
- package/dist/chunks/{config-K0C2tcLB.js → config-9PYRZlKE.js} +1 -1
- package/dist/chunks/{config-K0C2tcLB.js.map → config-9PYRZlKE.js.map} +1 -1
- package/dist/chunks/{config-Zc_bUvZY2.js → config-BDfTXHo12.js} +1 -1
- package/dist/chunks/{config-Zc_bUvZY2.js.map → config-BDfTXHo12.js.map} +1 -1
- package/dist/chunks/config-BE6n20np2.js.map +1 -0
- package/dist/chunks/config-BJ7Bv_G2.js +148 -0
- package/dist/chunks/config-BJ7Bv_G2.js.map +1 -0
- package/dist/chunks/config-BKvX0q102.js.map +1 -0
- package/dist/chunks/{config-CpwwkFBO.js → config-Baj7Wvx-.js} +1 -1
- package/dist/chunks/{config-CpwwkFBO.js.map → config-Baj7Wvx-.js.map} +1 -1
- package/dist/chunks/{config-D8tSR2uS.js → config-Be9k9bOf.js} +2 -8
- package/dist/chunks/config-Be9k9bOf.js.map +1 -0
- package/dist/chunks/config-BoNR7_PD2.js.map +1 -0
- package/dist/chunks/{config-DxCTVBfx.js → config-BsPDQ0dS.js} +2 -2
- package/dist/chunks/config-BsPDQ0dS.js.map +1 -0
- package/dist/chunks/{config-DpqOnJiz2.js → config-CDat1wXf2.js} +1 -1
- package/dist/chunks/config-CDat1wXf2.js.map +1 -0
- package/dist/chunks/config-CN2ULSQL.js.map +1 -0
- package/dist/chunks/{config-Cw6TDzWv2.js → config-COpuWGr_.js} +1 -1
- package/dist/chunks/config-COpuWGr_.js.map +1 -0
- package/dist/chunks/config-CY0_6cMi2.js.map +1 -0
- package/dist/chunks/{config-B-g6lT19.js → config-DA6SnEoy.js} +5 -2
- package/dist/chunks/config-DA6SnEoy.js.map +1 -0
- package/dist/chunks/config-DAX8Deh1.js +68 -0
- package/dist/chunks/config-DAX8Deh1.js.map +1 -0
- package/dist/chunks/config-DKdlqXiY.js.map +1 -1
- package/dist/chunks/{config-CvC0sX8p.js → config-MRvhUl-O.js} +1 -1
- package/dist/chunks/{config-CvC0sX8p.js.map → config-MRvhUl-O.js.map} +1 -1
- package/dist/chunks/config-Rw9OyW702.js.map +1 -0
- package/dist/chunks/config-ScuvHw6k2.js +42 -0
- package/dist/chunks/config-ScuvHw6k2.js.map +1 -0
- package/dist/chunks/config-T3qdQBgU2.js.map +1 -0
- package/dist/chunks/config-eXB87DOi.js.map +1 -0
- package/dist/chunks/context-BSYR7RZs.js.map +1 -1
- package/dist/chunks/{en-B3xMeswz.js → en-COdzxgAX.js} +30 -5
- package/dist/chunks/en-COdzxgAX.js.map +1 -0
- package/dist/chunks/{es-CA-mkU--.js → es-ClqJ-4hr.js} +30 -5
- package/dist/chunks/es-ClqJ-4hr.js.map +1 -0
- package/dist/chunks/{es-Bv8vvqdT.js → es-DGMHwknJ.js} +3 -3
- package/dist/chunks/{es-Bv8vvqdT.js.map → es-DGMHwknJ.js.map} +1 -1
- package/dist/chunks/generated-BRkbhyxk.js +3270 -0
- package/dist/chunks/generated-BRkbhyxk.js.map +1 -0
- package/dist/chunks/grKbdStyles-CeZhGQHr.js +43 -0
- package/dist/chunks/grKbdStyles-CeZhGQHr.js.map +1 -0
- package/dist/chunks/{grTextareaStyles-CWI2SqC8.js → grTextareaStyles-iOcHuVMM.js} +2 -2
- package/dist/chunks/{grTextareaStyles-CWI2SqC8.js.map → grTextareaStyles-iOcHuVMM.js.map} +1 -1
- package/dist/chunks/{granular-provider-CtrXGa7c.js → granular-provider-C6uAujdJ.js} +23 -21
- package/dist/chunks/granular-provider-C6uAujdJ.js.map +1 -0
- package/dist/chunks/grip-vertical-lRha2S7q.js +16 -0
- package/dist/chunks/grip-vertical-lRha2S7q.js.map +1 -0
- package/dist/chunks/icon-Dag2tIMs.js +21 -0
- package/dist/chunks/icon-Dag2tIMs.js.map +1 -0
- package/dist/chunks/{keyboard-zYvvyHd4.js → keyboard-DE2yhz_b.js} +10 -1
- package/dist/chunks/keyboard-DE2yhz_b.js.map +1 -0
- package/dist/chunks/{optionFilter-BVY988pC.js → optionFilter-DYw9XPPx.js} +1 -71
- package/dist/chunks/optionFilter-DYw9XPPx.js.map +1 -0
- package/dist/chunks/{useOverlayLayer-BupuP_hs.js → overlayStack-C8cfsqXa.js} +11 -61
- package/dist/chunks/overlayStack-C8cfsqXa.js.map +1 -0
- package/dist/chunks/{portalRoot-CWD4zHBm.js → portalRoot-CVhCgdG6.js} +10 -1
- package/dist/chunks/{portalRoot-CWD4zHBm.js.map → portalRoot-CVhCgdG6.js.map} +1 -1
- package/dist/chunks/{ru-COWxgh2A.js → ru-9z8ix45c.js} +30 -5
- package/dist/chunks/ru-9z8ix45c.js.map +1 -0
- package/dist/chunks/useDragSort-0l47h4yb.js +265 -0
- package/dist/chunks/useDragSort-0l47h4yb.js.map +1 -0
- package/dist/chunks/{useModalOverlay-DCM4d3yA.js → useModalOverlay-DrQVYwwA.js} +2 -2
- package/dist/chunks/{useModalOverlay-DCM4d3yA.js.map → useModalOverlay-DrQVYwwA.js.map} +1 -1
- package/dist/chunks/{usePortalTarget-RyzEgNTb.js → usePortalTarget-Cn-Wiz5d.js} +1 -1
- package/dist/chunks/{usePortalTarget-RyzEgNTb.js.map → usePortalTarget-Cn-Wiz5d.js.map} +1 -1
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-CQ9T7Lly.js → GrAutocomplete-gY4Xopza.js} +35 -26
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-gY4Xopza.js.map +1 -0
- package/dist/components/GrAutocomplete/index.js +1 -1
- package/dist/components/GrAutocomplete/styles.css +1 -1
- package/dist/components/GrBreadcrumbs/chunks/{GrBreadcrumbs-BCNGDS6k.js → GrBreadcrumbs-DS_-PBVt.js} +8 -7
- package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-DS_-PBVt.js.map +1 -0
- package/dist/components/GrBreadcrumbs/index.js +1 -1
- package/dist/components/GrCollapse/chunks/{GrCollapse-B2Fc6A9r.js.map → GrCollapse-Kk0DE8tb.js.map} +1 -1
- package/dist/components/GrCollapse/index.js +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-bnYBRGk8.js → GrColorPicker-CkBTzkqn.js} +4 -4
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-bnYBRGk8.js.map → GrColorPicker-CkBTzkqn.js.map} +1 -1
- package/dist/components/GrColorPicker/index.js +2 -2
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-MWDYDPNC.js → GrCommandPalette-BugTlGdj.js} +41 -16
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-BugTlGdj.js.map +1 -0
- package/dist/components/GrCommandPalette/index.js +2 -2
- package/dist/components/GrCommandPalette/styles.css +1 -1
- package/dist/components/GrConfigProvider/index.js +2 -2
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DaQmlp1_.js → GrConfirmDialog-Ds5T0GiR.js} +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-DaQmlp1_.js.map → GrConfirmDialog-Ds5T0GiR.js.map} +1 -1
- package/dist/components/GrConfirmDialog/index.js +1 -1
- package/dist/components/GrDataTable/chunks/{GrDataTable-DVFho9Cj.js → GrDataTable-CYe2A2WA.js} +498 -48
- package/dist/components/GrDataTable/chunks/GrDataTable-CYe2A2WA.js.map +1 -0
- package/dist/components/GrDataTable/index.js +2 -2
- package/dist/components/GrDialog/chunks/{GrDialog-AE1gIZuz.js → GrDialog-C3tYaA8v.js} +1 -1
- package/dist/components/GrDialog/chunks/{GrDialog-AE1gIZuz.js.map → GrDialog-C3tYaA8v.js.map} +1 -1
- package/dist/components/GrDialog/index.js +1 -1
- package/dist/components/GrDialogService/chunks/{GrDialogService-B4DYWDkA.js → GrDialogService-CTBiiJDI.js} +3 -3
- package/dist/components/GrDialogService/chunks/{GrDialogService-B4DYWDkA.js.map → GrDialogService-CTBiiJDI.js.map} +1 -1
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrDivider/chunks/{GrDivider-B2Xwc_QO.js → GrDivider-xxq1_fIl.js} +1 -1
- package/dist/components/GrDivider/chunks/{GrDivider-B2Xwc_QO.js.map → GrDivider-xxq1_fIl.js.map} +1 -1
- package/dist/components/GrDivider/index.js +2 -2
- package/dist/components/GrDrawer/chunks/{GrDrawer-DQwi6aRw.js → GrDrawer-BSSuiNHU.js} +1 -1
- package/dist/components/GrDrawer/chunks/{GrDrawer-DQwi6aRw.js.map → GrDrawer-BSSuiNHU.js.map} +1 -1
- package/dist/components/GrDrawer/index.js +1 -1
- package/dist/components/GrDropdown/chunks/{GrDropdown-BLng6V1m.js → GrDropdown-Bh6UK2M5.js} +3 -3
- package/dist/components/GrDropdown/chunks/{GrDropdown-BLng6V1m.js.map → GrDropdown-Bh6UK2M5.js.map} +1 -1
- package/dist/components/GrDropdown/index.js +1 -1
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BImk2Vs7.js → GrDropdownMenu-LyfEFUbg.js} +2 -2
- package/dist/components/GrDropdownMenu/chunks/{GrDropdownMenu-BImk2Vs7.js.map → GrDropdownMenu-LyfEFUbg.js.map} +1 -1
- package/dist/components/GrDropdownMenu/index.js +2 -2
- package/dist/components/GrFileUpload/chunks/{GrFileUpload-D0B-BEsd.js → GrFileUpload-DuDzC43S.js} +2 -2
- package/dist/components/GrFileUpload/chunks/{GrFileUpload-D0B-BEsd.js.map → GrFileUpload-DuDzC43S.js.map} +1 -1
- package/dist/components/GrFileUpload/index.js +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-BeCnE8XQ.js → GrImageViewer-DfD4euH5.js} +48 -23
- package/dist/components/GrImageViewer/chunks/GrImageViewer-DfD4euH5.js.map +1 -0
- package/dist/components/GrImageViewer/index.js +1 -1
- package/dist/components/GrInput/chunks/{GrInput-DD2gYdqS.js → GrInput-2wd7Crs0.js} +1 -1
- package/dist/components/GrInput/chunks/GrInput-2wd7Crs0.js.map +1 -0
- package/dist/components/GrInput/index.js +1 -1
- package/dist/components/GrInputTag/chunks/{GrInputTag-DmNunixz.js → GrInputTag-CyV2eY7Z.js} +20 -33
- package/dist/components/GrInputTag/chunks/GrInputTag-CyV2eY7Z.js.map +1 -0
- package/dist/components/GrInputTag/index.js +1 -1
- package/dist/components/GrKbd/chunks/{GrKbd-DRFPvW3G.js → GrKbd-DU2lBkRV.js} +218 -52
- package/dist/components/GrKbd/chunks/GrKbd-DU2lBkRV.js.map +1 -0
- package/dist/components/GrKbd/index.js +3 -3
- package/dist/components/GrModal/chunks/{GrModal-B8I78T0N.js → GrModal-CbMMr6W1.js} +1 -1
- package/dist/components/GrModal/chunks/{GrModal-B8I78T0N.js.map → GrModal-CbMMr6W1.js.map} +1 -1
- package/dist/components/GrModal/index.js +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-CZOyPz4J.js → GrNumberInput-B5CHZDBj.js} +2 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-CZOyPz4J.js.map → GrNumberInput-B5CHZDBj.js.map} +1 -1
- package/dist/components/GrNumberInput/index.js +1 -1
- package/dist/components/GrPagination/chunks/{GrPagination-B6N0QZBf.js → GrPagination-BM4FrD6Q.js} +4 -4
- package/dist/components/GrPagination/chunks/GrPagination-BM4FrD6Q.js.map +1 -0
- package/dist/components/GrPagination/index.js +1 -1
- package/dist/components/GrPopover/chunks/{GrPopover-B1eRCOoO.js → GrPopover-DMH4Jjst.js} +1 -1
- package/dist/components/GrPopover/chunks/{GrPopover-B1eRCOoO.js.map → GrPopover-DMH4Jjst.js.map} +1 -1
- package/dist/components/GrPopover/index.js +1 -1
- package/dist/components/GrProgressBar/chunks/{GrProgressBar-BiHnaitu.js → GrProgressBar-xwbNM9QB.js} +1 -1
- package/dist/components/GrProgressBar/chunks/GrProgressBar-xwbNM9QB.js.map +1 -0
- package/dist/components/GrProgressBar/index.js +1 -1
- package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-CTogjcHz.js → GrProgressCircle-B8olXRo5.js} +1 -1
- package/dist/components/GrProgressCircle/chunks/{GrProgressCircle-CTogjcHz.js.map → GrProgressCircle-B8olXRo5.js.map} +1 -1
- package/dist/components/GrProgressCircle/index.js +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ct174NVD.js → GrPromptDialog-Pwr9NPxK.js} +3 -3
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-Ct174NVD.js.map → GrPromptDialog-Pwr9NPxK.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrRadio/chunks/{GrRadio-DksDro_U.js → GrRadio-DWiRaMzt.js} +8 -32
- package/dist/components/GrRadio/chunks/GrRadio-DWiRaMzt.js.map +1 -0
- package/dist/components/GrRadio/index.js +1 -1
- package/dist/components/GrRadioGroup/chunks/{GrRadioGroup-BOIhejE6.js → GrRadioGroup-DIuBw0wk.js} +34 -27
- package/dist/components/GrRadioGroup/chunks/GrRadioGroup-DIuBw0wk.js.map +1 -0
- package/dist/components/GrRadioGroup/index.js +1 -1
- package/dist/components/GrRating/chunks/{GrRating-JrHXz0Rv.js → GrRating-DVFH3lYu.js} +10 -9
- package/dist/components/GrRating/chunks/GrRating-DVFH3lYu.js.map +1 -0
- package/dist/components/GrRating/index.js +2 -2
- package/dist/components/GrSegmented/chunks/{GrSegmented-BQIlH5KI.js → GrSegmented-CQw3ijqa.js} +47 -65
- package/dist/components/GrSegmented/chunks/GrSegmented-CQw3ijqa.js.map +1 -0
- package/dist/components/GrSegmented/index.js +1 -1
- package/dist/components/GrSelect/chunks/{GrSelect-aqkPCqjd.js → GrSelect-B-Voni7D.js} +69 -55
- package/dist/components/GrSelect/chunks/GrSelect-B-Voni7D.js.map +1 -0
- package/dist/components/GrSelect/index.js +1 -1
- package/dist/components/GrSelect/styles.css +1 -1
- package/dist/components/GrSidebar/chunks/{GrSidebar-DRGnnViw.js → GrSidebar-i7AxjhY_.js} +3 -2
- package/dist/components/GrSidebar/chunks/{GrSidebar-DRGnnViw.js.map → GrSidebar-i7AxjhY_.js.map} +1 -1
- package/dist/components/GrSidebar/index.js +2 -2
- package/dist/components/GrSkeleton/index.js +1 -1
- package/dist/components/GrSlider/chunks/{GrSlider-DkU9IQSX.js → GrSlider-CyhVsNKn.js} +21 -34
- package/dist/components/GrSlider/chunks/GrSlider-CyhVsNKn.js.map +1 -0
- package/dist/components/GrSlider/index.js +2 -2
- package/dist/components/GrSortableList/chunks/GrSortableList-XB7mXM1W.js +277 -0
- package/dist/components/GrSortableList/chunks/GrSortableList-XB7mXM1W.js.map +1 -0
- package/dist/components/GrSortableList/index.js +3 -0
- package/dist/components/GrSplitter/chunks/{GrSplitter-7To8jbJ8.js → GrSplitter-BzxVQwbY.js} +17 -37
- package/dist/components/GrSplitter/chunks/GrSplitter-BzxVQwbY.js.map +1 -0
- package/dist/components/GrSplitter/index.js +2 -2
- package/dist/components/GrStatistic/chunks/{GrStatistic-CttyoYPT.js → GrStatistic-DOsM6s5V.js} +54 -7
- package/dist/components/GrStatistic/chunks/GrStatistic-DOsM6s5V.js.map +1 -0
- package/dist/components/GrStatistic/index.js +2 -2
- package/dist/components/GrSwitch/chunks/{GrSwitch-B9TYbiih.js → GrSwitch-DpyvVrZG.js} +1 -1
- package/dist/components/GrSwitch/chunks/{GrSwitch-B9TYbiih.js.map → GrSwitch-DpyvVrZG.js.map} +1 -1
- package/dist/components/GrSwitch/index.js +2 -2
- package/dist/components/GrTabPanels/index.js +1 -1
- package/dist/components/GrTable/chunks/{GrTable-BB-mI_NM.js → GrTable-BfqgwETQ.js} +7 -1
- package/dist/components/GrTable/chunks/GrTable-BfqgwETQ.js.map +1 -0
- package/dist/components/GrTable/index.js +1 -1
- package/dist/components/GrTabs/chunks/{GrTabs-B8CDq2D9.js → GrTabs-f3-oQenu.js} +55 -65
- package/dist/components/GrTabs/chunks/GrTabs-f3-oQenu.js.map +1 -0
- package/dist/components/GrTabs/index.js +1 -1
- package/dist/components/GrTextarea/chunks/{GrTextarea-DUh5Tlo4.js → GrTextarea-DSvd1nSy.js} +1 -1
- package/dist/components/GrTextarea/chunks/{GrTextarea-DUh5Tlo4.js.map → GrTextarea-DSvd1nSy.js.map} +1 -1
- package/dist/components/GrTextarea/index.js +2 -2
- package/dist/components/GrTimeline/chunks/{GrTimeline-CE9b3s18.js → GrTimeline-B-h2Yanw.js} +1 -1
- package/dist/components/GrTimeline/chunks/{GrTimeline-CE9b3s18.js.map → GrTimeline-B-h2Yanw.js.map} +1 -1
- package/dist/components/GrTimeline/index.js +2 -2
- package/dist/components/GrToaster/chunks/{GrToaster-BKQy3Srm.js → GrToaster-Dqja0Lf9.js} +114 -13
- package/dist/components/GrToaster/chunks/GrToaster-Dqja0Lf9.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-DEbejfhA.js → GrTooltip-Cn08heWz.js} +2 -2
- package/dist/components/GrTooltip/chunks/{GrTooltip-DEbejfhA.js.map → GrTooltip-Cn08heWz.js.map} +1 -1
- package/dist/components/GrTooltip/index.js +2 -2
- package/dist/components/GrTree/chunks/{GrTree-wfdbvygw.js → GrTree-BR9a2pay.js} +148 -111
- package/dist/components/GrTree/chunks/GrTree-BR9a2pay.js.map +1 -0
- package/dist/components/GrTree/index.js +2 -2
- package/dist/components/GrTree/styles.css +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-lLWmAMtI.js → GrTreeSelect--muUeB0v.js} +28 -14
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect--muUeB0v.js.map +1 -0
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/composables/useAnnouncer.js +1 -1
- package/dist/composables/useComboboxNavigation.js +71 -0
- package/dist/composables/useComboboxNavigation.js.map +1 -0
- package/dist/composables/useDismissible.js +1 -1
- package/dist/composables/useDragGesture.js +55 -0
- package/dist/composables/useDragGesture.js.map +1 -0
- package/dist/composables/useDragSort.js +2 -0
- package/dist/composables/useFloating.js +22 -1
- package/dist/composables/useFloating.js.map +1 -1
- package/dist/composables/useGrComponentConfig.js +2 -0
- package/dist/composables/useGranularityTranslations.js +2 -0
- package/dist/composables/useOverlayLayer.js +64 -1
- package/dist/composables/useOverlayLayer.js.map +1 -0
- package/dist/composables/usePortalTarget.js +1 -1
- package/dist/composables/useRovingFocus.js +150 -0
- package/dist/composables/useRovingFocus.js.map +1 -0
- package/dist/directives/hotkey.js +13 -7
- package/dist/directives/hotkey.js.map +1 -1
- package/dist/directives/loading.js +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 +77 -68
- package/dist/styles/themes/dark.css +6 -1
- package/dist/testing.js +199 -0
- package/dist/testing.js.map +1 -0
- package/dist/theme-apply.js +33 -0
- package/dist/theme-apply.js.map +1 -0
- package/dist/theme.js +501 -0
- package/dist/theme.js.map +1 -0
- package/dist/tokens.js +1 -3131
- package/dist/types/src/components/GrAlert/defaults.d.ts +1 -1
- package/dist/types/src/components/GrAutocomplete/defaults.d.ts +1 -1
- package/dist/types/src/components/GrAvatar/defaults.d.ts +1 -1
- package/dist/types/src/components/GrBadge/defaults.d.ts +1 -1
- package/dist/types/src/components/GrBottomNav/defaults.d.ts +1 -1
- package/dist/types/src/components/GrBreadcrumbs/defaults.d.ts +1 -1
- package/dist/types/src/components/GrBreadcrumbs/grBreadcrumbsStyles.d.ts +5 -2
- package/dist/types/src/components/GrButton/defaults.d.ts +1 -1
- package/dist/types/src/components/GrCard/defaults.d.ts +1 -1
- package/dist/types/src/components/GrCheckbox/defaults.d.ts +1 -1
- package/dist/types/src/components/GrCheckboxGroup/defaults.d.ts +1 -1
- package/dist/types/src/components/GrCollapse/defaults.d.ts +1 -1
- package/dist/types/src/components/GrColorPicker/defaults.d.ts +1 -1
- package/dist/types/src/components/GrColorPicker/grColorPickerStyles.d.ts +2 -2
- package/dist/types/src/components/GrCommandPalette/GrCommandPalette.vue.d.ts +6 -6
- package/dist/types/src/components/GrCommandPalette/defaults.d.ts +1 -1
- package/dist/types/src/components/GrCommandPalette/filtering.d.ts +6 -2
- package/dist/types/src/components/GrConfigProvider/context.d.ts +6 -22
- package/dist/types/src/components/GrConfigProvider/index.d.ts +1 -1
- package/dist/types/src/components/GrConfirmDialog/defaults.d.ts +1 -1
- package/dist/types/src/components/GrDataTable/GrDataTable.vue.d.ts +53 -0
- package/dist/types/src/components/GrDataTable/defaults.d.ts +1 -1
- package/dist/types/src/components/GrDataTable/grDataTableStyles.d.ts +38 -0
- package/dist/types/src/components/GrDialog/defaults.d.ts +1 -1
- package/dist/types/src/components/GrDivider/defaults.d.ts +1 -1
- package/dist/types/src/components/GrDrawer/defaults.d.ts +1 -1
- package/dist/types/src/components/GrEmptyState/defaults.d.ts +1 -1
- package/dist/types/src/components/GrFileUpload/defaults.d.ts +1 -1
- package/dist/types/src/components/GrFormField/defaults.d.ts +1 -1
- package/dist/types/src/components/GrFormFile/defaults.d.ts +1 -1
- package/dist/types/src/components/GrFormSection/defaults.d.ts +1 -1
- package/dist/types/src/components/GrIcon/defaults.d.ts +1 -1
- package/dist/types/src/components/GrImageViewer/composables/usePointerGestures.d.ts +1 -0
- package/dist/types/src/components/GrImageViewer/composables/useZoomPan.d.ts +1 -2
- package/dist/types/src/components/GrInput/defaults.d.ts +1 -1
- package/dist/types/src/components/GrInputTag/defaults.d.ts +1 -1
- package/dist/types/src/components/GrKbd/GrKbd.vue.d.ts +18 -3
- package/dist/types/src/components/GrKbd/defaults.d.ts +1 -1
- package/dist/types/src/components/GrKbd/grKbdStyles.d.ts +20 -3
- package/dist/types/src/components/GrKbd/index.d.ts +6 -0
- package/dist/types/src/components/GrLink/defaults.d.ts +1 -1
- package/dist/types/src/components/GrModal/defaults.d.ts +1 -1
- package/dist/types/src/components/GrNumberInput/defaults.d.ts +1 -1
- package/dist/types/src/components/GrPagination/defaults.d.ts +1 -1
- package/dist/types/src/components/GrPopover/GrPopover.vue.d.ts +1 -1
- package/dist/types/src/components/GrPopover/defaults.d.ts +1 -1
- package/dist/types/src/components/GrProgressBar/defaults.d.ts +1 -1
- package/dist/types/src/components/GrProgressCircle/defaults.d.ts +1 -1
- package/dist/types/src/components/GrProgressCircle/grProgressCircleStyles.d.ts +1 -1
- package/dist/types/src/components/GrPromptDialog/defaults.d.ts +1 -1
- package/dist/types/src/components/GrRadio/defaults.d.ts +1 -1
- package/dist/types/src/components/GrRadio/grRadioGroupContext.d.ts +12 -4
- package/dist/types/src/components/GrRadioGroup/defaults.d.ts +1 -1
- package/dist/types/src/components/GrRating/GrRating.vue.d.ts +9 -4
- package/dist/types/src/components/GrRating/defaults.d.ts +1 -1
- package/dist/types/src/components/GrSegmented/defaults.d.ts +1 -1
- package/dist/types/src/components/GrSelect/defaults.d.ts +1 -1
- package/dist/types/src/components/GrSlider/defaults.d.ts +1 -1
- package/dist/types/src/components/GrSortableList/GrSortableList.vue.d.ts +68 -0
- package/dist/types/src/components/GrSortableList/config.d.ts +1 -0
- package/dist/types/src/components/GrSortableList/grSortableListStyles.d.ts +37 -0
- package/dist/types/src/components/GrSortableList/index.d.ts +9 -0
- package/dist/types/src/components/GrSortableList/safelist.d.ts +1 -0
- package/dist/types/src/components/GrSplitter/defaults.d.ts +1 -1
- package/dist/types/src/components/GrStatistic/GrStatistic.vue.d.ts +12 -9
- package/dist/types/src/components/GrStatistic/defaults.d.ts +1 -1
- package/dist/types/src/components/GrStatistic/grStatisticStyles.d.ts +1 -2
- package/dist/types/src/components/GrSwitch/defaults.d.ts +1 -1
- package/dist/types/src/components/GrTable/GrTable.vue.d.ts +13 -0
- package/dist/types/src/components/GrTable/defaults.d.ts +1 -1
- package/dist/types/src/components/GrTabs/GrTabs.vue.d.ts +6 -2
- package/dist/types/src/components/GrTabs/defaults.d.ts +1 -1
- package/dist/types/src/components/GrTextarea/defaults.d.ts +1 -1
- package/dist/types/src/components/GrTextarea/grTextareaStyles.d.ts +2 -2
- package/dist/types/src/components/GrTimeline/defaults.d.ts +1 -1
- package/dist/types/src/components/GrTimeline/grTimelineStyles.d.ts +1 -1
- package/dist/types/src/components/GrToaster/GrToaster.vue.d.ts +8 -0
- package/dist/types/src/components/GrToaster/grToasterStyles.d.ts +27 -0
- package/dist/types/src/components/GrTooltip/defaults.d.ts +1 -1
- package/dist/types/src/components/GrTree/defaults.d.ts +1 -1
- package/dist/types/src/components/GrTree/grTreeInteractionContext.d.ts +0 -1
- package/dist/types/src/components/GrTree/grTreeProps.d.ts +11 -4
- package/dist/types/src/components/GrTree/safelist.d.ts +1 -1
- package/dist/types/src/components/GrTreeSelect/defaults.d.ts +1 -1
- package/dist/types/src/components/shared/hotkey.d.ts +7 -15
- package/dist/types/src/components/shared/hotkeyTokens.d.ts +37 -0
- package/dist/types/src/components/shared/icon.d.ts +13 -0
- package/dist/types/src/composables/internal/dragSortGeometry.d.ts +59 -0
- package/dist/types/src/composables/internal/overlayStack.d.ts +11 -0
- package/dist/types/src/composables/useDragGesture.d.ts +63 -0
- package/dist/types/src/composables/useDragSort.d.ts +80 -0
- package/dist/types/src/composables/useFloating.d.ts +5 -1
- package/dist/types/src/composables/useGrComponentConfig.d.ts +41 -0
- package/dist/types/src/composables/useGranularityTranslations.d.ts +13 -0
- package/dist/types/src/composables/useRovingFocus.d.ts +108 -0
- package/dist/types/src/granular-provider/shared.d.ts +1 -0
- package/dist/types/src/i18n/messages/en.d.ts +48 -0
- package/dist/types/src/i18n/messages/es.d.ts +48 -0
- package/dist/types/src/i18n/messages/ru.d.ts +48 -0
- package/dist/types/src/index.d.ts +12 -0
- package/dist/types/src/internal/keyboard.d.ts +6 -0
- package/dist/types/src/testing/announcer.d.ts +10 -0
- package/dist/types/src/testing/context.d.ts +36 -0
- package/dist/types/src/testing/env.d.ts +25 -0
- package/dist/types/src/testing/geometry.d.ts +23 -0
- package/dist/types/src/testing/index.d.ts +12 -0
- package/dist/types/src/testing/pointer.d.ts +31 -0
- package/dist/types/src/theme/apply.d.ts +27 -0
- package/dist/types/src/theme/color.d.ts +35 -0
- package/dist/types/src/theme/extendTheme.d.ts +63 -0
- package/dist/types/src/theme/index.d.ts +22 -0
- package/dist/types/src/theme/roles.d.ts +16 -0
- package/dist/types/src/theme/tone.d.ts +32 -0
- package/dist/types/src/theme/validate.d.ts +16 -0
- package/dist/types/src/tokens/types.d.ts +8 -0
- package/dist/web-types.json +214 -13
- package/package.json +47 -4
- package/tokens/themes/dark.json +2 -1
- package/dist/chunks/chevron-right-BfqMVpbB.js.map +0 -1
- package/dist/chunks/config-8Pcutu1j.js.map +0 -1
- package/dist/chunks/config-B-g6lT19.js.map +0 -1
- package/dist/chunks/config-BE6n20np.js.map +0 -1
- package/dist/chunks/config-BKvX0q10.js.map +0 -1
- package/dist/chunks/config-BNfQwyL4.js +0 -97
- package/dist/chunks/config-BNfQwyL4.js.map +0 -1
- package/dist/chunks/config-BoNR7_PD.js.map +0 -1
- package/dist/chunks/config-CN2ULSQL2.js.map +0 -1
- package/dist/chunks/config-CY0_6cMi.js.map +0 -1
- package/dist/chunks/config-Cw6TDzWv2.js.map +0 -1
- package/dist/chunks/config-D8tSR2uS.js.map +0 -1
- package/dist/chunks/config-DpqOnJiz2.js.map +0 -1
- package/dist/chunks/config-DxCTVBfx.js.map +0 -1
- package/dist/chunks/config-LGrZIdaZ.js +0 -21
- package/dist/chunks/config-LGrZIdaZ.js.map +0 -1
- package/dist/chunks/config-Rw9OyW70.js.map +0 -1
- package/dist/chunks/config-T3qdQBgU.js.map +0 -1
- package/dist/chunks/config-eXB87DOi2.js.map +0 -1
- package/dist/chunks/en-B3xMeswz.js.map +0 -1
- package/dist/chunks/es-CA-mkU--.js.map +0 -1
- package/dist/chunks/grKbdStyles-DAoxiytu.js +0 -31
- package/dist/chunks/grKbdStyles-DAoxiytu.js.map +0 -1
- package/dist/chunks/granular-provider-CtrXGa7c.js.map +0 -1
- package/dist/chunks/keyboard-zYvvyHd4.js.map +0 -1
- package/dist/chunks/optionFilter-BVY988pC.js.map +0 -1
- package/dist/chunks/ru-COWxgh2A.js.map +0 -1
- package/dist/chunks/useOverlayLayer-BupuP_hs.js.map +0 -1
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-CQ9T7Lly.js.map +0 -1
- package/dist/components/GrBreadcrumbs/chunks/GrBreadcrumbs-BCNGDS6k.js.map +0 -1
- package/dist/components/GrCommandPalette/chunks/GrCommandPalette-MWDYDPNC.js.map +0 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-DVFho9Cj.js.map +0 -1
- package/dist/components/GrImageViewer/chunks/GrImageViewer-BeCnE8XQ.js.map +0 -1
- package/dist/components/GrInput/chunks/GrInput-DD2gYdqS.js.map +0 -1
- package/dist/components/GrInputTag/chunks/GrInputTag-DmNunixz.js.map +0 -1
- package/dist/components/GrKbd/chunks/GrKbd-DRFPvW3G.js.map +0 -1
- package/dist/components/GrPagination/chunks/GrPagination-B6N0QZBf.js.map +0 -1
- package/dist/components/GrProgressBar/chunks/GrProgressBar-BiHnaitu.js.map +0 -1
- package/dist/components/GrRadio/chunks/GrRadio-DksDro_U.js.map +0 -1
- package/dist/components/GrRadioGroup/chunks/GrRadioGroup-BOIhejE6.js.map +0 -1
- package/dist/components/GrRating/chunks/GrRating-JrHXz0Rv.js.map +0 -1
- package/dist/components/GrSegmented/chunks/GrSegmented-BQIlH5KI.js.map +0 -1
- package/dist/components/GrSelect/chunks/GrSelect-aqkPCqjd.js.map +0 -1
- package/dist/components/GrSlider/chunks/GrSlider-DkU9IQSX.js.map +0 -1
- package/dist/components/GrSplitter/chunks/GrSplitter-7To8jbJ8.js.map +0 -1
- package/dist/components/GrStatistic/chunks/GrStatistic-CttyoYPT.js.map +0 -1
- package/dist/components/GrTable/chunks/GrTable-BB-mI_NM.js.map +0 -1
- package/dist/components/GrTabs/chunks/GrTabs-B8CDq2D9.js.map +0 -1
- package/dist/components/GrToaster/chunks/GrToaster-BKQy3Srm.js.map +0 -1
- package/dist/components/GrTree/chunks/GrTree-wfdbvygw.js.map +0 -1
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-lLWmAMtI.js.map +0 -1
- package/dist/tokens.js.map +0 -1
- package/dist/chunks/{config-8Pcutu1j.js → config-8Pcutu1j2.js} +0 -0
- package/dist/chunks/{config-BE6n20np.js → config-BE6n20np2.js} +0 -0
- package/dist/chunks/{config-BKvX0q10.js → config-BKvX0q102.js} +0 -0
- package/dist/chunks/{config-BoNR7_PD.js → config-BoNR7_PD2.js} +0 -0
- package/dist/chunks/{config-CN2ULSQL2.js → config-CN2ULSQL.js} +0 -0
- package/dist/chunks/{config-CY0_6cMi.js → config-CY0_6cMi2.js} +0 -0
- package/dist/chunks/{config-Rw9OyW70.js → config-Rw9OyW702.js} +0 -0
- package/dist/chunks/{config-T3qdQBgU.js → config-T3qdQBgU2.js} +0 -0
- package/dist/chunks/{config-eXB87DOi2.js → config-eXB87DOi.js} +0 -0
- package/dist/components/GrCollapse/chunks/{GrCollapse-B2Fc6A9r.js → GrCollapse-Kk0DE8tb.js} +1 -1
- /package/dist/types/src/composables/{internal/useComboboxNavigation.d.ts → useComboboxNavigation.d.ts} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrPagination-B6N0QZBf.js","names":[],"sources":["../../../../src/components/GrPagination/GrPagination.vue","../../../../src/components/GrPagination/GrPagination.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport {\n type GrPaginationSize,\n ellipsisSizes,\n jumperSizes,\n labelSizes,\n navButtonSizes,\n pageListGaps,\n pageSizes,\n pageSizeSelectWidths,\n rowGaps,\n} from './grPaginationStyles'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Номера усекаются многоточием\n * (алгоритм boundary/sibling, как у MUI): всегда видны первая/последняя страница\n * и `siblingCount` соседей вокруг текущей.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n /** Сколько соседних страниц показывать вокруг текущей. По умолчанию `1`. */\n siblingCount?: number\n /** Сколько крайних страниц всегда показывать с каждого края. По умолчанию `1`. */\n boundaryCount?: number\n /**\n * Компактный вариант: вместо нумерованных страниц показывается индикатор\n * «текущая / всего» — удобно для узких мест (мобайл, тулбары таблиц).\n */\n compact?: boolean\n /** Показывать поле «перейти к странице» с быстрым переходом по вводу номера. */\n showJumper?: boolean\n /** Показывать селект размера страницы. */\n showPageSize?: boolean\n /** Показывать диапазон показанных элементов — «1–20 из 137». Слот `#total` сильнее. */\n showTotal?: boolean\n /** i18n-подпись перед полем перехода. По умолчанию — `gr.pagination.jumpTo`. */\n jumperLabel?: string\n /**\n * Имя навигационного лендмарка. По умолчанию — `gr.pagination.label`; задавать\n * стоит там, где пагинаций на странице несколько и их надо различать.\n */\n ariaLabel?: string\n /** Гасит всю пагинацию: номера, кнопки, селект размера и поле перехода. */\n disabled?: boolean\n size?: GrPaginationSize\n}\n\nexport interface GrPaginationEmits {\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n siblingCount: 1,\n boundaryCount: 1,\n compact: false,\n showJumper: false,\n showPageSize: false,\n showTotal: false,\n jumperLabel: undefined,\n ariaLabel: undefined,\n disabled: false,\n size: undefined,\n})\n\ndefineSlots<{\n /** Диапазон показанных элементов целиком — вместо строки из локали. */\n total?: (props: { from: number, to: number, total: number }) => unknown\n}>()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrPagination' })\n\nconst rowClass = computed(() => rowGaps[resolvedSize.value])\nconst pageListClass = computed(() => pageListGaps[resolvedSize.value])\nconst pageClass = computed(() => pageSizes[resolvedSize.value])\nconst jumperClass = computed(() => jumperSizes[resolvedSize.value])\nconst ellipsisClass = computed(() => ellipsisSizes[resolvedSize.value])\nconst labelClass = computed(() => labelSizes[resolvedSize.value])\nconst selectWrapClass = computed(() => pageSizeSelectWidths[resolvedSize.value])\nconst navButtonSize = computed(() => navButtonSizes[resolvedSize.value])\n\nconst emit = defineEmits<GrPaginationEmits>()\n\n// Делитель клампится: `pageSize: 0` дал бы `Infinity` страниц и бесконечный\n// цикл в `range()`.\nconst resolvedPageSize = computed(() => Math.max(1, Math.trunc(props.pageSize)))\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / resolvedPageSize.value)))\n\n// Рендер идёт от зажатого номера: вотчер ниже просит родителя подтянуть `page`,\n// но на первом рендере он молчит, и без клампа пагинация осталась бы вовсе без\n// активной страницы.\nconst currentPage = computed(() => Math.min(Math.max(Math.trunc(props.page), 1), pageCount.value))\n\n// Контролируемый компонент: если `total`/`pageSize` уменьшились и текущая `page`\n// вышла за диапазон — просим родителя подтянуть её к последней странице.\nwatch(pageCount, (count) => {\n if (props.page > count)\n emit('update:page', count)\n})\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\nconst rangeFrom = computed(() => (props.total === 0 ? 0 : (currentPage.value - 1) * resolvedPageSize.value + 1))\nconst rangeTo = computed(() => Math.min(currentPage.value * resolvedPageSize.value, props.total))\n\ntype PaginationItem = number | 'ellipsis-start' | 'ellipsis-end'\n\nfunction range(start: number, end: number): number[] {\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n return list\n}\n\n// Алгоритм usePagination как в MUI: крайние `boundaryCount` страниц + `siblingCount`\n// соседей вокруг текущей, разрывы схлопываются в многоточие. Одиночный «пропущенный»\n// номер показываем как номер, а не как «…» (визуально ровнее).\nconst items = computed<PaginationItem[]>(() => {\n const count = pageCount.value\n const current = currentPage.value\n const boundaryCount = Math.max(0, props.boundaryCount)\n const siblingCount = Math.max(0, props.siblingCount)\n\n const startPages = range(1, Math.min(boundaryCount, count))\n const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count)\n\n const siblingsStart = Math.max(\n Math.min(current - siblingCount, count - boundaryCount - siblingCount * 2 - 1),\n boundaryCount + 2,\n )\n const siblingsEnd = Math.min(\n Math.max(current + siblingCount, boundaryCount + siblingCount * 2 + 2),\n endPages.length > 0 ? endPages[0] - 2 : count - 1,\n )\n\n return [\n ...startPages,\n ...(siblingsStart > boundaryCount + 2\n ? ['ellipsis-start' as const]\n : boundaryCount + 1 < count - boundaryCount\n ? [boundaryCount + 1]\n : []),\n ...range(siblingsStart, siblingsEnd),\n ...(siblingsEnd < count - boundaryCount - 1\n ? ['ellipsis-end' as const]\n : count - boundaryCount > boundaryCount\n ? [count - boundaryCount]\n : []),\n ...endPages,\n ]\n})\n\nfunction goTo(page: number): void {\n if (props.disabled)\n return\n\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, currentPage.value - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, currentPage.value + 1))\n}\n\nfunction first(): void {\n goTo(1)\n}\n\nfunction last(): void {\n goTo(pageCount.value)\n}\n\n// «Перейти к странице»: клампим введённый номер к диапазону [1, pageCount]\n// и сбрасываем поле после перехода.\nconst jumperValue = ref('')\n\nfunction submitJumper(): void {\n const parsed = Number.parseInt(jumperValue.value, 10)\n jumperValue.value = ''\n if (Number.isNaN(parsed))\n return\n\n goTo(Math.min(pageCount.value, Math.max(1, parsed)))\n}\n\nconst resolvedJumperLabel = computed(() => props.jumperLabel ?? t('gr.pagination.jumpTo', 'Go to'))\nconst resolvedAriaLabel = computed(() => props.ariaLabel ?? t('gr.pagination.label', 'Pagination'))\nconst statusText = computed(() =>\n t('gr.pagination.status', 'Page {page} of {count}', { page: currentPage.value, count: pageCount.value }),\n)\nconst totalText = computed(() =>\n t('gr.pagination.total', '{from}–{to} of {total}', { from: rangeFrom.value, to: rangeTo.value, total: props.total }),\n)\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end\"\n :class=\"rowClass\"\n data-gr-pagination\n role=\"navigation\"\n :aria-label=\"resolvedAriaLabel\"\n >\n <div\n v-if=\"showTotal\"\n data-gr-pagination-total\n class=\"text-[var(--gr-muted-fg)] [font-variant-numeric:tabular-nums]\"\n :class=\"labelClass\"\n >\n <slot name=\"total\" :from=\"rangeFrom\" :to=\"rangeTo\" :total=\"total\">\n {{ totalText }}\n </slot>\n </div>\n\n <div v-if=\"showPageSize\" :class=\"selectWrapClass\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :size=\"resolvedSize\"\n :disabled=\"disabled\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage <= 1\" :aria-label=\"t('gr.pagination.first', 'First page')\" data-gr-pagination-first @click=\"first\">\n «\n </GrButton>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage <= 1\" data-gr-pagination-prev @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <!-- Компактный вариант: индикатор «текущая / всего» вместо нумерованных страниц. -->\n <div\n v-if=\"compact\"\n data-gr-pagination-compact\n class=\"px-2 text-[var(--gr-fg)] [font-variant-numeric:tabular-nums]\"\n :class=\"labelClass\"\n role=\"status\"\n >\n {{ currentPage }} / {{ pageCount }}\n </div>\n\n <template v-else>\n <ul class=\"flex items-center [list-style:none]\" :class=\"pageListClass\" data-gr-pagination-pages role=\"list\">\n <li v-for=\"(item, index) in items\" :key=\"index\" :aria-hidden=\"typeof item === 'string' ? 'true' : undefined\">\n <span\n v-if=\"item === 'ellipsis-start' || item === 'ellipsis-end'\"\n data-gr-pagination-ellipsis\n class=\"grid place-items-center text-[var(--gr-muted-fg)]\"\n :class=\"ellipsisClass\"\n >…</span>\n <button\n v-else\n type=\"button\"\n data-gr-pagination-page\n :disabled=\"disabled\"\n :aria-current=\"item === currentPage ? 'page' : undefined\"\n :aria-label=\"t('gr.pagination.page', 'Page {n}', { n: item })\"\n class=\"rounded-[var(--gr-radius-control)] font-600 transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] disabled:cursor-not-allowed\"\n :class=\"[\n pageClass,\n item === currentPage\n ? 'bg-[var(--gr-primary)] text-[var(--gr-primary-fg)]'\n // Подложка тональная, а не `--gr-muted`: серый на белом фоне даёт\n // 1.10:1 и на светлой теме hover был практически не виден, хотя у\n // соседних «назад/вперёд» (ghost-кнопки) он тонирован. Ряд обязан\n // читаться одним контролом.\n : 'text-[var(--gr-muted-fg)] hover:bg-[var(--gr-accent)] hover:text-[var(--gr-accent-fg)]',\n ]\"\n @click=\"goTo(item)\"\n >\n {{ item }}\n </button>\n </li>\n </ul>\n\n <!-- В компактном режиме смену страницы объявляет видимый индикатор. -->\n <div data-gr-pagination-status role=\"status\" class=\"sr-only\">\n {{ statusText }}\n </div>\n </template>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage >= pageCount\" data-gr-pagination-next @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage >= pageCount\" :aria-label=\"t('gr.pagination.last', 'Last page')\" data-gr-pagination-last @click=\"last\">\n »\n </GrButton>\n\n <!-- «Перейти к странице»: Enter или blur применяют введённый номер. -->\n <div v-if=\"showJumper\" class=\"flex items-center gap-2 text-[var(--gr-muted-fg)]\" :class=\"labelClass\">\n <span>{{ resolvedJumperLabel }}</span>\n <input\n v-model=\"jumperValue\"\n type=\"number\"\n min=\"1\"\n :max=\"pageCount\"\n inputmode=\"numeric\"\n data-gr-pagination-jumper\n :disabled=\"disabled\"\n :aria-label=\"resolvedJumperLabel\"\n class=\"rounded-[var(--gr-radius-control)] border border-[var(--gr-brd)] bg-[var(--gr-bg)] px-2 text-center text-[var(--gr-fg)] [font-variant-numeric:tabular-nums] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] disabled:cursor-not-allowed\"\n :class=\"jumperClass\"\n @keydown.enter=\"submitJumper\"\n @blur=\"submitJumper\"\n >\n </div>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref, watch } from 'vue'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport GrButton from '../GrButton/GrButton.vue'\nimport GrSelect from '../GrSelect/GrSelect.vue'\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport {\n type GrPaginationSize,\n ellipsisSizes,\n jumperSizes,\n labelSizes,\n navButtonSizes,\n pageListGaps,\n pageSizes,\n pageSizeSelectWidths,\n rowGaps,\n} from './grPaginationStyles'\n\n/**\n * Пропы пагинации.\n *\n * `total` интерпретируется как общее количество элементов; число страниц\n * вычисляется из `total` и `pageSize`. Номера усекаются многоточием\n * (алгоритм boundary/sibling, как у MUI): всегда видны первая/последняя страница\n * и `siblingCount` соседей вокруг текущей.\n */\nexport interface GrPaginationProps {\n page: number\n pageSize: number\n total: number\n pageSizes?: number[]\n /** Сколько соседних страниц показывать вокруг текущей. По умолчанию `1`. */\n siblingCount?: number\n /** Сколько крайних страниц всегда показывать с каждого края. По умолчанию `1`. */\n boundaryCount?: number\n /**\n * Компактный вариант: вместо нумерованных страниц показывается индикатор\n * «текущая / всего» — удобно для узких мест (мобайл, тулбары таблиц).\n */\n compact?: boolean\n /** Показывать поле «перейти к странице» с быстрым переходом по вводу номера. */\n showJumper?: boolean\n /** Показывать селект размера страницы. */\n showPageSize?: boolean\n /** Показывать диапазон показанных элементов — «1–20 из 137». Слот `#total` сильнее. */\n showTotal?: boolean\n /** i18n-подпись перед полем перехода. По умолчанию — `gr.pagination.jumpTo`. */\n jumperLabel?: string\n /**\n * Имя навигационного лендмарка. По умолчанию — `gr.pagination.label`; задавать\n * стоит там, где пагинаций на странице несколько и их надо различать.\n */\n ariaLabel?: string\n /** Гасит всю пагинацию: номера, кнопки, селект размера и поле перехода. */\n disabled?: boolean\n size?: GrPaginationSize\n}\n\nexport interface GrPaginationEmits {\n (e: 'update:page', value: number): void\n (e: 'update:pageSize', value: number): void\n}\n\nconst { t } = useGranularityTranslations()\n\nconst props = withDefaults(defineProps<GrPaginationProps>(), {\n pageSizes: () => [10, 20, 50],\n siblingCount: 1,\n boundaryCount: 1,\n compact: false,\n showJumper: false,\n showPageSize: false,\n showTotal: false,\n jumperLabel: undefined,\n ariaLabel: undefined,\n disabled: false,\n size: undefined,\n})\n\ndefineSlots<{\n /** Диапазон показанных элементов целиком — вместо строки из локали. */\n total?: (props: { from: number, to: number, total: number }) => unknown\n}>()\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrPagination' })\n\nconst rowClass = computed(() => rowGaps[resolvedSize.value])\nconst pageListClass = computed(() => pageListGaps[resolvedSize.value])\nconst pageClass = computed(() => pageSizes[resolvedSize.value])\nconst jumperClass = computed(() => jumperSizes[resolvedSize.value])\nconst ellipsisClass = computed(() => ellipsisSizes[resolvedSize.value])\nconst labelClass = computed(() => labelSizes[resolvedSize.value])\nconst selectWrapClass = computed(() => pageSizeSelectWidths[resolvedSize.value])\nconst navButtonSize = computed(() => navButtonSizes[resolvedSize.value])\n\nconst emit = defineEmits<GrPaginationEmits>()\n\n// Делитель клампится: `pageSize: 0` дал бы `Infinity` страниц и бесконечный\n// цикл в `range()`.\nconst resolvedPageSize = computed(() => Math.max(1, Math.trunc(props.pageSize)))\nconst pageCount = computed(() => Math.max(1, Math.ceil(props.total / resolvedPageSize.value)))\n\n// Рендер идёт от зажатого номера: вотчер ниже просит родителя подтянуть `page`,\n// но на первом рендере он молчит, и без клампа пагинация осталась бы вовсе без\n// активной страницы.\nconst currentPage = computed(() => Math.min(Math.max(Math.trunc(props.page), 1), pageCount.value))\n\n// Контролируемый компонент: если `total`/`pageSize` уменьшились и текущая `page`\n// вышла за диапазон — просим родителя подтянуть её к последней странице.\nwatch(pageCount, (count) => {\n if (props.page > count)\n emit('update:page', count)\n})\n\nconst pageSizeModel = computed({\n get: () => String(props.pageSize),\n set: (value: string) => emit('update:pageSize', Number(value)),\n})\n\nconst pageSizeOptions = computed(() =>\n props.pageSizes.map(pageSize => ({\n value: String(pageSize),\n label: String(pageSize),\n })),\n)\n\nconst rangeFrom = computed(() => (props.total === 0 ? 0 : (currentPage.value - 1) * resolvedPageSize.value + 1))\nconst rangeTo = computed(() => Math.min(currentPage.value * resolvedPageSize.value, props.total))\n\ntype PaginationItem = number | 'ellipsis-start' | 'ellipsis-end'\n\nfunction range(start: number, end: number): number[] {\n const list: number[] = []\n for (let page = start; page <= end; page += 1)\n list.push(page)\n return list\n}\n\n// Алгоритм usePagination как в MUI: крайние `boundaryCount` страниц + `siblingCount`\n// соседей вокруг текущей, разрывы схлопываются в многоточие. Одиночный «пропущенный»\n// номер показываем как номер, а не как «…» (визуально ровнее).\nconst items = computed<PaginationItem[]>(() => {\n const count = pageCount.value\n const current = currentPage.value\n const boundaryCount = Math.max(0, props.boundaryCount)\n const siblingCount = Math.max(0, props.siblingCount)\n\n const startPages = range(1, Math.min(boundaryCount, count))\n const endPages = range(Math.max(count - boundaryCount + 1, boundaryCount + 1), count)\n\n const siblingsStart = Math.max(\n Math.min(current - siblingCount, count - boundaryCount - siblingCount * 2 - 1),\n boundaryCount + 2,\n )\n const siblingsEnd = Math.min(\n Math.max(current + siblingCount, boundaryCount + siblingCount * 2 + 2),\n endPages.length > 0 ? endPages[0] - 2 : count - 1,\n )\n\n return [\n ...startPages,\n ...(siblingsStart > boundaryCount + 2\n ? ['ellipsis-start' as const]\n : boundaryCount + 1 < count - boundaryCount\n ? [boundaryCount + 1]\n : []),\n ...range(siblingsStart, siblingsEnd),\n ...(siblingsEnd < count - boundaryCount - 1\n ? ['ellipsis-end' as const]\n : count - boundaryCount > boundaryCount\n ? [count - boundaryCount]\n : []),\n ...endPages,\n ]\n})\n\nfunction goTo(page: number): void {\n if (props.disabled)\n return\n\n emit('update:page', page)\n}\n\nfunction prev(): void {\n goTo(Math.max(1, currentPage.value - 1))\n}\n\nfunction next(): void {\n goTo(Math.min(pageCount.value, currentPage.value + 1))\n}\n\nfunction first(): void {\n goTo(1)\n}\n\nfunction last(): void {\n goTo(pageCount.value)\n}\n\n// «Перейти к странице»: клампим введённый номер к диапазону [1, pageCount]\n// и сбрасываем поле после перехода.\nconst jumperValue = ref('')\n\nfunction submitJumper(): void {\n const parsed = Number.parseInt(jumperValue.value, 10)\n jumperValue.value = ''\n if (Number.isNaN(parsed))\n return\n\n goTo(Math.min(pageCount.value, Math.max(1, parsed)))\n}\n\nconst resolvedJumperLabel = computed(() => props.jumperLabel ?? t('gr.pagination.jumpTo', 'Go to'))\nconst resolvedAriaLabel = computed(() => props.ariaLabel ?? t('gr.pagination.label', 'Pagination'))\nconst statusText = computed(() =>\n t('gr.pagination.status', 'Page {page} of {count}', { page: currentPage.value, count: pageCount.value }),\n)\nconst totalText = computed(() =>\n t('gr.pagination.total', '{from}–{to} of {total}', { from: rangeFrom.value, to: rangeTo.value, total: props.total }),\n)\n</script>\n\n<template>\n <div\n class=\"flex flex-wrap items-center justify-end\"\n :class=\"rowClass\"\n data-gr-pagination\n role=\"navigation\"\n :aria-label=\"resolvedAriaLabel\"\n >\n <div\n v-if=\"showTotal\"\n data-gr-pagination-total\n class=\"text-[var(--gr-muted-fg)] [font-variant-numeric:tabular-nums]\"\n :class=\"labelClass\"\n >\n <slot name=\"total\" :from=\"rangeFrom\" :to=\"rangeTo\" :total=\"total\">\n {{ totalText }}\n </slot>\n </div>\n\n <div v-if=\"showPageSize\" :class=\"selectWrapClass\">\n <GrSelect\n v-model=\"pageSizeModel\"\n :options=\"pageSizeOptions\"\n :size=\"resolvedSize\"\n :disabled=\"disabled\"\n :aria-label=\"t('gr.pagination.pageSize', 'Page size')\"\n />\n </div>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage <= 1\" :aria-label=\"t('gr.pagination.first', 'First page')\" data-gr-pagination-first @click=\"first\">\n «\n </GrButton>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage <= 1\" data-gr-pagination-prev @click=\"prev\">\n {{ t('gr.pagination.prev', 'Prev') }}\n </GrButton>\n\n <!-- Компактный вариант: индикатор «текущая / всего» вместо нумерованных страниц. -->\n <div\n v-if=\"compact\"\n data-gr-pagination-compact\n class=\"px-2 text-[var(--gr-fg)] [font-variant-numeric:tabular-nums]\"\n :class=\"labelClass\"\n role=\"status\"\n >\n {{ currentPage }} / {{ pageCount }}\n </div>\n\n <template v-else>\n <ul class=\"flex items-center [list-style:none]\" :class=\"pageListClass\" data-gr-pagination-pages role=\"list\">\n <li v-for=\"(item, index) in items\" :key=\"index\" :aria-hidden=\"typeof item === 'string' ? 'true' : undefined\">\n <span\n v-if=\"item === 'ellipsis-start' || item === 'ellipsis-end'\"\n data-gr-pagination-ellipsis\n class=\"grid place-items-center text-[var(--gr-muted-fg)]\"\n :class=\"ellipsisClass\"\n >…</span>\n <button\n v-else\n type=\"button\"\n data-gr-pagination-page\n :disabled=\"disabled\"\n :aria-current=\"item === currentPage ? 'page' : undefined\"\n :aria-label=\"t('gr.pagination.page', 'Page {n}', { n: item })\"\n class=\"rounded-[var(--gr-radius-control)] font-600 transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] disabled:cursor-not-allowed\"\n :class=\"[\n pageClass,\n item === currentPage\n ? 'bg-[var(--gr-primary)] text-[var(--gr-primary-fg)]'\n // Подложка тональная, а не `--gr-muted`: серый на белом фоне даёт\n // 1.10:1 и на светлой теме hover был практически не виден, хотя у\n // соседних «назад/вперёд» (ghost-кнопки) он тонирован. Ряд обязан\n // читаться одним контролом.\n : 'text-[var(--gr-muted-fg)] hover:bg-[var(--gr-accent)] hover:text-[var(--gr-accent-fg)]',\n ]\"\n @click=\"goTo(item)\"\n >\n {{ item }}\n </button>\n </li>\n </ul>\n\n <!-- В компактном режиме смену страницы объявляет видимый индикатор. -->\n <div data-gr-pagination-status role=\"status\" class=\"sr-only\">\n {{ statusText }}\n </div>\n </template>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage >= pageCount\" data-gr-pagination-next @click=\"next\">\n {{ t('gr.pagination.next', 'Next') }}\n </GrButton>\n\n <GrButton variant=\"ghost\" :size=\"navButtonSize\" :disabled=\"disabled || currentPage >= pageCount\" :aria-label=\"t('gr.pagination.last', 'Last page')\" data-gr-pagination-last @click=\"last\">\n »\n </GrButton>\n\n <!-- «Перейти к странице»: Enter или blur применяют введённый номер. -->\n <div v-if=\"showJumper\" class=\"flex items-center gap-2 text-[var(--gr-muted-fg)]\" :class=\"labelClass\">\n <span>{{ resolvedJumperLabel }}</span>\n <input\n v-model=\"jumperValue\"\n type=\"number\"\n min=\"1\"\n :max=\"pageCount\"\n inputmode=\"numeric\"\n data-gr-pagination-jumper\n :disabled=\"disabled\"\n :aria-label=\"resolvedJumperLabel\"\n class=\"rounded-[var(--gr-radius-control)] border border-[var(--gr-brd)] bg-[var(--gr-bg)] px-2 text-center text-[var(--gr-fg)] [font-variant-numeric:tabular-nums] transition-colors duration-[var(--gr-duration-fast)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--gr-ring)] disabled:cursor-not-allowed\"\n :class=\"jumperClass\"\n @keydown.enter=\"submitJumper\"\n @blur=\"submitJumper\"\n >\n </div>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrProgressBar-BiHnaitu.js","names":[],"sources":["../../../../src/components/GrProgressBar/GrProgressBar.vue","../../../../src/components/GrProgressBar/GrProgressBar.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport {\n bufferClass,\n grProgressBarFillClass,\n type GrProgressBarSize,\n type GrProgressBarTone,\n rowGaps,\n trackSizes,\n valueTextSizes,\n} from './grStyle'\n\n/**\n * Пропы {@link GrProgressBar}.\n *\n * Линейный индикатор прогресса: определённый режим по `value` и неопределённый\n * по `indeterminate`, необязательный слой буфера и подпись значения.\n */\nexport interface GrProgressBarProps {\n /** Текущее значение `0..100`; выходящие за границы клампятся, `NaN` → `0`. */\n value: number\n /** Загружено с запасом: слой позади заливки, `0..100`. Не задан — слоя нет. */\n buffer?: number\n /** Прогресс неизвестен: полоса бежит, значение наружу не объявляется. */\n indeterminate?: boolean\n /** Показать значение подписью справа от трека. */\n showValue?: boolean\n /** Свой формат значения. Управляет и подписью, и `aria-valuetext`. */\n formatValue?: (value: number) => string\n /** Метка для скринридера (обязательна, если рядом нет видимого заголовка). */\n ariaLabel?: string\n /** Цветовая тональность заливки. */\n tone?: GrProgressBarTone\n /** Толщина трека. */\n size?: GrProgressBarSize\n /** Убрать рамку трека: внутри карточки вторая рамка только шумит. */\n borderless?: boolean\n}\n\nconst props = withDefaults(defineProps<GrProgressBarProps>(), {\n buffer: undefined,\n indeterminate: false,\n showValue: false,\n formatValue: undefined,\n ariaLabel: undefined,\n tone: 'primary',\n size: undefined,\n // `undefined`, а не `false`: иначе значение из `componentDefaults` не дошло бы.\n borderless: undefined,\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrProgressBar' })\nconst resolvedBorderless = useGrComponentProp('GrProgressBar', 'borderless', () => props.borderless, false)\nconst trackClassName = computed(() => trackSizes[resolvedSize.value])\nconst rowGapClassName = computed(() => rowGaps[resolvedSize.value])\nconst valueClassName = computed(() => valueTextSizes[resolvedSize.value])\n\nfunction clampValue(value: number): number {\n if (Number.isNaN(value))\n return 0\n return Math.min(100, Math.max(0, value))\n}\n\nconst safe = computed(() => clampValue(props.value))\nconst safeBuffer = computed(() => (props.buffer === undefined ? undefined : clampValue(props.buffer)))\n\nconst fillClassName = computed(() => grProgressBarFillClass(props.tone))\n\n/**\n * `aria-valuetext` нужен только со своим форматом: «45» при `aria-valuemax=\"100\"`\n * диктор и так читает как проценты, дублировать это текстом незачем.\n */\nconst valueText = computed(() => (props.formatValue ? props.formatValue(safe.value) : undefined))\nconst valueLabel = computed(() => valueText.value ?? `${Math.round(safe.value)}%`)\n</script>\n\n<template>\n <div\n data-gr-progress-bar\n class=\"w-full flex items-center\"\n :class=\"rowGapClassName\"\n >\n <div\n data-gr-progress-bar-track\n :data-gr-progress-bar-indeterminate=\"indeterminate ? '' : undefined\"\n role=\"progressbar\"\n :aria-label=\"ariaLabel\"\n :aria-valuenow=\"indeterminate ? undefined : safe\"\n :aria-valuetext=\"indeterminate ? undefined : valueText\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n class=\"relative min-w-0 flex-1 rounded-[var(--gr-radius-full)] bg-[var(--gr-muted)] overflow-hidden\"\n :class=\"[trackClassName, resolvedBorderless ? '' : 'border border-[var(--gr-brd)]']\"\n >\n <div\n v-if=\"safeBuffer !== undefined && !indeterminate\"\n data-gr-progress-bar-buffer\n class=\"absolute inset-y-0 left-0\"\n :class=\"bufferClass\"\n :style=\"{ width: `${safeBuffer}%` }\"\n />\n <div\n data-gr-progress-bar-fill\n class=\"absolute inset-y-0 left-0\"\n :class=\"fillClassName\"\n :style=\"indeterminate ? undefined : { width: `${safe}%` }\"\n />\n </div>\n <span\n v-if=\"showValue && !indeterminate\"\n data-gr-progress-bar-value\n class=\"text-[var(--gr-muted-fg)] [font-variant-numeric:tabular-nums] min-w-[4ch] text-right\"\n :class=\"valueClassName\"\n >{{ valueLabel }}</span>\n </div>\n</template>\n\n<style>\n@keyframes gr-progress-indeterminate {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(250%);\n }\n}\n\n/* Ширина бегущей полосы задаётся здесь, а не инлайном: инлайн-стиль перебил бы\n раскладку под `reduce` ниже. */\n[data-gr-progress-bar-indeterminate] [data-gr-progress-bar-fill] {\n width: 40%;\n animation-name: gr-progress-indeterminate;\n animation-duration: var(--gr-progress-indeterminate-duration, 1.4s);\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n}\n\n/* Замерев в исходном кадре, полоса легла бы у левого края и читалась как\n «прогресс 40%». Нейтральная заливка на всю ширину не притворяется ни нулём,\n ни завершением: она говорит «работа идёт, значение неизвестно». */\n@media (prefers-reduced-motion: reduce) {\n [data-gr-progress-bar-indeterminate] [data-gr-progress-bar-fill] {\n width: 100%;\n animation: none;\n background-color: var(--gr-progress-indeterminate-bg, var(--gr-muted-fg));\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport {\n bufferClass,\n grProgressBarFillClass,\n type GrProgressBarSize,\n type GrProgressBarTone,\n rowGaps,\n trackSizes,\n valueTextSizes,\n} from './grStyle'\n\n/**\n * Пропы {@link GrProgressBar}.\n *\n * Линейный индикатор прогресса: определённый режим по `value` и неопределённый\n * по `indeterminate`, необязательный слой буфера и подпись значения.\n */\nexport interface GrProgressBarProps {\n /** Текущее значение `0..100`; выходящие за границы клампятся, `NaN` → `0`. */\n value: number\n /** Загружено с запасом: слой позади заливки, `0..100`. Не задан — слоя нет. */\n buffer?: number\n /** Прогресс неизвестен: полоса бежит, значение наружу не объявляется. */\n indeterminate?: boolean\n /** Показать значение подписью справа от трека. */\n showValue?: boolean\n /** Свой формат значения. Управляет и подписью, и `aria-valuetext`. */\n formatValue?: (value: number) => string\n /** Метка для скринридера (обязательна, если рядом нет видимого заголовка). */\n ariaLabel?: string\n /** Цветовая тональность заливки. */\n tone?: GrProgressBarTone\n /** Толщина трека. */\n size?: GrProgressBarSize\n /** Убрать рамку трека: внутри карточки вторая рамка только шумит. */\n borderless?: boolean\n}\n\nconst props = withDefaults(defineProps<GrProgressBarProps>(), {\n buffer: undefined,\n indeterminate: false,\n showValue: false,\n formatValue: undefined,\n ariaLabel: undefined,\n tone: 'primary',\n size: undefined,\n // `undefined`, а не `false`: иначе значение из `componentDefaults` не дошло бы.\n borderless: undefined,\n})\n\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrProgressBar' })\nconst resolvedBorderless = useGrComponentProp('GrProgressBar', 'borderless', () => props.borderless, false)\nconst trackClassName = computed(() => trackSizes[resolvedSize.value])\nconst rowGapClassName = computed(() => rowGaps[resolvedSize.value])\nconst valueClassName = computed(() => valueTextSizes[resolvedSize.value])\n\nfunction clampValue(value: number): number {\n if (Number.isNaN(value))\n return 0\n return Math.min(100, Math.max(0, value))\n}\n\nconst safe = computed(() => clampValue(props.value))\nconst safeBuffer = computed(() => (props.buffer === undefined ? undefined : clampValue(props.buffer)))\n\nconst fillClassName = computed(() => grProgressBarFillClass(props.tone))\n\n/**\n * `aria-valuetext` нужен только со своим форматом: «45» при `aria-valuemax=\"100\"`\n * диктор и так читает как проценты, дублировать это текстом незачем.\n */\nconst valueText = computed(() => (props.formatValue ? props.formatValue(safe.value) : undefined))\nconst valueLabel = computed(() => valueText.value ?? `${Math.round(safe.value)}%`)\n</script>\n\n<template>\n <div\n data-gr-progress-bar\n class=\"w-full flex items-center\"\n :class=\"rowGapClassName\"\n >\n <div\n data-gr-progress-bar-track\n :data-gr-progress-bar-indeterminate=\"indeterminate ? '' : undefined\"\n role=\"progressbar\"\n :aria-label=\"ariaLabel\"\n :aria-valuenow=\"indeterminate ? undefined : safe\"\n :aria-valuetext=\"indeterminate ? undefined : valueText\"\n aria-valuemin=\"0\"\n aria-valuemax=\"100\"\n class=\"relative min-w-0 flex-1 rounded-[var(--gr-radius-full)] bg-[var(--gr-muted)] overflow-hidden\"\n :class=\"[trackClassName, resolvedBorderless ? '' : 'border border-[var(--gr-brd)]']\"\n >\n <div\n v-if=\"safeBuffer !== undefined && !indeterminate\"\n data-gr-progress-bar-buffer\n class=\"absolute inset-y-0 left-0\"\n :class=\"bufferClass\"\n :style=\"{ width: `${safeBuffer}%` }\"\n />\n <div\n data-gr-progress-bar-fill\n class=\"absolute inset-y-0 left-0\"\n :class=\"fillClassName\"\n :style=\"indeterminate ? undefined : { width: `${safe}%` }\"\n />\n </div>\n <span\n v-if=\"showValue && !indeterminate\"\n data-gr-progress-bar-value\n class=\"text-[var(--gr-muted-fg)] [font-variant-numeric:tabular-nums] min-w-[4ch] text-right\"\n :class=\"valueClassName\"\n >{{ valueLabel }}</span>\n </div>\n</template>\n\n<style>\n@keyframes gr-progress-indeterminate {\n from {\n transform: translateX(-100%);\n }\n to {\n transform: translateX(250%);\n }\n}\n\n/* Ширина бегущей полосы задаётся здесь, а не инлайном: инлайн-стиль перебил бы\n раскладку под `reduce` ниже. */\n[data-gr-progress-bar-indeterminate] [data-gr-progress-bar-fill] {\n width: 40%;\n animation-name: gr-progress-indeterminate;\n animation-duration: var(--gr-progress-indeterminate-duration, 1.4s);\n animation-timing-function: linear;\n animation-iteration-count: infinite;\n}\n\n/* Замерев в исходном кадре, полоса легла бы у левого края и читалась как\n «прогресс 40%». Нейтральная заливка на всю ширину не притворяется ни нулём,\n ни завершением: она говорит «работа идёт, значение неизвестно». */\n@media (prefers-reduced-motion: reduce) {\n [data-gr-progress-bar-indeterminate] [data-gr-progress-bar-fill] {\n width: 100%;\n animation: none;\n background-color: var(--gr-progress-indeterminate-bg, var(--gr-muted-fg));\n }\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyCA,MAAM,QAAQ;EAYd,MAAM,eAAe,yBAAyB,MAAM,MAAM,EAAE,WAAW,gBAAgB,CAAC;EACxF,MAAM,qBAAqB,mBAAmB,iBAAiB,oBAAoB,MAAM,YAAY,KAAK;EAC1G,MAAM,iBAAiB,eAAe,WAAW,aAAa,MAAM;EACpE,MAAM,kBAAkB,eAAe,QAAQ,aAAa,MAAM;EAClE,MAAM,iBAAiB,eAAe,eAAe,aAAa,MAAM;EAExE,SAAS,WAAW,OAAuB;GACzC,IAAI,OAAO,MAAM,KAAK,GACpB,OAAO;GACT,OAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC;EACzC;EAEA,MAAM,OAAO,eAAe,WAAW,MAAM,KAAK,CAAC;EACnD,MAAM,aAAa,eAAgB,MAAM,WAAW,KAAA,IAAY,KAAA,IAAY,WAAW,MAAM,MAAM,CAAE;EAErG,MAAM,gBAAgB,eAAe,uBAAuB,MAAM,IAAI,CAAC;;;;;EAMvE,MAAM,YAAY,eAAgB,MAAM,cAAc,MAAM,YAAY,KAAK,KAAK,IAAI,KAAA,CAAU;EAChG,MAAM,aAAa,eAAe,UAAU,SAAS,GAAG,KAAK,MAAM,KAAK,KAAK,EAAE,EAAE;;uBAI/E,mBAqCM,OAAA;IApCJ,wBAAA;IACA,OAAK,eAAA,CAAC,4BACE,gBAAA,KAAe,CAAA;OAEvB,mBAyBM,OAAA;IAxBJ,8BAAA;IACC,sCAAoC,QAAA,gBAAa,KAAQ,KAAA;IAC1D,MAAK;IACJ,cAAY,QAAA;IACZ,iBAAe,QAAA,gBAAgB,KAAA,IAAY,KAAA;IAC3C,kBAAgB,QAAA,gBAAgB,KAAA,IAAY,UAAA;IAC7C,iBAAc;IACd,iBAAc;IACd,OAAK,eAAA,CAAC,gGAA8F,CAC3F,eAAA,OAAgB,MAAA,kBAAA,IAAkB,KAAA,+BAAA,CAAA,CAAA;OAGnC,WAAA,UAAe,KAAA,KAAS,CAAK,QAAA,iBAAA,UAAA,GADrC,mBAME,OAAA;;IAJA,+BAAA;IACA,OAAK,eAAA,CAAC,6BACE,MAAA,WAAA,CAAW,CAAA;IAClB,OAAK,eAAA,EAAA,OAAA,GAAc,WAAA,MAAU,GAAA,CAAA;gDAEhC,mBAKE,OAAA;IAJA,6BAAA;IACA,OAAK,eAAA,CAAC,6BACE,cAAA,KAAa,CAAA;IACpB,OAAK,eAAE,QAAA,gBAAgB,KAAA,IAAS,EAAA,OAAA,GAAe,KAAA,MAAI,GAAA,CAAA;kCAIhD,QAAA,aAAS,CAAK,QAAA,iBAAA,UAAA,GADtB,mBAKwB,QAAA;;IAHtB,8BAAA;IACA,OAAK,eAAA,CAAC,wFACE,eAAA,KAAc,CAAA;sBACpB,WAAA,KAAU,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrRadio-DksDro_U.js","names":[],"sources":["../../../../src/components/GrRadio/grRadioGroupContext.ts","../../../../src/components/GrRadio/GrRadio.vue","../../../../src/components/GrRadio/GrRadio.vue"],"sourcesContent":["import type { ComputedRef, InjectionKey } from 'vue'\n\nimport type { GrButtonSize } from '../GrButton/grButtonStyles'\n\n/**\n * Значение переключателя. Не только строка: перечисления в реальных формах —\n * это id числом или флаг. Объекты сюда намеренно не входят: значение уходит в\n * `data-value` и в скрытый input нативной формы, а значит должно иметь\n * однозначное строковое представление.\n */\nexport type GrRadioValue = string | number | boolean\n\nexport type GrRadioEntry = {\n value: () => GrRadioValue\n disabled: () => boolean\n}\n\nexport type GrRadioGroupContext = {\n modelValue: ComputedRef<GrRadioValue>\n name: ComputedRef<string | undefined>\n disabled: ComputedRef<boolean>\n /**\n * Только для чтения: выбор видно, но он не меняется. У роли `radio` нет\n * `aria-readonly` — состояние объявляет сама группа, переключателю остаётся\n * не прикидываться кликабельным.\n */\n readonly: ComputedRef<boolean>\n /** Ошибка группы: до вида переключателя она доходит только через контекст. */\n invalid: ComputedRef<boolean>\n size: ComputedRef<GrButtonSize>\n setValue: (value: GrRadioValue) => void\n /**\n * Регистрация переключателя в группе. Порядок регистрации совпадает с\n * порядком в документе (setup детей идёт сверху вниз), поэтому группа знает\n * состав и в slot-режиме, и на сервере — без обхода DOM.\n */\n register: (entry: GrRadioEntry) => () => void\n /**\n * Значение переключателя, который держит `tabindex=\"0\"`. Roving tabindex\n * WAI-ARIA: группа — одна остановка Tab, внутрь попадают стрелками. Если не\n * выбрано ничего, остановкой становится первый доступный переключатель.\n */\n rovingValue: ComputedRef<GrRadioValue | undefined>\n /** Переводит выбор на соседний доступный переключатель (стрелки). */\n moveSelection: (from: GrRadioValue, direction: 1 | -1) => GrRadioValue | undefined\n /** Переводит выбор на первый или последний доступный (`Home`/`End`). */\n selectEdge: (edge: 'first' | 'last') => GrRadioValue | undefined\n}\n\nexport const GR_RADIO_GROUP_CONTEXT: InjectionKey<GrRadioGroupContext> = Symbol('GR_RADIO_GROUP_CONTEXT')\n","<script setup lang=\"ts\">\nimport { computed, inject, nextTick, onUnmounted, ref, useId, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDescriptionSizes,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioDotSizes,\n grRadioLabelClassFor,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT, type GrRadioValue } from './grRadioGroupContext'\n\nexport type { GrRadioValue } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name`/`invalid` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n *\n * Доступным контролом является сам элемент с `role=\"radio\"`: он держит фокус,\n * `aria-checked` и клавиатуру. Внутрь него ничего интерактивного не вкладывается —\n * `role=\"radio\"` объявляет потомков презентационными, и вложенный native `<input>`\n * (даже скрытый и с `tabindex=\"-1\"`) ломает виджет для скринридеров и падает\n * в axe на `nested-interactive`. Для отправки нативной формой рядом рендерится\n * `input[type=\"hidden\"]` — он не фокусируется и не является интерактивным.\n */\nexport interface GrRadioProps {\n value: GrRadioValue\n modelValue?: GrRadioValue\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. Складывается с `invalid` группы. */\n invalid?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nexport interface GrRadioEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n}\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n invalid: false,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrRadioEmits>()\n\nconst slots = useSlots()\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedReadonly = computed(() => group?.readonly.value ?? false)\n\n// «Или», а не `??`: ошибку может объявить и группа, и сам переключатель.\nconst resolvedInvalid = computed(() => props.invalid || (group?.invalid.value ?? false))\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\n// Приоритет: локальный проп → размер группы → `GrConfigProvider` → `md`.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize(\n () => props.size ?? group?.size.value,\n { component: 'GrRadio' },\n)\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\n/** В DOM значение живёт строкой: `data-value` и скрытый input иначе не умеют. */\nconst domValue = computed(() => String(props.value))\n\n// Нативная форма получает значение только от выбранного и не-disabled элемента —\n// ровно как поступил бы native radio. Без `name` отправлять нечего.\nconst submitsValue = computed(\n () => checked.value && !resolvedDisabled.value && Boolean(resolvedName.value),\n)\n\nconst descriptionId = useId()\nconst hasDescription = computed(() => Boolean(slots.description))\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass({\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n}))\nconst controlClassName = computed(() => grRadioControlClass({\n size: resolvedSize.value,\n checked: checked.value,\n disabled: resolvedDisabled.value,\n invalid: resolvedInvalid.value,\n}))\nconst dotClassName = computed(() => grRadioDotClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n}))\nconst labelClassName = computed(() => grRadioLabelClassFor(resolvedSize.value, checked.value))\nconst dotSizeClassName = computed(() => grRadioDotSizes[resolvedSize.value])\nconst descriptionClassName = computed(() => grRadioDescriptionSizes[resolvedSize.value])\n\nfunction setValue(next: GrRadioValue): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n\n// Паттерн radiogroup: группа — одна остановка Tab, внутрь попадают стрелками.\n// Вне группы (одиночный `GrRadio`) переключатель остаётся обычной остановкой.\nconst rovingTabindex = computed(() => {\n if (resolvedDisabled.value) return -1\n if (!group) return 0\n return group.rovingValue.value === props.value ? 0 : -1\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n/**\n * Фокус переезжает вслед за выбором — иначе следующая стрелка отсчитывалась бы\n * от прежнего элемента.\n */\nfunction focusValue(next: GrRadioValue | undefined): void {\n if (next === undefined) return\n\n void nextTick(() => {\n const group = rootEl.value?.closest('[data-gr-radio-group]')\n const target = [...(group?.querySelectorAll<HTMLElement>('[data-gr-radio]') ?? [])]\n .find(el => el.dataset.value === String(next))\n\n target?.focus()\n })\n}\n\nfunction onArrow(direction: 1 | -1, event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n event.preventDefault()\n focusValue(group.moveSelection(props.value, direction))\n}\n\nfunction onEdge(edge: 'first' | 'last', event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n event.preventDefault()\n focusValue(group.selectEdge(edge))\n}\n\nconst unregister = group?.register({\n value: () => props.value,\n disabled: () => resolvedDisabled.value,\n})\n\nonUnmounted(() => unregister?.())\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown.down=\"onArrow(1, $event)\"\n @keydown.right=\"onArrow(1, $event)\"\n @keydown.up=\"onArrow(-1, $event)\"\n @keydown.left=\"onArrow(-1, $event)\"\n @keydown.home=\"onEdge('first', $event)\"\n @keydown.end=\"onEdge('last', $event)\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :aria-describedby=\"hasDescription ? descriptionId : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n class=\"inline-flex items-start gap-2 select-none focus-visible:outline-none focus-visible:rounded-[var(--gr-radius-md)] focus-visible:shadow-[0_0_0_2px_var(--gr-ring),0_0_0_4px_var(--gr-bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown.down=\"onArrow(1, $event)\"\n @keydown.right=\"onArrow(1, $event)\"\n @keydown.up=\"onArrow(-1, $event)\"\n @keydown.left=\"onArrow(-1, $event)\"\n @keydown.home=\"onEdge('first', $event)\"\n @keydown.end=\"onEdge('last', $event)\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"mt-0.5 shrink-0 rounded-[var(--gr-radius-full)] border flex items-center justify-center transition-colors duration-[var(--gr-duration-fast)]\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotSizeClassName, dotClassName]\"\n />\n </span>\n\n <span class=\"grid gap-0.5\">\n <span data-gr-radio-label :class=\"labelClassName\">\n <slot />\n </span>\n\n <span\n v-if=\"hasDescription\"\n :id=\"descriptionId\"\n data-gr-radio-description\n :class=\"descriptionClassName\"\n >\n <slot name=\"description\" />\n </span>\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, inject, nextTick, onUnmounted, ref, useId, useSlots } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport type { GrButtonSize, GrButtonTone, GrButtonVariant } from '../GrButton'\n\nimport {\n grRadioButtonClass,\n grRadioControlClass,\n grRadioDescriptionSizes,\n grRadioDotBaseClass,\n grRadioDotClass,\n grRadioDotSizes,\n grRadioLabelClassFor,\n grRadioRootClass,\n type GrRadioVariant,\n} from './grRadioStyles'\nimport { GR_RADIO_GROUP_CONTEXT, type GrRadioValue } from './grRadioGroupContext'\n\nexport type { GrRadioValue } from './grRadioGroupContext'\n\n/**\n * GrRadio — одиночный элемент группы выбора.\n *\n * Может работать автономно (через `v-model`) либо внутри `GrRadioGroup`\n * (тогда `modelValue`/`disabled`/`size`/`name`/`invalid` приходят через `inject`).\n *\n * @prop value — значение этого элемента, сравнивается с `modelValue` группы.\n * @prop variant — визуальное представление: `radiobox` (круг+dot) или `button` (стиль `GrButton`).\n *\n * Доступным контролом является сам элемент с `role=\"radio\"`: он держит фокус,\n * `aria-checked` и клавиатуру. Внутрь него ничего интерактивного не вкладывается —\n * `role=\"radio\"` объявляет потомков презентационными, и вложенный native `<input>`\n * (даже скрытый и с `tabindex=\"-1\"`) ломает виджет для скринридеров и падает\n * в axe на `nested-interactive`. Для отправки нативной формой рядом рендерится\n * `input[type=\"hidden\"]` — он не фокусируется и не является интерактивным.\n */\nexport interface GrRadioProps {\n value: GrRadioValue\n modelValue?: GrRadioValue\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. Складывается с `invalid` группы. */\n invalid?: boolean\n name?: string\n required?: boolean\n form?: string\n id?: string\n size?: GrButtonSize\n variant?: GrRadioVariant\n buttonVariant?: GrButtonVariant\n buttonTone?: GrButtonTone\n selectedButtonVariant?: GrButtonVariant\n selectedButtonTone?: GrButtonTone\n ariaLabel?: string\n}\n\nexport interface GrRadioEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n}\n\nconst props = withDefaults(defineProps<GrRadioProps>(), {\n modelValue: undefined,\n disabled: undefined,\n invalid: false,\n name: undefined,\n required: false,\n form: undefined,\n id: undefined,\n size: undefined,\n variant: 'radiobox',\n buttonVariant: 'outline',\n buttonTone: 'neutral',\n selectedButtonVariant: 'primary',\n selectedButtonTone: 'primary',\n ariaLabel: undefined,\n})\n\nconst emit = defineEmits<GrRadioEmits>()\n\nconst slots = useSlots()\nconst group = inject(GR_RADIO_GROUP_CONTEXT, null)\n\nconst resolvedModelValue = computed(() => {\n if (props.modelValue !== undefined)\n return props.modelValue\n\n return group?.modelValue.value ?? ''\n})\n\nconst resolvedDisabled = computed(() => {\n if (props.disabled !== undefined)\n return props.disabled\n\n return group?.disabled.value ?? false\n})\n\nconst resolvedReadonly = computed(() => group?.readonly.value ?? false)\n\n// «Или», а не `??`: ошибку может объявить и группа, и сам переключатель.\nconst resolvedInvalid = computed(() => props.invalid || (group?.invalid.value ?? false))\n\nconst resolvedName = computed(() => {\n if (props.name)\n return props.name\n\n return group?.name.value\n})\n\n// Приоритет: локальный проп → размер группы → `GrConfigProvider` → `md`.\n// Группа специфичнее конфига, поэтому её значение подаётся как «локальное».\nconst resolvedSize = useGrComponentSize(\n () => props.size ?? group?.size.value,\n { component: 'GrRadio' },\n)\n\nconst checked = computed(() => resolvedModelValue.value === props.value)\n\n/** В DOM значение живёт строкой: `data-value` и скрытый input иначе не умеют. */\nconst domValue = computed(() => String(props.value))\n\n// Нативная форма получает значение только от выбранного и не-disabled элемента —\n// ровно как поступил бы native radio. Без `name` отправлять нечего.\nconst submitsValue = computed(\n () => checked.value && !resolvedDisabled.value && Boolean(resolvedName.value),\n)\n\nconst descriptionId = useId()\nconst hasDescription = computed(() => Boolean(slots.description))\n\nconst buttonClassName = computed(() => {\n return grRadioButtonClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n size: resolvedSize.value,\n buttonVariant: props.buttonVariant,\n buttonTone: props.buttonTone,\n selectedButtonVariant: props.selectedButtonVariant,\n selectedButtonTone: props.selectedButtonTone,\n })\n})\n\nconst rootClassName = computed(() => grRadioRootClass({\n disabled: resolvedDisabled.value,\n readonly: resolvedReadonly.value,\n}))\nconst controlClassName = computed(() => grRadioControlClass({\n size: resolvedSize.value,\n checked: checked.value,\n disabled: resolvedDisabled.value,\n invalid: resolvedInvalid.value,\n}))\nconst dotClassName = computed(() => grRadioDotClass({\n checked: checked.value,\n disabled: resolvedDisabled.value,\n}))\nconst labelClassName = computed(() => grRadioLabelClassFor(resolvedSize.value, checked.value))\nconst dotSizeClassName = computed(() => grRadioDotSizes[resolvedSize.value])\nconst descriptionClassName = computed(() => grRadioDescriptionSizes[resolvedSize.value])\n\nfunction setValue(next: GrRadioValue): void {\n if (resolvedDisabled.value)\n return\n\n if (props.modelValue !== undefined) {\n emit('update:modelValue', next)\n return\n }\n\n group?.setValue(next)\n}\n\nfunction onButtonClick(): void {\n setValue(props.value)\n}\n\n// Паттерн radiogroup: группа — одна остановка Tab, внутрь попадают стрелками.\n// Вне группы (одиночный `GrRadio`) переключатель остаётся обычной остановкой.\nconst rovingTabindex = computed(() => {\n if (resolvedDisabled.value) return -1\n if (!group) return 0\n return group.rovingValue.value === props.value ? 0 : -1\n})\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n/**\n * Фокус переезжает вслед за выбором — иначе следующая стрелка отсчитывалась бы\n * от прежнего элемента.\n */\nfunction focusValue(next: GrRadioValue | undefined): void {\n if (next === undefined) return\n\n void nextTick(() => {\n const group = rootEl.value?.closest('[data-gr-radio-group]')\n const target = [...(group?.querySelectorAll<HTMLElement>('[data-gr-radio]') ?? [])]\n .find(el => el.dataset.value === String(next))\n\n target?.focus()\n })\n}\n\nfunction onArrow(direction: 1 | -1, event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n event.preventDefault()\n focusValue(group.moveSelection(props.value, direction))\n}\n\nfunction onEdge(edge: 'first' | 'last', event: KeyboardEvent): void {\n if (!group || resolvedDisabled.value) return\n\n event.preventDefault()\n focusValue(group.selectEdge(edge))\n}\n\nconst unregister = group?.register({\n value: () => props.value,\n disabled: () => resolvedDisabled.value,\n})\n\nonUnmounted(() => unregister?.())\n</script>\n\n<template>\n <div\n v-if=\"variant === 'button'\"\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-button\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n :class=\"buttonClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown.down=\"onArrow(1, $event)\"\n @keydown.right=\"onArrow(1, $event)\"\n @keydown.up=\"onArrow(-1, $event)\"\n @keydown.left=\"onArrow(-1, $event)\"\n @keydown.home=\"onEdge('first', $event)\"\n @keydown.end=\"onEdge('last', $event)\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <slot />\n </div>\n\n <div\n v-else\n :id=\"id\"\n ref=\"rootEl\"\n data-gr-radio\n role=\"radio\"\n :aria-checked=\"checked ? 'true' : 'false'\"\n :aria-label=\"ariaLabel\"\n :aria-disabled=\"resolvedDisabled ? 'true' : undefined\"\n :aria-invalid=\"resolvedInvalid ? 'true' : undefined\"\n :aria-required=\"required ? 'true' : undefined\"\n :aria-describedby=\"hasDescription ? descriptionId : undefined\"\n :data-value=\"domValue\"\n :tabindex=\"rovingTabindex\"\n class=\"inline-flex items-start gap-2 select-none focus-visible:outline-none focus-visible:rounded-[var(--gr-radius-md)] focus-visible:shadow-[0_0_0_2px_var(--gr-ring),0_0_0_4px_var(--gr-bg)]\"\n :class=\"rootClassName\"\n @click=\"onButtonClick\"\n @keydown.space.prevent=\"onButtonClick\"\n @keydown.enter.prevent=\"onButtonClick\"\n @keydown.down=\"onArrow(1, $event)\"\n @keydown.right=\"onArrow(1, $event)\"\n @keydown.up=\"onArrow(-1, $event)\"\n @keydown.left=\"onArrow(-1, $event)\"\n @keydown.home=\"onEdge('first', $event)\"\n @keydown.end=\"onEdge('last', $event)\"\n >\n <input\n v-if=\"submitsValue\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"domValue\"\n :form=\"form\"\n >\n\n <span\n data-gr-radio-control\n aria-hidden=\"true\"\n class=\"mt-0.5 shrink-0 rounded-[var(--gr-radius-full)] border flex items-center justify-center transition-colors duration-[var(--gr-duration-fast)]\"\n :class=\"controlClassName\"\n >\n <span\n data-gr-radio-dot\n :class=\"[grRadioDotBaseClass, dotSizeClassName, dotClassName]\"\n />\n </span>\n\n <span class=\"grid gap-0.5\">\n <span data-gr-radio-label :class=\"labelClassName\">\n <slot />\n </span>\n\n <span\n v-if=\"hasDescription\"\n :id=\"descriptionId\"\n data-gr-radio-description\n :class=\"descriptionClassName\"\n >\n <slot name=\"description\" />\n </span>\n </span>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrRadioGroup-BOIhejE6.js","names":["$slots"],"sources":["../../../../src/components/GrRadioGroup/GrRadioGroup.vue","../../../../src/components/GrRadioGroup/GrRadioGroup.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, provide, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\n\nimport type { GrButtonSize } from '../GrButton'\nimport GrButtonGroup from '../GrButtonGroup/GrButtonGroup.vue'\nimport GrRadio from '../GrRadio/GrRadio.vue'\nimport { GR_RADIO_GROUP_CONTEXT } from '../GrRadio'\nimport type { GrRadioEntry, GrRadioValue } from '../GrRadio/grRadioGroupContext'\n\nexport type GrRadioGroupVariant = 'radiobox' | 'button'\nexport type GrRadioGroupOrientation = 'vertical' | 'horizontal'\nexport interface GrRadioGroupOption {\n value: GrRadioValue\n label: string\n /** Отключить одну опцию, не переходя на слот. */\n disabled?: boolean\n /** Пояснение под подписью. Рисуется только в варианте `radiobox`. */\n description?: string\n}\n\n/**\n * GrRadioGroup — контейнер группы `GrRadio`.\n *\n * Может работать как через слот (ручной рендер `GrRadio`), так и через проп `options`.\n * Предоставляет дочерним `GrRadio` общий `modelValue`/`disabled`/`size`/`name` через `inject`.\n */\nexport interface GrRadioGroupProps {\n modelValue: GrRadioValue\n options?: GrRadioGroupOption[]\n name?: string\n disabled?: boolean\n /** Только для чтения: выбор видно, но он не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n variant?: GrRadioGroupVariant\n /**\n * Раскладка варианта `radiobox`. Кнопочный вариант всегда горизонтальный —\n * его собирает `GrButtonGroup`.\n */\n orientation?: GrRadioGroupOrientation\n size?: GrButtonSize\n ariaLabel?: string\n}\n\nexport interface GrRadioGroupEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n (e: 'change', value: GrRadioValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrRadioGroupProps>(), {\n options: undefined,\n name: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n variant: 'radiobox',\n orientation: 'vertical',\n size: undefined,\n ariaLabel: undefined,\n})\n\n// Эффективный размер группы: локальный проп → `GrConfigProvider` → `md`.\n// Дочерние `GrRadio` получают уже разрешённое значение через контекст, поэтому\n// провайдер работает и для них — без второго чтения конфига.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrRadioGroup' })\n\n// Контекст `GrFormField`. Группа — не labelable-элемент, поэтому имя приходит\n// через `aria-labelledby` на подпись поля, а не через `<label for>`.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\n\nconst emit = defineEmits<GrRadioGroupEmits>()\ndefineSlots<{\n /** Собственная разметка переключателей вместо генерации из `options`. */\n default?: () => any\n}>()\n\n\nfunction setValue(next: GrRadioValue): void {\n if (isDisabled.value || isReadonly.value)\n return\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nconst listClass = computed(() => (props.orientation === 'horizontal'\n ? 'flex flex-wrap items-start gap-x-6 gap-y-2'\n : 'grid gap-2'))\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// Фокус ходит между радиокнопками: без границы каждая стрелка давала бы\n// потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nfunction focus(): void {\n rootEl.value?.querySelector<HTMLElement>('[role=\"radio\"][tabindex=\"0\"]')?.focus()\n}\n\nfunction blur(): void {\n rootEl.value?.querySelector<HTMLElement>('[role=\"radio\"][tabindex=\"0\"]')?.blur()\n}\n\ndefineExpose({ focus, blur })\n\nconst entries = ref<GrRadioEntry[]>([])\n\nfunction register(entry: GrRadioEntry): () => void {\n entries.value.push(entry)\n return () => {\n const index = entries.value.indexOf(entry)\n if (index >= 0) entries.value.splice(index, 1)\n }\n}\n\nconst enabledValues = computed(() =>\n entries.value.filter(entry => !entry.disabled()).map(entry => entry.value()),\n)\n\nconst rovingValue = computed(() => {\n if (enabledValues.value.includes(props.modelValue)) return props.modelValue\n return enabledValues.value[0]\n})\n\nfunction moveSelection(from: GrRadioValue, direction: 1 | -1): GrRadioValue | undefined {\n const values = enabledValues.value\n if (values.length === 0) return undefined\n\n const current = values.indexOf(from)\n // Стрелки зациклены — это требование паттерна radiogroup.\n const next = values[(current + direction + values.length) % values.length]\n\n if (next !== undefined) setValue(next)\n return next\n}\n\n/** `Home`/`End` — на края набора; как в `GrSegmented`, где паттерн реализован целиком. */\nfunction selectEdge(edge: 'first' | 'last'): GrRadioValue | undefined {\n const values = enabledValues.value\n const next = edge === 'first' ? values[0] : values.at(-1)\n\n if (next !== undefined) setValue(next)\n return next\n}\n\nprovide(GR_RADIO_GROUP_CONTEXT, {\n modelValue: computed(() => props.modelValue),\n name: computed(() => props.name),\n disabled: computed(() => isDisabled.value),\n readonly: isReadonly,\n invalid: isInvalid,\n size: resolvedSize,\n setValue,\n register,\n rovingValue,\n moveSelection,\n selectEdge,\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootEl\"\n data-gr-radio-group\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <template v-if=\"$slots.default\">\n <GrButtonGroup v-if=\"variant === 'button'\">\n <slot />\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <slot />\n </div>\n </template>\n <template v-else>\n <GrButtonGroup v-if=\"variant === 'button'\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n variant=\"button\"\n :size=\"resolvedSize\"\n >\n {{ opt.label }}\n </GrRadio>\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n >\n {{ opt.label }}\n\n <template v-if=\"opt.description\" #description>\n {{ opt.description }}\n </template>\n </GrRadio>\n </div>\n </template>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, provide, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\n\nimport type { GrButtonSize } from '../GrButton'\nimport GrButtonGroup from '../GrButtonGroup/GrButtonGroup.vue'\nimport GrRadio from '../GrRadio/GrRadio.vue'\nimport { GR_RADIO_GROUP_CONTEXT } from '../GrRadio'\nimport type { GrRadioEntry, GrRadioValue } from '../GrRadio/grRadioGroupContext'\n\nexport type GrRadioGroupVariant = 'radiobox' | 'button'\nexport type GrRadioGroupOrientation = 'vertical' | 'horizontal'\nexport interface GrRadioGroupOption {\n value: GrRadioValue\n label: string\n /** Отключить одну опцию, не переходя на слот. */\n disabled?: boolean\n /** Пояснение под подписью. Рисуется только в варианте `radiobox`. */\n description?: string\n}\n\n/**\n * GrRadioGroup — контейнер группы `GrRadio`.\n *\n * Может работать как через слот (ручной рендер `GrRadio`), так и через проп `options`.\n * Предоставляет дочерним `GrRadio` общий `modelValue`/`disabled`/`size`/`name` через `inject`.\n */\nexport interface GrRadioGroupProps {\n modelValue: GrRadioValue\n options?: GrRadioGroupOption[]\n name?: string\n disabled?: boolean\n /** Только для чтения: выбор видно, но он не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n variant?: GrRadioGroupVariant\n /**\n * Раскладка варианта `radiobox`. Кнопочный вариант всегда горизонтальный —\n * его собирает `GrButtonGroup`.\n */\n orientation?: GrRadioGroupOrientation\n size?: GrButtonSize\n ariaLabel?: string\n}\n\nexport interface GrRadioGroupEmits {\n (e: 'update:modelValue', value: GrRadioValue): void\n (e: 'change', value: GrRadioValue): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(defineProps<GrRadioGroupProps>(), {\n options: undefined,\n name: undefined,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n variant: 'radiobox',\n orientation: 'vertical',\n size: undefined,\n ariaLabel: undefined,\n})\n\n// Эффективный размер группы: локальный проп → `GrConfigProvider` → `md`.\n// Дочерние `GrRadio` получают уже разрешённое значение через контекст, поэтому\n// провайдер работает и для них — без второго чтения конфига.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrRadioGroup' })\n\n// Контекст `GrFormField`. Группа — не labelable-элемент, поэтому имя приходит\n// через `aria-labelledby` на подпись поля, а не через `<label for>`.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\n\nconst emit = defineEmits<GrRadioGroupEmits>()\ndefineSlots<{\n /** Собственная разметка переключателей вместо генерации из `options`. */\n default?: () => any\n}>()\n\n\nfunction setValue(next: GrRadioValue): void {\n if (isDisabled.value || isReadonly.value)\n return\n emit('update:modelValue', next)\n emit('change', next)\n}\n\nconst listClass = computed(() => (props.orientation === 'horizontal'\n ? 'flex flex-wrap items-start gap-x-6 gap-y-2'\n : 'grid gap-2'))\n\nconst rootEl = ref<HTMLElement | null>(null)\n\n// Фокус ходит между радиокнопками: без границы каждая стрелка давала бы\n// потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nfunction focus(): void {\n rootEl.value?.querySelector<HTMLElement>('[role=\"radio\"][tabindex=\"0\"]')?.focus()\n}\n\nfunction blur(): void {\n rootEl.value?.querySelector<HTMLElement>('[role=\"radio\"][tabindex=\"0\"]')?.blur()\n}\n\ndefineExpose({ focus, blur })\n\nconst entries = ref<GrRadioEntry[]>([])\n\nfunction register(entry: GrRadioEntry): () => void {\n entries.value.push(entry)\n return () => {\n const index = entries.value.indexOf(entry)\n if (index >= 0) entries.value.splice(index, 1)\n }\n}\n\nconst enabledValues = computed(() =>\n entries.value.filter(entry => !entry.disabled()).map(entry => entry.value()),\n)\n\nconst rovingValue = computed(() => {\n if (enabledValues.value.includes(props.modelValue)) return props.modelValue\n return enabledValues.value[0]\n})\n\nfunction moveSelection(from: GrRadioValue, direction: 1 | -1): GrRadioValue | undefined {\n const values = enabledValues.value\n if (values.length === 0) return undefined\n\n const current = values.indexOf(from)\n // Стрелки зациклены — это требование паттерна radiogroup.\n const next = values[(current + direction + values.length) % values.length]\n\n if (next !== undefined) setValue(next)\n return next\n}\n\n/** `Home`/`End` — на края набора; как в `GrSegmented`, где паттерн реализован целиком. */\nfunction selectEdge(edge: 'first' | 'last'): GrRadioValue | undefined {\n const values = enabledValues.value\n const next = edge === 'first' ? values[0] : values.at(-1)\n\n if (next !== undefined) setValue(next)\n return next\n}\n\nprovide(GR_RADIO_GROUP_CONTEXT, {\n modelValue: computed(() => props.modelValue),\n name: computed(() => props.name),\n disabled: computed(() => isDisabled.value),\n readonly: isReadonly,\n invalid: isInvalid,\n size: resolvedSize,\n setValue,\n register,\n rovingValue,\n moveSelection,\n selectEdge,\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootEl\"\n data-gr-radio-group\n role=\"radiogroup\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <template v-if=\"$slots.default\">\n <GrButtonGroup v-if=\"variant === 'button'\">\n <slot />\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <slot />\n </div>\n </template>\n <template v-else>\n <GrButtonGroup v-if=\"variant === 'button'\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n variant=\"button\"\n :size=\"resolvedSize\"\n >\n {{ opt.label }}\n </GrRadio>\n </GrButtonGroup>\n <div v-else :class=\"listClass\">\n <GrRadio\n v-for=\"opt in options ?? []\"\n :key=\"String(opt.value)\"\n :value=\"opt.value\"\n :disabled=\"opt.disabled\"\n >\n {{ opt.label }}\n\n <template v-if=\"opt.description\" #description>\n {{ opt.description }}\n </template>\n </GrRadio>\n </div>\n </template>\n </div>\n</template>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrRating-JrHXz0Rv.js","names":["$slots"],"sources":["../../../../src/components/GrRating/GrRating.vue","../../../../src/components/GrRating/GrRating.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\n\nimport {\n ratingFillClassByTone,\n ratingRootClass,\n ratingSymbolClass,\n ratingTextSizeBySize,\n ratingVoidClass,\n type GrRatingSize,\n type GrRatingTone,\n} from './grRatingStyles'\n\nexport type {\n GrRatingSize,\n GrRatingTone,\n} from './grRatingStyles'\n\n/**\n * Публичный GR-примитив «Rating» — шкала оценки символами (по умолчанию звёзды).\n *\n * Интерактивный режим реализует WAI-ARIA slider pattern (`role=\"slider\"`,\n * `aria-valuemin/max/now/valuetext`, стрелки / Home / End), режим только для\n * чтения — `role=\"img\"` с текстовой подписью, чтобы скринридер озвучил оценку\n * одной фразой, а не набором элементов.\n *\n * Символ по умолчанию рисуется инлайновым SVG (звезда с `fill=\"currentColor\"`) —\n * иконочные шрифты-маски `i-lucide-star` дают только контур. Свой символ\n * задаётся пропом `icon` (UnoCSS-класс иконки) или слотом `#symbol`.\n */\nexport interface GrRatingProps {\n /** Текущая оценка. Дробная (`3.5`) поддерживается при `allowHalf`. */\n modelValue: number\n /** Количество символов шкалы. */\n max?: number\n size?: GrRatingSize\n /** Тон заливки; точечно перекрывается переменной `--gr-rating-color`. */\n tone?: GrRatingTone\n /** Половинчатые оценки: клик по левой половине символа даёт `.5`. */\n allowHalf?: boolean\n /** Только показ: без ввода и фокуса. */\n readonly?: boolean\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n /** Повторный клик по текущей оценке сбрасывает её в `0`. */\n clearable?: boolean\n /** UnoCSS-класс иконки вместо встроенной звезды (например `i-lucide-heart`). */\n icon?: string\n /** Показывать числовую подпись справа от шкалы. */\n showText?: boolean\n /** Формат подписи. По умолчанию — само значение. Сильнее `texts`. */\n formatText?: (value: number) => string\n /**\n * Подписи по делениям: «3 из 5, нормально» вместо «3 из 5». Уходят и в\n * видимый текст, и в `aria-valuetext` — ради этого рейтинг и существует.\n * Массив короче `max` оставляет верхние деления без подписи.\n */\n texts?: string[]\n /**\n * Компактный вид: рисуются только заполненные символы. Осмысленно вместе с\n * `readonly` — в списках и таблицах пять звёзд в каждой строке съедают ширину.\n */\n compact?: boolean\n ariaLabel?: string\n /** Имя для нативной формы: hidden input со значением; `0` — «не выбрано», input не рендерится. */\n name?: string\n}\n\nexport interface GrRatingEmits {\n (e: 'update:modelValue', value: number): void\n /** Оценка зафиксирована (клик / клавиша). */\n (e: 'change', value: number): void\n /** Значение под курсором; `null` — курсор ушёл со шкалы. */\n (e: 'hoverChange', value: number | null): void\n /** Оценка снята повторным кликом; только при `clearable`. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrRatingProps>(),\n {\n max: 5,\n size: undefined,\n tone: 'warning',\n allowHalf: false,\n readonly: false,\n disabled: false,\n invalid: false,\n required: false,\n clearable: false,\n icon: undefined,\n showText: false,\n formatText: undefined,\n texts: undefined,\n compact: false,\n ariaLabel: undefined,\n name: undefined,\n },\n)\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrRating',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrRatingEmits>()\ndefineSlots<{\n /** Свой символ вместо звезды. `filled` — закрашенная половина символа. */\n symbol?: (props: { index: number, filled: boolean }) => any\n /** Подпись рядом с оценкой. */\n text?: (props: { value: number }) => any\n}>()\n\n\nconst { t } = useGranularityTranslations()\n\n// Контекст `GrFormField`: id/aria-describedby/invalid/required как fallback.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\nconst rootEl = ref<HTMLElement | null>(null)\n\nfunction focus(): void {\n rootEl.value?.querySelector<HTMLElement>('[data-gr-rating-scale]')?.focus()\n}\n\nfunction blur(): void {\n rootEl.value?.querySelector<HTMLElement>('[data-gr-rating-scale]')?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n/** Ввод возможен: не readonly и не disabled. */\nconst interactive = computed(() => !isReadonly.value && !isDisabled.value)\n\n/**\n * `aria-readonly`, `aria-invalid` и `aria-required` вешаются только в\n * slider-режиме: на `role=\"img\"` они не разрешены и дают critical в axe.\n *\n * Роль контрола (`slider`) сохраняется и в `disabled` — отключённый слайдер это\n * штатный ARIA-паттерн. А вот `readonly` — это уже не контрол, а картинка с\n * подписью: на `role=\"img\"` атрибуты `aria-value*`/`aria-disabled` запрещены\n * (axe: `aria-allowed-attr`), поэтому там их не выводим вовсе.\n */\nconst asSlider = computed(() => !props.readonly)\nconst step = computed(() => (props.allowHalf ? 0.5 : 1))\n\n// Значение под курсором имеет приоритет над моделью — это «предпросмотр» оценки.\nconst hoverValue = ref<number | null>(null)\nconst displayValue = computed(() => hoverValue.value ?? clamp(props.modelValue))\n\nfunction clamp(value: number): number {\n if (!Number.isFinite(value)) return 0\n return Math.min(props.max, Math.max(0, value))\n}\n\n/** Доля заливки символа с индексом `index` (0..1). */\nfunction fillRatio(index: number): number {\n return Math.min(1, Math.max(0, displayValue.value - index))\n}\n\nconst symbols = computed(() => {\n // Компактный вид показывает только заполненную часть шкалы; половинка\n // считается символом — иначе «2.5» потеряло бы половину звезды.\n const length = props.compact ? Math.ceil(clamp(props.modelValue)) : props.max\n return Array.from({ length }, (_, index) => index)\n})\n\n/** Подпись деления под текущим значением: половинки округляются вверх. */\nconst currentLabel = computed(() => {\n if (!props.texts?.length || displayValue.value <= 0) return undefined\n return props.texts[Math.ceil(displayValue.value) - 1]\n})\n\nconst valueText = computed(() => {\n const params = { value: displayValue.value, max: props.max }\n\n // Порядок слов задаёт локаль, а не конкатенация в коде.\n return currentLabel.value\n ? t('gr.rating.valueTextWithLabel', '{value} of {max}, {label}', { ...params, label: currentLabel.value })\n : t('gr.rating.valueText', '{value} of {max}', params)\n})\n\nconst resolvedAriaLabel = computed(() => props.ariaLabel ?? t('gr.rating.label', 'Rating'))\n\nconst text = computed(() => {\n if (props.formatText) return props.formatText(displayValue.value)\n return currentLabel.value ?? String(displayValue.value)\n})\n\n/** Значение, соответствующее позиции курсора внутри символа `index`. */\nfunction valueAt(index: number, event: MouseEvent): number {\n if (!props.allowHalf) return index + 1\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()\n return rect.width > 0 && event.clientX - rect.left < rect.width / 2 ? index + 0.5 : index + 1\n}\n\nfunction onScaleBlur(event: FocusEvent): void {\n resetHover()\n emit('blur', event)\n}\n\nfunction commit(value: number): void {\n const next = clamp(value)\n if (next !== props.modelValue) emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction onSymbolClick(index: number, event: MouseEvent): void {\n if (!interactive.value) return\n const next = valueAt(index, event)\n const cleared = props.clearable && next === props.modelValue\n\n commit(cleared ? 0 : next)\n if (cleared) emit('clear')\n}\n\nfunction onSymbolMouseMove(index: number, event: MouseEvent): void {\n if (!interactive.value) {\n // Режим сменился под курсором — оставлять подсветку от прошлого состояния нельзя.\n resetHover()\n return\n }\n const next = valueAt(index, event)\n if (next === hoverValue.value) return\n hoverValue.value = next\n emit('hoverChange', next)\n}\n\nfunction resetHover(): void {\n if (hoverValue.value === null) return\n hoverValue.value = null\n emit('hoverChange', null)\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (!interactive.value) return\n const current = clamp(props.modelValue)\n let next: number | null = null\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = current + step.value\n break\n case 'ArrowLeft':\n case 'ArrowDown':\n next = current - step.value\n break\n case 'Home':\n next = 0\n break\n case 'End':\n next = props.max\n break\n default:\n return\n }\n\n event.preventDefault()\n commit(next)\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n data-gr-rating\n class=\"inline-flex items-center gap-2\"\n >\n <!-- Нативная форма: роль-виджет не labelable и в submit не попадает. -->\n <input\n v-if=\"name && modelValue > 0\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(modelValue)\"\n >\n <div\n :id=\"resolvedId\"\n data-gr-rating-scale\n data-testid=\"gr-rating-scale\"\n :class=\"ratingRootClass({ size: resolvedSize, disabled, interactive })\"\n :role=\"asSlider ? 'slider' : 'img'\"\n :tabindex=\"asSlider ? (disabled ? -1 : 0) : undefined\"\n :aria-label=\"asSlider ? resolvedAriaLabel : `${resolvedAriaLabel}: ${valueText}`\"\n :aria-valuemin=\"asSlider ? 0 : undefined\"\n :aria-valuemax=\"asSlider ? max : undefined\"\n :aria-valuenow=\"asSlider ? displayValue : undefined\"\n :aria-valuetext=\"asSlider ? valueText : undefined\"\n :aria-orientation=\"asSlider ? 'horizontal' : undefined\"\n :aria-disabled=\"asSlider && disabled ? 'true' : undefined\"\n :aria-invalid=\"asSlider && isInvalid ? 'true' : undefined\"\n :aria-describedby=\"asSlider ? describedBy : undefined\"\n :aria-required=\"asSlider && isRequired ? 'true' : undefined\"\n :aria-readonly=\"asSlider && isReadonly ? 'true' : undefined\"\n @keydown=\"onKeydown\"\n @mouseleave=\"resetHover\"\n @focus=\"emit('focus', $event)\"\n @blur=\"onScaleBlur\"\n >\n <span\n v-for=\"index in symbols\"\n :key=\"index\"\n data-gr-rating-symbol\n :data-testid=\"`gr-rating-symbol-${index}`\"\n :class=\"ratingSymbolClass(resolvedSize)\"\n @click=\"onSymbolClick(index, $event)\"\n @mousemove=\"onSymbolMouseMove(index, $event)\"\n >\n <!-- Нижний слой — «пустой» символ, поверх него обрезанный по ширине «залитый». -->\n <span class=\"block h-full w-full\" :class=\"ratingVoidClass\" aria-hidden=\"true\">\n <slot name=\"symbol\" :index=\"index\" :filled=\"false\">\n <span v-if=\"icon\" class=\"block h-full w-full\" :class=\"icon\" />\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"block h-full w-full\">\n <path d=\"M12 2.5l2.9 6.1 6.6.9-4.8 4.7 1.2 6.6L12 17.7 6.1 20.8l1.2-6.6L2.5 9.5l6.6-.9z\" />\n </svg>\n </slot>\n </span>\n\n <span\n data-gr-rating-fill\n class=\"pointer-events-none absolute inset-y-0 left-0 overflow-hidden\"\n :class=\"ratingFillClassByTone[tone]\"\n :style=\"{ width: `${fillRatio(index) * 100}%` }\"\n aria-hidden=\"true\"\n >\n <span :class=\"ratingSymbolClass(resolvedSize)\">\n <slot name=\"symbol\" :index=\"index\" :filled=\"true\">\n <span v-if=\"icon\" class=\"block h-full w-full\" :class=\"icon\" />\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"block h-full w-full\">\n <path d=\"M12 2.5l2.9 6.1 6.6.9-4.8 4.7 1.2 6.6L12 17.7 6.1 20.8l1.2-6.6L2.5 9.5l6.6-.9z\" />\n </svg>\n </slot>\n </span>\n </span>\n </span>\n </div>\n\n <span\n v-if=\"showText || $slots.text\"\n data-gr-rating-text\n class=\"text-[var(--gr-muted-fg)] [font-variant-numeric:tabular-nums]\"\n :class=\"ratingTextSizeBySize[resolvedSize]\"\n >\n <slot name=\"text\" :value=\"displayValue\">{{ text }}</slot>\n </span>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\n\nimport { useGrComponentSize } from '../GrConfigProvider/context'\n\nimport { useGranularityTranslations } from '../../internal/granularityI18n'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\n\nimport {\n ratingFillClassByTone,\n ratingRootClass,\n ratingSymbolClass,\n ratingTextSizeBySize,\n ratingVoidClass,\n type GrRatingSize,\n type GrRatingTone,\n} from './grRatingStyles'\n\nexport type {\n GrRatingSize,\n GrRatingTone,\n} from './grRatingStyles'\n\n/**\n * Публичный GR-примитив «Rating» — шкала оценки символами (по умолчанию звёзды).\n *\n * Интерактивный режим реализует WAI-ARIA slider pattern (`role=\"slider\"`,\n * `aria-valuemin/max/now/valuetext`, стрелки / Home / End), режим только для\n * чтения — `role=\"img\"` с текстовой подписью, чтобы скринридер озвучил оценку\n * одной фразой, а не набором элементов.\n *\n * Символ по умолчанию рисуется инлайновым SVG (звезда с `fill=\"currentColor\"`) —\n * иконочные шрифты-маски `i-lucide-star` дают только контур. Свой символ\n * задаётся пропом `icon` (UnoCSS-класс иконки) или слотом `#symbol`.\n */\nexport interface GrRatingProps {\n /** Текущая оценка. Дробная (`3.5`) поддерживается при `allowHalf`. */\n modelValue: number\n /** Количество символов шкалы. */\n max?: number\n size?: GrRatingSize\n /** Тон заливки; точечно перекрывается переменной `--gr-rating-color`. */\n tone?: GrRatingTone\n /** Половинчатые оценки: клик по левой половине символа даёт `.5`. */\n allowHalf?: boolean\n /** Только показ: без ввода и фокуса. */\n readonly?: boolean\n disabled?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n /** Повторный клик по текущей оценке сбрасывает её в `0`. */\n clearable?: boolean\n /** UnoCSS-класс иконки вместо встроенной звезды (например `i-lucide-heart`). */\n icon?: string\n /** Показывать числовую подпись справа от шкалы. */\n showText?: boolean\n /** Формат подписи. По умолчанию — само значение. Сильнее `texts`. */\n formatText?: (value: number) => string\n /**\n * Подписи по делениям: «3 из 5, нормально» вместо «3 из 5». Уходят и в\n * видимый текст, и в `aria-valuetext` — ради этого рейтинг и существует.\n * Массив короче `max` оставляет верхние деления без подписи.\n */\n texts?: string[]\n /**\n * Компактный вид: рисуются только заполненные символы. Осмысленно вместе с\n * `readonly` — в списках и таблицах пять звёзд в каждой строке съедают ширину.\n */\n compact?: boolean\n ariaLabel?: string\n /** Имя для нативной формы: hidden input со значением; `0` — «не выбрано», input не рендерится. */\n name?: string\n}\n\nexport interface GrRatingEmits {\n (e: 'update:modelValue', value: number): void\n /** Оценка зафиксирована (клик / клавиша). */\n (e: 'change', value: number): void\n /** Значение под курсором; `null` — курсор ушёл со шкалы. */\n (e: 'hoverChange', value: number | null): void\n /** Оценка снята повторным кликом; только при `clearable`. */\n (e: 'clear'): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrRatingProps>(),\n {\n max: 5,\n size: undefined,\n tone: 'warning',\n allowHalf: false,\n readonly: false,\n disabled: false,\n invalid: false,\n required: false,\n clearable: false,\n icon: undefined,\n showText: false,\n formatText: undefined,\n texts: undefined,\n compact: false,\n ariaLabel: undefined,\n name: undefined,\n },\n)\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, {\n component: 'GrRating',\n supported: ['xs', 'sm', 'md', 'lg'],\n})\n\nconst emit = defineEmits<GrRatingEmits>()\ndefineSlots<{\n /** Свой символ вместо звезды. `filled` — закрашенная половина символа. */\n symbol?: (props: { index: number, filled: boolean }) => any\n /** Подпись рядом с оценкой. */\n text?: (props: { value: number }) => any\n}>()\n\n\nconst { t } = useGranularityTranslations()\n\n// Контекст `GrFormField`: id/aria-describedby/invalid/required как fallback.\nconst field = useGrFormFieldContext()\nconst resolvedId = computed(() => field?.id.value)\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\nconst describedBy = computed(() => field?.describedById.value)\n\nconst rootEl = ref<HTMLElement | null>(null)\n\nfunction focus(): void {\n rootEl.value?.querySelector<HTMLElement>('[data-gr-rating-scale]')?.focus()\n}\n\nfunction blur(): void {\n rootEl.value?.querySelector<HTMLElement>('[data-gr-rating-scale]')?.blur()\n}\n\ndefineExpose({ focus, blur })\n\n/** Ввод возможен: не readonly и не disabled. */\nconst interactive = computed(() => !isReadonly.value && !isDisabled.value)\n\n/**\n * `aria-readonly`, `aria-invalid` и `aria-required` вешаются только в\n * slider-режиме: на `role=\"img\"` они не разрешены и дают critical в axe.\n *\n * Роль контрола (`slider`) сохраняется и в `disabled` — отключённый слайдер это\n * штатный ARIA-паттерн. А вот `readonly` — это уже не контрол, а картинка с\n * подписью: на `role=\"img\"` атрибуты `aria-value*`/`aria-disabled` запрещены\n * (axe: `aria-allowed-attr`), поэтому там их не выводим вовсе.\n */\nconst asSlider = computed(() => !props.readonly)\nconst step = computed(() => (props.allowHalf ? 0.5 : 1))\n\n// Значение под курсором имеет приоритет над моделью — это «предпросмотр» оценки.\nconst hoverValue = ref<number | null>(null)\nconst displayValue = computed(() => hoverValue.value ?? clamp(props.modelValue))\n\nfunction clamp(value: number): number {\n if (!Number.isFinite(value)) return 0\n return Math.min(props.max, Math.max(0, value))\n}\n\n/** Доля заливки символа с индексом `index` (0..1). */\nfunction fillRatio(index: number): number {\n return Math.min(1, Math.max(0, displayValue.value - index))\n}\n\nconst symbols = computed(() => {\n // Компактный вид показывает только заполненную часть шкалы; половинка\n // считается символом — иначе «2.5» потеряло бы половину звезды.\n const length = props.compact ? Math.ceil(clamp(props.modelValue)) : props.max\n return Array.from({ length }, (_, index) => index)\n})\n\n/** Подпись деления под текущим значением: половинки округляются вверх. */\nconst currentLabel = computed(() => {\n if (!props.texts?.length || displayValue.value <= 0) return undefined\n return props.texts[Math.ceil(displayValue.value) - 1]\n})\n\nconst valueText = computed(() => {\n const params = { value: displayValue.value, max: props.max }\n\n // Порядок слов задаёт локаль, а не конкатенация в коде.\n return currentLabel.value\n ? t('gr.rating.valueTextWithLabel', '{value} of {max}, {label}', { ...params, label: currentLabel.value })\n : t('gr.rating.valueText', '{value} of {max}', params)\n})\n\nconst resolvedAriaLabel = computed(() => props.ariaLabel ?? t('gr.rating.label', 'Rating'))\n\nconst text = computed(() => {\n if (props.formatText) return props.formatText(displayValue.value)\n return currentLabel.value ?? String(displayValue.value)\n})\n\n/** Значение, соответствующее позиции курсора внутри символа `index`. */\nfunction valueAt(index: number, event: MouseEvent): number {\n if (!props.allowHalf) return index + 1\n const rect = (event.currentTarget as HTMLElement).getBoundingClientRect()\n return rect.width > 0 && event.clientX - rect.left < rect.width / 2 ? index + 0.5 : index + 1\n}\n\nfunction onScaleBlur(event: FocusEvent): void {\n resetHover()\n emit('blur', event)\n}\n\nfunction commit(value: number): void {\n const next = clamp(value)\n if (next !== props.modelValue) emit('update:modelValue', next)\n emit('change', next)\n}\n\nfunction onSymbolClick(index: number, event: MouseEvent): void {\n if (!interactive.value) return\n const next = valueAt(index, event)\n const cleared = props.clearable && next === props.modelValue\n\n commit(cleared ? 0 : next)\n if (cleared) emit('clear')\n}\n\nfunction onSymbolMouseMove(index: number, event: MouseEvent): void {\n if (!interactive.value) {\n // Режим сменился под курсором — оставлять подсветку от прошлого состояния нельзя.\n resetHover()\n return\n }\n const next = valueAt(index, event)\n if (next === hoverValue.value) return\n hoverValue.value = next\n emit('hoverChange', next)\n}\n\nfunction resetHover(): void {\n if (hoverValue.value === null) return\n hoverValue.value = null\n emit('hoverChange', null)\n}\n\nfunction onKeydown(event: KeyboardEvent): void {\n if (!interactive.value) return\n const current = clamp(props.modelValue)\n let next: number | null = null\n\n switch (event.key) {\n case 'ArrowRight':\n case 'ArrowUp':\n next = current + step.value\n break\n case 'ArrowLeft':\n case 'ArrowDown':\n next = current - step.value\n break\n case 'Home':\n next = 0\n break\n case 'End':\n next = props.max\n break\n default:\n return\n }\n\n event.preventDefault()\n commit(next)\n}\n</script>\n\n<template>\n <div\n ref=\"rootEl\"\n data-gr-rating\n class=\"inline-flex items-center gap-2\"\n >\n <!-- Нативная форма: роль-виджет не labelable и в submit не попадает. -->\n <input\n v-if=\"name && modelValue > 0\"\n type=\"hidden\"\n :name=\"name\"\n :value=\"String(modelValue)\"\n >\n <div\n :id=\"resolvedId\"\n data-gr-rating-scale\n data-testid=\"gr-rating-scale\"\n :class=\"ratingRootClass({ size: resolvedSize, disabled, interactive })\"\n :role=\"asSlider ? 'slider' : 'img'\"\n :tabindex=\"asSlider ? (disabled ? -1 : 0) : undefined\"\n :aria-label=\"asSlider ? resolvedAriaLabel : `${resolvedAriaLabel}: ${valueText}`\"\n :aria-valuemin=\"asSlider ? 0 : undefined\"\n :aria-valuemax=\"asSlider ? max : undefined\"\n :aria-valuenow=\"asSlider ? displayValue : undefined\"\n :aria-valuetext=\"asSlider ? valueText : undefined\"\n :aria-orientation=\"asSlider ? 'horizontal' : undefined\"\n :aria-disabled=\"asSlider && disabled ? 'true' : undefined\"\n :aria-invalid=\"asSlider && isInvalid ? 'true' : undefined\"\n :aria-describedby=\"asSlider ? describedBy : undefined\"\n :aria-required=\"asSlider && isRequired ? 'true' : undefined\"\n :aria-readonly=\"asSlider && isReadonly ? 'true' : undefined\"\n @keydown=\"onKeydown\"\n @mouseleave=\"resetHover\"\n @focus=\"emit('focus', $event)\"\n @blur=\"onScaleBlur\"\n >\n <span\n v-for=\"index in symbols\"\n :key=\"index\"\n data-gr-rating-symbol\n :data-testid=\"`gr-rating-symbol-${index}`\"\n :class=\"ratingSymbolClass(resolvedSize)\"\n @click=\"onSymbolClick(index, $event)\"\n @mousemove=\"onSymbolMouseMove(index, $event)\"\n >\n <!-- Нижний слой — «пустой» символ, поверх него обрезанный по ширине «залитый». -->\n <span class=\"block h-full w-full\" :class=\"ratingVoidClass\" aria-hidden=\"true\">\n <slot name=\"symbol\" :index=\"index\" :filled=\"false\">\n <span v-if=\"icon\" class=\"block h-full w-full\" :class=\"icon\" />\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"block h-full w-full\">\n <path d=\"M12 2.5l2.9 6.1 6.6.9-4.8 4.7 1.2 6.6L12 17.7 6.1 20.8l1.2-6.6L2.5 9.5l6.6-.9z\" />\n </svg>\n </slot>\n </span>\n\n <span\n data-gr-rating-fill\n class=\"pointer-events-none absolute inset-y-0 left-0 overflow-hidden\"\n :class=\"ratingFillClassByTone[tone]\"\n :style=\"{ width: `${fillRatio(index) * 100}%` }\"\n aria-hidden=\"true\"\n >\n <span :class=\"ratingSymbolClass(resolvedSize)\">\n <slot name=\"symbol\" :index=\"index\" :filled=\"true\">\n <span v-if=\"icon\" class=\"block h-full w-full\" :class=\"icon\" />\n <svg v-else viewBox=\"0 0 24 24\" fill=\"currentColor\" class=\"block h-full w-full\">\n <path d=\"M12 2.5l2.9 6.1 6.6.9-4.8 4.7 1.2 6.6L12 17.7 6.1 20.8l1.2-6.6L2.5 9.5l6.6-.9z\" />\n </svg>\n </slot>\n </span>\n </span>\n </span>\n </div>\n\n <span\n v-if=\"showText || $slots.text\"\n data-gr-rating-text\n class=\"text-[var(--gr-muted-fg)] [font-variant-numeric:tabular-nums]\"\n :class=\"ratingTextSizeBySize[resolvedSize]\"\n >\n <slot name=\"text\" :value=\"displayValue\">{{ text }}</slot>\n </span>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyFA,MAAM,QAAQ;EAuBd,MAAM,eAAe,yBAAyB,MAAM,MAAM;GACxD,WAAW;GACX,WAAW;IAAC;IAAM;IAAM;IAAM;GAAI;EACpC,CAAC;EAED,MAAM,OAAO;EASb,MAAM,EAAE,MAAM,2BAA2B;EAGzC,MAAM,QAAQ,sBAAsB;EACpC,MAAM,aAAa,eAAe,OAAO,GAAG,KAAK;EACjD,MAAM,EACJ,UAAU,YACV,SAAS,WAAW,UAAU,YAAY,UAAU,eAAe,uBAAuB,KAAK;EACjG,MAAM,cAAc,eAAe,OAAO,cAAc,KAAK;EAE7D,MAAM,SAAS,IAAwB,IAAI;EAE3C,SAAS,QAAc;GACrB,OAAO,OAAO,cAA2B,wBAAwB,CAAC,EAAE,MAAM;EAC5E;EAEA,SAAS,OAAa;GACpB,OAAO,OAAO,cAA2B,wBAAwB,CAAC,EAAE,KAAK;EAC3E;EAEA,SAAa;GAAE;GAAO;EAAK,CAAC;;EAG5B,MAAM,cAAc,eAAe,CAAC,WAAW,SAAS,CAAC,WAAW,KAAK;;;;;;;;;;EAWzE,MAAM,WAAW,eAAe,CAAC,MAAM,QAAQ;EAC/C,MAAM,OAAO,eAAgB,MAAM,YAAY,KAAM,CAAE;EAGvD,MAAM,aAAa,IAAmB,IAAI;EAC1C,MAAM,eAAe,eAAe,WAAW,SAAS,MAAM,MAAM,UAAU,CAAC;EAE/E,SAAS,MAAM,OAAuB;GACpC,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO;GACpC,OAAO,KAAK,IAAI,MAAM,KAAK,KAAK,IAAI,GAAG,KAAK,CAAC;EAC/C;;EAGA,SAAS,UAAU,OAAuB;GACxC,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,aAAa,QAAQ,KAAK,CAAC;EAC5D;EAEA,MAAM,UAAU,eAAe;GAG7B,MAAM,SAAS,MAAM,UAAU,KAAK,KAAK,MAAM,MAAM,UAAU,CAAC,IAAI,MAAM;GAC1E,OAAO,MAAM,KAAK,EAAE,OAAO,IAAI,GAAG,UAAU,KAAK;EACnD,CAAC;;EAGD,MAAM,eAAe,eAAe;GAClC,IAAI,CAAC,MAAM,OAAO,UAAU,aAAa,SAAS,GAAG,OAAO,KAAA;GAC5D,OAAO,MAAM,MAAM,KAAK,KAAK,aAAa,KAAK,IAAI;EACrD,CAAC;EAED,MAAM,YAAY,eAAe;GAC/B,MAAM,SAAS;IAAE,OAAO,aAAa;IAAO,KAAK,MAAM;GAAI;GAG3D,OAAO,aAAa,QAChB,EAAE,gCAAgC,6BAA6B;IAAE,GAAG;IAAQ,OAAO,aAAa;GAAM,CAAC,IACvG,EAAE,uBAAuB,oBAAoB,MAAM;EACzD,CAAC;EAED,MAAM,oBAAoB,eAAe,MAAM,aAAa,EAAE,mBAAmB,QAAQ,CAAC;EAE1F,MAAM,OAAO,eAAe;GAC1B,IAAI,MAAM,YAAY,OAAO,MAAM,WAAW,aAAa,KAAK;GAChE,OAAO,aAAa,SAAS,OAAO,aAAa,KAAK;EACxD,CAAC;;EAGD,SAAS,QAAQ,OAAe,OAA2B;GACzD,IAAI,CAAC,MAAM,WAAW,OAAO,QAAQ;GACrC,MAAM,OAAQ,MAAM,cAA8B,sBAAsB;GACxE,OAAO,KAAK,QAAQ,KAAK,MAAM,UAAU,KAAK,OAAO,KAAK,QAAQ,IAAI,QAAQ,KAAM,QAAQ;EAC9F;EAEA,SAAS,YAAY,OAAyB;GAC5C,WAAW;GACX,KAAK,QAAQ,KAAK;EACpB;EAEA,SAAS,OAAO,OAAqB;GACnC,MAAM,OAAO,MAAM,KAAK;GACxB,IAAI,SAAS,MAAM,YAAY,KAAK,qBAAqB,IAAI;GAC7D,KAAK,UAAU,IAAI;EACrB;EAEA,SAAS,cAAc,OAAe,OAAyB;GAC7D,IAAI,CAAC,YAAY,OAAO;GACxB,MAAM,OAAO,QAAQ,OAAO,KAAK;GACjC,MAAM,UAAU,MAAM,aAAa,SAAS,MAAM;GAElD,OAAO,UAAU,IAAI,IAAI;GACzB,IAAI,SAAS,KAAK,OAAO;EAC3B;EAEA,SAAS,kBAAkB,OAAe,OAAyB;GACjE,IAAI,CAAC,YAAY,OAAO;IAEtB,WAAW;IACX;GACF;GACA,MAAM,OAAO,QAAQ,OAAO,KAAK;GACjC,IAAI,SAAS,WAAW,OAAO;GAC/B,WAAW,QAAQ;GACnB,KAAK,eAAe,IAAI;EAC1B;EAEA,SAAS,aAAmB;GAC1B,IAAI,WAAW,UAAU,MAAM;GAC/B,WAAW,QAAQ;GACnB,KAAK,eAAe,IAAI;EAC1B;EAEA,SAAS,UAAU,OAA4B;GAC7C,IAAI,CAAC,YAAY,OAAO;GACxB,MAAM,UAAU,MAAM,MAAM,UAAU;GACtC,IAAI,OAAsB;GAE1B,QAAQ,MAAM,KAAd;IACE,KAAK;IACL,KAAK;KACH,OAAO,UAAU,KAAK;KACtB;IACF,KAAK;IACL,KAAK;KACH,OAAO,UAAU,KAAK;KACtB;IACF,KAAK;KACH,OAAO;KACP;IACF,KAAK;KACH,OAAO,MAAM;KACb;IACF,SACE;GACJ;GAEA,MAAM,eAAe;GACrB,OAAO,IAAI;EACb;;uBAIE,mBAiFM,OAAA;aAhFA;IAAJ,KAAI;IACJ,kBAAA;IACA,OAAM;;IAIE,QAAA,QAAQ,QAAA,aAAU,KAAA,UAAA,GAD1B,mBAKC,SAAA;;KAHC,MAAK;KACJ,MAAM,QAAA;KACN,OAAO,OAAO,QAAA,UAAU;;IAE3B,mBA2DM,OAAA;KA1DH,IAAI,WAAA;KACL,wBAAA;KACA,eAAY;KACX,OAAK,eAAE,MAAA,eAAA,CAAe,CAAA;MAAA,MAAS,MAAA,YAAA;MAAY,UAAE,QAAA;MAAQ,aAAE,YAAA;KAAW,CAAA,CAAA;KAClE,MAAM,SAAA,QAAQ,WAAA;KACd,UAAU,SAAA,QAAY,QAAA,WAAQ,KAAA,IAAa,KAAA;KAC3C,cAAY,SAAA,QAAW,kBAAA,QAAiB,GAAM,kBAAA,MAAiB,IAAK,UAAA;KACpE,iBAAe,SAAA,QAAQ,IAAO,KAAA;KAC9B,iBAAe,SAAA,QAAW,QAAA,MAAM,KAAA;KAChC,iBAAe,SAAA,QAAW,aAAA,QAAe,KAAA;KACzC,kBAAgB,SAAA,QAAW,UAAA,QAAY,KAAA;KACvC,oBAAkB,SAAA,QAAQ,eAAkB,KAAA;KAC5C,iBAAe,SAAA,SAAY,QAAA,WAAQ,SAAY,KAAA;KAC/C,gBAAc,SAAA,SAAY,MAAA,SAAA,IAAS,SAAY,KAAA;KAC/C,oBAAkB,SAAA,QAAW,YAAA,QAAc,KAAA;KAC3C,iBAAe,SAAA,SAAY,MAAA,UAAA,IAAU,SAAY,KAAA;KACjD,iBAAe,SAAA,SAAY,MAAA,UAAA,IAAU,SAAY,KAAA;KACxC;KACT,cAAY;KACZ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,SAAU,MAAM;KAC3B,QAAM;0BAEP,mBAmCO,UAAA,MAAA,WAlCW,QAAA,QAAT,UAAK;yBADd,mBAmCO,QAAA;MAjCJ,KAAK;MACN,yBAAA;MACC,eAAW,oBAAsB;MACjC,OAAK,eAAE,MAAA,iBAAA,CAAiB,CAAC,MAAA,YAAA,CAAY,CAAA;MACrC,UAAK,WAAE,cAAc,OAAO,MAAM;MAClC,cAAS,WAAE,kBAAkB,OAAO,MAAM;SAG3C,mBAOO,QAAA;MAPD,OAAK,eAAA,CAAC,uBAA8B,MAAA,eAAA,CAAe,CAAA;MAAE,eAAY;SACrE,WAKO,KAAA,QAAA,UAAA;MALqB;MAAQ,QAAQ;cAKrC,CAJO,QAAA,QAAA,UAAA,GAAZ,mBAA8D,QAAA;;MAA5C,OAAK,eAAA,CAAC,uBAA8B,QAAA,IAAI,CAAA;kCAC1D,mBAEM,OAFN,YAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA2F,QAAA,EAArF,GAAE,iFAAgF,GAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA,GAAA,CAAA,GAK9F,mBAeO,QAAA;MAdL,uBAAA;MACA,OAAK,eAAA,CAAC,iEACE,MAAA,qBAAA,CAAqB,CAAC,QAAA,KAAI,CAAA;MACjC,OAAK,eAAA,EAAA,OAAA,GAAc,UAAU,KAAK,IAAA,IAAA,GAAA,CAAA;MACnC,eAAY;SAEZ,mBAOO,QAAA,EAPA,OAAK,eAAE,MAAA,iBAAA,CAAiB,CAAC,MAAA,YAAA,CAAY,CAAA,EAAA,GAAA,CAC1C,WAKO,KAAA,QAAA,UAAA;MALqB;MAAQ,QAAQ;cAKrC,CAJO,QAAA,QAAA,UAAA,GAAZ,mBAA8D,QAAA;;MAA5C,OAAK,eAAA,CAAC,uBAA8B,QAAA,IAAI,CAAA;kCAC1D,mBAEM,OAFN,YAEM,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,CADJ,mBAA2F,QAAA,EAArF,GAAE,iFAAgF,GAAA,MAAA,EAAA,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA,GAAA,CAAA,CAAA,GAAA,CAAA,CAAA,GAAA,IAAA,UAAA;;IAS5F,QAAA,YAAYA,KAAAA,OAAO,QAAA,UAAA,GAD3B,mBAOO,QAAA;;KALL,uBAAA;KACA,OAAK,eAAA,CAAC,iEACE,MAAA,oBAAA,CAAoB,CAAC,MAAA,YAAA,EAAY,CAAA;QAEzC,WAAyD,KAAA,QAAA,QAAA,EAAtC,OAAO,aAAA,MAAY,SAAmB,CAAA,gBAAA,gBAAd,KAAA,KAAI,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"GrSegmented-BQIlH5KI.js","names":[],"sources":["../../../../src/components/GrSegmented/GrSegmented.vue","../../../../src/components/GrSegmented/GrSegmented.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\n\nimport type { ComponentPublicInstance } from 'vue'\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedItemSpinnerClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedOrientation,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного GR-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /**\n * Направление ряда. Вертикаль — боковые фильтры; индикатор к ней готов по\n * построению, он двумерный.\n */\n orientation?: GrSegmentedOrientation\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Только для чтения: выбор видно, но он не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nexport interface GrSegmentedEmits {\n (e: 'update:modelValue', value: GrSegmentedValue): void\n (e: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: undefined,\n size: undefined,\n orientation: 'horizontal',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrSegmented' })\nconst resolvedVariant = useGrComponentProp('GrSegmented', 'variant', () => props.variant, 'pills')\n\nconst emit = defineEmits<GrSegmentedEmits>()\ndefineSlots<{\n /** Содержимое сегмента вместо подписи из `options`. */\n default?: (props: {\n option: GrSegmentedOption\n selected: boolean\n disabled: boolean\n loading: boolean\n }) => any\n}>()\n\n\n// `useId()` стабилен между сервером и клиентом, в отличие от `instance.uid`.\nconst fallbackName = `gr-segmented-${useId()}`\n\n// Контекст `GrFormField` + общий контракт форм-контрола.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\n\nconst rootRef = ref<HTMLElement | null>(null)\n\n// Фокус ходит между сегментами: без границы каждая стрелка давала бы\n// потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootRef, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nfunction focus(): void {\n rootRef.value?.querySelector<HTMLElement>('[data-gr-segmented-item][tabindex=\"0\"]')?.focus()\n}\n\nfunction blur(): void {\n rootRef.value?.querySelector<HTMLElement>('[data-gr-segmented-item][tabindex=\"0\"]')?.blur()\n}\n\ndefineExpose({ focus, blur })\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst isVertical = computed(() => props.orientation === 'vertical')\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: resolvedVariant.value,\n orientation: props.orientation,\n block: props.block,\n disabled: isDisabled.value,\n}))\n\n/**\n * Раскладка задаётся треками, а не классами: число сегментов известно только в\n * рантайме. По вертикали колонка одна — сегменты одинаковой ширины по\n * построению, и `block` решает лишь, занимать ли ширину контейнера.\n */\nconst rootStyle = computed<Record<string, string>>(() => {\n const empty = props.options.length === 0\n const track = () => props.block && !isVertical.value ? 'minmax(0,1fr)' : 'minmax(0,max-content)'\n\n return {\n ...grSegmentedRootStyle({\n variant: resolvedVariant.value,\n size: resolvedSize.value,\n orientation: props.orientation,\n }),\n gridTemplateColumns: isVertical.value\n ? 'minmax(0,1fr)'\n : empty ? 'none' : props.options.map(track).join(' '),\n ...(isVertical.value\n ? { gridTemplateRows: empty ? 'none' : props.options.map(track).join(' ') }\n : {}),\n }\n})\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(resolvedVariant.value))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return isDisabled.value || Boolean(option.disabled)\n}\n\n/**\n * Занятый сегмент не «недоступен», а работает: нативный `disabled` и\n * `aria-disabled` ему не ставим — состояние несёт `aria-busy`. Но выбор он не\n * принимает, поэтому блокировка считается отдельно от `disabled`.\n */\nfunction isOptionBusy(option: GrSegmentedOption): boolean {\n return Boolean(option.loading)\n}\n\nfunction isOptionBlocked(option: GrSegmentedOption): boolean {\n return resolveOptionDisabled(option) || isOptionBusy(option)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n // Один guard на все пути: проверяй `readonly` только клавиатура, и клик по\n // сегменту менял бы значение вопреки заявленному контракту.\n if (isReadonly.value || isOptionBlocked(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !isOptionBlocked(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (isDisabled.value || isReadonly.value) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(resolvedVariant, () => scheduleMeasure())\nwatch(resolvedSize, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n// Смена ориентации переставляет сегменты: без пересчёта индикатор остался бы в\n// координатах прежней раскладки.\nwatch(() => props.orientation, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootRef\"\n data-gr-segmented\n :data-variant=\"resolvedVariant\"\n role=\"radiogroup\"\n :aria-orientation=\"orientation\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Значение для нативной формы уходит одним скрытым полем рядом с\n сегментами, а не вложенным в каждый `role=\"radio\"`: роль объявляет своих\n потомков презентационными, и вложенный интерактивный контрол ломает\n виджет для скринридеров (axe: `nested-interactive`). Скрытое поле не\n фокусируется и интерактивным не считается. -->\n <input\n v-if=\"selectedOption && !resolveOptionDisabled(selectedOption)\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"String(selectedOption.value)\"\n >\n\n <span\n v-if=\"hasIndicator\"\n data-gr-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-gr-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-label=\"option.ariaLabel\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :aria-busy=\"isOptionBusy(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant: resolvedVariant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :loading=\"isOptionBusy(option)\"\n >\n <!-- Спиннер занимает место иконки: две крутилки рядом читались бы как ошибка. -->\n <IconLoader\n v-if=\"isOptionBusy(option)\"\n data-gr-segmented-spinner\n aria-hidden=\"true\"\n :class=\"grSegmentedItemSpinnerClass\"\n />\n <component\n :is=\"option.icon\"\n v-else-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>","<script setup lang=\"ts\">\nimport { computed, nextTick, onBeforeUnmount, onMounted, ref, useId, watch } from 'vue'\n\nimport { useGrComponentProp, useGrComponentSize } from '../GrConfigProvider/context'\nimport { useGrFormFieldContext } from '../GrFormField/context'\nimport { useGrFormControl } from '../../composables/useGrFormControl'\nimport { useFocusWithin } from '../../composables/internal/useFocusWithin'\n\nimport type { ComponentPublicInstance } from 'vue'\nimport IconLoader from '~icons/lucide/loader-circle'\n\nimport {\n grSegmentedIndicatorClass,\n grSegmentedItemClass,\n grSegmentedItemIconClass,\n grSegmentedItemLabelClass,\n grSegmentedItemSpinnerClass,\n grSegmentedRootClass,\n grSegmentedRootStyle,\n type GrSegmentedOption,\n type GrSegmentedOrientation,\n type GrSegmentedSize,\n type GrSegmentedValue,\n type GrSegmentedVariant,\n} from './grSegmentedStyles'\n\ntype IndicatorGeometry = {\n width: number\n height: number\n x: number\n y: number\n}\n\n/**\n * Пропсы публичного GR-примитива «Segmented».\n */\nexport interface GrSegmentedProps {\n modelValue: GrSegmentedValue\n options: GrSegmentedOption[]\n variant?: GrSegmentedVariant\n size?: GrSegmentedSize\n /**\n * Направление ряда. Вертикаль — боковые фильтры; индикатор к ней готов по\n * построению, он двумерный.\n */\n orientation?: GrSegmentedOrientation\n /** Длительность анимации индикатора в мс. */\n indicatorDuration?: number\n /** Растягивать сегмент на всю ширину контейнера. */\n block?: boolean\n disabled?: boolean\n /** Только для чтения: выбор видно, но он не меняется. */\n readonly?: boolean\n /** Визуальное и ARIA-состояние ошибки. */\n invalid?: boolean\n /** Обязательное поле (`aria-required`). */\n required?: boolean\n /** Имя скрытых radio-inputs для интеграции с нативной формой. */\n name?: string\n ariaLabel?: string\n}\n\nexport interface GrSegmentedEmits {\n (e: 'update:modelValue', value: GrSegmentedValue): void\n (e: 'change', value: GrSegmentedValue, option: GrSegmentedOption): void\n (e: 'focus', event: FocusEvent): void\n (e: 'blur', event: FocusEvent): void\n}\n\nconst props = withDefaults(\n defineProps<GrSegmentedProps>(),\n {\n variant: undefined,\n size: undefined,\n orientation: 'horizontal',\n indicatorDuration: 300,\n block: false,\n disabled: false,\n readonly: false,\n invalid: false,\n required: false,\n name: undefined,\n ariaLabel: undefined,\n },\n)\n\n// Эффективный размер: локальный проп → `GrConfigProvider` → дефолт компонента.\nconst resolvedSize = useGrComponentSize(() => props.size, { component: 'GrSegmented' })\nconst resolvedVariant = useGrComponentProp('GrSegmented', 'variant', () => props.variant, 'pills')\n\nconst emit = defineEmits<GrSegmentedEmits>()\ndefineSlots<{\n /** Содержимое сегмента вместо подписи из `options`. */\n default?: (props: {\n option: GrSegmentedOption\n selected: boolean\n disabled: boolean\n loading: boolean\n }) => any\n}>()\n\n\n// `useId()` стабилен между сервером и клиентом, в отличие от `instance.uid`.\nconst fallbackName = `gr-segmented-${useId()}`\n\n// Контекст `GrFormField` + общий контракт форм-контрола.\nconst field = useGrFormFieldContext()\nconst fieldId = computed(() => field?.id.value)\nconst describedBy = computed(() => field?.describedById.value)\nconst labelledBy = computed(() => (props.ariaLabel ? undefined : field?.labelId.value))\nconst {\n disabled: isDisabled,\n invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props)\n\nconst rootRef = ref<HTMLElement | null>(null)\n\n// Фокус ходит между сегментами: без границы каждая стрелка давала бы\n// потребителю пару `blur` + `focus`.\nconst { onFocusIn, onFocusOut } = useFocusWithin(rootRef, {\n enter: event => emit('focus', event),\n leave: event => emit('blur', event),\n})\n\nfunction focus(): void {\n rootRef.value?.querySelector<HTMLElement>('[data-gr-segmented-item][tabindex=\"0\"]')?.focus()\n}\n\nfunction blur(): void {\n rootRef.value?.querySelector<HTMLElement>('[data-gr-segmented-item][tabindex=\"0\"]')?.blur()\n}\n\ndefineExpose({ focus, blur })\nconst itemRefs = ref(new Map<string, HTMLElement>())\nconst indicatorGeometry = ref<IndicatorGeometry | null>(null)\nconst indicatorReady = ref(false)\n\nlet resizeObserver: ResizeObserver | null = null\nlet observedRoot: HTMLElement | null = null\nlet observedItem: HTMLElement | null = null\nlet scheduled = false\n\nconst selectedIndex = computed(() => props.options.findIndex(option => option.value === props.modelValue))\nconst selectedOption = computed(() => props.options[selectedIndex.value] ?? null)\nconst enabledOptions = computed(() => props.options.filter(option => !resolveOptionDisabled(option)))\nconst indicatorDuration = computed(() => Math.max(0, Math.round(props.indicatorDuration)))\nconst isVertical = computed(() => props.orientation === 'vertical')\nconst rootClassName = computed(() => grSegmentedRootClass({\n variant: resolvedVariant.value,\n orientation: props.orientation,\n block: props.block,\n disabled: isDisabled.value,\n}))\n\n/**\n * Раскладка задаётся треками, а не классами: число сегментов известно только в\n * рантайме. По вертикали колонка одна — сегменты одинаковой ширины по\n * построению, и `block` решает лишь, занимать ли ширину контейнера.\n */\nconst rootStyle = computed<Record<string, string>>(() => {\n const empty = props.options.length === 0\n const track = () => props.block && !isVertical.value ? 'minmax(0,1fr)' : 'minmax(0,max-content)'\n\n return {\n ...grSegmentedRootStyle({\n variant: resolvedVariant.value,\n size: resolvedSize.value,\n orientation: props.orientation,\n }),\n gridTemplateColumns: isVertical.value\n ? 'minmax(0,1fr)'\n : empty ? 'none' : props.options.map(track).join(' '),\n ...(isVertical.value\n ? { gridTemplateRows: empty ? 'none' : props.options.map(track).join(' ') }\n : {}),\n }\n})\nconst indicatorClassName = computed(() => grSegmentedIndicatorClass(resolvedVariant.value))\nconst resolvedName = computed(() => props.name ?? fallbackName)\nconst hasIndicator = computed(() => selectedIndex.value !== -1 && indicatorGeometry.value !== null)\n\nconst indicatorStyle = computed<Record<string, string>>(() => {\n const geometry = indicatorGeometry.value\n if (!geometry) {\n return {\n opacity: '0',\n width: '0px',\n height: '0px',\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: 'translate3d(0px, 0px, 0px)',\n }\n }\n\n return {\n opacity: indicatorReady.value ? '1' : '0',\n width: `${geometry.width}px`,\n height: `${geometry.height}px`,\n transitionDuration: `${indicatorDuration.value}ms`,\n transform: `translate3d(${geometry.x}px, ${geometry.y}px, 0)`,\n }\n})\n\nfunction getOptionKey(value: GrSegmentedValue): string {\n return String(value)\n}\n\nfunction resolveOptionDisabled(option: GrSegmentedOption): boolean {\n return isDisabled.value || Boolean(option.disabled)\n}\n\n/**\n * Занятый сегмент не «недоступен», а работает: нативный `disabled` и\n * `aria-disabled` ему не ставим — состояние несёт `aria-busy`. Но выбор он не\n * принимает, поэтому блокировка считается отдельно от `disabled`.\n */\nfunction isOptionBusy(option: GrSegmentedOption): boolean {\n return Boolean(option.loading)\n}\n\nfunction isOptionBlocked(option: GrSegmentedOption): boolean {\n return resolveOptionDisabled(option) || isOptionBusy(option)\n}\n\nfunction isOptionSelected(option: GrSegmentedOption): boolean {\n return option.value === props.modelValue\n}\n\nfunction isIconOnlyOption(option: GrSegmentedOption): boolean {\n return Boolean(option.icon) && !option.label\n}\n\nfunction isFocusableOption(option: GrSegmentedOption, index: number): boolean {\n if (resolveOptionDisabled(option)) {\n return false\n }\n\n if (isOptionSelected(option)) {\n return true\n }\n\n if (selectedIndex.value !== -1) {\n return false\n }\n\n return enabledOptions.value[0]?.value === props.options[index]?.value\n}\n\nfunction setItemRef(value: GrSegmentedValue, element: Element | ComponentPublicInstance | null): void {\n const key = getOptionKey(value)\n\n if (!(element instanceof HTMLElement)) {\n itemRefs.value.delete(key)\n return\n }\n\n itemRefs.value.set(key, element)\n}\n\nfunction isSameGeometry(left: IndicatorGeometry | null, right: IndicatorGeometry | null): boolean {\n if (left === right) {\n return true\n }\n\n if (!left || !right) {\n return false\n }\n\n return left.width === right.width\n && left.height === right.height\n && left.x === right.x\n && left.y === right.y\n}\n\nfunction measureIndicator(): void {\n const root = rootRef.value\n const option = selectedOption.value\n if (!root || !option) {\n if (indicatorGeometry.value !== null)\n indicatorGeometry.value = null\n\n if (indicatorReady.value)\n indicatorReady.value = false\n\n return\n }\n\n const selectedItem = itemRefs.value.get(getOptionKey(option.value))\n if (!selectedItem) {\n return\n }\n\n const rootRect = root.getBoundingClientRect()\n const itemRect = selectedItem.getBoundingClientRect()\n\n const nextGeometry = {\n width: Math.max(Math.round(itemRect.width), 0),\n height: Math.max(Math.round(itemRect.height), 0),\n x: Math.round(itemRect.left - rootRect.left),\n y: Math.max(Math.round(itemRect.top - rootRect.top) - 1, 0),\n } satisfies IndicatorGeometry\n\n if (!isSameGeometry(indicatorGeometry.value, nextGeometry)) {\n indicatorGeometry.value = nextGeometry\n }\n\n const nextReady = nextGeometry.width > 0 && nextGeometry.height > 0\n if (indicatorReady.value !== nextReady) {\n indicatorReady.value = nextReady\n }\n}\n\nfunction refreshObserver(): void {\n if (typeof ResizeObserver === 'undefined') {\n return\n }\n\n const root = rootRef.value\n const option = selectedOption.value\n const selectedItem = option\n ? itemRefs.value.get(getOptionKey(option.value)) ?? null\n : null\n\n if (!resizeObserver) {\n resizeObserver = new ResizeObserver(() => scheduleMeasure())\n }\n\n if (!root) {\n resizeObserver.disconnect()\n observedRoot = null\n observedItem = null\n return\n }\n\n if (observedRoot === root && observedItem === selectedItem) {\n return\n }\n\n resizeObserver.disconnect()\n resizeObserver.observe(root)\n\n if (selectedItem) {\n resizeObserver.observe(selectedItem)\n }\n\n observedRoot = root\n observedItem = selectedItem\n}\n\nfunction scheduleMeasure(): void {\n if (scheduled) {\n return\n }\n\n scheduled = true\n void nextTick(() => {\n scheduled = false\n measureIndicator()\n refreshObserver()\n })\n}\n\nfunction emitValue(option: GrSegmentedOption): void {\n // Один guard на все пути: проверяй `readonly` только клавиатура, и клик по\n // сегменту менял бы значение вопреки заявленному контракту.\n if (isReadonly.value || isOptionBlocked(option) || option.value === props.modelValue) {\n return\n }\n\n emit('update:modelValue', option.value)\n emit('change', option.value, option)\n}\n\nfunction getNextEnabledIndex(startIndex: number, direction: 1 | -1): number {\n if (props.options.length === 0) {\n return -1\n }\n\n let cursor = startIndex\n for (let step = 0; step < props.options.length; step += 1) {\n cursor = (cursor + direction + props.options.length) % props.options.length\n const option = props.options[cursor]\n if (option && !isOptionBlocked(option)) {\n return cursor\n }\n }\n\n return -1\n}\n\nfunction focusIndex(index: number): void {\n const option = props.options[index]\n if (!option) {\n return\n }\n\n itemRefs.value.get(getOptionKey(option.value))?.focus()\n}\n\nfunction onKeydown(event: KeyboardEvent, index: number): void {\n if (isDisabled.value || isReadonly.value) {\n return\n }\n\n if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, 1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {\n event.preventDefault()\n const nextIndex = getNextEnabledIndex(index, -1)\n if (nextIndex === -1) {\n return\n }\n\n const option = props.options[nextIndex]\n if (option) {\n emitValue(option)\n focusIndex(nextIndex)\n }\n return\n }\n\n if (event.key === 'Home') {\n event.preventDefault()\n const firstOption = enabledOptions.value[0]\n if (!firstOption) {\n return\n }\n\n emitValue(firstOption)\n focusIndex(props.options.findIndex(option => option.value === firstOption.value))\n return\n }\n\n if (event.key === 'End') {\n event.preventDefault()\n const lastOption = enabledOptions.value.at(-1)\n if (!lastOption) {\n return\n }\n\n emitValue(lastOption)\n focusIndex(props.options.findIndex(option => option.value === lastOption.value))\n return\n }\n\n if (event.key === ' ' || event.key === 'Enter') {\n event.preventDefault()\n const option = props.options[index]\n if (option) {\n emitValue(option)\n }\n }\n}\n\nwatch(() => props.modelValue, () => scheduleMeasure())\nwatch(() => props.options, () => scheduleMeasure(), { deep: true })\nwatch(resolvedVariant, () => scheduleMeasure())\nwatch(resolvedSize, () => scheduleMeasure())\nwatch(() => props.block, () => scheduleMeasure())\n// Смена ориентации переставляет сегменты: без пересчёта индикатор остался бы в\n// координатах прежней раскладки.\nwatch(() => props.orientation, () => scheduleMeasure())\n\nonMounted(() => scheduleMeasure())\nonBeforeUnmount(() => {\n resizeObserver?.disconnect()\n observedRoot = null\n observedItem = null\n})\n</script>\n\n<template>\n <div\n :id=\"fieldId\"\n ref=\"rootRef\"\n data-gr-segmented\n :data-variant=\"resolvedVariant\"\n role=\"radiogroup\"\n :aria-orientation=\"orientation\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"labelledBy\"\n :aria-describedby=\"describedBy\"\n :aria-invalid=\"isInvalid ? 'true' : undefined\"\n :aria-required=\"isRequired ? 'true' : undefined\"\n :aria-readonly=\"isReadonly ? 'true' : undefined\"\n :aria-disabled=\"isDisabled ? 'true' : undefined\"\n :class=\"rootClassName\"\n :style=\"rootStyle\"\n @focusin=\"onFocusIn\"\n @focusout=\"onFocusOut\"\n >\n <!-- Значение для нативной формы уходит одним скрытым полем рядом с\n сегментами, а не вложенным в каждый `role=\"radio\"`: роль объявляет своих\n потомков презентационными, и вложенный интерактивный контрол ломает\n виджет для скринридеров (axe: `nested-interactive`). Скрытое поле не\n фокусируется и интерактивным не считается. -->\n <input\n v-if=\"selectedOption && !resolveOptionDisabled(selectedOption)\"\n type=\"hidden\"\n :name=\"resolvedName\"\n :value=\"String(selectedOption.value)\"\n >\n\n <span\n v-if=\"hasIndicator\"\n data-gr-segmented-indicator\n aria-hidden=\"true\"\n :class=\"indicatorClassName\"\n :style=\"indicatorStyle\"\n />\n\n <button\n v-for=\"(option, index) in options\"\n :key=\"option.value\"\n :ref=\"element => setItemRef(option.value, element)\"\n data-gr-segmented-item\n :data-value=\"String(option.value)\"\n type=\"button\"\n role=\"radio\"\n :aria-label=\"option.ariaLabel\"\n :aria-checked=\"isOptionSelected(option) ? 'true' : 'false'\"\n :aria-disabled=\"resolveOptionDisabled(option) ? 'true' : undefined\"\n :aria-busy=\"isOptionBusy(option) ? 'true' : undefined\"\n :disabled=\"resolveOptionDisabled(option)\"\n :tabindex=\"isFocusableOption(option, index) ? 0 : -1\"\n :class=\"grSegmentedItemClass({\n variant: resolvedVariant,\n selected: isOptionSelected(option),\n disabled: resolveOptionDisabled(option),\n iconOnly: isIconOnlyOption(option),\n })\"\n @click=\"emitValue(option)\"\n @keydown=\"onKeydown($event, index)\"\n >\n <slot\n :option=\"option\"\n :selected=\"isOptionSelected(option)\"\n :disabled=\"resolveOptionDisabled(option)\"\n :loading=\"isOptionBusy(option)\"\n >\n <!-- Спиннер занимает место иконки: две крутилки рядом читались бы как ошибка. -->\n <IconLoader\n v-if=\"isOptionBusy(option)\"\n data-gr-segmented-spinner\n aria-hidden=\"true\"\n :class=\"grSegmentedItemSpinnerClass\"\n />\n <component\n :is=\"option.icon\"\n v-else-if=\"option.icon\"\n aria-hidden=\"true\"\n :class=\"grSegmentedItemIconClass\"\n />\n <span v-if=\"option.label\" :class=\"grSegmentedItemLabelClass\">\n {{ option.label }}\n </span>\n </slot>\n </button>\n </div>\n</template>"],"mappings":"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"}
|