@feugene/granularity 0.46.0 → 0.48.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/{x-circle-BCBURwyX.js → check-circle-Bnrb-CGJ.js} +8 -32
- package/dist/chunks/check-circle-Bnrb-CGJ.js.map +1 -0
- package/dist/chunks/{config-BqT6PMzO.js → config-B42iJ48O.js} +5 -1
- package/dist/chunks/{config-BqT6PMzO.js.map → config-B42iJ48O.js.map} +1 -1
- package/dist/chunks/{config-BGCyyXiD.js → config-BAse0laT.js} +5 -1
- package/dist/chunks/{config-BGCyyXiD.js.map → config-BAse0laT.js.map} +1 -1
- package/dist/chunks/{config-MmPym-Z7.js → config-BLP94dPY.js} +5 -1
- package/dist/chunks/{config-MmPym-Z7.js.map → config-BLP94dPY.js.map} +1 -1
- package/dist/chunks/{config-CjKpUcmy.js → config-Bdf1bt-m.js} +25 -16
- package/dist/chunks/config-Bdf1bt-m.js.map +1 -0
- package/dist/chunks/{config-DeyT9dJl.js → config-Bs1HwUcx.js} +47 -4
- package/dist/chunks/{config-DeyT9dJl.js.map → config-Bs1HwUcx.js.map} +1 -1
- package/dist/chunks/{config-CIkdsWUk.js → config-CIUU9ZpY.js} +4 -1
- package/dist/chunks/config-CIUU9ZpY.js.map +1 -0
- package/dist/chunks/{config-B3J0alyY.js → config-CzeZg2LO.js} +49 -4
- package/dist/chunks/config-CzeZg2LO.js.map +1 -0
- package/dist/chunks/{config-DCq4JySZ.js → config-D3HOuHDe.js} +1 -1
- package/dist/chunks/{config-DCq4JySZ.js.map → config-D3HOuHDe.js.map} +1 -1
- package/dist/chunks/{config-BhfNgyWL.js → config-DENqOoh0.js} +5 -1
- package/dist/chunks/config-DENqOoh0.js.map +1 -0
- package/dist/chunks/controlState-DoImKciE.js +35 -0
- package/dist/chunks/controlState-DoImKciE.js.map +1 -0
- package/dist/chunks/{en-DiHzFact.js → en-Bxyn5lW7.js} +12 -2
- package/dist/chunks/en-Bxyn5lW7.js.map +1 -0
- package/dist/chunks/{es-CeKkMxZ_.js → es-Bj_vc18Z.js} +3 -3
- package/dist/chunks/{es-CeKkMxZ_.js.map → es-Bj_vc18Z.js.map} +1 -1
- package/dist/chunks/{es-1ZL6NtkB.js → es-Dtl-a7ui.js} +12 -2
- package/dist/chunks/es-Dtl-a7ui.js.map +1 -0
- package/dist/chunks/{generated-CQ4h6Ieq.js → generated-hELWB0gZ.js} +98 -0
- package/dist/chunks/{generated-CQ4h6Ieq.js.map → generated-hELWB0gZ.js.map} +1 -1
- package/dist/chunks/{grTableStyles-uuMPSOBR.js → grTableStyles-DkQPMfx9.js} +16 -3
- package/dist/chunks/grTableStyles-DkQPMfx9.js.map +1 -0
- package/dist/chunks/{granular-provider-CsN_R9dB.js → granular-provider-BOQp7N5o.js} +9 -9
- package/dist/chunks/{granular-provider-CsN_R9dB.js.map → granular-provider-BOQp7N5o.js.map} +1 -1
- package/dist/chunks/{ru-Dr2RYn0P.js → ru-hrB4I1u_.js} +12 -2
- package/dist/chunks/ru-hrB4I1u_.js.map +1 -0
- package/dist/chunks/x-circle-BigSZy8m.js +26 -0
- package/dist/chunks/x-circle-BigSZy8m.js.map +1 -0
- package/dist/components/GrAlert/chunks/{GrAlert-D72y1IEh.js → GrAlert-CfEZghSY.js} +2 -1
- package/dist/components/GrAlert/chunks/{GrAlert-D72y1IEh.js.map → GrAlert-CfEZghSY.js.map} +1 -1
- package/dist/components/GrAlert/index.js +1 -1
- package/dist/components/GrAutocomplete/chunks/{GrAutocomplete-D396_JrW.js → GrAutocomplete-C-uovdv8.js} +320 -224
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-C-uovdv8.js.map +1 -0
- package/dist/components/GrAutocomplete/index.js +1 -1
- package/dist/components/GrAutocomplete/styles.css +1 -1
- package/dist/components/GrAvatar/chunks/{GrAvatar-dWEXiCJG.js → GrAvatar-Do5-C409.js} +12 -1
- package/dist/components/GrAvatar/chunks/GrAvatar-Do5-C409.js.map +1 -0
- package/dist/components/GrAvatar/index.js +2 -2
- package/dist/components/GrAvatar/themes/light.css +35 -0
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-9mTR-YZ_.js → GrColorPicker-BRM_4lyI.js} +1 -1
- package/dist/components/GrColorPicker/chunks/{GrColorPicker-9mTR-YZ_.js.map → GrColorPicker-BRM_4lyI.js.map} +1 -1
- package/dist/components/GrColorPicker/index.js +1 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-Ha4s3DMT.js.map → GrCommandPalette-BJHmkDQv.js.map} +1 -1
- package/dist/components/GrCommandPalette/index.js +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-e15nFI8M.js → GrConfirmDialog-BzUQwbec.js} +1 -1
- package/dist/components/GrConfirmDialog/chunks/{GrConfirmDialog-e15nFI8M.js.map → GrConfirmDialog-BzUQwbec.js.map} +1 -1
- package/dist/components/GrConfirmDialog/index.js +1 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-DQxYYgQk.js +1519 -0
- package/dist/components/GrDataTable/chunks/GrDataTable-DQxYYgQk.js.map +1 -0
- package/dist/components/GrDataTable/index.js +2 -2
- package/dist/components/GrDataTable/themes/light.css +10 -0
- package/dist/components/GrDialogService/chunks/{GrDialogService-BmXiYmUp.js → GrDialogService-Dtq0ZCN5.js} +3 -3
- package/dist/components/GrDialogService/chunks/{GrDialogService-BmXiYmUp.js.map → GrDialogService-Dtq0ZCN5.js.map} +1 -1
- package/dist/components/GrDialogService/index.js +1 -1
- package/dist/components/GrFormField/chunks/GrFormField-DO61vxpS.js.map +1 -1
- package/dist/components/GrInput/chunks/{GrInput-TFeBm3gQ.js → GrInput-DV4gMWPR.js} +40 -11
- package/dist/components/GrInput/chunks/GrInput-DV4gMWPR.js.map +1 -0
- package/dist/components/GrInput/index.js +2 -2
- package/dist/components/GrInputTag/chunks/{GrInputTag-Ci8AO1ze.js → GrInputTag-CO0YU-ok.js} +30 -6
- package/dist/components/GrInputTag/chunks/GrInputTag-CO0YU-ok.js.map +1 -0
- package/dist/components/GrInputTag/index.js +2 -2
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-C6q8dEEZ.js → GrJsonViewer-D7CyWzhT.js} +1 -1
- package/dist/components/GrJsonViewer/chunks/{GrJsonViewer-C6q8dEEZ.js.map → GrJsonViewer-D7CyWzhT.js.map} +1 -1
- package/dist/components/GrJsonViewer/index.js +1 -1
- package/dist/components/GrNumberInput/chunks/{GrNumberInput-CL5QhOmx.js → GrNumberInput-BI6-vkcs.js} +48 -16
- package/dist/components/GrNumberInput/chunks/GrNumberInput-BI6-vkcs.js.map +1 -0
- package/dist/components/GrNumberInput/index.js +2 -2
- package/dist/components/GrPagination/chunks/{GrPagination-DhbNZHEF.js → GrPagination-BbaF5eXk.js} +10 -7
- package/dist/components/GrPagination/chunks/GrPagination-BbaF5eXk.js.map +1 -0
- package/dist/components/GrPagination/index.js +2 -2
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-8RxQsOQ_.js → GrPromptDialog-BEL-QgwE.js} +3 -3
- package/dist/components/GrPromptDialog/chunks/{GrPromptDialog-8RxQsOQ_.js.map → GrPromptDialog-BEL-QgwE.js.map} +1 -1
- package/dist/components/GrPromptDialog/index.js +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-NEEJZ8zi.js → GrResponseErrorBanner-Bdd5c07x.js} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-NEEJZ8zi.js.map → GrResponseErrorBanner-Bdd5c07x.js.map} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-ZvBvEQDZ.js → GrResponseErrorBanner-DySe1ym4.js} +1 -1
- package/dist/components/GrResponseErrorBanner/chunks/{GrResponseErrorBanner-ZvBvEQDZ.js.map → GrResponseErrorBanner-DySe1ym4.js.map} +1 -1
- package/dist/components/GrResponseErrorBanner/index.js +2 -2
- package/dist/components/GrSelect/chunks/{GrSelect-Do1iAyp1.js → GrSelect-0iI4wdR_.js} +3 -3
- package/dist/components/GrSelect/chunks/{GrSelect-Do1iAyp1.js.map → GrSelect-0iI4wdR_.js.map} +1 -1
- package/dist/components/GrSelect/index.js +1 -1
- package/dist/components/GrTable/chunks/{GrTable-iwzHvlMC.js → GrTable-CKJ-HrsN.js} +1 -1
- package/dist/components/GrTable/chunks/{GrTable-iwzHvlMC.js.map → GrTable-CKJ-HrsN.js.map} +1 -1
- package/dist/components/GrTable/index.js +2 -2
- package/dist/components/GrTextarea/chunks/{GrTextarea-BII3w61K.js → GrTextarea-Bfgc0DD2.js} +73 -32
- package/dist/components/GrTextarea/chunks/GrTextarea-Bfgc0DD2.js.map +1 -0
- package/dist/components/GrTextarea/index.js +2 -2
- package/dist/components/GrToaster/chunks/{GrToaster-D-sV-9of.js → GrToaster-CZixNk0l.js} +2 -1
- package/dist/components/GrToaster/chunks/{GrToaster-D-sV-9of.js.map → GrToaster-CZixNk0l.js.map} +1 -1
- package/dist/components/GrToaster/index.js +1 -1
- package/dist/components/GrTransfer/chunks/{GrTransfer-8FA78ny6.js → GrTransfer-DdfBUf35.js} +2 -2
- package/dist/components/GrTransfer/chunks/{GrTransfer-8FA78ny6.js.map → GrTransfer-DdfBUf35.js.map} +1 -1
- package/dist/components/GrTransfer/index.js +1 -1
- package/dist/components/GrTreeSelect/chunks/{GrTreeSelect-DBu5nMGA.js → GrTreeSelect-B6KoDYiZ.js} +43 -18
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-B6KoDYiZ.js.map +1 -0
- package/dist/components/GrTreeSelect/index.js +2 -2
- 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 +32 -32
- package/dist/theme.js +1 -1
- package/dist/tokens.js +1 -1
- package/dist/types/components/GrAutocomplete/composables/useAutocompleteNavigation.d.ts +39 -0
- package/dist/types/components/GrAutocomplete/composables/useAutocompletePanel.d.ts +36 -0
- package/dist/types/components/GrAutocomplete/composables/useAutocompleteValues.d.ts +28 -0
- package/dist/types/components/GrAutocomplete/composables/useAutocompleteVirtual.d.ts +33 -0
- package/dist/types/components/GrAutocomplete/composables/useRemoteOptions.d.ts +33 -0
- package/dist/types/components/GrAvatar/GrAvatar.vue.d.ts +11 -0
- package/dist/types/components/GrAvatar/defaults.d.ts +1 -0
- package/dist/types/components/GrAvatar/grAvatarStyles.d.ts +26 -1
- package/dist/types/components/GrDataTable/GrDataTable.vue.d.ts +60 -1
- package/dist/types/components/GrDataTable/composables/useDataTableColumnOrder.d.ts +45 -0
- package/dist/types/components/GrDataTable/composables/useDataTableColumnWidths.d.ts +38 -0
- package/dist/types/components/GrDataTable/composables/useDataTableExpansion.d.ts +46 -0
- package/dist/types/components/GrDataTable/composables/useDataTableLayout.d.ts +29 -0
- package/dist/types/components/GrDataTable/composables/useDataTableRowKeys.d.ts +16 -0
- package/dist/types/components/GrDataTable/composables/useDataTableSelection.d.ts +32 -0
- package/dist/types/components/GrDataTable/composables/useDataTableSort.d.ts +37 -0
- package/dist/types/components/GrDataTable/grDataTableStyles.d.ts +23 -0
- package/dist/types/components/GrFormField/GrFormField.vue.d.ts +7 -1
- package/dist/types/components/GrPagination/GrPagination.vue.d.ts +17 -2
- package/dist/types/components/GrPagination/grPaginationStyles.d.ts +15 -6
- package/dist/types/components/GrPagination/index.d.ts +1 -1
- package/dist/types/components/GrTable/grTableStyles.d.ts +16 -3
- package/dist/types/components/shared/controlState.d.ts +36 -0
- package/dist/types/i18n/messages/en.d.ts +20 -0
- package/dist/types/i18n/messages/es.d.ts +20 -0
- package/dist/types/i18n/messages/ru.d.ts +20 -0
- package/dist/web-types.json +79 -3
- package/package.json +1 -1
- package/dist/chunks/config-B3J0alyY.js.map +0 -1
- package/dist/chunks/config-BhfNgyWL.js.map +0 -1
- package/dist/chunks/config-CIkdsWUk.js.map +0 -1
- package/dist/chunks/config-CjKpUcmy.js.map +0 -1
- package/dist/chunks/en-DiHzFact.js.map +0 -1
- package/dist/chunks/es-1ZL6NtkB.js.map +0 -1
- package/dist/chunks/grTableStyles-uuMPSOBR.js.map +0 -1
- package/dist/chunks/ru-Dr2RYn0P.js.map +0 -1
- package/dist/chunks/x-circle-BCBURwyX.js.map +0 -1
- package/dist/components/GrAutocomplete/chunks/GrAutocomplete-D396_JrW.js.map +0 -1
- package/dist/components/GrAvatar/chunks/GrAvatar-dWEXiCJG.js.map +0 -1
- package/dist/components/GrDataTable/chunks/GrDataTable-xNSmO_I-.js +0 -1134
- package/dist/components/GrDataTable/chunks/GrDataTable-xNSmO_I-.js.map +0 -1
- package/dist/components/GrInput/chunks/GrInput-TFeBm3gQ.js.map +0 -1
- package/dist/components/GrInputTag/chunks/GrInputTag-Ci8AO1ze.js.map +0 -1
- package/dist/components/GrNumberInput/chunks/GrNumberInput-CL5QhOmx.js.map +0 -1
- package/dist/components/GrPagination/chunks/GrPagination-DhbNZHEF.js.map +0 -1
- package/dist/components/GrTextarea/chunks/GrTextarea-BII3w61K.js.map +0 -1
- package/dist/components/GrTreeSelect/chunks/GrTreeSelect-DBu5nMGA.js.map +0 -1
- package/dist/components/GrCommandPalette/chunks/{GrCommandPalette-Ha4s3DMT.js → GrCommandPalette-BJHmkDQv.js} +1 -1
|
@@ -7,14 +7,14 @@ import { r as isComposingEvent } from "../../../chunks/keyboard-DE2yhz_b.js";
|
|
|
7
7
|
import { t as loader_circle_default } from "../../../chunks/loader-circle-CfVLz7lb.js";
|
|
8
8
|
import { useFloating } from "../../../composables/useFloating.js";
|
|
9
9
|
import { useDismissible } from "../../../composables/useDismissible.js";
|
|
10
|
+
import { a as resolveSelectedOptions, t as filterOptions } from "../../../chunks/optionFilter-DYw9XPPx.js";
|
|
10
11
|
import { useVirtualList } from "../../../composables/useVirtualList.js";
|
|
12
|
+
import { useComboboxNavigation } from "../../../composables/useComboboxNavigation.js";
|
|
11
13
|
import { n as useGrFormFieldContext } from "../../../chunks/context-DdNIxa1w.js";
|
|
12
14
|
import { useGrFormControl } from "../../../composables/useGrFormControl.js";
|
|
13
15
|
import { t as useFocusWithin } from "../../../chunks/useFocusWithin-BwHE0rzm.js";
|
|
14
16
|
import { t as useControlledOpen } from "../../../chunks/useControlledOpen-DV3SqLTP.js";
|
|
15
|
-
import { useComboboxNavigation } from "../../../composables/useComboboxNavigation.js";
|
|
16
17
|
import { useRovingFocus } from "../../../composables/useRovingFocus.js";
|
|
17
|
-
import { a as resolveSelectedOptions, t as filterOptions } from "../../../chunks/optionFilter-DYw9XPPx.js";
|
|
18
18
|
import { t as useControlAddons } from "../../../chunks/useControlAddons-asUr10Pf.js";
|
|
19
19
|
import { d as chipSizeForBadgeScale } from "../../../chunks/grChipStyles-DcDyxAL0.js";
|
|
20
20
|
import { t as GrChip_default } from "../../GrChip/chunks/GrChip-CKqyzYcv.js";
|
|
@@ -24,7 +24,262 @@ import { t as check_default } from "../../../chunks/check-BZI24Qs4.js";
|
|
|
24
24
|
import { t as chevron_down_default } from "../../../chunks/chevron-down-CQS2OG_s.js";
|
|
25
25
|
import { t as _plugin_vue_export_helper_default } from "../../../chunks/_plugin-vue_export-helper-BOaGB7Aw.js";
|
|
26
26
|
import { Fragment, Teleport, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, mergeProps, nextTick, normalizeClass, normalizeStyle, onBeforeUnmount, openBlock, ref, renderList, renderSlot, shallowRef, toDisplayString, unref, useId, vShow, watch, withCtx, withDirectives, withModifiers } from "vue";
|
|
27
|
-
import '../styles.css';//#region src/components/GrAutocomplete/
|
|
27
|
+
import '../styles.css';//#region src/components/GrAutocomplete/composables/useRemoteOptions.ts
|
|
28
|
+
function useRemoteOptions(options) {
|
|
29
|
+
const remoteOptions = shallowRef([]);
|
|
30
|
+
const remoteAnswered = ref(false);
|
|
31
|
+
const remoteLoading = ref(false);
|
|
32
|
+
let searchTimer = null;
|
|
33
|
+
let searchSeq = 0;
|
|
34
|
+
let inflight = null;
|
|
35
|
+
function isAbortError(error) {
|
|
36
|
+
return error instanceof Error && error.name === "AbortError";
|
|
37
|
+
}
|
|
38
|
+
async function runFetch(query) {
|
|
39
|
+
const fetchOptions = options.fetchOptions();
|
|
40
|
+
if (!fetchOptions) return;
|
|
41
|
+
inflight?.abort();
|
|
42
|
+
const controller = new AbortController();
|
|
43
|
+
inflight = controller;
|
|
44
|
+
const seq = ++searchSeq;
|
|
45
|
+
remoteLoading.value = true;
|
|
46
|
+
try {
|
|
47
|
+
const result = await fetchOptions(query, controller.signal);
|
|
48
|
+
if (seq !== searchSeq) return;
|
|
49
|
+
remoteOptions.value = result;
|
|
50
|
+
remoteAnswered.value = true;
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (seq !== searchSeq || isAbortError(error)) return;
|
|
53
|
+
remoteOptions.value = [];
|
|
54
|
+
remoteAnswered.value = true;
|
|
55
|
+
options.onSearchError(error);
|
|
56
|
+
} finally {
|
|
57
|
+
if (seq === searchSeq) remoteLoading.value = false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function scheduleSearch(value) {
|
|
61
|
+
if (searchTimer) clearTimeout(searchTimer);
|
|
62
|
+
searchTimer = setTimeout(() => {
|
|
63
|
+
if (options.minQueryLength() > 0 && value.trim().length < options.minQueryLength()) return;
|
|
64
|
+
options.onSearch(value.trim());
|
|
65
|
+
runFetch(value.trim());
|
|
66
|
+
}, options.debounce());
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Снять запланированный и летящий запрос. Инкремент `searchSeq` обязателен
|
|
70
|
+
* вместе со снятием `remoteLoading`: после него `finally` в `runFetch` считает
|
|
71
|
+
* себя устаревшим и флаг не тронет — спиннер остался бы навсегда.
|
|
72
|
+
*/
|
|
73
|
+
function cancelSearch() {
|
|
74
|
+
if (searchTimer) clearTimeout(searchTimer);
|
|
75
|
+
searchTimer = null;
|
|
76
|
+
inflight?.abort();
|
|
77
|
+
inflight = null;
|
|
78
|
+
searchSeq += 1;
|
|
79
|
+
remoteLoading.value = false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Подпись состава стартового списка. Сравнивать идентичность массива нельзя:
|
|
83
|
+
* инлайн-литерал `:options="[...]"` пересоздаётся каждым ререндером родителя —
|
|
84
|
+
* в том числе тем, который вызвал сам компонент своим `update:modelValue`, —
|
|
85
|
+
* и remote-результаты исчезали бы прямо посреди выбора.
|
|
86
|
+
*/
|
|
87
|
+
function optionsSignature(list) {
|
|
88
|
+
return (list ?? []).map((o) => `${String(o.value)}\u0000${o.label}`).join("");
|
|
89
|
+
}
|
|
90
|
+
watch(() => optionsSignature(options.options()), () => {
|
|
91
|
+
if (!options.fetchOptions()) return;
|
|
92
|
+
cancelSearch();
|
|
93
|
+
remoteAnswered.value = false;
|
|
94
|
+
remoteOptions.value = [];
|
|
95
|
+
});
|
|
96
|
+
onBeforeUnmount(cancelSearch);
|
|
97
|
+
return {
|
|
98
|
+
remoteOptions,
|
|
99
|
+
remoteAnswered,
|
|
100
|
+
remoteLoading,
|
|
101
|
+
scheduleSearch,
|
|
102
|
+
cancelSearch
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/components/GrAutocomplete/composables/useAutocompleteValues.ts
|
|
107
|
+
function useAutocompleteValues(options) {
|
|
108
|
+
const optionsResolved = computed(() => options.fetchOptions() && options.remoteAnswered.value ? options.remoteOptions.value : options.options() ?? []);
|
|
109
|
+
/** `0` — валидное значение, поэтому «пусто» проверяется явно, а не через falsy. */
|
|
110
|
+
function isEmptyValue(value) {
|
|
111
|
+
return value === void 0 || value === null || value === "";
|
|
112
|
+
}
|
|
113
|
+
function toArray(value) {
|
|
114
|
+
if (Array.isArray(value)) return value;
|
|
115
|
+
if (isEmptyValue(value)) return [];
|
|
116
|
+
return [value];
|
|
117
|
+
}
|
|
118
|
+
const modelSingle = computed(() => {
|
|
119
|
+
const model = options.modelValue();
|
|
120
|
+
const raw = Array.isArray(model) ? model[0] : model;
|
|
121
|
+
return isEmptyValue(raw) ? "" : raw;
|
|
122
|
+
});
|
|
123
|
+
const selectedValues = computed(() => options.multiple() ? toArray(options.modelValue()) : isEmptyValue(modelSingle.value) ? [] : [modelSingle.value]);
|
|
124
|
+
const hasSelection = computed(() => selectedValues.value.length > 0);
|
|
125
|
+
function labelFor(value) {
|
|
126
|
+
return optionsResolved.value.find((o) => o.value === value)?.label ?? String(value);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
optionsResolved,
|
|
130
|
+
isEmptyValue,
|
|
131
|
+
modelSingle,
|
|
132
|
+
selectedValues,
|
|
133
|
+
hasSelection,
|
|
134
|
+
labelFor,
|
|
135
|
+
selectedOptions: computed(() => resolveSelectedOptions(selectedValues.value, optionsResolved.value)),
|
|
136
|
+
singleSelectedLabel: computed(() => options.multiple() || isEmptyValue(modelSingle.value) ? "" : labelFor(modelSingle.value))
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/components/GrAutocomplete/composables/useAutocompletePanel.ts
|
|
141
|
+
function useAutocompletePanel(options) {
|
|
142
|
+
const searchQuery = computed(() => {
|
|
143
|
+
if (!options.filterable() || options.fetchOptions()) return "";
|
|
144
|
+
if (!options.multiple() && !options.dirty.value) return "";
|
|
145
|
+
return options.query.value.trim();
|
|
146
|
+
});
|
|
147
|
+
const filteredOptions = computed(() => filterOptions(options.optionsResolved.value, searchQuery.value, options.filter()));
|
|
148
|
+
const belowMinQuery = computed(() => options.minQueryLength() > 0 && options.query.value.trim().length < options.minQueryLength());
|
|
149
|
+
/**
|
|
150
|
+
* Опции к показу и навигации. Ниже `minQueryLength` — пусто: список ещё
|
|
151
|
+
* относится к прошлому запросу, показывать его под подсказкой «введите ещё N»
|
|
152
|
+
* значит дезинформировать.
|
|
153
|
+
*/
|
|
154
|
+
const effectiveOptions = computed(() => belowMinQuery.value ? [] : filteredOptions.value);
|
|
155
|
+
const canAddCustom = computed(() => {
|
|
156
|
+
if (!options.allowCustomValue()) return false;
|
|
157
|
+
const v = options.query.value.trim();
|
|
158
|
+
if (!v) return false;
|
|
159
|
+
if (options.multiple() && options.selectedValues.value.includes(v)) return false;
|
|
160
|
+
if (!options.multiple() && v === options.modelSingle.value) return false;
|
|
161
|
+
return !options.optionsResolved.value.some((o) => o.value === v || o.label === v);
|
|
162
|
+
});
|
|
163
|
+
return {
|
|
164
|
+
searchQuery,
|
|
165
|
+
belowMinQuery,
|
|
166
|
+
effectiveOptions,
|
|
167
|
+
canAddCustom,
|
|
168
|
+
showEmpty: computed(() => !options.isLoading.value && effectiveOptions.value.length === 0 && !canAddCustom.value)
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/components/GrAutocomplete/composables/useAutocompleteVirtual.ts
|
|
173
|
+
/** Оценка высоты опции: `py-2` вокруг строки кегля `sm`. Уточняется замером. */
|
|
174
|
+
var OPTION_SIZE_ESTIMATE = 36;
|
|
175
|
+
function useAutocompleteVirtual(options) {
|
|
176
|
+
const addOffset = computed(() => options.canAddCustom.value ? 1 : 0);
|
|
177
|
+
const virtualCount = computed(() => options.effectiveOptions.value.length + addOffset.value);
|
|
178
|
+
const virtualizer = useVirtualList({
|
|
179
|
+
container: options.listboxEl,
|
|
180
|
+
count: () => options.virtual() ? virtualCount.value : 0,
|
|
181
|
+
source: () => options.effectiveOptions.value,
|
|
182
|
+
itemSize: OPTION_SIZE_ESTIMATE,
|
|
183
|
+
viewportSize: () => options.dropdownMaxHeight()
|
|
184
|
+
});
|
|
185
|
+
/** Виден ли «Add …»: вне виртуального окна его рисовать нельзя — он элемент набора. */
|
|
186
|
+
const showAddOption = computed(() => {
|
|
187
|
+
if (!options.canAddCustom.value) return false;
|
|
188
|
+
return !options.virtual() || virtualizer.range.value.start === 0;
|
|
189
|
+
});
|
|
190
|
+
/** Опции к отрисовке вместе с их абсолютным индексом в `effectiveOptions`. */
|
|
191
|
+
const renderedOptions = computed(() => {
|
|
192
|
+
const all = options.effectiveOptions.value;
|
|
193
|
+
if (!options.virtual()) return all.map((option, index) => ({
|
|
194
|
+
option,
|
|
195
|
+
index
|
|
196
|
+
}));
|
|
197
|
+
const { start, end } = virtualizer.range.value;
|
|
198
|
+
const from = Math.max(0, start - addOffset.value);
|
|
199
|
+
const to = Math.max(0, end - addOffset.value);
|
|
200
|
+
return all.slice(from, to).map((option, offset) => ({
|
|
201
|
+
option,
|
|
202
|
+
index: from + offset
|
|
203
|
+
}));
|
|
204
|
+
});
|
|
205
|
+
/**
|
|
206
|
+
* Размер набора и позиция в нём объявляются только при виртуализации: в обычном
|
|
207
|
+
* режиме диктор выводит их из DOM, а при неполном наборе получил бы «3 из 15»
|
|
208
|
+
* на списке в десять тысяч.
|
|
209
|
+
*/
|
|
210
|
+
function optionSetProps(virtualIndex) {
|
|
211
|
+
if (!options.virtual()) return void 0;
|
|
212
|
+
return {
|
|
213
|
+
"aria-setsize": virtualCount.value,
|
|
214
|
+
"aria-posinset": virtualIndex + 1
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
addOffset,
|
|
219
|
+
virtualCount,
|
|
220
|
+
virtualizer,
|
|
221
|
+
showAddOption,
|
|
222
|
+
renderedOptions,
|
|
223
|
+
optionSetProps,
|
|
224
|
+
listboxStyle: computed(() => {
|
|
225
|
+
const base = { maxHeight: `${options.dropdownMaxHeight()}px` };
|
|
226
|
+
if (!options.virtual()) return base;
|
|
227
|
+
return {
|
|
228
|
+
...base,
|
|
229
|
+
...virtualizer.spacerStyle.value
|
|
230
|
+
};
|
|
231
|
+
})
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
//#endregion
|
|
235
|
+
//#region src/components/GrAutocomplete/composables/useAutocompleteNavigation.ts
|
|
236
|
+
function useAutocompleteNavigation(options) {
|
|
237
|
+
const navigableItems = computed(() => {
|
|
238
|
+
const items = options.canAddCustom.value ? [{ kind: "add" }] : [];
|
|
239
|
+
options.effectiveOptions.value.forEach((option, index) => {
|
|
240
|
+
if (!option.disabled) items.push({
|
|
241
|
+
kind: "option",
|
|
242
|
+
option,
|
|
243
|
+
index
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
return items;
|
|
247
|
+
});
|
|
248
|
+
function isSelected(value) {
|
|
249
|
+
return options.selectedValues.value.includes(value);
|
|
250
|
+
}
|
|
251
|
+
function navigableIndexOf(value) {
|
|
252
|
+
return navigableItems.value.findIndex((item) => item.kind === "option" && item.option.value === value);
|
|
253
|
+
}
|
|
254
|
+
/** Прокрутка к активному: сперва окно виртуального списка, затем доводка. */
|
|
255
|
+
async function scrollActiveIntoView(item) {
|
|
256
|
+
if (options.virtual()) options.scrollToIndex(item.kind === "add" ? 0 : item.index + options.addOffset.value);
|
|
257
|
+
await nextTick();
|
|
258
|
+
const id = item.kind === "add" ? options.addOptionDomId.value : options.optionDomId(item.index);
|
|
259
|
+
document.getElementById(id)?.scrollIntoView?.({ block: "nearest" });
|
|
260
|
+
}
|
|
261
|
+
const { activeIndex, activeItem, activeDescendantId, init: initActiveIndex, reset: resetActive, handleNavigationKeys } = useComboboxNavigation({
|
|
262
|
+
items: () => navigableItems.value,
|
|
263
|
+
open: () => options.open.value,
|
|
264
|
+
idOf: (item) => item.kind === "add" ? options.addOptionDomId.value : options.optionDomId(item.index),
|
|
265
|
+
initialIndex: () => navigableItems.value.findIndex((item) => item.kind === "option" && isSelected(item.option.value)),
|
|
266
|
+
scrollTo: (item) => scrollActiveIntoView(item)
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
navigableItems,
|
|
270
|
+
isSelected,
|
|
271
|
+
navigableIndexOf,
|
|
272
|
+
activeIndex,
|
|
273
|
+
activeItem,
|
|
274
|
+
activeDescendantId,
|
|
275
|
+
activeValue: computed(() => activeItem.value?.kind === "option" ? activeItem.value.option.value : void 0),
|
|
276
|
+
initActiveIndex,
|
|
277
|
+
resetActive,
|
|
278
|
+
handleNavigationKeys
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/components/GrAutocomplete/GrAutocomplete.vue?vue&type=script&setup=true&lang.ts
|
|
28
283
|
var _hoisted_1 = { class: "truncate" };
|
|
29
284
|
var _hoisted_2 = [
|
|
30
285
|
"id",
|
|
@@ -87,7 +342,6 @@ var _hoisted_15 = {
|
|
|
87
342
|
key: 2,
|
|
88
343
|
"data-gr-autocomplete-empty": ""
|
|
89
344
|
};
|
|
90
|
-
var OPTION_SIZE_ESTIMATE = 36;
|
|
91
345
|
//#endregion
|
|
92
346
|
//#region src/components/GrAutocomplete/GrAutocomplete.vue
|
|
93
347
|
var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/* @__PURE__ */ defineComponent({
|
|
@@ -225,47 +479,23 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
225
479
|
const { disabled: isDisabled, invalid: isInvalid, required: isRequired, readonly: isReadonly } = useGrFormControl(() => props);
|
|
226
480
|
const describedBy = computed(() => field?.describedById.value);
|
|
227
481
|
const locked = computed(() => isDisabled.value || isReadonly.value);
|
|
228
|
-
const remoteOptions =
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
* в том числе тем, который вызвал сам компонент своим `update:modelValue`, —
|
|
236
|
-
* и remote-результаты исчезали бы прямо посреди выбора.
|
|
237
|
-
*/
|
|
238
|
-
function optionsSignature(options) {
|
|
239
|
-
return (options ?? []).map((o) => `${String(o.value)}\u0000${o.label}`).join("");
|
|
240
|
-
}
|
|
241
|
-
watch(() => optionsSignature(props.options), () => {
|
|
242
|
-
if (!props.fetchOptions) return;
|
|
243
|
-
cancelSearch();
|
|
244
|
-
remoteAnswered.value = false;
|
|
245
|
-
remoteOptions.value = [];
|
|
482
|
+
const { remoteOptions, remoteAnswered, remoteLoading, scheduleSearch } = useRemoteOptions({
|
|
483
|
+
fetchOptions: () => props.fetchOptions,
|
|
484
|
+
options: () => props.options,
|
|
485
|
+
debounce: () => props.debounce,
|
|
486
|
+
minQueryLength: () => props.minQueryLength,
|
|
487
|
+
onSearch: (value) => emit("search", value),
|
|
488
|
+
onSearchError: (error) => emit("searchError", error)
|
|
246
489
|
});
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (isEmptyValue(value)) return [];
|
|
255
|
-
return [value];
|
|
256
|
-
}
|
|
257
|
-
const modelSingle = computed(() => {
|
|
258
|
-
const raw = Array.isArray(props.modelValue) ? props.modelValue[0] : props.modelValue;
|
|
259
|
-
return isEmptyValue(raw) ? "" : raw;
|
|
490
|
+
const { optionsResolved, modelSingle, selectedValues, hasSelection, selectedOptions, singleSelectedLabel } = useAutocompleteValues({
|
|
491
|
+
modelValue: () => props.modelValue,
|
|
492
|
+
options: () => props.options,
|
|
493
|
+
multiple: () => props.multiple,
|
|
494
|
+
fetchOptions: () => props.fetchOptions,
|
|
495
|
+
remoteOptions,
|
|
496
|
+
remoteAnswered
|
|
260
497
|
});
|
|
261
|
-
const
|
|
262
|
-
const hasSelection = computed(() => selectedValues.value.length > 0);
|
|
263
|
-
function labelFor(value) {
|
|
264
|
-
return optionsResolved.value.find((o) => o.value === value)?.label ?? String(value);
|
|
265
|
-
}
|
|
266
|
-
/** Опции выбранных значений (для chips в multiple). Неизвестные значения показываем как есть. */
|
|
267
|
-
const selectedOptions = computed(() => resolveSelectedOptions(selectedValues.value, optionsResolved.value));
|
|
268
|
-
const singleSelectedLabel = computed(() => props.multiple || isEmptyValue(modelSingle.value) ? "" : labelFor(modelSingle.value));
|
|
498
|
+
const isLoading = computed(() => props.loading || remoteLoading.value);
|
|
269
499
|
const query = ref("");
|
|
270
500
|
const dirty = ref(false);
|
|
271
501
|
const { open, setOpen } = useControlledOpen(() => props.open, (next) => emit("update:open", next));
|
|
@@ -297,28 +527,20 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
297
527
|
zIndexVar: "--gr-z-dropdown"
|
|
298
528
|
});
|
|
299
529
|
useDismissible(open, closeDropdown);
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
530
|
+
const { belowMinQuery, effectiveOptions, canAddCustom, showEmpty } = useAutocompletePanel({
|
|
531
|
+
optionsResolved,
|
|
532
|
+
query,
|
|
533
|
+
dirty,
|
|
534
|
+
filterable: () => props.filterable,
|
|
535
|
+
filter: () => props.filter,
|
|
536
|
+
fetchOptions: () => props.fetchOptions,
|
|
537
|
+
multiple: () => props.multiple,
|
|
538
|
+
minQueryLength: () => props.minQueryLength,
|
|
539
|
+
allowCustomValue: () => props.allowCustomValue,
|
|
540
|
+
selectedValues,
|
|
541
|
+
modelSingle,
|
|
542
|
+
isLoading
|
|
304
543
|
});
|
|
305
|
-
const filteredOptions = computed(() => filterOptions(optionsResolved.value, searchQuery.value, props.filter));
|
|
306
|
-
const belowMinQuery = computed(() => props.minQueryLength > 0 && query.value.trim().length < props.minQueryLength);
|
|
307
|
-
/**
|
|
308
|
-
* Опции к показу и навигации. Ниже `minQueryLength` — пусто: список ещё
|
|
309
|
-
* относится к прошлому запросу, показывать его под подсказкой «введите ещё N»
|
|
310
|
-
* значит дезинформировать.
|
|
311
|
-
*/
|
|
312
|
-
const effectiveOptions = computed(() => belowMinQuery.value ? [] : filteredOptions.value);
|
|
313
|
-
const canAddCustom = computed(() => {
|
|
314
|
-
if (!props.allowCustomValue) return false;
|
|
315
|
-
const v = query.value.trim();
|
|
316
|
-
if (!v) return false;
|
|
317
|
-
if (props.multiple && selectedValues.value.includes(v)) return false;
|
|
318
|
-
if (!props.multiple && v === modelSingle.value) return false;
|
|
319
|
-
return !optionsResolved.value.some((o) => o.value === v || o.label === v);
|
|
320
|
-
});
|
|
321
|
-
const showEmpty = computed(() => !isLoading.value && effectiveOptions.value.length === 0 && !canAddCustom.value);
|
|
322
544
|
const listboxId = useId();
|
|
323
545
|
/**
|
|
324
546
|
* Id опции строится от позиции, а не от значения: значение с пробелом дало бы
|
|
@@ -329,100 +551,24 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
329
551
|
return `${listboxId}-opt-${index}`;
|
|
330
552
|
}
|
|
331
553
|
const addOptionDomId = computed(() => `${listboxId}-add`);
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
*/
|
|
339
|
-
/** Оценка высоты опции: `py-2` вокруг строки кегля `sm`. Уточняется замером. */
|
|
340
|
-
const addOffset = computed(() => canAddCustom.value ? 1 : 0);
|
|
341
|
-
const virtualCount = computed(() => effectiveOptions.value.length + addOffset.value);
|
|
342
|
-
const virtualizer = useVirtualList({
|
|
343
|
-
container: listboxEl,
|
|
344
|
-
count: () => props.virtual ? virtualCount.value : 0,
|
|
345
|
-
source: () => effectiveOptions.value,
|
|
346
|
-
itemSize: OPTION_SIZE_ESTIMATE,
|
|
347
|
-
viewportSize: () => props.dropdownMaxHeight
|
|
348
|
-
});
|
|
349
|
-
/** Виден ли «Add …»: вне виртуального окна его рисовать нельзя — он элемент набора. */
|
|
350
|
-
const showAddOption = computed(() => {
|
|
351
|
-
if (!canAddCustom.value) return false;
|
|
352
|
-
return !props.virtual || virtualizer.range.value.start === 0;
|
|
353
|
-
});
|
|
354
|
-
/** Опции к отрисовке вместе с их абсолютным индексом в `filteredOptions`. */
|
|
355
|
-
const renderedOptions = computed(() => {
|
|
356
|
-
const all = effectiveOptions.value;
|
|
357
|
-
if (!props.virtual) return all.map((option, index) => ({
|
|
358
|
-
option,
|
|
359
|
-
index
|
|
360
|
-
}));
|
|
361
|
-
const { start, end } = virtualizer.range.value;
|
|
362
|
-
const from = Math.max(0, start - addOffset.value);
|
|
363
|
-
const to = Math.max(0, end - addOffset.value);
|
|
364
|
-
return all.slice(from, to).map((option, offset) => ({
|
|
365
|
-
option,
|
|
366
|
-
index: from + offset
|
|
367
|
-
}));
|
|
368
|
-
});
|
|
369
|
-
/**
|
|
370
|
-
* Размер набора и позиция в нём объявляются только при виртуализации: в обычном
|
|
371
|
-
* режиме диктор выводит их из DOM, а при неполном наборе получил бы «3 из 15»
|
|
372
|
-
* на списке в десять тысяч.
|
|
373
|
-
*/
|
|
374
|
-
function optionSetProps(virtualIndex) {
|
|
375
|
-
if (!props.virtual) return void 0;
|
|
376
|
-
return {
|
|
377
|
-
"aria-setsize": virtualCount.value,
|
|
378
|
-
"aria-posinset": virtualIndex + 1
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
const listboxStyle = computed(() => {
|
|
382
|
-
const base = { maxHeight: `${props.dropdownMaxHeight}px` };
|
|
383
|
-
if (!props.virtual) return base;
|
|
384
|
-
return {
|
|
385
|
-
...base,
|
|
386
|
-
...virtualizer.spacerStyle.value
|
|
387
|
-
};
|
|
388
|
-
});
|
|
389
|
-
/**
|
|
390
|
-
* Клавиатурная навигация ходит и по опциям, и по варианту «добавить своё»:
|
|
391
|
-
* иначе при непустом списке Enter всегда уходил бы в активную опцию, и
|
|
392
|
-
* закоммитить произвольное значение с клавиатуры было бы нечем.
|
|
393
|
-
*/
|
|
394
|
-
const navigableItems = computed(() => {
|
|
395
|
-
const items = canAddCustom.value ? [{ kind: "add" }] : [];
|
|
396
|
-
effectiveOptions.value.forEach((option, index) => {
|
|
397
|
-
if (!option.disabled) items.push({
|
|
398
|
-
kind: "option",
|
|
399
|
-
option,
|
|
400
|
-
index
|
|
401
|
-
});
|
|
402
|
-
});
|
|
403
|
-
return items;
|
|
554
|
+
const { addOffset, virtualizer, showAddOption, renderedOptions, optionSetProps, listboxStyle } = useAutocompleteVirtual({
|
|
555
|
+
listboxEl,
|
|
556
|
+
effectiveOptions,
|
|
557
|
+
canAddCustom,
|
|
558
|
+
virtual: () => props.virtual,
|
|
559
|
+
dropdownMaxHeight: () => props.dropdownMaxHeight
|
|
404
560
|
});
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const id = item.kind === "add" ? addOptionDomId.value : optionDomId(item.index);
|
|
416
|
-
document.getElementById(id)?.scrollIntoView?.({ block: "nearest" });
|
|
417
|
-
}
|
|
418
|
-
const { activeIndex, activeItem, activeDescendantId, init: initActiveIndex, reset: resetActive, handleNavigationKeys } = useComboboxNavigation({
|
|
419
|
-
items: () => navigableItems.value,
|
|
420
|
-
open: () => open.value,
|
|
421
|
-
idOf: (item) => item.kind === "add" ? addOptionDomId.value : optionDomId(item.index),
|
|
422
|
-
initialIndex: () => navigableItems.value.findIndex((item) => item.kind === "option" && isSelected(item.option.value)),
|
|
423
|
-
scrollTo: (item) => scrollActiveIntoView(item)
|
|
561
|
+
const { isSelected, navigableIndexOf, activeIndex, activeItem, activeDescendantId, activeValue, initActiveIndex, resetActive, handleNavigationKeys } = useAutocompleteNavigation({
|
|
562
|
+
effectiveOptions,
|
|
563
|
+
canAddCustom,
|
|
564
|
+
selectedValues,
|
|
565
|
+
open,
|
|
566
|
+
virtual: () => props.virtual,
|
|
567
|
+
addOffset,
|
|
568
|
+
scrollToIndex: (index) => virtualizer.scrollToIndex(index),
|
|
569
|
+
optionDomId,
|
|
570
|
+
addOptionDomId
|
|
424
571
|
});
|
|
425
|
-
const activeValue = computed(() => activeItem.value?.kind === "option" ? activeItem.value.option.value : void 0);
|
|
426
572
|
function openDropdown() {
|
|
427
573
|
if (locked.value || open.value) return;
|
|
428
574
|
setOpen(true);
|
|
@@ -433,56 +579,6 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
433
579
|
function focusInput() {
|
|
434
580
|
inputEl.value?.focus();
|
|
435
581
|
}
|
|
436
|
-
let searchTimer = null;
|
|
437
|
-
let searchSeq = 0;
|
|
438
|
-
let inflight = null;
|
|
439
|
-
function isAbortError(error) {
|
|
440
|
-
return error instanceof Error && error.name === "AbortError";
|
|
441
|
-
}
|
|
442
|
-
async function runFetch(query) {
|
|
443
|
-
const fetchOptions = props.fetchOptions;
|
|
444
|
-
if (!fetchOptions) return;
|
|
445
|
-
inflight?.abort();
|
|
446
|
-
const controller = new AbortController();
|
|
447
|
-
inflight = controller;
|
|
448
|
-
const seq = ++searchSeq;
|
|
449
|
-
remoteLoading.value = true;
|
|
450
|
-
try {
|
|
451
|
-
const result = await fetchOptions(query, controller.signal);
|
|
452
|
-
if (seq !== searchSeq) return;
|
|
453
|
-
remoteOptions.value = result;
|
|
454
|
-
remoteAnswered.value = true;
|
|
455
|
-
} catch (error) {
|
|
456
|
-
if (seq !== searchSeq || isAbortError(error)) return;
|
|
457
|
-
remoteOptions.value = [];
|
|
458
|
-
remoteAnswered.value = true;
|
|
459
|
-
emit("searchError", error);
|
|
460
|
-
} finally {
|
|
461
|
-
if (seq === searchSeq) remoteLoading.value = false;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
function scheduleSearch(value) {
|
|
465
|
-
if (searchTimer) clearTimeout(searchTimer);
|
|
466
|
-
searchTimer = setTimeout(() => {
|
|
467
|
-
if (props.minQueryLength > 0 && value.trim().length < props.minQueryLength) return;
|
|
468
|
-
emit("search", value.trim());
|
|
469
|
-
runFetch(value.trim());
|
|
470
|
-
}, props.debounce);
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Снять запланированный и летящий запрос. Инкремент `searchSeq` обязателен
|
|
474
|
-
* вместе со снятием `remoteLoading`: после него `finally` в `runFetch` считает
|
|
475
|
-
* себя устаревшим и флаг не тронет — спиннер остался бы навсегда.
|
|
476
|
-
*/
|
|
477
|
-
function cancelSearch() {
|
|
478
|
-
if (searchTimer) clearTimeout(searchTimer);
|
|
479
|
-
searchTimer = null;
|
|
480
|
-
inflight?.abort();
|
|
481
|
-
inflight = null;
|
|
482
|
-
searchSeq += 1;
|
|
483
|
-
remoteLoading.value = false;
|
|
484
|
-
}
|
|
485
|
-
onBeforeUnmount(cancelSearch);
|
|
486
582
|
function onInput(event) {
|
|
487
583
|
const value = event.target.value;
|
|
488
584
|
query.value = value;
|
|
@@ -666,7 +762,7 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
666
762
|
class: "inline-flex shrink-0 items-center justify-center text-[var(--gr-muted-fg)]",
|
|
667
763
|
style: normalizeStyle(unref(prefixStyle))
|
|
668
764
|
}, [renderSlot(_ctx.$slots, "prefix", {}, void 0, true)], 4)) : createCommentVNode("", true),
|
|
669
|
-
__props.multiple ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(selectedOptions
|
|
765
|
+
__props.multiple ? (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(unref(selectedOptions), (option, chipIndex) => {
|
|
670
766
|
return openBlock(), createBlock(GrChip_default, {
|
|
671
767
|
key: option.value,
|
|
672
768
|
ref_for: true,
|
|
@@ -707,7 +803,7 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
707
803
|
role: "combobox",
|
|
708
804
|
value: query.value,
|
|
709
805
|
disabled: unref(isDisabled),
|
|
710
|
-
placeholder: hasSelection
|
|
806
|
+
placeholder: unref(hasSelection) && __props.multiple ? void 0 : __props.placeholder,
|
|
711
807
|
"aria-label": __props.ariaLabel,
|
|
712
808
|
"aria-invalid": unref(isInvalid) ? "true" : void 0,
|
|
713
809
|
"aria-describedby": describedBy.value,
|
|
@@ -751,7 +847,7 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
751
847
|
"aria-hidden": "true"
|
|
752
848
|
})]))
|
|
753
849
|
], 34),
|
|
754
|
-
__props.name ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(selectedValues
|
|
850
|
+
__props.name ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(unref(selectedValues), (value) => {
|
|
755
851
|
return openBlock(), createElementBlock("input", {
|
|
756
852
|
key: `hidden-${String(value)}`,
|
|
757
853
|
type: "hidden",
|
|
@@ -778,7 +874,7 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
778
874
|
"data-gr-autocomplete-panel": "",
|
|
779
875
|
"data-gr-overlay-root": "",
|
|
780
876
|
style: unref(floatingStyle)
|
|
781
|
-
}), [createElementVNode("div", { class: normalizeClass(unref(autocompletePanelClasses)) }, [showAddOption
|
|
877
|
+
}), [createElementVNode("div", { class: normalizeClass(unref(autocompletePanelClasses)) }, [unref(showAddOption) || unref(renderedOptions).length ? (openBlock(), createElementBlock("div", {
|
|
782
878
|
key: 0,
|
|
783
879
|
id: unref(listboxId),
|
|
784
880
|
ref_key: "listboxEl",
|
|
@@ -786,17 +882,17 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
786
882
|
"data-gr-autocomplete-listbox": "",
|
|
787
883
|
"data-gr-virtual": __props.virtual ? "" : void 0,
|
|
788
884
|
class: normalizeClass(["p-1 overflow-auto", __props.virtual ? "flex flex-col" : ""]),
|
|
789
|
-
style: normalizeStyle(listboxStyle
|
|
885
|
+
style: normalizeStyle(unref(listboxStyle)),
|
|
790
886
|
role: "listbox",
|
|
791
887
|
"aria-multiselectable": __props.multiple ? "true" : void 0
|
|
792
|
-
}, [showAddOption
|
|
888
|
+
}, [unref(showAddOption) ? (openBlock(), createElementBlock("button", mergeProps({
|
|
793
889
|
key: 0,
|
|
794
890
|
id: addOptionDomId.value,
|
|
795
891
|
type: "button",
|
|
796
892
|
role: "option",
|
|
797
893
|
"aria-selected": "false",
|
|
798
894
|
tabindex: "-1"
|
|
799
|
-
}, optionSetProps(0), {
|
|
895
|
+
}, unref(optionSetProps)(0), {
|
|
800
896
|
"data-testid": "gr-autocomplete-add-option",
|
|
801
897
|
"data-gr-autocomplete-add-option": "",
|
|
802
898
|
class: unref(autocompleteOptionClass)({
|
|
@@ -806,35 +902,35 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
806
902
|
onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"])),
|
|
807
903
|
onClick: commitCustom,
|
|
808
904
|
onMousemove: _cache[1] || (_cache[1] = ($event) => activeIndex.value = 0)
|
|
809
|
-
}), toDisplayString(unref(t)("gr.autocomplete.addOption", "Add \"{value}\"", { value: query.value.trim() })), 17, _hoisted_8)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(renderedOptions
|
|
905
|
+
}), toDisplayString(unref(t)("gr.autocomplete.addOption", "Add \"{value}\"", { value: query.value.trim() })), 17, _hoisted_8)) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(renderedOptions), ({ option, index: optionIndex }) => {
|
|
810
906
|
return openBlock(), createElementBlock("button", mergeProps({
|
|
811
907
|
id: optionDomId(optionIndex),
|
|
812
908
|
key: option.value,
|
|
813
909
|
ref_for: true,
|
|
814
|
-
ref: (el) => __props.virtual && unref(virtualizer).measure(optionIndex + addOffset
|
|
910
|
+
ref: (el) => __props.virtual && unref(virtualizer).measure(optionIndex + unref(addOffset), el),
|
|
815
911
|
"data-gr-autocomplete-option": "",
|
|
816
912
|
type: "button",
|
|
817
913
|
role: "option",
|
|
818
914
|
tabindex: "-1"
|
|
819
|
-
}, { ref_for: true }, optionSetProps(optionIndex + addOffset
|
|
915
|
+
}, { ref_for: true }, unref(optionSetProps)(optionIndex + unref(addOffset)), {
|
|
820
916
|
disabled: option.disabled,
|
|
821
|
-
"aria-selected": isSelected(option.value) ? "true" : "false",
|
|
917
|
+
"aria-selected": unref(isSelected)(option.value) ? "true" : "false",
|
|
822
918
|
"aria-disabled": option.disabled ? "true" : void 0,
|
|
823
919
|
class: unref(autocompleteOptionClass)({
|
|
824
920
|
disabled: !!option.disabled,
|
|
825
|
-
active: activeValue
|
|
921
|
+
active: unref(activeValue) === option.value
|
|
826
922
|
}),
|
|
827
923
|
onMousedown: _cache[2] || (_cache[2] = withModifiers(() => {}, ["prevent"])),
|
|
828
924
|
onClick: ($event) => chooseOption(option),
|
|
829
|
-
onMousemove: ($event) => activeIndex.value = navigableIndexOf(option.value)
|
|
925
|
+
onMousemove: ($event) => activeIndex.value = unref(navigableIndexOf)(option.value)
|
|
830
926
|
}), [renderSlot(_ctx.$slots, "option", {
|
|
831
927
|
option,
|
|
832
|
-
selected: isSelected(option.value)
|
|
833
|
-
}, () => [createElementVNode("span", _hoisted_10, [createElementVNode("span", _hoisted_11, [isSelected(option.value) ? (openBlock(), createBlock(unref(check_default), {
|
|
928
|
+
selected: unref(isSelected)(option.value)
|
|
929
|
+
}, () => [createElementVNode("span", _hoisted_10, [createElementVNode("span", _hoisted_11, [unref(isSelected)(option.value) ? (openBlock(), createBlock(unref(check_default), {
|
|
834
930
|
key: 0,
|
|
835
931
|
class: "block h-4 w-4 text-[var(--gr-primary-text)]"
|
|
836
932
|
})) : createCommentVNode("", true)]), createElementVNode("span", _hoisted_12, toDisplayString(option.label), 1)])], true)], 16, _hoisted_9);
|
|
837
|
-
}), 128))], 14, _hoisted_7)) : createCommentVNode("", true), isLoading.value || belowMinQuery
|
|
933
|
+
}), 128))], 14, _hoisted_7)) : createCommentVNode("", true), isLoading.value || unref(belowMinQuery) || unref(showEmpty) ? (openBlock(), createElementBlock("div", {
|
|
838
934
|
key: 1,
|
|
839
935
|
"data-gr-autocomplete-status": "",
|
|
840
936
|
role: "status",
|
|
@@ -843,7 +939,7 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
843
939
|
}, [isLoading.value ? (openBlock(), createElementBlock("span", _hoisted_13, [renderSlot(_ctx.$slots, "loading", {}, () => [createVNode(unref(loader_circle_default), {
|
|
844
940
|
class: "block h-4 w-4 animate-spin",
|
|
845
941
|
"aria-hidden": "true"
|
|
846
|
-
}), createElementVNode("span", null, toDisplayString(resolvedLoadingText.value), 1)], true)])) : belowMinQuery
|
|
942
|
+
}), createElementVNode("span", null, toDisplayString(resolvedLoadingText.value), 1)], true)])) : unref(belowMinQuery) ? (openBlock(), createElementBlock("span", _hoisted_14, toDisplayString(resolvedTypeMoreText.value), 1)) : (openBlock(), createElementBlock("span", _hoisted_15, [renderSlot(_ctx.$slots, "empty", {}, () => [createTextVNode(toDisplayString(resolvedNoResultsText.value), 1)], true)]))], 2)) : createCommentVNode("", true)], 2)], 16), [[vShow, unref(open)]])]),
|
|
847
943
|
_: 3
|
|
848
944
|
}, 8, [
|
|
849
945
|
"enter-active-class",
|
|
@@ -860,6 +956,6 @@ var GrAutocomplete_default = /*#__PURE__*/ _plugin_vue_export_helper_default(/*
|
|
|
860
956
|
}]]);
|
|
861
957
|
};
|
|
862
958
|
}
|
|
863
|
-
}), [["__scopeId", "data-v-
|
|
959
|
+
}), [["__scopeId", "data-v-c4989846"]]);
|
|
864
960
|
//#endregion
|
|
865
961
|
export { GrAutocomplete_default as t };
|