@feugene/granularity 0.42.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/chunks/config-BXRmsn3t.js +172 -0
- package/dist/chunks/config-BXRmsn3t.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-C_QkUTRL.js} +32 -1
- package/dist/chunks/en-C_QkUTRL.js.map +1 -0
- package/dist/chunks/{es-DCkqHTlU.js → es-B4t_3Wnh.js} +3 -3
- package/dist/chunks/{es-DCkqHTlU.js.map → es-B4t_3Wnh.js.map} +1 -1
- package/dist/chunks/{es-QpzdIEut.js → es-DemSEhZE.js} +32 -1
- package/dist/chunks/es-DemSEhZE.js.map +1 -0
- package/dist/chunks/{generated-B95IBXs6.js → generated-VcxJTo_q.js} +210 -0
- package/dist/chunks/generated-VcxJTo_q.js.map +1 -0
- package/dist/chunks/{granular-provider-BG_2XqkS.js → granular-provider-BohXQcNt.js} +7 -1
- package/dist/chunks/granular-provider-BohXQcNt.js.map +1 -0
- package/dist/chunks/{ru-D_1Ya489.js → ru-CLUErjhY.js} +32 -1
- package/dist/chunks/ru-CLUErjhY.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/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/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/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 +23 -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/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/granular-provider/shared.d.ts +3 -0
- package/dist/types/i18n/messages/en.d.ts +58 -0
- package/dist/types/i18n/messages/es.d.ts +58 -0
- package/dist/types/i18n/messages/ru.d.ts +58 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/web-types.json +718 -288
- package/package.json +17 -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,1368 @@
|
|
|
1
|
+
import { a as useGrComponentSize, i as useGrComponentProp } from "../../../chunks/context-CPMRfwVr.js";
|
|
2
|
+
import { n as useGranularityTranslations } from "../../../chunks/granularityI18n-BC5zsj_d.js";
|
|
3
|
+
import { t as usePortalTarget } from "../../../chunks/usePortalTarget-BhrRgdqW.js";
|
|
4
|
+
import { useGrFormControl } from "../../../composables/useGrFormControl.js";
|
|
5
|
+
import { t as useFocusWithin } from "../../../chunks/useFocusWithin-BwHE0rzm.js";
|
|
6
|
+
import { useRovingFocus } from "../../../composables/useRovingFocus.js";
|
|
7
|
+
import { i as normalizeOptionQuery, r as matchesQueryParts } from "../../../chunks/optionFilter-DYw9XPPx.js";
|
|
8
|
+
import { t as check_default } from "../../../chunks/check-BZI24Qs4.js";
|
|
9
|
+
import { t as GrButton_default } from "../../GrButton/chunks/GrButton-CS4TqMtG.js";
|
|
10
|
+
import { t as GrButtonGroup_default } from "../../GrButtonGroup/chunks/GrButtonGroup-CKyiAV8K.js";
|
|
11
|
+
import { useAnnouncer } from "../../../composables/useAnnouncer.js";
|
|
12
|
+
import { t as resolveScrollOverflow } from "../../../chunks/scrollOverflow-B_kEdcol.js";
|
|
13
|
+
import { useDragGesture } from "../../../composables/useDragGesture.js";
|
|
14
|
+
import { t as chevron_left_default } from "../../../chunks/chevron-left-D8AUBAM7.js";
|
|
15
|
+
import { t as chevron_right_default } from "../../../chunks/chevron-right-C6Z01h0X.js";
|
|
16
|
+
import { t as GrCheckbox_default } from "../../GrCheckbox/chunks/GrCheckbox-Dp201frb.js";
|
|
17
|
+
import { t as GrInput_default } from "../../GrInput/chunks/GrInput-TFeBm3gQ.js";
|
|
18
|
+
import { n as hitTest, t as autoScrollDelta } from "../../../chunks/dragSortGeometry-CY-llfrQ.js";
|
|
19
|
+
import { C as transferTitleClass, S as transferStatusClass, _ as transferLabelClass, a as grTransferHeaderClass, b as transferReorderIconClass, c as grTransferPanelClass, d as transferActionIconClass, f as transferActionInertClass, g as transferGhostLabelClass, h as transferGhostCountClass, i as grTransferEmptyClass, l as grTransferReorderClass, m as transferGhostClass, o as grTransferMarkClass, p as transferCounterClass, r as grTransferActionsClass, s as grTransferOptionClass, u as grTransferSearchClass, v as transferListBase, x as transferRootBase, y as transferMarkIconClass } from "../../../chunks/config-DmED6aJV.js";
|
|
20
|
+
import { Fragment, Teleport, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, markRaw, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, onScopeDispose, openBlock, ref, renderList, renderSlot, shallowRef, toDisplayString, unref, useId, useSlots, watch, withCtx } from "vue";
|
|
21
|
+
import '../styles.css';//#region src/components/GrTransfer/transferModel.ts
|
|
22
|
+
/**
|
|
23
|
+
* Ключи без дублей, первый выигрывает.
|
|
24
|
+
*
|
|
25
|
+
* Нормализация идёт **на чтение** и наружу сама по себе не эмитится: эмит из
|
|
26
|
+
* рендера — источник петель обновления. Нормализованный вид уезжает потребителю
|
|
27
|
+
* при первом же коммите.
|
|
28
|
+
*/
|
|
29
|
+
function normalizeKeys(keys) {
|
|
30
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31
|
+
const next = [];
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
if (seen.has(key)) continue;
|
|
34
|
+
seen.add(key);
|
|
35
|
+
next.push(key);
|
|
36
|
+
}
|
|
37
|
+
return next;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Раскладка каталога по панелям.
|
|
41
|
+
*
|
|
42
|
+
* `target` строится обходом **модели**, а не `items`: порядок правой панели
|
|
43
|
+
* задаёт пользователь, и он же является значением. `source` — обходом `items`,
|
|
44
|
+
* потому что порядок каталога принадлежит потребителю.
|
|
45
|
+
*/
|
|
46
|
+
function splitByModel(items, keyOf, model) {
|
|
47
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
48
|
+
const duplicated = [];
|
|
49
|
+
for (const item of items) {
|
|
50
|
+
const key = keyOf(item);
|
|
51
|
+
if (byKey.has(key)) {
|
|
52
|
+
duplicated.push(key);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
byKey.set(key, item);
|
|
56
|
+
}
|
|
57
|
+
const wanted = normalizeKeys(model);
|
|
58
|
+
const inTarget = new Set(wanted);
|
|
59
|
+
const target = [];
|
|
60
|
+
const unresolved = [];
|
|
61
|
+
for (const key of wanted) {
|
|
62
|
+
const item = byKey.get(key);
|
|
63
|
+
if (item === void 0) {
|
|
64
|
+
unresolved.push(key);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
target.push(item);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
source: items.filter((item) => !inTarget.has(keyOf(item)) && byKey.get(keyOf(item)) === item),
|
|
71
|
+
target,
|
|
72
|
+
unresolved,
|
|
73
|
+
duplicated
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Изъятие блока. Вход не мутируется. */
|
|
77
|
+
function removeKeys(order, moving) {
|
|
78
|
+
const drop = new Set(moving);
|
|
79
|
+
return order.filter((key) => !drop.has(key));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Вставка блока перед ключом `before`; `null` или ненайденный ключ — в конец.
|
|
83
|
+
*
|
|
84
|
+
* Цель выражена ключом, а не индексом, намеренно: индекс пришлось бы
|
|
85
|
+
* пересчитывать после изъятия блока, и это ровно тот класс off-by-one, из-за
|
|
86
|
+
* которого `insertionIndex` в `dragSortGeometry` носит поправку «элемент
|
|
87
|
+
* временно вынут». Здесь поправка не нужна вовсе.
|
|
88
|
+
*/
|
|
89
|
+
function insertKeys(order, moving, before) {
|
|
90
|
+
const block = normalizeKeys(moving);
|
|
91
|
+
if (block.length === 0) return order.slice();
|
|
92
|
+
const rest = removeKeys(order, block);
|
|
93
|
+
const at = before === null ? -1 : rest.indexOf(before);
|
|
94
|
+
if (at < 0) return [...rest, ...block];
|
|
95
|
+
return [
|
|
96
|
+
...rest.slice(0, at),
|
|
97
|
+
...block,
|
|
98
|
+
...rest.slice(at)
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Куда встанет блок при сдвиге на шаг; `undefined` — двигаться некуда.
|
|
103
|
+
*
|
|
104
|
+
* Рассыпанное выделение при сдвиге схлопывается в непрерывный блок: это
|
|
105
|
+
* предсказуемо и объяснимо, в отличие от попытки двигать каждый кусок
|
|
106
|
+
* независимо.
|
|
107
|
+
*/
|
|
108
|
+
function stepTarget(order, moving, delta) {
|
|
109
|
+
const block = normalizeKeys(moving).filter((key) => order.includes(key));
|
|
110
|
+
if (block.length === 0) return void 0;
|
|
111
|
+
const rest = removeKeys(order, block);
|
|
112
|
+
const first = order.findIndex((key) => block.includes(key));
|
|
113
|
+
const before = order.slice(0, first).filter((key) => !block.includes(key)).length;
|
|
114
|
+
const next = Math.min(rest.length, Math.max(0, before + delta));
|
|
115
|
+
if (next === before) return void 0;
|
|
116
|
+
return rest[next] ?? null;
|
|
117
|
+
}
|
|
118
|
+
/** Крайняя позиция блока: `-1` — в начало, `1` — в конец. */
|
|
119
|
+
function edgeTarget(order, moving, edge) {
|
|
120
|
+
const block = normalizeKeys(moving).filter((key) => order.includes(key));
|
|
121
|
+
if (block.length === 0) return void 0;
|
|
122
|
+
const rest = removeKeys(order, block);
|
|
123
|
+
if (edge === "end") return rest.length === 0 ? void 0 : null;
|
|
124
|
+
const first = rest[0];
|
|
125
|
+
return first === void 0 ? void 0 : first;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Какая строка останется под фокусом после изъятия.
|
|
129
|
+
*
|
|
130
|
+
* Без этого фокус после переноса падает на тело документа, и клавиатурный
|
|
131
|
+
* сценарий обрывается на первом же шаге — тот же довод, что у `drop()` в
|
|
132
|
+
* `GrSortableList`.
|
|
133
|
+
*/
|
|
134
|
+
function keyAfterRemoval(visible, removed, focused) {
|
|
135
|
+
const drop = new Set(removed);
|
|
136
|
+
const rest = visible.filter((key) => !drop.has(key));
|
|
137
|
+
if (rest.length === 0) return void 0;
|
|
138
|
+
if (focused !== void 0 && !drop.has(focused)) return focused;
|
|
139
|
+
const from = focused === void 0 ? 0 : visible.indexOf(focused);
|
|
140
|
+
for (let index = from; index < visible.length; index += 1) {
|
|
141
|
+
const key = visible[index];
|
|
142
|
+
if (!drop.has(key)) return key;
|
|
143
|
+
}
|
|
144
|
+
for (let index = from - 1; index >= 0; index -= 1) {
|
|
145
|
+
const key = visible[index];
|
|
146
|
+
if (!drop.has(key)) return key;
|
|
147
|
+
}
|
|
148
|
+
return rest[0];
|
|
149
|
+
}
|
|
150
|
+
function contains(rect, point) {
|
|
151
|
+
return point.x >= rect.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Панель под указателем; `null` — указатель в зазоре или за пределами обеих.
|
|
155
|
+
*
|
|
156
|
+
* Считается по прямоугольнику **обёртки панели**, а не по ближайшей строке:
|
|
157
|
+
* `hitTest` при промахе возвращает ближайший элемент, и указатель над пустой
|
|
158
|
+
* правой панелью резолвился бы в строку левой.
|
|
159
|
+
*/
|
|
160
|
+
function sideAtPoint(rects, point) {
|
|
161
|
+
if (rects.source && contains(rects.source, point)) return "source";
|
|
162
|
+
if (rects.target && contains(rects.target, point)) return "target";
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Перед какой строкой встанет блок; `null` — в конец панели.
|
|
167
|
+
*
|
|
168
|
+
* Цель — ключ, а не индекс: индекс пришлось бы пересчитывать после изъятия
|
|
169
|
+
* блока, и именно ради этого `insertionIndex` в `dragSortGeometry` носит
|
|
170
|
+
* поправку «элемент временно вынут». Для вставки из чужой панели поправка
|
|
171
|
+
* вредна — там ничего не вынимается, — поэтому та функция здесь не годится.
|
|
172
|
+
*/
|
|
173
|
+
function dropBefore(keys, spans, position) {
|
|
174
|
+
if (keys.length === 0 || spans.length === 0) return null;
|
|
175
|
+
const hit = hitTest(spans, position);
|
|
176
|
+
if (!hit) return null;
|
|
177
|
+
const index = hit.fraction < .5 ? hit.index : hit.index + 1;
|
|
178
|
+
return index >= keys.length ? null : keys[index] ?? null;
|
|
179
|
+
}
|
|
180
|
+
/** Пройден ли порог начала жеста. */
|
|
181
|
+
function passedThreshold(from, to, threshold = 4) {
|
|
182
|
+
return Math.hypot(to.x - from.x, to.y - from.y) >= threshold;
|
|
183
|
+
}
|
|
184
|
+
//#endregion
|
|
185
|
+
//#region src/components/GrTransfer/useTransferDrag.ts
|
|
186
|
+
function rectOf(el) {
|
|
187
|
+
if (!el) return null;
|
|
188
|
+
const box = el.getBoundingClientRect();
|
|
189
|
+
return {
|
|
190
|
+
left: box.left,
|
|
191
|
+
right: box.right,
|
|
192
|
+
top: box.top,
|
|
193
|
+
bottom: box.bottom
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Перетаскивание между панелями.
|
|
198
|
+
*
|
|
199
|
+
* `useDragSort` здесь не подходит: его `resolveTarget` получает попадание, но не
|
|
200
|
+
* точку указателя, а панель-приёмник определяется прямоугольником под точкой —
|
|
201
|
+
* `hitTest` при промахе отдаёт ближайшую строку, и указатель над пустой правой
|
|
202
|
+
* панелью резолвился бы в строку левой.
|
|
203
|
+
*/
|
|
204
|
+
function useTransferDrag(options) {
|
|
205
|
+
const session = shallowRef(null);
|
|
206
|
+
const spot = shallowRef(null);
|
|
207
|
+
const moved = ref(false);
|
|
208
|
+
const pointer = shallowRef({
|
|
209
|
+
x: 0,
|
|
210
|
+
y: 0
|
|
211
|
+
});
|
|
212
|
+
let origin = {
|
|
213
|
+
x: 0,
|
|
214
|
+
y: 0
|
|
215
|
+
};
|
|
216
|
+
let pending = null;
|
|
217
|
+
/** Последняя непустая сторона: в зазоре между панелями индикатор не мигает. */
|
|
218
|
+
let lastSide = null;
|
|
219
|
+
let frame = null;
|
|
220
|
+
let cursor = {
|
|
221
|
+
x: 0,
|
|
222
|
+
y: 0
|
|
223
|
+
};
|
|
224
|
+
function stopAutoScroll() {
|
|
225
|
+
if (frame !== null && typeof cancelAnimationFrame === "function") cancelAnimationFrame(frame);
|
|
226
|
+
frame = null;
|
|
227
|
+
}
|
|
228
|
+
function tickAutoScroll() {
|
|
229
|
+
frame = null;
|
|
230
|
+
const side = lastSide;
|
|
231
|
+
const scroller = side ? options.scrollerEl(side) : null;
|
|
232
|
+
if (scroller) {
|
|
233
|
+
const box = scroller.getBoundingClientRect();
|
|
234
|
+
const delta = autoScrollDelta({
|
|
235
|
+
start: box.top,
|
|
236
|
+
end: box.bottom
|
|
237
|
+
}, cursor.y);
|
|
238
|
+
if (delta !== 0) scroller.scrollTop += delta;
|
|
239
|
+
}
|
|
240
|
+
scheduleAutoScroll();
|
|
241
|
+
}
|
|
242
|
+
function scheduleAutoScroll() {
|
|
243
|
+
if (session.value === null || typeof requestAnimationFrame !== "function") return;
|
|
244
|
+
frame = requestAnimationFrame(tickAutoScroll);
|
|
245
|
+
}
|
|
246
|
+
function reset() {
|
|
247
|
+
session.value = null;
|
|
248
|
+
spot.value = null;
|
|
249
|
+
pending = null;
|
|
250
|
+
lastSide = null;
|
|
251
|
+
stopAutoScroll();
|
|
252
|
+
detachEscape();
|
|
253
|
+
unlockSelection();
|
|
254
|
+
}
|
|
255
|
+
function onEscape(event) {
|
|
256
|
+
if (event.key !== "Escape" || session.value === null) return;
|
|
257
|
+
event.preventDefault();
|
|
258
|
+
event.stopPropagation();
|
|
259
|
+
abort();
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Выделение текста на время жеста.
|
|
263
|
+
*
|
|
264
|
+
* `select-none` на самих строках недостаточно: выделение **начинается** внутри
|
|
265
|
+
* компонента, а тянется по всему документу — и пользователь, ведя строку через
|
|
266
|
+
* страницу, подсвечивает попутно чужие заголовки и абзацы. Гасим на теле
|
|
267
|
+
* документа и возвращаем прежнее значение, а не пустую строку: потребитель мог
|
|
268
|
+
* задать своё.
|
|
269
|
+
*/
|
|
270
|
+
let selectionLock = null;
|
|
271
|
+
function lockSelection() {
|
|
272
|
+
if (typeof document === "undefined" || selectionLock !== null) return;
|
|
273
|
+
const style = document.body.style;
|
|
274
|
+
selectionLock = {
|
|
275
|
+
userSelect: style.userSelect,
|
|
276
|
+
webkitUserSelect: style.webkitUserSelect ?? ""
|
|
277
|
+
};
|
|
278
|
+
style.userSelect = "none";
|
|
279
|
+
style.webkitUserSelect = "none";
|
|
280
|
+
window.getSelection?.()?.removeAllRanges();
|
|
281
|
+
}
|
|
282
|
+
function unlockSelection() {
|
|
283
|
+
if (typeof document === "undefined" || selectionLock === null) return;
|
|
284
|
+
const style = document.body.style;
|
|
285
|
+
style.userSelect = selectionLock.userSelect;
|
|
286
|
+
style.webkitUserSelect = selectionLock.webkitUserSelect;
|
|
287
|
+
selectionLock = null;
|
|
288
|
+
}
|
|
289
|
+
let escapeAttached = false;
|
|
290
|
+
function attachEscape() {
|
|
291
|
+
if (escapeAttached || typeof window === "undefined") return;
|
|
292
|
+
window.addEventListener("keydown", onEscape, true);
|
|
293
|
+
escapeAttached = true;
|
|
294
|
+
}
|
|
295
|
+
function detachEscape() {
|
|
296
|
+
if (!escapeAttached || typeof window === "undefined") return;
|
|
297
|
+
window.removeEventListener("keydown", onEscape, true);
|
|
298
|
+
escapeAttached = false;
|
|
299
|
+
}
|
|
300
|
+
function resolveSpot(point) {
|
|
301
|
+
const active = session.value;
|
|
302
|
+
if (!active) return;
|
|
303
|
+
const side = sideAtPoint({
|
|
304
|
+
source: rectOf(options.panelEl("source")),
|
|
305
|
+
target: rectOf(options.panelEl("target"))
|
|
306
|
+
}, point) ?? lastSide;
|
|
307
|
+
if (side === null) {
|
|
308
|
+
spot.value = null;
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
lastSide = side;
|
|
312
|
+
if (side === active.side && !options.allowSameSide(side)) {
|
|
313
|
+
spot.value = null;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const keys = options.visibleKeys(side);
|
|
317
|
+
const spans = keys.map((key) => {
|
|
318
|
+
const box = options.rowEl(side, key)?.getBoundingClientRect();
|
|
319
|
+
return {
|
|
320
|
+
start: box?.top ?? 0,
|
|
321
|
+
end: box?.bottom ?? 0
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
spot.value = {
|
|
325
|
+
side,
|
|
326
|
+
before: dropBefore(keys, spans, point.y)
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
const drag = useDragGesture({
|
|
330
|
+
disabled: options.disabled,
|
|
331
|
+
onMove: (event) => {
|
|
332
|
+
cursor = {
|
|
333
|
+
x: event.clientX,
|
|
334
|
+
y: event.clientY
|
|
335
|
+
};
|
|
336
|
+
pointer.value = cursor;
|
|
337
|
+
if (session.value === null) {
|
|
338
|
+
if (pending === null || !passedThreshold(origin, cursor)) return;
|
|
339
|
+
session.value = pending;
|
|
340
|
+
moved.value = true;
|
|
341
|
+
lockSelection();
|
|
342
|
+
attachEscape();
|
|
343
|
+
scheduleAutoScroll();
|
|
344
|
+
}
|
|
345
|
+
event.preventDefault();
|
|
346
|
+
resolveSpot(cursor);
|
|
347
|
+
},
|
|
348
|
+
onEnd: () => {
|
|
349
|
+
const active = session.value;
|
|
350
|
+
const target = spot.value;
|
|
351
|
+
if (active && target) options.onDrop(active, target);
|
|
352
|
+
reset();
|
|
353
|
+
},
|
|
354
|
+
onCancel: () => {
|
|
355
|
+
if (session.value !== null) options.onCancel?.();
|
|
356
|
+
reset();
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
/** Объявлен после `drag`: слушатель `Esc` вешается раньше и зовёт его косвенно. */
|
|
360
|
+
function abort() {
|
|
361
|
+
drag.stop(false);
|
|
362
|
+
}
|
|
363
|
+
onScopeDispose(reset);
|
|
364
|
+
return {
|
|
365
|
+
session,
|
|
366
|
+
spot,
|
|
367
|
+
pointer,
|
|
368
|
+
consumeClick: () => {
|
|
369
|
+
const was = moved.value;
|
|
370
|
+
moved.value = false;
|
|
371
|
+
return was;
|
|
372
|
+
},
|
|
373
|
+
startFrom: (side, key) => (event) => {
|
|
374
|
+
if (event.pointerType === "touch" || options.disabled()) return;
|
|
375
|
+
const block = options.blockFor(side, key);
|
|
376
|
+
if (block.length === 0) return;
|
|
377
|
+
origin = {
|
|
378
|
+
x: event.clientX,
|
|
379
|
+
y: event.clientY
|
|
380
|
+
};
|
|
381
|
+
pending = {
|
|
382
|
+
side,
|
|
383
|
+
keys: block
|
|
384
|
+
};
|
|
385
|
+
moved.value = false;
|
|
386
|
+
drag.start(event);
|
|
387
|
+
},
|
|
388
|
+
cancel: () => drag.stop(false)
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
//#endregion
|
|
392
|
+
//#region src/components/GrTransfer/transferSelection.ts
|
|
393
|
+
var emptySelection = {
|
|
394
|
+
keys: /* @__PURE__ */ new Set(),
|
|
395
|
+
anchor: void 0
|
|
396
|
+
};
|
|
397
|
+
/** `Shift` сильнее `Ctrl`; их сочетание объединяет диапазон с выделением. */
|
|
398
|
+
function selectIntentFrom(event) {
|
|
399
|
+
const additive = event.ctrlKey || event.metaKey;
|
|
400
|
+
if (event.shiftKey) return {
|
|
401
|
+
mode: "range",
|
|
402
|
+
additive
|
|
403
|
+
};
|
|
404
|
+
return {
|
|
405
|
+
mode: additive ? "toggle" : "replace",
|
|
406
|
+
additive
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
function rangeBetween(visible, from, to) {
|
|
410
|
+
const start = visible.indexOf(from);
|
|
411
|
+
const end = visible.indexOf(to);
|
|
412
|
+
if (start < 0 || end < 0) return [];
|
|
413
|
+
return visible.slice(Math.min(start, end), Math.max(start, end) + 1);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Новое состояние выделения после жеста по строке `key`.
|
|
417
|
+
*
|
|
418
|
+
* Диапазон считается по **видимому** порядку, а не по каталогу: пользователь
|
|
419
|
+
* растягивает то, что видит, и после фильтра «от сих до сих» обязано значить
|
|
420
|
+
* то же самое.
|
|
421
|
+
*/
|
|
422
|
+
function applySelect(state, key, intent, visible, isSelectable) {
|
|
423
|
+
if (!isSelectable(key)) return state;
|
|
424
|
+
if (intent.mode === "range" && state.anchor !== void 0) {
|
|
425
|
+
const range = rangeBetween(visible, state.anchor, key).filter(isSelectable);
|
|
426
|
+
if (range.length > 0) {
|
|
427
|
+
const keys = intent.additive ? new Set(state.keys) : /* @__PURE__ */ new Set();
|
|
428
|
+
range.forEach((item) => keys.add(item));
|
|
429
|
+
return {
|
|
430
|
+
keys,
|
|
431
|
+
anchor: state.anchor
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
if (intent.mode === "toggle") {
|
|
436
|
+
const keys = new Set(state.keys);
|
|
437
|
+
if (keys.has(key)) keys.delete(key);
|
|
438
|
+
else keys.add(key);
|
|
439
|
+
return {
|
|
440
|
+
keys,
|
|
441
|
+
anchor: key
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
keys: /* @__PURE__ */ new Set([key]),
|
|
446
|
+
anchor: key
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
/** Всё видимое и выбираемое; уже выбрано всё — снять. */
|
|
450
|
+
function toggleAllVisible(state, visible, isSelectable) {
|
|
451
|
+
const selectable = visible.filter(isSelectable);
|
|
452
|
+
if (selectable.length === 0) return {
|
|
453
|
+
keys: new Set(state.keys),
|
|
454
|
+
anchor: void 0
|
|
455
|
+
};
|
|
456
|
+
if (selectable.every((key) => state.keys.has(key))) {
|
|
457
|
+
const keys = new Set(state.keys);
|
|
458
|
+
selectable.forEach((key) => keys.delete(key));
|
|
459
|
+
return {
|
|
460
|
+
keys,
|
|
461
|
+
anchor: void 0
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
const keys = new Set(state.keys);
|
|
465
|
+
selectable.forEach((key) => keys.add(key));
|
|
466
|
+
return {
|
|
467
|
+
keys,
|
|
468
|
+
anchor: void 0
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
/** Ключи, которых больше нет в панели. Якорь исчез — забываем и его. */
|
|
472
|
+
function pruneSelection(state, present) {
|
|
473
|
+
const alive = new Set(present);
|
|
474
|
+
const keys = /* @__PURE__ */ new Set();
|
|
475
|
+
state.keys.forEach((key) => {
|
|
476
|
+
if (alive.has(key)) keys.add(key);
|
|
477
|
+
});
|
|
478
|
+
const anchor = state.anchor !== void 0 && alive.has(state.anchor) ? state.anchor : void 0;
|
|
479
|
+
if (keys.size === state.keys.size && anchor === state.anchor) return state;
|
|
480
|
+
return {
|
|
481
|
+
keys,
|
|
482
|
+
anchor
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
/** Состояние отметки «выбрать всё видимое» в шапке панели. */
|
|
486
|
+
function allVisibleState(state, visible, isSelectable) {
|
|
487
|
+
const selectable = visible.filter(isSelectable);
|
|
488
|
+
if (selectable.length === 0) return "unchecked";
|
|
489
|
+
const picked = selectable.filter((key) => state.keys.has(key)).length;
|
|
490
|
+
if (picked === 0) return "unchecked";
|
|
491
|
+
return picked === selectable.length ? "checked" : "indeterminate";
|
|
492
|
+
}
|
|
493
|
+
//#endregion
|
|
494
|
+
//#region ~icons/lucide/arrow-left
|
|
495
|
+
var _hoisted_1$2 = {
|
|
496
|
+
viewBox: "0 0 24 24",
|
|
497
|
+
width: "1.2em",
|
|
498
|
+
height: "1.2em"
|
|
499
|
+
};
|
|
500
|
+
function render$2(_ctx, _cache) {
|
|
501
|
+
return openBlock(), createElementBlock("svg", _hoisted_1$2, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
|
|
502
|
+
fill: "none",
|
|
503
|
+
stroke: "currentColor",
|
|
504
|
+
"stroke-linecap": "round",
|
|
505
|
+
"stroke-linejoin": "round",
|
|
506
|
+
"stroke-width": "2",
|
|
507
|
+
d: "m12 19l-7-7l7-7m7 7H5"
|
|
508
|
+
}, null, -1)])]);
|
|
509
|
+
}
|
|
510
|
+
var arrow_left_default = markRaw({
|
|
511
|
+
name: "lucide-arrow-left",
|
|
512
|
+
render: render$2
|
|
513
|
+
});
|
|
514
|
+
//#endregion
|
|
515
|
+
//#region ~icons/lucide/arrow-right
|
|
516
|
+
var _hoisted_1$1 = {
|
|
517
|
+
viewBox: "0 0 24 24",
|
|
518
|
+
width: "1.2em",
|
|
519
|
+
height: "1.2em"
|
|
520
|
+
};
|
|
521
|
+
function render$1(_ctx, _cache) {
|
|
522
|
+
return openBlock(), createElementBlock("svg", _hoisted_1$1, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
|
|
523
|
+
fill: "none",
|
|
524
|
+
stroke: "currentColor",
|
|
525
|
+
"stroke-linecap": "round",
|
|
526
|
+
"stroke-linejoin": "round",
|
|
527
|
+
"stroke-width": "2",
|
|
528
|
+
d: "M5 12h14m-7-7l7 7l-7 7"
|
|
529
|
+
}, null, -1)])]);
|
|
530
|
+
}
|
|
531
|
+
var arrow_right_default = markRaw({
|
|
532
|
+
name: "lucide-arrow-right",
|
|
533
|
+
render: render$1
|
|
534
|
+
});
|
|
535
|
+
//#endregion
|
|
536
|
+
//#region src/components/GrTransfer/GrTransfer.vue?vue&type=script&setup=true&lang.ts
|
|
537
|
+
var _hoisted_1 = [
|
|
538
|
+
"data-dragging",
|
|
539
|
+
"aria-label",
|
|
540
|
+
"aria-disabled"
|
|
541
|
+
];
|
|
542
|
+
var _hoisted_2 = ["data-gr-transfer-panel"];
|
|
543
|
+
var _hoisted_3 = ["id"];
|
|
544
|
+
var _hoisted_4 = ["aria-disabled", "aria-label"];
|
|
545
|
+
var _hoisted_5 = ["aria-disabled", "aria-label"];
|
|
546
|
+
var _hoisted_6 = [
|
|
547
|
+
"id",
|
|
548
|
+
"data-gr-transfer-list",
|
|
549
|
+
"data-overflow",
|
|
550
|
+
"aria-label",
|
|
551
|
+
"aria-labelledby",
|
|
552
|
+
"aria-describedby",
|
|
553
|
+
"aria-invalid",
|
|
554
|
+
"aria-required",
|
|
555
|
+
"aria-readonly",
|
|
556
|
+
"aria-disabled",
|
|
557
|
+
"onKeydown"
|
|
558
|
+
];
|
|
559
|
+
var _hoisted_7 = [
|
|
560
|
+
"aria-selected",
|
|
561
|
+
"aria-disabled",
|
|
562
|
+
"tabindex",
|
|
563
|
+
"onPointerdown",
|
|
564
|
+
"onClick",
|
|
565
|
+
"onFocus",
|
|
566
|
+
"onDblclick"
|
|
567
|
+
];
|
|
568
|
+
//#endregion
|
|
569
|
+
//#region src/components/GrTransfer/GrTransfer.vue
|
|
570
|
+
var GrTransfer_default = /* @__PURE__ */ defineComponent({
|
|
571
|
+
__name: "GrTransfer",
|
|
572
|
+
props: {
|
|
573
|
+
items: {},
|
|
574
|
+
modelValue: {},
|
|
575
|
+
itemKey: {
|
|
576
|
+
type: [String, Function],
|
|
577
|
+
default: void 0
|
|
578
|
+
},
|
|
579
|
+
itemLabel: {
|
|
580
|
+
type: [String, Function],
|
|
581
|
+
default: void 0
|
|
582
|
+
},
|
|
583
|
+
itemDisabled: {
|
|
584
|
+
type: [String, Function],
|
|
585
|
+
default: void 0
|
|
586
|
+
},
|
|
587
|
+
sourceTitle: { default: void 0 },
|
|
588
|
+
targetTitle: { default: void 0 },
|
|
589
|
+
searchable: {
|
|
590
|
+
type: Boolean,
|
|
591
|
+
default: true
|
|
592
|
+
},
|
|
593
|
+
filter: {
|
|
594
|
+
type: Function,
|
|
595
|
+
default: void 0
|
|
596
|
+
},
|
|
597
|
+
sortable: {
|
|
598
|
+
type: Boolean,
|
|
599
|
+
default: true
|
|
600
|
+
},
|
|
601
|
+
draggable: {
|
|
602
|
+
type: Boolean,
|
|
603
|
+
default: void 0
|
|
604
|
+
},
|
|
605
|
+
maxHeight: { default: void 0 },
|
|
606
|
+
size: { default: void 0 },
|
|
607
|
+
disabled: {
|
|
608
|
+
type: Boolean,
|
|
609
|
+
default: false
|
|
610
|
+
},
|
|
611
|
+
readonly: {
|
|
612
|
+
type: Boolean,
|
|
613
|
+
default: false
|
|
614
|
+
},
|
|
615
|
+
invalid: {
|
|
616
|
+
type: Boolean,
|
|
617
|
+
default: false
|
|
618
|
+
},
|
|
619
|
+
required: {
|
|
620
|
+
type: Boolean,
|
|
621
|
+
default: false
|
|
622
|
+
},
|
|
623
|
+
ariaLabel: { default: void 0 }
|
|
624
|
+
},
|
|
625
|
+
emits: [
|
|
626
|
+
"update:modelValue",
|
|
627
|
+
"change",
|
|
628
|
+
"transfer",
|
|
629
|
+
"search",
|
|
630
|
+
"focus",
|
|
631
|
+
"blur"
|
|
632
|
+
],
|
|
633
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
634
|
+
const props = __props;
|
|
635
|
+
const emit = __emit;
|
|
636
|
+
const slots = useSlots();
|
|
637
|
+
/**
|
|
638
|
+
* Свой `#header` уносит с собой заголовок панели, а вместе с ним и `id`, на
|
|
639
|
+
* который смотрит `aria-labelledby` списка: панель осталась бы без доступного
|
|
640
|
+
* имени, и axe ловит это как `aria-input-field-name`. Имя поэтому не зависит от
|
|
641
|
+
* разметки потребителя — при своей шапке оно приходит из `aria-label`.
|
|
642
|
+
*/
|
|
643
|
+
const hasHeaderSlot = computed(() => Boolean(slots.header));
|
|
644
|
+
const { t } = useGranularityTranslations();
|
|
645
|
+
const { announce } = useAnnouncer();
|
|
646
|
+
const { target: portalTarget, enabled: teleportEnabled } = usePortalTarget();
|
|
647
|
+
const resolvedSize = useGrComponentSize(() => props.size, { component: "GrTransfer" });
|
|
648
|
+
const resolvedDraggable = useGrComponentProp("GrTransfer", "draggable", () => props.draggable, true);
|
|
649
|
+
const { disabled: isDisabled, readonly: isReadonly, invalid: isInvalid, required: isRequired, locked: isLocked, id: fieldId, labelId: fieldLabelId, describedBy } = useGrFormControl(() => props);
|
|
650
|
+
const rootEl = ref(null);
|
|
651
|
+
const uid = useId();
|
|
652
|
+
const sourceTitleId = `${uid}-source-title`;
|
|
653
|
+
const targetTitleId = `${uid}-target-title`;
|
|
654
|
+
const sourceListId = `${uid}-source-list`;
|
|
655
|
+
const targetListId = `${uid}-target-list`;
|
|
656
|
+
if (process.env.NODE_ENV !== "production") {
|
|
657
|
+
if (!Array.isArray(props.items)) console.warn(`[granularity] GrTransfer: обязательный проп \`items\` должен быть массивом — получено ${typeof props.items}.`);
|
|
658
|
+
if (!Array.isArray(props.modelValue)) console.warn(`[granularity] GrTransfer: обязательный проп \`modelValue\` должен быть массивом — получено ${typeof props.modelValue}.`);
|
|
659
|
+
}
|
|
660
|
+
/**
|
|
661
|
+
* Синтетический ключ для элемента без своего: общий ключ пометил бы выбранными
|
|
662
|
+
* все строки разом. Приём и причина — из `GrDataTable`.
|
|
663
|
+
*/
|
|
664
|
+
const syntheticKeys = /* @__PURE__ */ new WeakMap();
|
|
665
|
+
let syntheticCount = 0;
|
|
666
|
+
let warnedSynthetic = false;
|
|
667
|
+
function keyOf(item) {
|
|
668
|
+
const source = props.itemKey ?? "id";
|
|
669
|
+
const value = typeof source === "function" ? source(item) : item[source];
|
|
670
|
+
if (typeof value === "string" && value !== "") return value;
|
|
671
|
+
if (typeof value === "number") return value;
|
|
672
|
+
const known = syntheticKeys.get(item);
|
|
673
|
+
if (known !== void 0) return known;
|
|
674
|
+
syntheticCount += 1;
|
|
675
|
+
const generated = `${uid}-synthetic-${syntheticCount}`;
|
|
676
|
+
syntheticKeys.set(item, generated);
|
|
677
|
+
if (process.env.NODE_ENV !== "production" && !warnedSynthetic) {
|
|
678
|
+
warnedSynthetic = true;
|
|
679
|
+
console.warn("[granularity] GrTransfer: у элемента нет значения `itemKey` — выдан временный ключ. Он не переживёт перезагрузку данных, и `modelValue` с ним не сойдётся: задайте `itemKey`.");
|
|
680
|
+
}
|
|
681
|
+
return generated;
|
|
682
|
+
}
|
|
683
|
+
function labelOf(item) {
|
|
684
|
+
const source = props.itemLabel ?? "label";
|
|
685
|
+
return typeof source === "function" ? source(item) : String(item[source] ?? "");
|
|
686
|
+
}
|
|
687
|
+
function disabledOf(item) {
|
|
688
|
+
const source = props.itemDisabled ?? "disabled";
|
|
689
|
+
return typeof source === "function" ? source(item) : Boolean(item[source]);
|
|
690
|
+
}
|
|
691
|
+
const split = computed(() => splitByModel(props.items ?? [], keyOf, props.modelValue ?? []));
|
|
692
|
+
if (process.env.NODE_ENV !== "production") watch(split, (value) => {
|
|
693
|
+
if (value.duplicated.length > 0) console.warn(`[granularity] GrTransfer: ключи повторяются в \`items\` (${value.duplicated.join(", ")}) — рисуется первый элемент с таким ключом.`);
|
|
694
|
+
if (value.unresolved.length > 0 && (props.items?.length ?? 0) > 0) console.warn(`[granularity] GrTransfer: ключи из \`modelValue\` не найдены в \`items\` (${value.unresolved.join(", ")}). Они сохранены в значении, но не показаны: каталог мог ещё не приехать.`);
|
|
695
|
+
}, { immediate: true });
|
|
696
|
+
const queries = ref({
|
|
697
|
+
source: "",
|
|
698
|
+
target: ""
|
|
699
|
+
});
|
|
700
|
+
const selections = ref({
|
|
701
|
+
source: emptySelection,
|
|
702
|
+
target: emptySelection
|
|
703
|
+
});
|
|
704
|
+
function itemsOf(side) {
|
|
705
|
+
return side === "source" ? split.value.source : split.value.target;
|
|
706
|
+
}
|
|
707
|
+
function matches(item, query) {
|
|
708
|
+
if (props.filter) return props.filter(item, query);
|
|
709
|
+
return matchesQueryParts([labelOf(item)], normalizeOptionQuery(query));
|
|
710
|
+
}
|
|
711
|
+
function visibleOf(side) {
|
|
712
|
+
const query = queries.value[side];
|
|
713
|
+
if (!props.searchable || normalizeOptionQuery(query) === "") return itemsOf(side);
|
|
714
|
+
return itemsOf(side).filter((item) => matches(item, query));
|
|
715
|
+
}
|
|
716
|
+
const visible = computed(() => ({
|
|
717
|
+
source: visibleOf("source"),
|
|
718
|
+
target: visibleOf("target")
|
|
719
|
+
}));
|
|
720
|
+
const visibleKeys = computed(() => ({
|
|
721
|
+
source: visible.value.source.map(keyOf),
|
|
722
|
+
target: visible.value.target.map(keyOf)
|
|
723
|
+
}));
|
|
724
|
+
function selectableOf(side) {
|
|
725
|
+
return (key) => {
|
|
726
|
+
const item = itemsOf(side).find((candidate) => keyOf(candidate) === key);
|
|
727
|
+
return item !== void 0 && !disabledOf(item);
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
watch(split, () => {
|
|
731
|
+
selections.value = {
|
|
732
|
+
source: pruneSelection(selections.value.source, split.value.source.map(keyOf)),
|
|
733
|
+
target: pruneSelection(selections.value.target, split.value.target.map(keyOf))
|
|
734
|
+
};
|
|
735
|
+
});
|
|
736
|
+
const titles = computed(() => ({
|
|
737
|
+
source: props.sourceTitle ?? t("gr.transfer.sourceTitle", "Available"),
|
|
738
|
+
target: props.targetTitle ?? t("gr.transfer.targetTitle", "Selected")
|
|
739
|
+
}));
|
|
740
|
+
function setSelection(side, next) {
|
|
741
|
+
selections.value = {
|
|
742
|
+
...selections.value,
|
|
743
|
+
[side]: next
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
function toggleAll(side) {
|
|
747
|
+
if (isDisabled.value) return;
|
|
748
|
+
setSelection(side, toggleAllVisible(selections.value[side], visibleKeys.value[side], selectableOf(side)));
|
|
749
|
+
}
|
|
750
|
+
/** Что реально уедет: отмеченное минус запрещённое. Скрытое поиском — уезжает. */
|
|
751
|
+
function movableOf(side) {
|
|
752
|
+
const selectable = selectableOf(side);
|
|
753
|
+
return itemsOf(side).map(keyOf).filter((key) => selections.value[side].keys.has(key) && selectable(key));
|
|
754
|
+
}
|
|
755
|
+
const canToTarget = computed(() => !isLocked.value && movableOf("source").length > 0);
|
|
756
|
+
const canToSource = computed(() => !isLocked.value && movableOf("target").length > 0);
|
|
757
|
+
function commit(next) {
|
|
758
|
+
emit("update:modelValue", next);
|
|
759
|
+
emit("change", next);
|
|
760
|
+
}
|
|
761
|
+
async function transfer(keys, direction) {
|
|
762
|
+
if (isLocked.value || keys.length === 0) return;
|
|
763
|
+
const from = direction === "toTarget" ? "source" : "target";
|
|
764
|
+
const to = direction === "toTarget" ? "target" : "source";
|
|
765
|
+
const model = normalizeKeys(props.modelValue ?? []);
|
|
766
|
+
const nextFocus = keyAfterRemoval(visibleKeys.value[from], keys, rovingOf(from).rovingKey.value);
|
|
767
|
+
commit(direction === "toTarget" ? insertKeys(model, keys, null) : removeKeys(model, keys));
|
|
768
|
+
selections.value = {
|
|
769
|
+
...selections.value,
|
|
770
|
+
[from]: emptySelection,
|
|
771
|
+
[to]: {
|
|
772
|
+
keys: new Set(keys),
|
|
773
|
+
anchor: keys[0]
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
markArrived(keys);
|
|
777
|
+
emit("transfer", keys, direction);
|
|
778
|
+
announce(t("gr.transfer.moved", "{count} moved to {list}", {
|
|
779
|
+
count: keys.length,
|
|
780
|
+
n: keys.length,
|
|
781
|
+
list: titles.value[to]
|
|
782
|
+
}));
|
|
783
|
+
await nextTick();
|
|
784
|
+
if (nextFocus !== void 0) rovingOf(from).focusKey(nextFocus);
|
|
785
|
+
else rovingOf(to).focusKey(keys[0]);
|
|
786
|
+
}
|
|
787
|
+
function transferSelected(direction) {
|
|
788
|
+
const side = direction === "toTarget" ? "source" : "target";
|
|
789
|
+
const keys = movableOf(side);
|
|
790
|
+
if (keys.length > 0) {
|
|
791
|
+
transfer(keys, direction);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const focused = rovingOf(side).rovingKey.value;
|
|
795
|
+
if (focused !== void 0 && selectableOf(side)(focused)) transfer([focused], direction);
|
|
796
|
+
}
|
|
797
|
+
/** Перестановка отключена, пока панель отфильтрована: см. `docs/components/GrTransfer.md`. */
|
|
798
|
+
const reorderAllowed = computed(() => props.sortable && !isLocked.value && normalizeOptionQuery(queries.value.target) === "");
|
|
799
|
+
function reorder(target, keys) {
|
|
800
|
+
if (target === void 0 || keys.length === 0) return;
|
|
801
|
+
const model = normalizeKeys(props.modelValue ?? []);
|
|
802
|
+
commit(insertKeys(model, keys, target));
|
|
803
|
+
const position = removeKeys(model, keys).findIndex((key) => key === target);
|
|
804
|
+
announce(t("gr.transfer.reordered", "Position {position} of {count}", {
|
|
805
|
+
position: (position < 0 ? model.length - keys.length : position) + 1,
|
|
806
|
+
count: model.length
|
|
807
|
+
}));
|
|
808
|
+
}
|
|
809
|
+
function moveBlock(delta) {
|
|
810
|
+
const keys = movableOf("target");
|
|
811
|
+
const moving = keys.length > 0 ? keys : blockUnderFocus("target");
|
|
812
|
+
if (moving.length === 0) return;
|
|
813
|
+
if (!reorderAllowed.value) {
|
|
814
|
+
if (props.sortable && !isLocked.value) announce(t("gr.transfer.reorderFiltered", "Reordering is off while a search is active"));
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
reorder(stepTarget(normalizeKeys(props.modelValue ?? []), moving, delta), moving);
|
|
818
|
+
}
|
|
819
|
+
function moveBlockToEdge(edge) {
|
|
820
|
+
const keys = movableOf("target");
|
|
821
|
+
const moving = keys.length > 0 ? keys : blockUnderFocus("target");
|
|
822
|
+
if (moving.length === 0 || !reorderAllowed.value) return;
|
|
823
|
+
reorder(edgeTarget(normalizeKeys(props.modelValue ?? []), moving, edge), moving);
|
|
824
|
+
}
|
|
825
|
+
function blockUnderFocus(side) {
|
|
826
|
+
const focused = rovingOf(side).rovingKey.value;
|
|
827
|
+
return focused !== void 0 && selectableOf(side)(focused) ? [focused] : [];
|
|
828
|
+
}
|
|
829
|
+
const sourceRows = ref(/* @__PURE__ */ new Map());
|
|
830
|
+
const targetRows = ref(/* @__PURE__ */ new Map());
|
|
831
|
+
const searchEls = ref({
|
|
832
|
+
source: null,
|
|
833
|
+
target: null
|
|
834
|
+
});
|
|
835
|
+
const panelEls = ref({
|
|
836
|
+
source: null,
|
|
837
|
+
target: null
|
|
838
|
+
});
|
|
839
|
+
const listEls = ref({
|
|
840
|
+
source: null,
|
|
841
|
+
target: null
|
|
842
|
+
});
|
|
843
|
+
function rowsOf(side) {
|
|
844
|
+
return side === "source" ? sourceRows.value : targetRows.value;
|
|
845
|
+
}
|
|
846
|
+
function registerRow(side, key, el) {
|
|
847
|
+
const map = rowsOf(side);
|
|
848
|
+
if (el instanceof HTMLElement) map.set(key, el);
|
|
849
|
+
else map.delete(key);
|
|
850
|
+
}
|
|
851
|
+
function makeRoving(side) {
|
|
852
|
+
return useRovingFocus({
|
|
853
|
+
items: () => visibleKeys.value[side],
|
|
854
|
+
elementFor: (key) => rowsOf(side).get(key) ?? null,
|
|
855
|
+
orientation: () => "vertical",
|
|
856
|
+
wrap: () => false,
|
|
857
|
+
skipDisabled: () => false,
|
|
858
|
+
onOverflow: (edge) => {
|
|
859
|
+
if (edge !== "start") return false;
|
|
860
|
+
const input = searchEls.value[side]?.querySelector("input");
|
|
861
|
+
if (!input) return false;
|
|
862
|
+
input.focus();
|
|
863
|
+
return true;
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
const sourceRoving = makeRoving("source");
|
|
868
|
+
const targetRoving = makeRoving("target");
|
|
869
|
+
function rovingOf(side) {
|
|
870
|
+
return side === "source" ? sourceRoving : targetRoving;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Признак того, что список продолжается за краем.
|
|
874
|
+
*
|
|
875
|
+
* Полоса прокрутки у панели своя и на macOS невидима, поэтому обрезанная
|
|
876
|
+
* посередине строка читается как дефект отрисовки, а не как «прокрути». Затухание
|
|
877
|
+
* рисует CSS по `data-overflow` — тот же приём, что у ряда вкладок `GrTabs`.
|
|
878
|
+
*/
|
|
879
|
+
const listOverflow = ref({
|
|
880
|
+
source: "none",
|
|
881
|
+
target: "none"
|
|
882
|
+
});
|
|
883
|
+
let overflowObserver = null;
|
|
884
|
+
let overflowScheduled = false;
|
|
885
|
+
function measureOverflow() {
|
|
886
|
+
const next = {
|
|
887
|
+
source: "none",
|
|
888
|
+
target: "none"
|
|
889
|
+
};
|
|
890
|
+
for (const side of ["source", "target"]) {
|
|
891
|
+
const el = listEls.value[side];
|
|
892
|
+
if (el) next[side] = resolveScrollOverflow(el.scrollTop, el.scrollHeight, el.clientHeight);
|
|
893
|
+
}
|
|
894
|
+
listOverflow.value = next;
|
|
895
|
+
}
|
|
896
|
+
function scheduleOverflow() {
|
|
897
|
+
if (overflowScheduled) return;
|
|
898
|
+
overflowScheduled = true;
|
|
899
|
+
nextTick(() => {
|
|
900
|
+
overflowScheduled = false;
|
|
901
|
+
measureOverflow();
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
onMounted(() => {
|
|
905
|
+
scheduleOverflow();
|
|
906
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
907
|
+
overflowObserver = new ResizeObserver(scheduleOverflow);
|
|
908
|
+
for (const side of ["source", "target"]) {
|
|
909
|
+
const el = listEls.value[side];
|
|
910
|
+
if (el) overflowObserver.observe(el);
|
|
911
|
+
}
|
|
912
|
+
});
|
|
913
|
+
onBeforeUnmount(() => {
|
|
914
|
+
overflowObserver?.disconnect();
|
|
915
|
+
overflowObserver = null;
|
|
916
|
+
});
|
|
917
|
+
watch([split, visible], scheduleOverflow);
|
|
918
|
+
/** Есть ли что переставлять: иначе кнопки в шапке гасятся, оставаясь в таб-порядке. */
|
|
919
|
+
const canReorder = computed(() => reorderAllowed.value && itemsOf("target").length > 1 && (movableOf("target").length > 0 || blockUnderFocus("target").length > 0));
|
|
920
|
+
/**
|
|
921
|
+
* Ключи, только что приехавшие в панель: подсветка живёт ~1.2 с и снимается сама.
|
|
922
|
+
* Таймер один на компонент — быстрый повторный перенос перезапускает его, а не
|
|
923
|
+
* копит хвосты.
|
|
924
|
+
*/
|
|
925
|
+
const arrived = ref(/* @__PURE__ */ new Set());
|
|
926
|
+
let arrivedTimer = null;
|
|
927
|
+
function markArrived(keys) {
|
|
928
|
+
if (typeof window === "undefined") return;
|
|
929
|
+
arrived.value = new Set(keys);
|
|
930
|
+
if (arrivedTimer !== null) clearTimeout(arrivedTimer);
|
|
931
|
+
arrivedTimer = window.setTimeout(() => {
|
|
932
|
+
arrivedTimer = null;
|
|
933
|
+
arrived.value = /* @__PURE__ */ new Set();
|
|
934
|
+
}, 1200);
|
|
935
|
+
}
|
|
936
|
+
onBeforeUnmount(() => {
|
|
937
|
+
if (arrivedTimer !== null) clearTimeout(arrivedTimer);
|
|
938
|
+
});
|
|
939
|
+
const dragging = useTransferDrag({
|
|
940
|
+
disabled: () => isLocked.value || !resolvedDraggable.value,
|
|
941
|
+
visibleKeys: (side) => visibleKeys.value[side],
|
|
942
|
+
rowEl: (side, key) => rowsOf(side).get(key) ?? null,
|
|
943
|
+
panelEl: (side) => panelEls.value[side],
|
|
944
|
+
scrollerEl: (side) => listEls.value[side],
|
|
945
|
+
blockFor: (side, key) => {
|
|
946
|
+
if (!selectableOf(side)(key)) return [];
|
|
947
|
+
const chosen = movableOf(side);
|
|
948
|
+
return chosen.includes(key) ? chosen : [key];
|
|
949
|
+
},
|
|
950
|
+
allowSameSide: (side) => side === "target" && reorderAllowed.value,
|
|
951
|
+
onDrop: (session, spot) => {
|
|
952
|
+
const model = normalizeKeys(props.modelValue ?? []);
|
|
953
|
+
if (spot.side === session.side) {
|
|
954
|
+
reorder(spot.before, [...session.keys]);
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
if (spot.side === "target") {
|
|
958
|
+
commit(insertKeys(model, [...session.keys], spot.before));
|
|
959
|
+
markArrived(session.keys);
|
|
960
|
+
emit("transfer", [...session.keys], "toTarget");
|
|
961
|
+
announce(t("gr.transfer.moved", "{count} moved to {list}", {
|
|
962
|
+
count: session.keys.length,
|
|
963
|
+
n: session.keys.length,
|
|
964
|
+
list: titles.value.target
|
|
965
|
+
}));
|
|
966
|
+
return;
|
|
967
|
+
}
|
|
968
|
+
commit(removeKeys(model, [...session.keys]));
|
|
969
|
+
markArrived(session.keys);
|
|
970
|
+
emit("transfer", [...session.keys], "toSource");
|
|
971
|
+
announce(t("gr.transfer.moved", "{count} moved to {list}", {
|
|
972
|
+
count: session.keys.length,
|
|
973
|
+
n: session.keys.length,
|
|
974
|
+
list: titles.value.source
|
|
975
|
+
}));
|
|
976
|
+
},
|
|
977
|
+
onCancel: () => announce(t("gr.transfer.cancelled", "Move cancelled"))
|
|
978
|
+
});
|
|
979
|
+
/**
|
|
980
|
+
* Вызов через обработчик, а не `@pointerdown="dragging.startFrom(...)"`: Vue
|
|
981
|
+
* считает такое выражение инлайн-инструкцией и возвращённую функцию
|
|
982
|
+
* выбрасывает — жест не начинался бы вовсе. Приём `GrSortableList`.
|
|
983
|
+
*/
|
|
984
|
+
function onRowPointerDown(side, key, event) {
|
|
985
|
+
dragging.startFrom(side, key)(event);
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Подпись переносимого. Первая строка блока названа, остальные — числом: список
|
|
989
|
+
* из десяти имён под курсором закрыл бы то самое место, куда человек целится.
|
|
990
|
+
*/
|
|
991
|
+
const ghost = computed(() => {
|
|
992
|
+
const session = dragging.session.value;
|
|
993
|
+
if (!session || session.keys.length === 0) return null;
|
|
994
|
+
const first = itemsOf(session.side).find((item) => keyOf(item) === session.keys[0]);
|
|
995
|
+
return {
|
|
996
|
+
label: first ? labelOf(first) : String(session.keys[0]),
|
|
997
|
+
rest: session.keys.length - 1
|
|
998
|
+
};
|
|
999
|
+
});
|
|
1000
|
+
const ghostStyle = computed(() => ({ transform: `translate3d(${dragging.pointer.value.x + 12}px, ${dragging.pointer.value.y + 12}px, 0)` }));
|
|
1001
|
+
function indicatorFor(side, key) {
|
|
1002
|
+
const spot = dragging.spot.value;
|
|
1003
|
+
if (!spot || spot.side !== side) return null;
|
|
1004
|
+
if (spot.before === key) return "before";
|
|
1005
|
+
const keys = visibleKeys.value[side];
|
|
1006
|
+
return spot.before === null && keys[keys.length - 1] === key ? "after" : null;
|
|
1007
|
+
}
|
|
1008
|
+
function isDraggingKey(side, key) {
|
|
1009
|
+
const session = dragging.session.value;
|
|
1010
|
+
return session !== null && session.side === side && session.keys.includes(key);
|
|
1011
|
+
}
|
|
1012
|
+
function onOptionClick(side, key, event) {
|
|
1013
|
+
if (isDisabled.value || dragging.consumeClick()) return;
|
|
1014
|
+
rovingOf(side).setActive(key);
|
|
1015
|
+
setSelection(side, applySelect(selections.value[side], key, selectIntentFrom(event), visibleKeys.value[side], selectableOf(side)));
|
|
1016
|
+
}
|
|
1017
|
+
function extendRange(side, key) {
|
|
1018
|
+
setSelection(side, applySelect(selections.value[side], key, {
|
|
1019
|
+
mode: "range",
|
|
1020
|
+
additive: false
|
|
1021
|
+
}, visibleKeys.value[side], selectableOf(side)));
|
|
1022
|
+
}
|
|
1023
|
+
function onListKeydown(side, event) {
|
|
1024
|
+
if (isDisabled.value || visibleKeys.value[side].length === 0) return;
|
|
1025
|
+
const roving = rovingOf(side);
|
|
1026
|
+
const current = roving.rovingKey.value;
|
|
1027
|
+
if ((event.ctrlKey || event.metaKey) && (event.key === "a" || event.key === "A")) {
|
|
1028
|
+
event.preventDefault();
|
|
1029
|
+
toggleAll(side);
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
if (event.key === " ") {
|
|
1033
|
+
event.preventDefault();
|
|
1034
|
+
if (current === void 0) return;
|
|
1035
|
+
if (event.shiftKey) extendRange(side, current);
|
|
1036
|
+
else setSelection(side, applySelect(selections.value[side], current, {
|
|
1037
|
+
mode: "toggle",
|
|
1038
|
+
additive: true
|
|
1039
|
+
}, visibleKeys.value[side], selectableOf(side)));
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
if (event.key === "Enter") {
|
|
1043
|
+
event.preventDefault();
|
|
1044
|
+
transferSelected(side === "source" ? "toTarget" : "toSource");
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
if (side === "target" && event.altKey) {
|
|
1048
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
1049
|
+
event.preventDefault();
|
|
1050
|
+
moveBlock(event.key === "ArrowDown" ? 1 : -1);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
if (event.key === "Home" || event.key === "End") {
|
|
1054
|
+
event.preventDefault();
|
|
1055
|
+
moveBlockToEdge(event.key === "Home" ? "start" : "end");
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
if (!roving.handleNavigationKeys(event)) return;
|
|
1060
|
+
const next = roving.rovingKey.value;
|
|
1061
|
+
if (event.shiftKey && next !== void 0) extendRange(side, next);
|
|
1062
|
+
}
|
|
1063
|
+
function onSearchKeydown(side, event) {
|
|
1064
|
+
if (event.key !== "ArrowDown") return;
|
|
1065
|
+
const first = visibleKeys.value[side][0];
|
|
1066
|
+
if (first === void 0) return;
|
|
1067
|
+
event.preventDefault();
|
|
1068
|
+
rovingOf(side).focusKey(first);
|
|
1069
|
+
}
|
|
1070
|
+
function onSearch(side, value) {
|
|
1071
|
+
queries.value = {
|
|
1072
|
+
...queries.value,
|
|
1073
|
+
[side]: value
|
|
1074
|
+
};
|
|
1075
|
+
emit("search", value, side);
|
|
1076
|
+
}
|
|
1077
|
+
const { onFocusIn, onFocusOut } = useFocusWithin(rootEl, {
|
|
1078
|
+
enter: (event) => emit("focus", event),
|
|
1079
|
+
leave: (event) => emit("blur", event)
|
|
1080
|
+
});
|
|
1081
|
+
const panelStyle = computed(() => ({ maxHeight: props.maxHeight === void 0 ? "var(--gr-transfer-panel-max-h, 18rem)" : typeof props.maxHeight === "number" ? `${props.maxHeight}px` : props.maxHeight }));
|
|
1082
|
+
function counterText(side) {
|
|
1083
|
+
return t("gr.transfer.selected", "{count} of {total}", {
|
|
1084
|
+
count: selections.value[side].keys.size,
|
|
1085
|
+
total: itemsOf(side).length
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
function shownText(side) {
|
|
1089
|
+
return t("gr.transfer.shown", "Showing {shown} of {total}", {
|
|
1090
|
+
shown: visible.value[side].length,
|
|
1091
|
+
total: itemsOf(side).length
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1094
|
+
function emptyText(side) {
|
|
1095
|
+
return normalizeOptionQuery(queries.value[side]) === "" ? t("gr.transfer.empty", "Nothing here yet") : t("gr.transfer.emptyFiltered", "Nothing matches the search");
|
|
1096
|
+
}
|
|
1097
|
+
function isFiltered(side) {
|
|
1098
|
+
return normalizeOptionQuery(queries.value[side]) !== "";
|
|
1099
|
+
}
|
|
1100
|
+
function allState(side) {
|
|
1101
|
+
return allVisibleState(selections.value[side], visibleKeys.value[side], selectableOf(side));
|
|
1102
|
+
}
|
|
1103
|
+
function focusSide(side) {
|
|
1104
|
+
const roving = rovingOf(side);
|
|
1105
|
+
const key = roving.rovingKey.value ?? visibleKeys.value[side][0];
|
|
1106
|
+
if (key !== void 0) {
|
|
1107
|
+
roving.focusKey(key);
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
searchEls.value[side]?.querySelector("input")?.focus();
|
|
1111
|
+
}
|
|
1112
|
+
__expose({
|
|
1113
|
+
focus: () => focusSide("target"),
|
|
1114
|
+
blur: () => {
|
|
1115
|
+
const active = document.activeElement;
|
|
1116
|
+
if (active instanceof HTMLElement && rootEl.value?.contains(active)) active.blur();
|
|
1117
|
+
},
|
|
1118
|
+
focusSide,
|
|
1119
|
+
transfer: (keys, direction) => void transfer(keys, direction),
|
|
1120
|
+
selectAll: (side) => toggleAll(side),
|
|
1121
|
+
clearSelection: (side) => {
|
|
1122
|
+
if (side) {
|
|
1123
|
+
setSelection(side, emptySelection);
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
selections.value = {
|
|
1127
|
+
source: emptySelection,
|
|
1128
|
+
target: emptySelection
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
});
|
|
1132
|
+
return (_ctx, _cache) => {
|
|
1133
|
+
return openBlock(), createElementBlock("div", {
|
|
1134
|
+
ref_key: "rootEl",
|
|
1135
|
+
ref: rootEl,
|
|
1136
|
+
"data-gr-transfer": "",
|
|
1137
|
+
"data-dragging": unref(dragging).session.value ? "true" : void 0,
|
|
1138
|
+
role: "group",
|
|
1139
|
+
class: normalizeClass(unref(transferRootBase)),
|
|
1140
|
+
"aria-label": __props.ariaLabel,
|
|
1141
|
+
"aria-disabled": unref(isDisabled) ? "true" : void 0,
|
|
1142
|
+
onFocusin: _cache[4] || (_cache[4] = (...args) => unref(onFocusIn) && unref(onFocusIn)(...args)),
|
|
1143
|
+
onFocusout: _cache[5] || (_cache[5] = (...args) => unref(onFocusOut) && unref(onFocusOut)(...args))
|
|
1144
|
+
}, [
|
|
1145
|
+
(openBlock(), createElementBlock(Fragment, null, renderList(["source", "target"], (side) => {
|
|
1146
|
+
return createElementVNode("div", {
|
|
1147
|
+
key: side,
|
|
1148
|
+
ref_for: true,
|
|
1149
|
+
ref: (el) => {
|
|
1150
|
+
panelEls.value[side] = el;
|
|
1151
|
+
},
|
|
1152
|
+
"data-gr-transfer-panel": side,
|
|
1153
|
+
class: normalizeClass(unref(grTransferPanelClass)(unref(dragging).spot.value?.side === side)),
|
|
1154
|
+
style: normalizeStyle(panelStyle.value)
|
|
1155
|
+
}, [
|
|
1156
|
+
renderSlot(_ctx.$slots, "header", {
|
|
1157
|
+
side,
|
|
1158
|
+
title: titles.value[side],
|
|
1159
|
+
selected: selections.value[side].keys.size,
|
|
1160
|
+
total: itemsOf(side).length,
|
|
1161
|
+
shown: visible.value[side].length
|
|
1162
|
+
}, () => [createElementVNode("div", { class: normalizeClass(unref(grTransferHeaderClass)(unref(resolvedSize))) }, [
|
|
1163
|
+
visible.value[side].length > 0 ? (openBlock(), createBlock(unref(GrCheckbox_default), {
|
|
1164
|
+
key: 0,
|
|
1165
|
+
"model-value": allState(side) === "checked",
|
|
1166
|
+
indeterminate: allState(side) === "indeterminate",
|
|
1167
|
+
disabled: unref(isDisabled),
|
|
1168
|
+
size: unref(resolvedSize),
|
|
1169
|
+
"aria-label": unref(t)("gr.transfer.selectAll", "Select everything shown"),
|
|
1170
|
+
"onUpdate:modelValue": ($event) => toggleAll(side)
|
|
1171
|
+
}, null, 8, [
|
|
1172
|
+
"model-value",
|
|
1173
|
+
"indeterminate",
|
|
1174
|
+
"disabled",
|
|
1175
|
+
"size",
|
|
1176
|
+
"aria-label",
|
|
1177
|
+
"onUpdate:modelValue"
|
|
1178
|
+
])) : createCommentVNode("", true),
|
|
1179
|
+
createElementVNode("span", {
|
|
1180
|
+
id: side === "source" ? sourceTitleId : targetTitleId,
|
|
1181
|
+
class: normalizeClass(unref(transferTitleClass))
|
|
1182
|
+
}, toDisplayString(titles.value[side]), 11, _hoisted_3),
|
|
1183
|
+
createElementVNode("span", { class: normalizeClass(unref(transferCounterClass)) }, toDisplayString(counterText(side)), 3),
|
|
1184
|
+
side === "target" && __props.sortable && visible.value[side].length > 1 ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [createElementVNode("button", {
|
|
1185
|
+
type: "button",
|
|
1186
|
+
"data-gr-transfer-move-up": "",
|
|
1187
|
+
class: normalizeClass(unref(grTransferReorderClass)(unref(resolvedSize))),
|
|
1188
|
+
"aria-disabled": canReorder.value ? void 0 : "true",
|
|
1189
|
+
"aria-label": unref(t)("gr.transfer.moveUp", "Move earlier"),
|
|
1190
|
+
onClick: _cache[0] || (_cache[0] = ($event) => canReorder.value && moveBlock(-1))
|
|
1191
|
+
}, [createVNode(unref(arrow_left_default), {
|
|
1192
|
+
class: normalizeClass(unref(transferReorderIconClass)),
|
|
1193
|
+
"aria-hidden": "true"
|
|
1194
|
+
}, null, 8, ["class"])], 10, _hoisted_4), createElementVNode("button", {
|
|
1195
|
+
type: "button",
|
|
1196
|
+
"data-gr-transfer-move-down": "",
|
|
1197
|
+
class: normalizeClass(unref(grTransferReorderClass)(unref(resolvedSize))),
|
|
1198
|
+
"aria-disabled": canReorder.value ? void 0 : "true",
|
|
1199
|
+
"aria-label": unref(t)("gr.transfer.moveDown", "Move later"),
|
|
1200
|
+
onClick: _cache[1] || (_cache[1] = ($event) => canReorder.value && moveBlock(1))
|
|
1201
|
+
}, [createVNode(unref(arrow_right_default), {
|
|
1202
|
+
class: normalizeClass(unref(transferReorderIconClass)),
|
|
1203
|
+
"aria-hidden": "true"
|
|
1204
|
+
}, null, 8, ["class"])], 10, _hoisted_5)], 64)) : createCommentVNode("", true)
|
|
1205
|
+
], 2)]),
|
|
1206
|
+
__props.searchable ? (openBlock(), createElementBlock("div", {
|
|
1207
|
+
key: 0,
|
|
1208
|
+
ref_for: true,
|
|
1209
|
+
ref: (el) => {
|
|
1210
|
+
searchEls.value[side] = el;
|
|
1211
|
+
},
|
|
1212
|
+
class: normalizeClass(unref(grTransferSearchClass)(unref(resolvedSize)))
|
|
1213
|
+
}, [createVNode(unref(GrInput_default), {
|
|
1214
|
+
"model-value": queries.value[side],
|
|
1215
|
+
type: "search",
|
|
1216
|
+
clearable: "",
|
|
1217
|
+
size: unref(resolvedSize),
|
|
1218
|
+
disabled: unref(isDisabled),
|
|
1219
|
+
placeholder: unref(t)("gr.transfer.search", "Search"),
|
|
1220
|
+
"aria-label": `${titles.value[side]} — ${unref(t)("gr.transfer.search", "Search")}`,
|
|
1221
|
+
"onUpdate:modelValue": (value) => onSearch(side, value),
|
|
1222
|
+
onKeydown: (event) => onSearchKeydown(side, event)
|
|
1223
|
+
}, null, 8, [
|
|
1224
|
+
"model-value",
|
|
1225
|
+
"size",
|
|
1226
|
+
"disabled",
|
|
1227
|
+
"placeholder",
|
|
1228
|
+
"aria-label",
|
|
1229
|
+
"onUpdate:modelValue",
|
|
1230
|
+
"onKeydown"
|
|
1231
|
+
])], 2)) : createCommentVNode("", true),
|
|
1232
|
+
createElementVNode("p", {
|
|
1233
|
+
class: normalizeClass(unref(transferStatusClass)),
|
|
1234
|
+
role: "status",
|
|
1235
|
+
"aria-live": "polite"
|
|
1236
|
+
}, toDisplayString(isFiltered(side) ? shownText(side) : ""), 3),
|
|
1237
|
+
createElementVNode("div", {
|
|
1238
|
+
id: side === "source" ? sourceListId : unref(fieldId) ?? targetListId,
|
|
1239
|
+
ref_for: true,
|
|
1240
|
+
ref: (el) => {
|
|
1241
|
+
listEls.value[side] = el;
|
|
1242
|
+
},
|
|
1243
|
+
"data-gr-transfer-list": side,
|
|
1244
|
+
"data-overflow": listOverflow.value[side],
|
|
1245
|
+
role: "listbox",
|
|
1246
|
+
"aria-multiselectable": "true",
|
|
1247
|
+
class: normalizeClass(unref(transferListBase)),
|
|
1248
|
+
"aria-label": hasHeaderSlot.value ? titles.value[side] : void 0,
|
|
1249
|
+
"aria-labelledby": hasHeaderSlot.value ? side === "target" ? unref(fieldLabelId) : void 0 : side === "target" && unref(fieldLabelId) ? `${unref(fieldLabelId)} ${targetTitleId}` : side === "source" ? sourceTitleId : targetTitleId,
|
|
1250
|
+
"aria-describedby": side === "target" ? unref(describedBy) : void 0,
|
|
1251
|
+
"aria-invalid": side === "target" && unref(isInvalid) ? "true" : void 0,
|
|
1252
|
+
"aria-required": side === "target" && unref(isRequired) ? "true" : void 0,
|
|
1253
|
+
"aria-readonly": side === "target" && unref(isReadonly) ? "true" : void 0,
|
|
1254
|
+
"aria-disabled": unref(isDisabled) ? "true" : void 0,
|
|
1255
|
+
onKeydown: (event) => onListKeydown(side, event),
|
|
1256
|
+
onScrollPassive: scheduleOverflow
|
|
1257
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(visible.value[side], (item, index) => {
|
|
1258
|
+
return openBlock(), createElementBlock("div", {
|
|
1259
|
+
key: keyOf(item),
|
|
1260
|
+
ref_for: true,
|
|
1261
|
+
ref: (el) => registerRow(side, keyOf(item), el),
|
|
1262
|
+
"data-gr-transfer-option": "",
|
|
1263
|
+
role: "option",
|
|
1264
|
+
"aria-selected": selections.value[side].keys.has(keyOf(item)) ? "true" : "false",
|
|
1265
|
+
"aria-disabled": disabledOf(item) ? "true" : void 0,
|
|
1266
|
+
tabindex: rovingOf(side).tabindexFor(keyOf(item)),
|
|
1267
|
+
class: normalizeClass(unref(grTransferOptionClass)({
|
|
1268
|
+
size: unref(resolvedSize),
|
|
1269
|
+
selected: selections.value[side].keys.has(keyOf(item)),
|
|
1270
|
+
disabled: disabledOf(item),
|
|
1271
|
+
draggable: unref(resolvedDraggable) && !unref(isLocked),
|
|
1272
|
+
dragging: isDraggingKey(side, keyOf(item)),
|
|
1273
|
+
arrived: arrived.value.has(keyOf(item)),
|
|
1274
|
+
indicator: indicatorFor(side, keyOf(item))
|
|
1275
|
+
})),
|
|
1276
|
+
onPointerdown: (event) => onRowPointerDown(side, keyOf(item), event),
|
|
1277
|
+
onClick: (event) => onOptionClick(side, keyOf(item), event),
|
|
1278
|
+
onFocus: ($event) => rovingOf(side).setActive(keyOf(item)),
|
|
1279
|
+
onDblclick: ($event) => !disabledOf(item) && transfer([keyOf(item)], side === "source" ? "toTarget" : "toSource")
|
|
1280
|
+
}, [createElementVNode("span", {
|
|
1281
|
+
"aria-hidden": "true",
|
|
1282
|
+
class: normalizeClass(unref(grTransferMarkClass)(unref(resolvedSize), selections.value[side].keys.has(keyOf(item))))
|
|
1283
|
+
}, [selections.value[side].keys.has(keyOf(item)) ? (openBlock(), createBlock(unref(check_default), {
|
|
1284
|
+
key: 0,
|
|
1285
|
+
class: normalizeClass(unref(transferMarkIconClass))
|
|
1286
|
+
}, null, 8, ["class"])) : createCommentVNode("", true)], 2), renderSlot(_ctx.$slots, "item", {
|
|
1287
|
+
item,
|
|
1288
|
+
side,
|
|
1289
|
+
index,
|
|
1290
|
+
selected: selections.value[side].keys.has(keyOf(item)),
|
|
1291
|
+
disabled: disabledOf(item)
|
|
1292
|
+
}, () => [createElementVNode("span", { class: normalizeClass(unref(transferLabelClass)) }, toDisplayString(labelOf(item)), 3)])], 42, _hoisted_7);
|
|
1293
|
+
}), 128))], 42, _hoisted_6),
|
|
1294
|
+
visible.value[side].length === 0 ? renderSlot(_ctx.$slots, "empty", {
|
|
1295
|
+
side,
|
|
1296
|
+
filtered: isFiltered(side)
|
|
1297
|
+
}, () => [createElementVNode("p", { class: normalizeClass(unref(grTransferEmptyClass)(unref(resolvedSize))) }, toDisplayString(emptyText(side)), 3)], void 0, 1) : createCommentVNode("", true)
|
|
1298
|
+
], 14, _hoisted_2);
|
|
1299
|
+
}), 64)),
|
|
1300
|
+
(openBlock(), createBlock(Teleport, {
|
|
1301
|
+
to: unref(portalTarget),
|
|
1302
|
+
disabled: !unref(teleportEnabled)
|
|
1303
|
+
}, [ghost.value ? (openBlock(), createElementBlock("div", {
|
|
1304
|
+
key: 0,
|
|
1305
|
+
"data-gr-transfer-ghost": "",
|
|
1306
|
+
"aria-hidden": "true",
|
|
1307
|
+
class: normalizeClass(unref(transferGhostClass)),
|
|
1308
|
+
style: normalizeStyle(ghostStyle.value)
|
|
1309
|
+
}, [createElementVNode("span", { class: normalizeClass(unref(transferGhostLabelClass)) }, toDisplayString(ghost.value.label), 3), ghost.value.rest > 0 ? (openBlock(), createElementBlock("span", {
|
|
1310
|
+
key: 0,
|
|
1311
|
+
class: normalizeClass(unref(transferGhostCountClass))
|
|
1312
|
+
}, "+" + toDisplayString(ghost.value.rest), 3)) : createCommentVNode("", true)], 6)) : createCommentVNode("", true)], 8, ["to", "disabled"])),
|
|
1313
|
+
createElementVNode("div", {
|
|
1314
|
+
class: normalizeClass(unref(grTransferActionsClass)(unref(resolvedSize))),
|
|
1315
|
+
style: { "order": "1" }
|
|
1316
|
+
}, [renderSlot(_ctx.$slots, "actions", {
|
|
1317
|
+
toTarget: () => transferSelected("toTarget"),
|
|
1318
|
+
toSource: () => transferSelected("toSource"),
|
|
1319
|
+
canToTarget: canToTarget.value,
|
|
1320
|
+
canToSource: canToSource.value
|
|
1321
|
+
}, () => [createVNode(unref(GrButtonGroup_default), {
|
|
1322
|
+
orientation: "vertical",
|
|
1323
|
+
attached: "",
|
|
1324
|
+
size: unref(resolvedSize)
|
|
1325
|
+
}, {
|
|
1326
|
+
default: withCtx(() => [createVNode(unref(GrButton_default), {
|
|
1327
|
+
"data-gr-transfer-to-target": "",
|
|
1328
|
+
variant: "secondary",
|
|
1329
|
+
"aria-disabled": canToTarget.value ? void 0 : "true",
|
|
1330
|
+
class: normalizeClass(canToTarget.value ? "" : unref(transferActionInertClass)),
|
|
1331
|
+
"aria-label": unref(t)("gr.transfer.toTarget", "Move to selected"),
|
|
1332
|
+
onClick: _cache[2] || (_cache[2] = ($event) => canToTarget.value && transferSelected("toTarget"))
|
|
1333
|
+
}, {
|
|
1334
|
+
default: withCtx(() => [createVNode(unref(chevron_right_default), {
|
|
1335
|
+
class: normalizeClass(unref(transferActionIconClass)),
|
|
1336
|
+
"aria-hidden": "true"
|
|
1337
|
+
}, null, 8, ["class"])]),
|
|
1338
|
+
_: 1
|
|
1339
|
+
}, 8, [
|
|
1340
|
+
"aria-disabled",
|
|
1341
|
+
"class",
|
|
1342
|
+
"aria-label"
|
|
1343
|
+
]), createVNode(unref(GrButton_default), {
|
|
1344
|
+
"data-gr-transfer-to-source": "",
|
|
1345
|
+
variant: "secondary",
|
|
1346
|
+
"aria-disabled": canToSource.value ? void 0 : "true",
|
|
1347
|
+
class: normalizeClass(canToSource.value ? "" : unref(transferActionInertClass)),
|
|
1348
|
+
"aria-label": unref(t)("gr.transfer.toSource", "Move back to available"),
|
|
1349
|
+
onClick: _cache[3] || (_cache[3] = ($event) => canToSource.value && transferSelected("toSource"))
|
|
1350
|
+
}, {
|
|
1351
|
+
default: withCtx(() => [createVNode(unref(chevron_left_default), {
|
|
1352
|
+
class: normalizeClass(unref(transferActionIconClass)),
|
|
1353
|
+
"aria-hidden": "true"
|
|
1354
|
+
}, null, 8, ["class"])]),
|
|
1355
|
+
_: 1
|
|
1356
|
+
}, 8, [
|
|
1357
|
+
"aria-disabled",
|
|
1358
|
+
"class",
|
|
1359
|
+
"aria-label"
|
|
1360
|
+
])]),
|
|
1361
|
+
_: 1
|
|
1362
|
+
}, 8, ["size"])])], 2)
|
|
1363
|
+
], 42, _hoisted_1);
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
});
|
|
1367
|
+
//#endregion
|
|
1368
|
+
export { GrTransfer_default as t };
|