@feugene/granularity 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/chunks/config-BXRmsn3t.js +172 -0
- package/dist/chunks/config-BXRmsn3t.js.map +1 -0
- package/dist/chunks/config-BkQ2jvXv.js +49 -0
- package/dist/chunks/config-BkQ2jvXv.js.map +1 -0
- package/dist/chunks/config-Dg0bSoGw.js +81 -0
- package/dist/chunks/config-Dg0bSoGw.js.map +1 -0
- package/dist/chunks/config-DmED6aJV.js +237 -0
- package/dist/chunks/config-DmED6aJV.js.map +1 -0
- package/dist/chunks/{config-DWL81-aM.js → config-hnoZvcIM.js} +2 -20
- package/dist/chunks/config-hnoZvcIM.js.map +1 -0
- package/dist/chunks/dragSortGeometry-CY-llfrQ.js +87 -0
- package/dist/chunks/dragSortGeometry-CY-llfrQ.js.map +1 -0
- package/dist/chunks/{en-B9E3jO6m.js → en-DiHzFact.js} +34 -1
- package/dist/chunks/en-DiHzFact.js.map +1 -0
- package/dist/chunks/{es-QpzdIEut.js → es-1ZL6NtkB.js} +34 -1
- package/dist/chunks/es-1ZL6NtkB.js.map +1 -0
- package/dist/chunks/{es-DCkqHTlU.js → es-CeKkMxZ_.js} +3 -3
- package/dist/chunks/{es-DCkqHTlU.js.map → es-CeKkMxZ_.js.map} +1 -1
- package/dist/chunks/{generated-B95IBXs6.js → generated-Hxu_sWPs.js} +238 -0
- package/dist/chunks/generated-Hxu_sWPs.js.map +1 -0
- package/dist/chunks/{granular-provider-BG_2XqkS.js → granular-provider-CCNbQa5U.js} +9 -1
- package/dist/chunks/granular-provider-CCNbQa5U.js.map +1 -0
- package/dist/chunks/{ru-D_1Ya489.js → ru-Dr2RYn0P.js} +34 -1
- package/dist/chunks/ru-Dr2RYn0P.js.map +1 -0
- package/dist/chunks/scrollOverflow-B_kEdcol.js +31 -0
- package/dist/chunks/scrollOverflow-B_kEdcol.js.map +1 -0
- package/dist/chunks/tones-CRJAaYyg.js +21 -0
- package/dist/chunks/tones-CRJAaYyg.js.map +1 -0
- package/dist/chunks/useScrollSpy-CIJpw0RI.js +360 -0
- package/dist/chunks/useScrollSpy-CIJpw0RI.js.map +1 -0
- package/dist/components/GrAffix/chunks/GrAffix-CWtC7yi8.js +272 -0
- package/dist/components/GrAffix/chunks/GrAffix-CWtC7yi8.js.map +1 -0
- package/dist/components/GrAffix/index.js +3 -0
- package/dist/components/GrCarousel/chunks/GrCarousel-ByuughtI.js +761 -0
- package/dist/components/GrCarousel/chunks/GrCarousel-ByuughtI.js.map +1 -0
- package/dist/components/GrCarousel/index.js +3 -0
- package/dist/components/GrCarousel/styles.css +1 -0
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-BwzRLs-i.js → GrColorPicker-9mTR-YZ_.js} +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-BwzRLs-i.js.map → GrColorPicker-9mTR-YZ_.js.map} +1 -1
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrDataTable/chunks/{GrDataTable-BBe7-uUe.js → GrDataTable-xNSmO_I-.js} +4 -3
- package/dist/components/GrDataTable/chunks/{GrDataTable-BBe7-uUe.js.map → GrDataTable-xNSmO_I-.js.map} +1 -1
- package/dist/components/GrDataTable/index.js +1 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-Dd2gJ-j_.js.map → GrImageViewer-DK-iVHHC.js.map} +1 -1
- package/dist/components/GrImageViewer/index.js +1 -1
- package/dist/components/GrInputTag/chunks/{GrInputTag-BKzm1PKw.js.map → GrInputTag-Ci8AO1ze.js.map} +1 -1
- package/dist/components/GrInputTag/index.js +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-BLCEWlwg.js → GrJsonViewer-CwCBMh4h.js} +2 -2
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-BLCEWlwg.js.map → GrJsonViewer-CwCBMh4h.js.map} +1 -1
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-Bm-V59TG.js.map → GrNumberInput-CL5QhOmx.js.map} +1 -1
- package/dist/components/GrNumberInput/index.js +1 -1
- package/dist/components/GrProgressBar/index.js +1 -1
- package/dist/components/GrScrollSpy/chunks/GrScrollSpy-CQCH3zLC.js +207 -0
- package/dist/components/GrScrollSpy/chunks/GrScrollSpy-CQCH3zLC.js.map +1 -0
- package/dist/components/GrScrollSpy/index.js +3 -0
- package/dist/components/GrSlider/chunks/{GrSlider-Db-jACXz.js.map → GrSlider-CO8N9SF6.js.map} +1 -1
- package/dist/components/GrSlider/index.js +1 -1
- package/dist/components/GrSortableList/chunks/{GrSortableList-ce1psLL0.js → GrSortableList-NKriuGm_.js} +2 -1
- package/dist/components/GrSortableList/chunks/{GrSortableList-ce1psLL0.js.map → GrSortableList-NKriuGm_.js.map} +1 -1
- package/dist/components/GrSortableList/index.js +1 -1
- package/dist/components/GrTabs/chunks/{GrTabs-BArefUFv.js → GrTabs-s9RzC1WX.js} +2 -31
- package/dist/components/GrTabs/chunks/GrTabs-s9RzC1WX.js.map +1 -0
- package/dist/components/GrTabs/index.js +1 -1
- package/dist/components/GrTransfer/chunks/GrTransfer-8FA78ny6.js +1368 -0
- package/dist/components/GrTransfer/chunks/GrTransfer-8FA78ny6.js.map +1 -0
- package/dist/components/GrTransfer/index.js +3 -0
- package/dist/components/GrTransfer/styles.css +1 -0
- package/dist/components/GrTree/chunks/{GrTree-BFWFc0Bm.js → GrTree-CkHzgYg1.js} +3 -3
- package/dist/components/GrTree/chunks/{GrTree-BFWFc0Bm.js.map → GrTree-CkHzgYg1.js.map} +1 -1
- package/dist/components/GrTree/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DJa2lYGe.js → GrTreeSelect-DUsv3_mF.js} +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DJa2lYGe.js.map → GrTreeSelect-DUsv3_mF.js.map} +1 -1
- package/dist/components/GrTreeSelect/index.js +1 -1
- package/dist/composables/useDragSort.js +179 -1
- package/dist/composables/useDragSort.js.map +1 -0
- package/dist/composables/useScrollSpy.js +2 -0
- 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 +26 -19
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrAffix/GrAffix.vue.d.ts +91 -0
- package/dist/types/components/GrAffix/affixState.d.ts +81 -0
- package/dist/types/components/GrAffix/config.d.ts +1 -0
- package/dist/types/components/GrAffix/grAffixStyles.d.ts +46 -0
- package/dist/types/components/GrAffix/index.d.ts +9 -0
- package/dist/types/components/GrAffix/safelist.d.ts +1 -0
- package/dist/types/components/GrAlert/GrAlert.vue.d.ts +1 -1
- package/dist/types/components/GrBadgeWrap/GrBadgeWrap.vue.d.ts +2 -2
- package/dist/types/components/GrButton/GrButton.vue.d.ts +2 -2
- package/dist/types/components/GrCard/GrCard.vue.d.ts +1 -1
- package/dist/types/components/GrCarousel/GrCarousel.vue.d.ts +151 -0
- package/dist/types/components/GrCarousel/GrCarouselSlide.vue.d.ts +53 -0
- package/dist/types/components/GrCarousel/carouselNavigation.d.ts +57 -0
- package/dist/types/components/GrCarousel/composables/useCarouselAutoplay.d.ts +33 -0
- package/dist/types/components/GrCarousel/composables/useCarouselSwipe.d.ts +20 -0
- package/dist/types/components/GrCarousel/config.d.ts +7 -0
- package/dist/types/components/GrCarousel/defaults.d.ts +13 -0
- package/dist/types/components/GrCarousel/grCarouselContext.d.ts +46 -0
- package/dist/types/components/GrCarousel/grCarouselStyles.d.ts +80 -0
- package/dist/types/components/GrCarousel/index.d.ts +12 -0
- package/dist/types/components/GrCarousel/safelist.d.ts +1 -0
- package/dist/types/components/GrCheckbox/GrCheckbox.vue.d.ts +3 -3
- package/dist/types/components/GrCheckboxGroup/GrCheckboxGroup.vue.d.ts +2 -2
- package/dist/types/components/GrChip/GrChip.vue.d.ts +2 -2
- package/dist/types/components/GrChipGroup/GrChipGroup.vue.d.ts +2 -2
- package/dist/types/components/GrCollapse/GrCollapse.vue.d.ts +1 -1
- package/dist/types/components/GrCollapse/GrCollapseItem.vue.d.ts +1 -1
- package/dist/types/components/GrColorPicker/GrColorPicker.vue.d.ts +3 -3
- package/dist/types/components/GrCommandPalette/GrCommandPalette.vue.d.ts +1 -1
- package/dist/types/components/GrConfirmDialog/GrConfirmDialog.vue.d.ts +1 -1
- package/dist/types/components/GrContextMenu/GrContextMenu.vue.d.ts +2 -2
- package/dist/types/components/GrDialog/GrDialog.vue.d.ts +1 -1
- package/dist/types/components/GrDivider/GrDivider.vue.d.ts +2 -2
- package/dist/types/components/GrDrawer/GrDrawer.vue.d.ts +1 -1
- package/dist/types/components/GrDropdown/GrDropdown.vue.d.ts +3 -3
- package/dist/types/components/GrDropdownMenu/GrDropdownMenu.vue.d.ts +3 -3
- package/dist/types/components/GrDropdownMenu/GrDropdownMenuItem.vue.d.ts +1 -1
- package/dist/types/components/GrEmptyState/GrEmptyState.vue.d.ts +1 -1
- package/dist/types/components/GrFileUpload/GrFileUpload.vue.d.ts +1 -1
- package/dist/types/components/GrFormField/GrFormField.vue.d.ts +3 -3
- package/dist/types/components/GrFormFile/GrFormFile.vue.d.ts +2 -2
- package/dist/types/components/GrFormSection/GrFormSection.vue.d.ts +1 -1
- package/dist/types/components/GrIcon/GrIcon.vue.d.ts +1 -1
- package/dist/types/components/GrImageViewer/GrImageViewer.vue.d.ts +3 -3
- package/dist/types/components/GrInput/GrInput.vue.d.ts +4 -4
- package/dist/types/components/GrInputTag/GrInputTag.vue.d.ts +3 -3
- package/dist/types/components/GrKbd/GrKbd.vue.d.ts +1 -1
- package/dist/types/components/GrLink/GrLink.vue.d.ts +1 -1
- package/dist/types/components/GrList/GrListItem.vue.d.ts +2 -2
- package/dist/types/components/GrNavbar/GrNavbar.vue.d.ts +2 -2
- package/dist/types/components/GrNumberInput/GrNumberInput.vue.d.ts +3 -3
- package/dist/types/components/GrPagination/GrPagination.vue.d.ts +1 -1
- package/dist/types/components/GrPopover/GrPopover.vue.d.ts +3 -3
- package/dist/types/components/GrPromptDialog/GrPromptDialog.vue.d.ts +3 -3
- package/dist/types/components/GrRadio/GrRadio.vue.d.ts +2 -2
- package/dist/types/components/GrRadioGroup/GrRadioGroup.vue.d.ts +2 -2
- package/dist/types/components/GrRating/GrRating.vue.d.ts +2 -2
- package/dist/types/components/GrScrollSpy/GrScrollSpy.vue.d.ts +105 -0
- package/dist/types/components/GrScrollSpy/config.d.ts +1 -0
- package/dist/types/components/GrScrollSpy/grScrollSpyStyles.d.ts +26 -0
- package/dist/types/components/GrScrollSpy/index.d.ts +9 -0
- package/dist/types/components/GrScrollSpy/safelist.d.ts +1 -0
- package/dist/types/components/GrScrollSpy/scrollSpyItems.d.ts +44 -0
- package/dist/types/components/GrSegmented/GrSegmented.vue.d.ts +2 -2
- package/dist/types/components/GrSidebar/GrSidebarItem.vue.d.ts +1 -1
- package/dist/types/components/GrSkeleton/GrSkeleton.vue.d.ts +1 -1
- package/dist/types/components/GrSlider/GrSlider.vue.d.ts +3 -3
- package/dist/types/components/GrSplitter/GrSplitter.vue.d.ts +1 -1
- package/dist/types/components/GrStatistic/GrStatistic.vue.d.ts +2 -2
- package/dist/types/components/GrSwitch/GrSwitch.vue.d.ts +3 -3
- package/dist/types/components/GrTable/GrTable.vue.d.ts +1 -1
- package/dist/types/components/GrTabs/GrTabs.vue.d.ts +1 -1
- package/dist/types/components/GrTextarea/GrTextarea.vue.d.ts +2 -2
- package/dist/types/components/GrTimeline/GrTimelineItem.vue.d.ts +2 -2
- package/dist/types/components/GrToaster/GrToaster.vue.d.ts +1 -1
- package/dist/types/components/GrTooltip/GrTooltip.vue.d.ts +3 -3
- package/dist/types/components/GrTransfer/GrTransfer.vue.d.ts +103 -0
- package/dist/types/components/GrTransfer/config.d.ts +1 -0
- package/dist/types/components/GrTransfer/defaults.d.ts +15 -0
- package/dist/types/components/GrTransfer/grTransferStyles.d.ts +127 -0
- package/dist/types/components/GrTransfer/index.d.ts +11 -0
- package/dist/types/components/GrTransfer/safelist.d.ts +1 -0
- package/dist/types/components/GrTransfer/transferGeometry.d.ts +44 -0
- package/dist/types/components/GrTransfer/transferModel.d.ts +68 -0
- package/dist/types/components/GrTransfer/transferSelection.d.ts +36 -0
- package/dist/types/components/GrTransfer/useTransferDrag.d.ts +48 -0
- package/dist/types/composables/internal/scrollSpyGeometry.d.ts +67 -0
- package/dist/types/composables/useScrollSpy.d.ts +59 -0
- package/dist/types/granular-provider/shared.d.ts +4 -0
- package/dist/types/i18n/messages/en.d.ts +64 -0
- package/dist/types/i18n/messages/es.d.ts +64 -0
- package/dist/types/i18n/messages/ru.d.ts +64 -0
- package/dist/types/index.d.ts +6 -0
- package/dist/web-types.json +809 -284
- package/package.json +25 -1
- package/dist/chunks/config-DWL81-aM.js.map +0 -1
- package/dist/chunks/en-B9E3jO6m.js.map +0 -1
- package/dist/chunks/es-QpzdIEut.js.map +0 -1
- package/dist/chunks/generated-B95IBXs6.js.map +0 -1
- package/dist/chunks/granular-provider-BG_2XqkS.js.map +0 -1
- package/dist/chunks/ru-D_1Ya489.js.map +0 -1
- package/dist/chunks/useDragSort-0l47h4yb.js +0 -265
- package/dist/chunks/useDragSort-0l47h4yb.js.map +0 -1
- package/dist/components/GrTabs/chunks/GrTabs-BArefUFv.js.map +0 -1
- package/dist/components/GrImageViewer/chunks/{GrImageViewer-Dd2gJ-j_.js → GrImageViewer-DK-iVHHC.js} +3 -3
- package/dist/components/GrInputTag/chunks/{GrInputTag-BKzm1PKw.js → GrInputTag-Ci8AO1ze.js} +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-Bm-V59TG.js → GrNumberInput-CL5QhOmx.js} +1 -1
- package/dist/components/GrSlider/chunks/{GrSlider-Db-jACXz.js → GrSlider-CO8N9SF6.js} +1 -1
|
@@ -0,0 +1,761 @@
|
|
|
1
|
+
import { i as useGrComponentProp } from "../../../chunks/context-CPMRfwVr.js";
|
|
2
|
+
import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
|
|
3
|
+
import { t as useFocusWithin } from "../../../chunks/useFocusWithin-BwHE0rzm.js";
|
|
4
|
+
import { useRovingFocus } from "../../../composables/useRovingFocus.js";
|
|
5
|
+
import { useAnnouncer } from "../../../composables/useAnnouncer.js";
|
|
6
|
+
import { t as resolveScrollOverflow } from "../../../chunks/scrollOverflow-B_kEdcol.js";
|
|
7
|
+
import { useDragGesture } from "../../../composables/useDragGesture.js";
|
|
8
|
+
import { a as carouselSlideBase, c as carouselToggleClass, d as carouselViewportSwipeClass, f as grCarouselControlClass, i as carouselRootBase, l as carouselTrackBase, m as grCarouselIndicatorsClass, o as carouselThumbFallbackClass, p as grCarouselIndicatorClass, r as carouselIconClass, s as carouselThumbImageClass, u as carouselViewportBase } from "../../../chunks/config-BXRmsn3t.js";
|
|
9
|
+
import { t as chevron_left_default } from "../../../chunks/chevron-left-D8AUBAM7.js";
|
|
10
|
+
import { t as chevron_right_default } from "../../../chunks/chevron-right-C6Z01h0X.js";
|
|
11
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, markRaw, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, onScopeDispose, openBlock, provide, ref, renderList, renderSlot, resolveDynamicComponent, toDisplayString, unref, useId, useSlots, watch, watchEffect } from "vue";
|
|
12
|
+
import '../styles.css';/**
|
|
13
|
+
* Доля ширины вьюпорта, начиная с которой жест засчитывается. Работает вместе
|
|
14
|
+
* с {@link SWIPE_THRESHOLD_PX} по максимуму: 60px на герое шириной 1200px —
|
|
15
|
+
* это нервно, а на узкой карточке доля выродилась бы в пару пикселей.
|
|
16
|
+
*/
|
|
17
|
+
var SWIPE_THRESHOLD_RATIO = .15;
|
|
18
|
+
/**
|
|
19
|
+
* Логическое направление жеста: `1` — вперёд, `-1` — назад, `0` — не свайп.
|
|
20
|
+
*
|
|
21
|
+
* `dx` физический, в пикселях экрана; разворот под RTL — забота вызывающего,
|
|
22
|
+
* модуль про направление письма не знает. Палец, ушедший влево, листает вперёд:
|
|
23
|
+
* содержимое едет за пальцем, как в нативной прокрутке.
|
|
24
|
+
*
|
|
25
|
+
* Отдельным модулем, потому что в jsdom раскладки нет и через смонтированный
|
|
26
|
+
* компонент ни одна ветка не проверяется — тот же довод, что у
|
|
27
|
+
* {@link resolveScrollOverflow}.
|
|
28
|
+
*/
|
|
29
|
+
function resolveSwipeDirection(dx, dy, threshold = 60) {
|
|
30
|
+
if (Math.abs(dx) < threshold) return 0;
|
|
31
|
+
if (Math.abs(dx) < Math.abs(dy) * 1.5) return 0;
|
|
32
|
+
return dx < 0 ? 1 : -1;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Порог жеста для ленты шириной `width`. При нулевой ширине — а в jsdom она
|
|
36
|
+
* всегда нулевая — вырождается в пиксельный минимум, и жест остаётся
|
|
37
|
+
* проверяемым.
|
|
38
|
+
*/
|
|
39
|
+
function swipeThresholdFor(width) {
|
|
40
|
+
return Math.max(60, width * SWIPE_THRESHOLD_RATIO);
|
|
41
|
+
}
|
|
42
|
+
/** Смещение ленты за пальцем: за краем — вязкое, иначе один в один. */
|
|
43
|
+
function resistOffset(delta, atEdge) {
|
|
44
|
+
return atEdge ? delta / 4 : delta;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Индекс, зажатый в границы ленты. Пустая лента даёт `0`: слайда с таким
|
|
48
|
+
* номером нет, но отрицательный индекс уехал бы в CSS-переменную трека.
|
|
49
|
+
*/
|
|
50
|
+
function clampIndex(index, count) {
|
|
51
|
+
if (count <= 0) return 0;
|
|
52
|
+
if (!Number.isFinite(index)) return 0;
|
|
53
|
+
return Math.min(Math.max(Math.trunc(index), 0), count - 1);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Шаг по ленте. Без `loop` на краю возвращает тот же индекс — вызывающий по
|
|
57
|
+
* этому признаку и гасит стрелку, отдельного предиката не нужно.
|
|
58
|
+
*/
|
|
59
|
+
function stepIndex(index, delta, count, loop) {
|
|
60
|
+
if (count <= 0) return 0;
|
|
61
|
+
const next = clampIndex(index, count) + delta;
|
|
62
|
+
if (loop) return (next % count + count) % count;
|
|
63
|
+
return clampIndex(next, count);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Насколько прокрутить полосу переключателей, чтобы кадр был виден.
|
|
67
|
+
*
|
|
68
|
+
* Считается своей арифметикой, а не `scrollIntoView`: тот прокручивает **всех**
|
|
69
|
+
* предков, включая документ, — и карусель, уехавшая под сгиб, на каждом шаге
|
|
70
|
+
* автопрокрутки утаскивала бы страницу обратно к себе. Здесь двигается только
|
|
71
|
+
* сама полоса.
|
|
72
|
+
*
|
|
73
|
+
* Движение минимальное: элемент уже виден — `scrollLeft` не меняется вовсе.
|
|
74
|
+
*/
|
|
75
|
+
function stripScrollLeft(scrollLeft, viewport, itemStart, itemEnd, padding = 0) {
|
|
76
|
+
if (viewport <= 0) return scrollLeft;
|
|
77
|
+
if (itemStart - padding < scrollLeft) return Math.max(0, itemStart - padding);
|
|
78
|
+
if (itemEnd + padding > scrollLeft + viewport) return itemEnd + padding - viewport;
|
|
79
|
+
return scrollLeft;
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/components/GrCarousel/composables/useCarouselAutoplay.ts
|
|
83
|
+
/**
|
|
84
|
+
* Таймер автопрокрутки с паузой по остатку.
|
|
85
|
+
*
|
|
86
|
+
* Модель взята у `useToast` (`{ handle, remaining, startedAt }`), и `setInterval`
|
|
87
|
+
* здесь не годится принципиально: паузу, сохраняющую остаток, на нём не
|
|
88
|
+
* выразить — курсор, снятый за полсекунды до шага, откладывал бы шаг на полный
|
|
89
|
+
* интервал.
|
|
90
|
+
*
|
|
91
|
+
* Композабл локальный: публичный потребовал бы записей в трёх реестрах пакета,
|
|
92
|
+
* а переиспользовать его сегодня некому.
|
|
93
|
+
*/
|
|
94
|
+
function useCarouselAutoplay(options) {
|
|
95
|
+
let handle = null;
|
|
96
|
+
let remaining = 0;
|
|
97
|
+
let startedAt = 0;
|
|
98
|
+
const armed = ref(false);
|
|
99
|
+
function clear() {
|
|
100
|
+
if (handle !== null) {
|
|
101
|
+
clearTimeout(handle);
|
|
102
|
+
handle = null;
|
|
103
|
+
}
|
|
104
|
+
armed.value = false;
|
|
105
|
+
}
|
|
106
|
+
function arm() {
|
|
107
|
+
if (typeof window === "undefined" || remaining <= 0) return;
|
|
108
|
+
startedAt = Date.now();
|
|
109
|
+
armed.value = true;
|
|
110
|
+
handle = window.setTimeout(() => {
|
|
111
|
+
handle = null;
|
|
112
|
+
remaining = options.interval();
|
|
113
|
+
options.advance();
|
|
114
|
+
if (options.enabled() && !options.paused()) arm();
|
|
115
|
+
else clear();
|
|
116
|
+
}, remaining);
|
|
117
|
+
}
|
|
118
|
+
function pause() {
|
|
119
|
+
if (handle === null) {
|
|
120
|
+
armed.value = false;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
clearTimeout(handle);
|
|
124
|
+
handle = null;
|
|
125
|
+
armed.value = false;
|
|
126
|
+
remaining = Math.max(0, remaining - (Date.now() - startedAt));
|
|
127
|
+
}
|
|
128
|
+
function restart() {
|
|
129
|
+
clear();
|
|
130
|
+
remaining = options.interval();
|
|
131
|
+
if (options.enabled() && !options.paused()) arm();
|
|
132
|
+
}
|
|
133
|
+
watchEffect(() => {
|
|
134
|
+
const ms = options.interval();
|
|
135
|
+
if (!options.enabled() || options.paused() || ms <= 0) {
|
|
136
|
+
pause();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (handle !== null) return;
|
|
140
|
+
if (remaining <= 0) remaining = ms;
|
|
141
|
+
arm();
|
|
142
|
+
});
|
|
143
|
+
onScopeDispose(clear);
|
|
144
|
+
return {
|
|
145
|
+
running: computed(() => armed.value),
|
|
146
|
+
restart
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
//#region src/components/GrCarousel/composables/useCarouselSwipe.ts
|
|
151
|
+
/**
|
|
152
|
+
* Что внутри слайда остаётся нажатием, а не превращается в протяжку. Ссылка и
|
|
153
|
+
* кнопка в онбординг-слайде — обычный интерактив, и жест их перехватывать
|
|
154
|
+
* не вправе (приём `GrToaster`).
|
|
155
|
+
*/
|
|
156
|
+
var INTERACTIVE_SELECTOR = "a[href], button, input, textarea, select, summary, [role=\"button\"], [contenteditable]";
|
|
157
|
+
function useCarouselSwipe(options) {
|
|
158
|
+
const offset = ref(0);
|
|
159
|
+
let startX = 0;
|
|
160
|
+
let startY = 0;
|
|
161
|
+
let threshold = 0;
|
|
162
|
+
let rtl = false;
|
|
163
|
+
function reset() {
|
|
164
|
+
offset.value = 0;
|
|
165
|
+
}
|
|
166
|
+
const drag = useDragGesture({
|
|
167
|
+
disabled: options.disabled,
|
|
168
|
+
onStart: (event) => {
|
|
169
|
+
if (event.target?.closest(INTERACTIVE_SELECTOR)) return false;
|
|
170
|
+
const viewport = options.viewport();
|
|
171
|
+
if (!viewport) return false;
|
|
172
|
+
startX = event.clientX;
|
|
173
|
+
startY = event.clientY;
|
|
174
|
+
offset.value = 0;
|
|
175
|
+
threshold = swipeThresholdFor(viewport.getBoundingClientRect().width);
|
|
176
|
+
rtl = getComputedStyle(viewport).direction === "rtl";
|
|
177
|
+
options.onStart?.();
|
|
178
|
+
},
|
|
179
|
+
onMove: (event) => {
|
|
180
|
+
event.preventDefault();
|
|
181
|
+
const dx = event.clientX - startX;
|
|
182
|
+
const dy = event.clientY - startY;
|
|
183
|
+
if (Math.abs(dx) < Math.abs(dy) * 1.5) {
|
|
184
|
+
offset.value = 0;
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const logical = physicalToLogical(dx < 0 ? 1 : -1);
|
|
188
|
+
offset.value = resistOffset(dx, options.atEdge(logical));
|
|
189
|
+
},
|
|
190
|
+
onEnd: (event) => {
|
|
191
|
+
const direction = event ? resolveSwipeDirection(event.clientX - startX, event.clientY - startY, threshold) : 0;
|
|
192
|
+
reset();
|
|
193
|
+
if (direction !== 0) options.onSwipe(physicalToLogical(direction));
|
|
194
|
+
},
|
|
195
|
+
onCancel: reset
|
|
196
|
+
});
|
|
197
|
+
/** В RTL содержимое течёт справа налево, и палец влево листает назад. */
|
|
198
|
+
function physicalToLogical(direction) {
|
|
199
|
+
return rtl ? direction === 1 ? -1 : 1 : direction;
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
isDragging: drag.isDragging,
|
|
203
|
+
offset,
|
|
204
|
+
start: drag.start
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
//#endregion
|
|
208
|
+
//#region src/components/GrCarousel/grCarouselContext.ts
|
|
209
|
+
var GR_CAROUSEL_CONTEXT = Symbol("GR_CAROUSEL_CONTEXT");
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region ~icons/lucide/pause
|
|
212
|
+
var _hoisted_1$3 = {
|
|
213
|
+
viewBox: "0 0 24 24",
|
|
214
|
+
width: "1.2em",
|
|
215
|
+
height: "1.2em"
|
|
216
|
+
};
|
|
217
|
+
function render$2(_ctx, _cache) {
|
|
218
|
+
return openBlock(), createElementBlock("svg", _hoisted_1$3, [..._cache[0] || (_cache[0] = [createElementVNode("g", {
|
|
219
|
+
fill: "none",
|
|
220
|
+
stroke: "currentColor",
|
|
221
|
+
"stroke-linecap": "round",
|
|
222
|
+
"stroke-linejoin": "round",
|
|
223
|
+
"stroke-width": "2"
|
|
224
|
+
}, [createElementVNode("rect", {
|
|
225
|
+
width: "5",
|
|
226
|
+
height: "18",
|
|
227
|
+
x: "14",
|
|
228
|
+
y: "3",
|
|
229
|
+
rx: "1"
|
|
230
|
+
}), createElementVNode("rect", {
|
|
231
|
+
width: "5",
|
|
232
|
+
height: "18",
|
|
233
|
+
x: "5",
|
|
234
|
+
y: "3",
|
|
235
|
+
rx: "1"
|
|
236
|
+
})], -1)])]);
|
|
237
|
+
}
|
|
238
|
+
var pause_default = markRaw({
|
|
239
|
+
name: "lucide-pause",
|
|
240
|
+
render: render$2
|
|
241
|
+
});
|
|
242
|
+
//#endregion
|
|
243
|
+
//#region ~icons/lucide/play
|
|
244
|
+
var _hoisted_1$2 = {
|
|
245
|
+
viewBox: "0 0 24 24",
|
|
246
|
+
width: "1.2em",
|
|
247
|
+
height: "1.2em"
|
|
248
|
+
};
|
|
249
|
+
function render$1(_ctx, _cache) {
|
|
250
|
+
return openBlock(), createElementBlock("svg", _hoisted_1$2, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
|
|
251
|
+
fill: "none",
|
|
252
|
+
stroke: "currentColor",
|
|
253
|
+
"stroke-linecap": "round",
|
|
254
|
+
"stroke-linejoin": "round",
|
|
255
|
+
"stroke-width": "2",
|
|
256
|
+
d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z"
|
|
257
|
+
}, null, -1)])]);
|
|
258
|
+
}
|
|
259
|
+
var play_default = markRaw({
|
|
260
|
+
name: "lucide-play",
|
|
261
|
+
render: render$1
|
|
262
|
+
});
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region src/components/GrCarousel/GrCarousel.vue?vue&type=script&setup=true&lang.ts
|
|
265
|
+
var _hoisted_1$1 = [
|
|
266
|
+
"role",
|
|
267
|
+
"aria-roledescription",
|
|
268
|
+
"aria-label",
|
|
269
|
+
"aria-labelledby"
|
|
270
|
+
];
|
|
271
|
+
var _hoisted_2 = ["aria-live"];
|
|
272
|
+
var _hoisted_3 = ["aria-label"];
|
|
273
|
+
var _hoisted_4 = ["aria-disabled", "aria-label"];
|
|
274
|
+
var _hoisted_5 = ["aria-disabled", "aria-label"];
|
|
275
|
+
var _hoisted_6 = [
|
|
276
|
+
"data-variant",
|
|
277
|
+
"data-overflow",
|
|
278
|
+
"aria-label"
|
|
279
|
+
];
|
|
280
|
+
var _hoisted_7 = [
|
|
281
|
+
"id",
|
|
282
|
+
"aria-selected",
|
|
283
|
+
"aria-controls",
|
|
284
|
+
"aria-label",
|
|
285
|
+
"tabindex",
|
|
286
|
+
"onClick"
|
|
287
|
+
];
|
|
288
|
+
var _hoisted_8 = ["src"];
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region src/components/GrCarousel/GrCarousel.vue
|
|
291
|
+
var GrCarousel_default = /* @__PURE__ */ defineComponent({
|
|
292
|
+
__name: "GrCarousel",
|
|
293
|
+
props: {
|
|
294
|
+
modelValue: { default: void 0 },
|
|
295
|
+
autoplay: {
|
|
296
|
+
type: Boolean,
|
|
297
|
+
default: false
|
|
298
|
+
},
|
|
299
|
+
autoplayInterval: { default: 5e3 },
|
|
300
|
+
loop: {
|
|
301
|
+
type: Boolean,
|
|
302
|
+
default: true
|
|
303
|
+
},
|
|
304
|
+
indicators: { default: void 0 },
|
|
305
|
+
tone: { default: void 0 },
|
|
306
|
+
activationMode: { default: void 0 },
|
|
307
|
+
arrows: {
|
|
308
|
+
type: Boolean,
|
|
309
|
+
default: true
|
|
310
|
+
},
|
|
311
|
+
swipe: {
|
|
312
|
+
type: Boolean,
|
|
313
|
+
default: true
|
|
314
|
+
},
|
|
315
|
+
landmark: {
|
|
316
|
+
type: Boolean,
|
|
317
|
+
default: false
|
|
318
|
+
},
|
|
319
|
+
ariaLabel: { default: void 0 },
|
|
320
|
+
ariaLabelledby: { default: void 0 },
|
|
321
|
+
prevLabel: { default: void 0 },
|
|
322
|
+
nextLabel: { default: void 0 },
|
|
323
|
+
playLabel: { default: void 0 },
|
|
324
|
+
pauseLabel: { default: void 0 },
|
|
325
|
+
indicatorsLabel: { default: void 0 }
|
|
326
|
+
},
|
|
327
|
+
emits: ["update:modelValue"],
|
|
328
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
329
|
+
const props = __props;
|
|
330
|
+
const emit = __emit;
|
|
331
|
+
const { t } = useGranularityTranslations();
|
|
332
|
+
const { announce } = useAnnouncer();
|
|
333
|
+
const resolvedIndicators = useGrComponentProp("GrCarousel", "indicators", () => props.indicators, "dots");
|
|
334
|
+
const resolvedActivation = useGrComponentProp("GrCarousel", "activationMode", () => props.activationMode, "automatic");
|
|
335
|
+
const resolvedTone = useGrComponentProp("GrCarousel", "tone", () => props.tone, "primary");
|
|
336
|
+
const rootEl = ref(null);
|
|
337
|
+
const viewportEl = ref(null);
|
|
338
|
+
const indicatorsEl = ref(null);
|
|
339
|
+
const tabRefs = ref([]);
|
|
340
|
+
const entries = ref([]);
|
|
341
|
+
const total = computed(() => entries.value.length);
|
|
342
|
+
let reorderScheduled = false;
|
|
343
|
+
/**
|
|
344
|
+
* Порядок регистрации совпадает с документом только при первом монтировании:
|
|
345
|
+
* кадр, появившийся посреди ленты позже, встал бы в конец, и переключатели
|
|
346
|
+
* разъехались бы с лентой. Пересортировка откладывается на такт — к этому
|
|
347
|
+
* моменту узлы уже в DOM.
|
|
348
|
+
*/
|
|
349
|
+
function scheduleReorder() {
|
|
350
|
+
if (reorderScheduled || typeof document === "undefined") return;
|
|
351
|
+
reorderScheduled = true;
|
|
352
|
+
nextTick(() => {
|
|
353
|
+
reorderScheduled = false;
|
|
354
|
+
entries.value = [...entries.value].sort((left, right) => {
|
|
355
|
+
const a = left.el();
|
|
356
|
+
const b = right.el();
|
|
357
|
+
if (!a || !b) return 0;
|
|
358
|
+
return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
function register(entry) {
|
|
363
|
+
entries.value.push(entry);
|
|
364
|
+
scheduleReorder();
|
|
365
|
+
return () => {
|
|
366
|
+
const index = entries.value.findIndex((item) => item.id === entry.id);
|
|
367
|
+
if (index >= 0) entries.value.splice(index, 1);
|
|
368
|
+
scheduleReorder();
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const internalIndex = ref(0);
|
|
372
|
+
const currentIndex = computed(() => clampIndex(props.modelValue ?? internalIndex.value, total.value));
|
|
373
|
+
function setIndex(next) {
|
|
374
|
+
const value = clampIndex(next, total.value);
|
|
375
|
+
if (value === currentIndex.value) return;
|
|
376
|
+
internalIndex.value = value;
|
|
377
|
+
emit("update:modelValue", value);
|
|
378
|
+
}
|
|
379
|
+
const showIndicators = computed(() => resolvedIndicators.value !== "none" && total.value > 1);
|
|
380
|
+
const showArrows = computed(() => props.arrows && total.value > 1);
|
|
381
|
+
const isThumbnails = computed(() => resolvedIndicators.value === "thumbnails");
|
|
382
|
+
const atStart = computed(() => !props.loop && currentIndex.value <= 0);
|
|
383
|
+
const atEnd = computed(() => !props.loop && currentIndex.value >= total.value - 1);
|
|
384
|
+
const reducedMotion = ref(false);
|
|
385
|
+
const hovered = ref(false);
|
|
386
|
+
const documentHidden = ref(false);
|
|
387
|
+
/** `null` — тумблер не трогали, и решает проп. */
|
|
388
|
+
const userIntent = ref(null);
|
|
389
|
+
const playing = computed(() => total.value > 1 && (userIntent.value ?? (props.autoplay && !reducedMotion.value)));
|
|
390
|
+
/**
|
|
391
|
+
* Живой регион читает кадр, только когда лента стоит: у движущейся карусели
|
|
392
|
+
* объявление каждого кадра превратило бы страницу в неработающую.
|
|
393
|
+
*/
|
|
394
|
+
const liveMode = computed(() => playing.value ? "off" : "polite");
|
|
395
|
+
const swipeGesture = useCarouselSwipe({
|
|
396
|
+
disabled: () => !props.swipe || total.value <= 1,
|
|
397
|
+
viewport: () => viewportEl.value,
|
|
398
|
+
atEdge: (direction) => direction === 1 ? atEnd.value : atStart.value,
|
|
399
|
+
onStart: stopAutoplay,
|
|
400
|
+
onSwipe: (direction) => {
|
|
401
|
+
commitUserNavigation(stepIndex(currentIndex.value, direction, total.value, props.loop));
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
const autoplayTimer = useCarouselAutoplay({
|
|
405
|
+
interval: () => props.autoplayInterval,
|
|
406
|
+
enabled: () => playing.value,
|
|
407
|
+
paused: () => hovered.value || documentHidden.value || swipeGesture.isDragging.value,
|
|
408
|
+
advance: () => {
|
|
409
|
+
const next = stepIndex(currentIndex.value, 1, total.value, props.loop);
|
|
410
|
+
if (next === currentIndex.value) {
|
|
411
|
+
userIntent.value = false;
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
setIndex(next);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
function stopAutoplay() {
|
|
418
|
+
if (!playing.value) return;
|
|
419
|
+
userIntent.value = false;
|
|
420
|
+
announce(t("gr.carousel.autoplayStopped", "Automatic slide show stopped"));
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* `aria-live` обязан стать `polite` **раньше**, чем поменяется содержимое:
|
|
424
|
+
* иначе мутация случится, пока регион ещё `off`, и не прозвучит вовсе.
|
|
425
|
+
*/
|
|
426
|
+
async function commitUserNavigation(next) {
|
|
427
|
+
if (playing.value) {
|
|
428
|
+
stopAutoplay();
|
|
429
|
+
await nextTick();
|
|
430
|
+
}
|
|
431
|
+
setIndex(next);
|
|
432
|
+
}
|
|
433
|
+
function toggleAutoplay() {
|
|
434
|
+
if (playing.value) {
|
|
435
|
+
stopAutoplay();
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
userIntent.value = true;
|
|
439
|
+
autoplayTimer.restart();
|
|
440
|
+
}
|
|
441
|
+
function goTo(index) {
|
|
442
|
+
commitUserNavigation(clampIndex(index, total.value));
|
|
443
|
+
}
|
|
444
|
+
function step(delta) {
|
|
445
|
+
commitUserNavigation(stepIndex(currentIndex.value, delta, total.value, props.loop));
|
|
446
|
+
}
|
|
447
|
+
const roving = useRovingFocus({
|
|
448
|
+
items: () => entries.value.map((_, index) => index),
|
|
449
|
+
elementFor: (index) => tabRefs.value[index],
|
|
450
|
+
orientation: () => "horizontal",
|
|
451
|
+
wrap: () => true,
|
|
452
|
+
initialKey: () => total.value > 0 ? currentIndex.value : void 0,
|
|
453
|
+
beforeFocus: (index) => scrollTabIntoView(index)
|
|
454
|
+
});
|
|
455
|
+
watch(total, (count) => {
|
|
456
|
+
if (count === 0) {
|
|
457
|
+
roving.reset();
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
const requested = props.modelValue ?? internalIndex.value;
|
|
461
|
+
const clamped = clampIndex(requested, count);
|
|
462
|
+
if (clamped !== requested) {
|
|
463
|
+
internalIndex.value = clamped;
|
|
464
|
+
emit("update:modelValue", clamped);
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
/**
|
|
468
|
+
* Прокрутка **полосы**, а не страницы.
|
|
469
|
+
*
|
|
470
|
+
* `scrollIntoView` двигает всех предков, включая документ: карусель, уехавшая
|
|
471
|
+
* под сгиб, на каждом шаге автопрокрутки утаскивала бы страницу обратно к себе.
|
|
472
|
+
* Поэтому позиция считается сама и присваивается `scrollLeft` полосы.
|
|
473
|
+
*/
|
|
474
|
+
function scrollTabIntoView(index) {
|
|
475
|
+
const strip = indicatorsEl.value;
|
|
476
|
+
const tab = tabRefs.value[index];
|
|
477
|
+
if (!strip || !tab) return;
|
|
478
|
+
const next = stripScrollLeft(strip.scrollLeft, strip.clientWidth, tab.offsetLeft, tab.offsetLeft + tab.offsetWidth);
|
|
479
|
+
if (next !== strip.scrollLeft) strip.scrollLeft = next;
|
|
480
|
+
}
|
|
481
|
+
function setTabRef(el, index) {
|
|
482
|
+
tabRefs.value[index] = el;
|
|
483
|
+
}
|
|
484
|
+
function onIndicatorKeydown(event) {
|
|
485
|
+
if (total.value === 0) return;
|
|
486
|
+
const current = roving.rovingKey.value ?? 0;
|
|
487
|
+
if ((event.key === "Enter" || event.key === " ") && resolvedActivation.value === "manual") {
|
|
488
|
+
event.preventDefault();
|
|
489
|
+
commitUserNavigation(current);
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
if (!roving.handleNavigationKeys(event)) return;
|
|
493
|
+
if (resolvedActivation.value !== "manual") commitUserNavigation(roving.rovingKey.value ?? current);
|
|
494
|
+
}
|
|
495
|
+
function onIndicatorClick(index) {
|
|
496
|
+
roving.setActive(index);
|
|
497
|
+
commitUserNavigation(index);
|
|
498
|
+
}
|
|
499
|
+
const { onFocusIn, onFocusOut } = useFocusWithin(rootEl, { enter: stopAutoplay });
|
|
500
|
+
const stripOverflow = ref("none");
|
|
501
|
+
let resizeObserver = null;
|
|
502
|
+
let measureScheduled = false;
|
|
503
|
+
function measureOverflow() {
|
|
504
|
+
const el = indicatorsEl.value;
|
|
505
|
+
if (!el || !isThumbnails.value) {
|
|
506
|
+
stripOverflow.value = "none";
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
stripOverflow.value = resolveScrollOverflow(el.scrollLeft, el.scrollWidth, el.clientWidth);
|
|
510
|
+
}
|
|
511
|
+
function scheduleMeasure() {
|
|
512
|
+
if (measureScheduled) return;
|
|
513
|
+
measureScheduled = true;
|
|
514
|
+
nextTick(() => {
|
|
515
|
+
measureScheduled = false;
|
|
516
|
+
measureOverflow();
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
function onVisibilityChange() {
|
|
520
|
+
documentHidden.value = document.hidden;
|
|
521
|
+
}
|
|
522
|
+
onMounted(() => {
|
|
523
|
+
reducedMotion.value = typeof window !== "undefined" && window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches === true;
|
|
524
|
+
document.addEventListener("visibilitychange", onVisibilityChange);
|
|
525
|
+
scheduleMeasure();
|
|
526
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
527
|
+
resizeObserver = new ResizeObserver(scheduleMeasure);
|
|
528
|
+
if (indicatorsEl.value) resizeObserver.observe(indicatorsEl.value);
|
|
529
|
+
});
|
|
530
|
+
onBeforeUnmount(() => {
|
|
531
|
+
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
532
|
+
resizeObserver?.disconnect();
|
|
533
|
+
resizeObserver = null;
|
|
534
|
+
});
|
|
535
|
+
watch([total, isThumbnails], scheduleMeasure);
|
|
536
|
+
watch(currentIndex, async (index) => {
|
|
537
|
+
await nextTick();
|
|
538
|
+
scrollTabIntoView(index);
|
|
539
|
+
});
|
|
540
|
+
function entryAt(index) {
|
|
541
|
+
return entries.value[index];
|
|
542
|
+
}
|
|
543
|
+
function positionLabelAt(index) {
|
|
544
|
+
return t("gr.carousel.slidePosition", "{index} of {total}", {
|
|
545
|
+
index: index + 1,
|
|
546
|
+
total: total.value
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
function indicatorName(index) {
|
|
550
|
+
return entryAt(index)?.label() ?? positionLabelAt(index);
|
|
551
|
+
}
|
|
552
|
+
function tabDomId(index) {
|
|
553
|
+
return `${entryAt(index)?.id ?? index}-tab`;
|
|
554
|
+
}
|
|
555
|
+
const trackStyle = computed(() => {
|
|
556
|
+
const style = {
|
|
557
|
+
"--gr-carousel-index": String(currentIndex.value),
|
|
558
|
+
"--gr-carousel-drag": `${swipeGesture.offset.value}px`
|
|
559
|
+
};
|
|
560
|
+
if (swipeGesture.isDragging.value) style.transition = "none";
|
|
561
|
+
return style;
|
|
562
|
+
});
|
|
563
|
+
provide(GR_CAROUSEL_CONTEXT, {
|
|
564
|
+
register,
|
|
565
|
+
indexOf: (id) => entries.value.findIndex((entry) => entry.id === id),
|
|
566
|
+
isCurrent: (id) => entries.value[currentIndex.value]?.id === id,
|
|
567
|
+
total,
|
|
568
|
+
slideRole: computed(() => showIndicators.value ? "tabpanel" : "group"),
|
|
569
|
+
slideRoledescription: computed(() => t("gr.carousel.slideRoledescription", "slide")),
|
|
570
|
+
tabIdFor: (id) => {
|
|
571
|
+
if (!showIndicators.value) return void 0;
|
|
572
|
+
const index = entries.value.findIndex((entry) => entry.id === id);
|
|
573
|
+
return index >= 0 ? tabDomId(index) : void 0;
|
|
574
|
+
},
|
|
575
|
+
positionLabel: (id) => {
|
|
576
|
+
const index = entries.value.findIndex((entry) => entry.id === id);
|
|
577
|
+
return positionLabelAt(index >= 0 ? index : 0);
|
|
578
|
+
}
|
|
579
|
+
});
|
|
580
|
+
if (process.env.NODE_ENV !== "production") watchEffect(() => {
|
|
581
|
+
if (!props.ariaLabel && !props.ariaLabelledby) console.warn("[granularity] GrCarousel: у карусели нет доступного имени — передайте `ariaLabel` или `ariaLabelledby`. `aria-roledescription` именем не является.");
|
|
582
|
+
if (!props.arrows && resolvedIndicators.value === "none" && total.value > 1) console.warn("[granularity] GrCarousel: `arrows: false` вместе с `indicators: \"none\"` не оставляет ни одного способа сменить кадр с клавиатуры.");
|
|
583
|
+
if (isThumbnails.value && total.value > 0 && entries.value.every((entry) => !entry.hasThumbnail() && !entry.thumbnailSrc())) console.warn("[granularity] GrCarousel: `indicators=\"thumbnails\"`, но ни один слайд не дал миниатюру — задайте проп `thumbnail` или слот `#thumbnail` у `GrCarouselSlide`.");
|
|
584
|
+
});
|
|
585
|
+
__expose({
|
|
586
|
+
/** Текущий кадр — единственный способ прочитать позицию без `v-model`. */
|
|
587
|
+
index: computed(() => currentIndex.value),
|
|
588
|
+
/** Число кадров: состав живёт в слоте, снаружи его посчитать нечем. */
|
|
589
|
+
count: total,
|
|
590
|
+
/** Идёт ли показ. Пауза под курсором его не выключает. */
|
|
591
|
+
playing: computed(() => playing.value),
|
|
592
|
+
next: () => step(1),
|
|
593
|
+
prev: () => step(-1),
|
|
594
|
+
goTo,
|
|
595
|
+
play: () => {
|
|
596
|
+
if (!playing.value) toggleAutoplay();
|
|
597
|
+
},
|
|
598
|
+
pause: stopAutoplay
|
|
599
|
+
});
|
|
600
|
+
return (_ctx, _cache) => {
|
|
601
|
+
return openBlock(), createElementBlock("div", {
|
|
602
|
+
ref_key: "rootEl",
|
|
603
|
+
ref: rootEl,
|
|
604
|
+
"data-gr-carousel": "",
|
|
605
|
+
class: normalizeClass(unref(carouselRootBase)),
|
|
606
|
+
role: __props.landmark ? "region" : "group",
|
|
607
|
+
"aria-roledescription": unref(t)("gr.carousel.roledescription", "carousel"),
|
|
608
|
+
"aria-label": __props.ariaLabelledby ? void 0 : __props.ariaLabel,
|
|
609
|
+
"aria-labelledby": __props.ariaLabelledby,
|
|
610
|
+
onMouseenter: _cache[3] || (_cache[3] = ($event) => hovered.value = true),
|
|
611
|
+
onMouseleave: _cache[4] || (_cache[4] = ($event) => hovered.value = false),
|
|
612
|
+
onFocusin: _cache[5] || (_cache[5] = (...args) => unref(onFocusIn) && unref(onFocusIn)(...args)),
|
|
613
|
+
onFocusout: _cache[6] || (_cache[6] = (...args) => unref(onFocusOut) && unref(onFocusOut)(...args))
|
|
614
|
+
}, [createElementVNode("div", {
|
|
615
|
+
ref_key: "viewportEl",
|
|
616
|
+
ref: viewportEl,
|
|
617
|
+
"data-gr-carousel-viewport": "",
|
|
618
|
+
class: normalizeClass([unref(carouselViewportBase), props.swipe ? unref(carouselViewportSwipeClass) : ""]),
|
|
619
|
+
"aria-live": liveMode.value,
|
|
620
|
+
"aria-atomic": "true",
|
|
621
|
+
onPointerdown: _cache[2] || (_cache[2] = (...args) => unref(swipeGesture).start && unref(swipeGesture).start(...args))
|
|
622
|
+
}, [
|
|
623
|
+
createElementVNode("div", {
|
|
624
|
+
"data-gr-carousel-track": "",
|
|
625
|
+
class: normalizeClass(unref(carouselTrackBase)),
|
|
626
|
+
style: normalizeStyle(trackStyle.value)
|
|
627
|
+
}, [renderSlot(_ctx.$slots, "default")], 6),
|
|
628
|
+
props.autoplay && total.value > 1 ? (openBlock(), createElementBlock("button", {
|
|
629
|
+
key: 0,
|
|
630
|
+
type: "button",
|
|
631
|
+
"data-gr-carousel-toggle": "",
|
|
632
|
+
class: normalizeClass(unref(carouselToggleClass)),
|
|
633
|
+
"aria-label": playing.value ? __props.pauseLabel ?? unref(t)("gr.carousel.pause", "Stop automatic slide show") : __props.playLabel ?? unref(t)("gr.carousel.play", "Start automatic slide show"),
|
|
634
|
+
onClick: toggleAutoplay
|
|
635
|
+
}, [(openBlock(), createBlock(resolveDynamicComponent(playing.value ? unref(pause_default) : unref(play_default)), {
|
|
636
|
+
class: normalizeClass(unref(carouselIconClass)),
|
|
637
|
+
"aria-hidden": "true"
|
|
638
|
+
}, null, 8, ["class"]))], 10, _hoisted_3)) : createCommentVNode("", true),
|
|
639
|
+
showArrows.value ? (openBlock(), createElementBlock("button", {
|
|
640
|
+
key: 1,
|
|
641
|
+
type: "button",
|
|
642
|
+
"data-gr-carousel-prev": "",
|
|
643
|
+
class: normalizeClass(unref(grCarouselControlClass)("prev", atStart.value)),
|
|
644
|
+
"aria-disabled": atStart.value ? "true" : void 0,
|
|
645
|
+
"aria-label": __props.prevLabel ?? unref(t)("gr.carousel.previous", "Previous slide"),
|
|
646
|
+
onClick: _cache[0] || (_cache[0] = ($event) => atStart.value ? void 0 : step(-1))
|
|
647
|
+
}, [renderSlot(_ctx.$slots, "prev", { disabled: atStart.value }, () => [createVNode(unref(chevron_left_default), {
|
|
648
|
+
class: normalizeClass(unref(carouselIconClass)),
|
|
649
|
+
"aria-hidden": "true"
|
|
650
|
+
}, null, 8, ["class"])])], 10, _hoisted_4)) : createCommentVNode("", true),
|
|
651
|
+
showArrows.value ? (openBlock(), createElementBlock("button", {
|
|
652
|
+
key: 2,
|
|
653
|
+
type: "button",
|
|
654
|
+
"data-gr-carousel-next": "",
|
|
655
|
+
class: normalizeClass(unref(grCarouselControlClass)("next", atEnd.value)),
|
|
656
|
+
"aria-disabled": atEnd.value ? "true" : void 0,
|
|
657
|
+
"aria-label": __props.nextLabel ?? unref(t)("gr.carousel.next", "Next slide"),
|
|
658
|
+
onClick: _cache[1] || (_cache[1] = ($event) => atEnd.value ? void 0 : step(1))
|
|
659
|
+
}, [renderSlot(_ctx.$slots, "next", { disabled: atEnd.value }, () => [createVNode(unref(chevron_right_default), {
|
|
660
|
+
class: normalizeClass(unref(carouselIconClass)),
|
|
661
|
+
"aria-hidden": "true"
|
|
662
|
+
}, null, 8, ["class"])])], 10, _hoisted_5)) : createCommentVNode("", true)
|
|
663
|
+
], 42, _hoisted_2), showIndicators.value ? (openBlock(), createElementBlock("div", {
|
|
664
|
+
key: 0,
|
|
665
|
+
ref_key: "indicatorsEl",
|
|
666
|
+
ref: indicatorsEl,
|
|
667
|
+
role: "tablist",
|
|
668
|
+
"data-gr-carousel-indicators": "",
|
|
669
|
+
"data-variant": unref(resolvedIndicators),
|
|
670
|
+
"data-overflow": stripOverflow.value,
|
|
671
|
+
class: normalizeClass(unref(grCarouselIndicatorsClass)(unref(resolvedIndicators))),
|
|
672
|
+
"aria-label": __props.indicatorsLabel ?? unref(t)("gr.carousel.indicators", "Slides"),
|
|
673
|
+
onKeydown: onIndicatorKeydown,
|
|
674
|
+
onScrollPassive: scheduleMeasure
|
|
675
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(entries.value, (entry, index) => {
|
|
676
|
+
return openBlock(), createElementBlock("button", {
|
|
677
|
+
id: tabDomId(index),
|
|
678
|
+
key: entry.id,
|
|
679
|
+
ref_for: true,
|
|
680
|
+
ref: (el) => setTabRef(el, index),
|
|
681
|
+
type: "button",
|
|
682
|
+
role: "tab",
|
|
683
|
+
"data-gr-carousel-indicator": "",
|
|
684
|
+
"aria-selected": index === currentIndex.value ? "true" : "false",
|
|
685
|
+
"aria-controls": entry.id,
|
|
686
|
+
"aria-label": indicatorName(index),
|
|
687
|
+
tabindex: unref(roving).tabindexFor(index),
|
|
688
|
+
class: normalizeClass(unref(grCarouselIndicatorClass)(unref(resolvedIndicators), index === currentIndex.value, unref(resolvedTone))),
|
|
689
|
+
onClick: ($event) => onIndicatorClick(index)
|
|
690
|
+
}, [isThumbnails.value && entry.hasThumbnail() ? (openBlock(), createBlock(resolveDynamicComponent(entry.thumbnail), { key: 0 })) : isThumbnails.value && entry.thumbnailSrc() ? (openBlock(), createElementBlock("img", {
|
|
691
|
+
key: 1,
|
|
692
|
+
src: entry.thumbnailSrc(),
|
|
693
|
+
alt: "",
|
|
694
|
+
loading: "lazy",
|
|
695
|
+
decoding: "async",
|
|
696
|
+
class: normalizeClass(unref(carouselThumbImageClass))
|
|
697
|
+
}, null, 10, _hoisted_8)) : isThumbnails.value ? (openBlock(), createElementBlock("span", {
|
|
698
|
+
key: 2,
|
|
699
|
+
class: normalizeClass(unref(carouselThumbFallbackClass))
|
|
700
|
+
}, toDisplayString(index + 1), 3)) : createCommentVNode("", true)], 10, _hoisted_7);
|
|
701
|
+
}), 128))], 42, _hoisted_6)) : createCommentVNode("", true)], 42, _hoisted_1$1);
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
//#endregion
|
|
706
|
+
//#region src/components/GrCarousel/GrCarouselSlide.vue?vue&type=script&setup=true&lang.ts
|
|
707
|
+
var _hoisted_1 = [
|
|
708
|
+
"id",
|
|
709
|
+
"role",
|
|
710
|
+
"aria-roledescription",
|
|
711
|
+
"aria-labelledby",
|
|
712
|
+
"aria-label",
|
|
713
|
+
"inert"
|
|
714
|
+
];
|
|
715
|
+
//#endregion
|
|
716
|
+
//#region src/components/GrCarousel/GrCarouselSlide.vue
|
|
717
|
+
var GrCarouselSlide_default = /* @__PURE__ */ defineComponent({
|
|
718
|
+
__name: "GrCarouselSlide",
|
|
719
|
+
props: {
|
|
720
|
+
label: { default: void 0 },
|
|
721
|
+
thumbnail: { default: void 0 }
|
|
722
|
+
},
|
|
723
|
+
setup(__props) {
|
|
724
|
+
const props = __props;
|
|
725
|
+
const carousel = inject(GR_CAROUSEL_CONTEXT);
|
|
726
|
+
if (!carousel) throw new Error("GrCarouselSlide must be used inside GrCarousel");
|
|
727
|
+
const slots = useSlots();
|
|
728
|
+
const slideId = useId();
|
|
729
|
+
const rootEl = ref(null);
|
|
730
|
+
function renderThumbnail() {
|
|
731
|
+
return slots.thumbnail?.();
|
|
732
|
+
}
|
|
733
|
+
onBeforeUnmount(carousel.register({
|
|
734
|
+
id: slideId,
|
|
735
|
+
label: () => props.label,
|
|
736
|
+
thumbnailSrc: () => props.thumbnail,
|
|
737
|
+
hasThumbnail: () => Boolean(slots.thumbnail),
|
|
738
|
+
thumbnail: renderThumbnail,
|
|
739
|
+
el: () => rootEl.value
|
|
740
|
+
}));
|
|
741
|
+
const isCurrent = computed(() => carousel.isCurrent(slideId));
|
|
742
|
+
const labelledBy = computed(() => carousel.tabIdFor(slideId));
|
|
743
|
+
const accessibleName = computed(() => props.label ?? carousel.positionLabel(slideId));
|
|
744
|
+
return (_ctx, _cache) => {
|
|
745
|
+
return openBlock(), createElementBlock("div", {
|
|
746
|
+
id: unref(slideId),
|
|
747
|
+
ref_key: "rootEl",
|
|
748
|
+
ref: rootEl,
|
|
749
|
+
"data-gr-carousel-slide": "",
|
|
750
|
+
class: normalizeClass(unref(carouselSlideBase)),
|
|
751
|
+
role: unref(carousel).slideRole.value,
|
|
752
|
+
"aria-roledescription": unref(carousel).slideRoledescription.value,
|
|
753
|
+
"aria-labelledby": labelledBy.value,
|
|
754
|
+
"aria-label": labelledBy.value ? void 0 : accessibleName.value,
|
|
755
|
+
inert: isCurrent.value ? void 0 : true
|
|
756
|
+
}, [renderSlot(_ctx.$slots, "default")], 10, _hoisted_1);
|
|
757
|
+
};
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
//#endregion
|
|
761
|
+
export { GrCarousel_default as n, GrCarouselSlide_default as t };
|