@eodash/eodash 5.7.0 → 5.8.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.
Files changed (119) hide show
  1. package/core/client/components/EodashOverlay.vue +27 -4
  2. package/core/client/composables/DefineEodash.js +0 -1
  3. package/core/client/composables/DefineWidgets.js +3 -1
  4. package/core/client/eodashSTAC/EodashCollection.js +87 -24
  5. package/core/client/eodashSTAC/cql.js +64 -0
  6. package/core/client/eodashSTAC/createLayers.js +353 -187
  7. package/core/client/eodashSTAC/helpers.js +694 -110
  8. package/core/client/eodashSTAC/mosaic.js +422 -0
  9. package/core/client/plugins/axios.js +31 -1
  10. package/core/client/store/actions.js +1 -1
  11. package/core/client/store/stac.js +54 -4
  12. package/core/client/store/states.js +20 -2
  13. package/core/client/types.ts +152 -28
  14. package/core/client/utils/bands-editor/exampleSchema.json +50 -50
  15. package/core/client/utils/states.js +10 -0
  16. package/dist/client/{DashboardLayout-kRsmsNW5.js → DashboardLayout-DEs4f_Vy.js} +2 -2
  17. package/dist/client/{DynamicWebComponent-CnKhYo0K.js → DynamicWebComponent-PvnV88BA.js} +1 -1
  18. package/dist/client/EodashChart-D-3AGKZY.js +260 -0
  19. package/dist/client/{EodashDatePicker-CMPDT-ce.js → EodashDatePicker-Dm_goZzs.js} +3 -3
  20. package/dist/client/{EodashItemFilter-BpdA8QQa.js → EodashItemFilter-CJy3zYzD.js} +2 -1
  21. package/dist/client/{EodashLayerControl-BHpDA4_0.js → EodashLayerControl-rL8UnpkA.js} +55 -33
  22. package/dist/client/{EodashLayoutSwitcher-B1fRUt7G.js → EodashLayoutSwitcher-ZRlESI9O.js} +3 -3
  23. package/dist/client/{EodashMapBtns-Cpned0tc.js → EodashMapBtns-CG7u2Aq8.js} +227 -112
  24. package/dist/client/EodashStacInfo-CJhhxthD.js +169 -0
  25. package/dist/client/{EodashTools-1zHz26gv.js → EodashTools-CWyK8QJT.js} +4 -4
  26. package/dist/client/{ExportState-DbOm8hBI.js → ExportState-BXhy5TOU.js} +20 -174
  27. package/dist/client/{Footer-BKg-VVkj.js → Footer-DZPvnhcT.js} +1 -1
  28. package/dist/client/{Header-DCrwCvmY.js → Header-CiGsDE_a.js} +2 -2
  29. package/dist/client/{MobileLayout-vWSHpluo.js → MobileLayout-BJHW7x5V.js} +2 -2
  30. package/dist/client/{PopUp-9uimfK49.js → PopUp-7_XnhvCc.js} +4 -168
  31. package/dist/client/{ProcessList-CTTIZa06.js → ProcessList-LL_UCbvc.js} +5 -5
  32. package/dist/client/VDivider-DWYM0WUr.js +168 -0
  33. package/dist/client/{VImg-CrmB76ZY.js → VImg-B75dgw2K.js} +2 -2
  34. package/dist/client/{VMain-OKZNpcEG.js → VMain-DvMmOL4f.js} +1 -1
  35. package/dist/client/{VTooltip-CJw2FyWk.js → VTooltip-ClWHUmWL.js} +2 -2
  36. package/dist/client/{WidgetsContainer-BaWOnE5R.js → WidgetsContainer-BqaWs4ch.js} +1 -1
  37. package/dist/client/{asWebComponent-BHWpMuE3.js → asWebComponent-IZanwL-A.js} +677 -379
  38. package/dist/client/{async-DSKj6g1f.js → async-CiNIOBVz.js} +31 -29
  39. package/dist/client/{create-layers-config-CBoYedqB.js → create-layers-config-3S3y_kZg.js} +17 -17
  40. package/dist/client/dialog-transition-c4ZSnzac.js +170 -0
  41. package/dist/client/eo-dash.js +1 -1
  42. package/dist/client/{forwardRefs-DH51zPUr.js → forwardRefs-B-c1_6yo.js} +8 -87
  43. package/dist/client/{handling-BrcDDlgS.js → handling-NvY8Csry.js} +3 -4
  44. package/dist/client/{helpers-DjZVl_WF.js → helpers-BFuh19CP.js} +1094 -194
  45. package/dist/client/{index-1JKE68n3.js → index-1wMAyuNL.js} +219 -67
  46. package/dist/client/{index-CSG_JoOA.js → index-B8jGJSTA.js} +62 -56
  47. package/dist/client/{index-Cu3-13Xg.js → index-BKQQJkhy.js} +167 -61
  48. package/dist/client/index-D0jUyol8.js +3167 -0
  49. package/dist/client/{index-CUjl-ZsI.js → index-KGxz5Egv.js} +2 -2
  50. package/dist/client/mosaic-vHvSuih6.js +459 -0
  51. package/dist/client/templates.js +251 -121
  52. package/dist/client/{transition-OHcz5Qq1.js → transition-BkyUsZh_.js} +1 -1
  53. package/dist/node/cli.js +5 -5
  54. package/dist/node/types.d.ts +4 -0
  55. package/dist/types/core/client/composables/DefineEodash.d.ts +1 -1
  56. package/dist/types/core/client/eodashSTAC/EodashCollection.d.ts +57 -25
  57. package/dist/types/core/client/eodashSTAC/cql.d.ts +15 -0
  58. package/dist/types/core/client/eodashSTAC/createLayers.d.ts +42 -23
  59. package/dist/types/core/client/eodashSTAC/helpers.d.ts +118 -19
  60. package/dist/types/core/client/eodashSTAC/mosaic.d.ts +56 -0
  61. package/dist/types/core/client/store/stac.d.ts +38 -8
  62. package/dist/types/core/client/store/states.d.ts +24 -2
  63. package/dist/types/core/client/types.d.ts +128 -24
  64. package/dist/types/core/client/utils/states.d.ts +12 -0
  65. package/dist/types/templates/baseConfig.d.ts +1 -100
  66. package/dist/types/templates/compare.d.ts +32 -3
  67. package/dist/types/templates/expert.d.ts +18 -0
  68. package/dist/types/templates/explore.d.ts +10 -1
  69. package/dist/types/templates/lite.d.ts +11 -0
  70. package/dist/types/widgets/EodashChart.vue.d.ts +5 -3
  71. package/dist/types/widgets/EodashItemCatalog/index.vue.d.ts +129 -2
  72. package/dist/types/widgets/EodashItemCatalog/methods/filters.d.ts +28 -6
  73. package/dist/types/widgets/EodashItemCatalog/methods/handlers.d.ts +17 -4
  74. package/dist/types/widgets/EodashItemCatalog/methods/map.d.ts +16 -4
  75. package/dist/types/widgets/EodashLayerControl.vue.d.ts +35 -16
  76. package/dist/types/widgets/EodashMap/EodashMapBtns.vue.d.ts +14 -10
  77. package/dist/types/widgets/EodashMap/index.vue.d.ts +86 -37
  78. package/dist/types/widgets/EodashMap/methods/create-layers-config.d.ts +1 -1
  79. package/dist/types/widgets/EodashMap/methods/index.d.ts +5 -1
  80. package/dist/types/widgets/EodashMap/methods/use-map-loading.d.ts +1 -0
  81. package/dist/types/widgets/EodashProcess/index.vue.d.ts +5 -3
  82. package/dist/types/widgets/EodashProcess/methods/custom-endpoints/layers/eoxhub-workspaces-endpoint.d.ts +1 -1
  83. package/dist/types/widgets/EodashProcess/states.d.ts +1 -1
  84. package/dist/types/widgets/EodashStacInfo.vue.d.ts +20 -0
  85. package/dist/types/widgets/EodashTimeSlider/index.vue.d.ts +20 -0
  86. package/dist/types/widgets/EodashTimeSlider/methods/index.d.ts +3 -8
  87. package/dist/types/widgets/EodashTimeSlider/types.d.ts +6 -16
  88. package/dist/types/widgets/EodashTools.vue.d.ts +5 -5
  89. package/package.json +34 -34
  90. package/templates/baseConfig.js +37 -6
  91. package/templates/compare.js +61 -7
  92. package/templates/expert.js +24 -2
  93. package/templates/explore.js +12 -5
  94. package/templates/lite.js +13 -0
  95. package/widgets/EodashChart.vue +162 -21
  96. package/widgets/EodashItemCatalog/index.vue +363 -35
  97. package/widgets/EodashItemCatalog/methods/filters.js +134 -70
  98. package/widgets/EodashItemCatalog/methods/handlers.js +113 -19
  99. package/widgets/EodashItemCatalog/methods/map.js +141 -28
  100. package/widgets/EodashLayerControl.vue +51 -25
  101. package/widgets/EodashMap/EodashMapBtns.vue +109 -48
  102. package/widgets/EodashMap/index.vue +91 -105
  103. package/widgets/EodashMap/methods/btns.js +64 -31
  104. package/widgets/EodashMap/methods/create-layers-config.js +16 -16
  105. package/widgets/EodashMap/methods/index.js +71 -8
  106. package/widgets/EodashMap/methods/use-map-loading.js +58 -0
  107. package/widgets/EodashProcess/index.vue +136 -16
  108. package/widgets/EodashProcess/methods/composables.js +13 -0
  109. package/widgets/EodashProcess/methods/outputs.js +0 -1
  110. package/widgets/EodashProcess/methods/utils.js +29 -27
  111. package/widgets/EodashProcess/states.js +1 -1
  112. package/widgets/EodashStacInfo.vue +64 -2
  113. package/widgets/EodashTimeSlider/index.vue +58 -2
  114. package/widgets/EodashTimeSlider/methods/index.js +5 -48
  115. package/widgets/EodashTimeSlider/types.ts +7 -17
  116. package/widgets/ExportState.vue +13 -4
  117. package/dist/client/EodashChart-CLDqE93C.js +0 -144
  118. package/dist/client/EodashStacInfo-BQspAHKH.js +0 -107
  119. package/dist/client/index-5Jhsa8Xe.js +0 -570
@@ -0,0 +1,3167 @@
1
+ import { onMounted, onUnmounted, inject, shallowRef, useId, provide, toRaw, computed, onBeforeUnmount, watch, toValue, toRef, onBeforeMount, nextTick, ref, createVNode, normalizeStyle, normalizeClass, withDirectives, createElementVNode, vShow, mergeProps, Fragment, toDisplayString, onDeactivated, withAsyncContext, useTemplateRef, openBlock, createElementBlock, createBlock, withCtx, renderList, createTextVNode, createCommentVNode, unref } from 'vue';
2
+ import { storeToRefs } from 'pinia';
3
+ import { H as useSTAcStore, L as useOnLayersUpdate, M as wrapInArray, z as useProxiedModel, N as getCurrentInstance, O as consoleError, p as propsFactory, g as genericComponent, c as useRender, V as VDefaultsProvider, P as defineComponent, Q as IconValue, m as makeTagProps, e as makeComponentProps, R as createSimpleFunctional, S as Ripple, T as useLink, l as provideTheme, h as useBorder, U as useVariant, W as useDensity, b as useDimension, i as useElevation, k as useRounded, X as makeVariantProps, r as makeThemeProps, Y as makeRouterProps, s as makeRoundedProps, v as makeElevationProps, d as makeDimensionProps, Z as makeDensityProps, w as makeBorderProps, $ as EventProp, a0 as deprecate, q as convertToUnit, a1 as genOverlays, K as VIcon, a2 as useTextColor, f as useBackgroundColor, o as provideDefaults, a3 as focusChild, a4 as isPrimitive, a5 as getPropertyFromItem, C as omit, n as useRtl, a6 as isClickInsideElement, a7 as getNextElement, a8 as focusableChildren, _ as _export_sfc, a9 as VRow, J as VBtn } from './asWebComponent-IZanwL-A.js';
4
+ import { Q as indicator, g as axios, q as mapEl, a3 as sanitizeBbox, a4 as tooltipAdapter, O as mapPosition, a5 as mapCompareEl } from './helpers-BFuh19CP.js';
5
+ import { b as buildCqlFilter, u as useMosaicState, r as renderLatestMosaic, a as useScheduleMosaicUpdate, c as useInitMosaic } from './mosaic-vHvSuih6.js';
6
+ import { i as inAndOut } from './easing-CH0-9wR8.js';
7
+ import { mdiViewDashboard } from '@mdi/js';
8
+ import _sfc_main$1 from './EodashLayoutSwitcher-ZRlESI9O.js';
9
+ import { u as useScopeId, V as VMenuSymbol, f as forwardRefs, m as makeVOverlayProps, a as VOverlay } from './forwardRefs-B-c1_6yo.js';
10
+ import { V as VDialogTransition } from './dialog-transition-c4ZSnzac.js';
11
+ import { a as VExpandTransition } from './VImg-B75dgw2K.js';
12
+ import { u as useSsrBoot } from './ssrBoot-Zgc_Ttvi.js';
13
+ import { M as MaybeTransition } from './transition-BkyUsZh_.js';
14
+ import { V as VAvatar, a as VDivider } from './VDivider-DWYM0WUr.js';
15
+ import { V as VTooltip } from './VTooltip-ClWHUmWL.js';
16
+
17
+ function throttle(fn, delay) {
18
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {
19
+ leading: true,
20
+ trailing: true
21
+ };
22
+ let timeoutId = 0;
23
+ let lastExec = 0;
24
+ let throttling = false;
25
+ let start = 0;
26
+ function clear() {
27
+ clearTimeout(timeoutId);
28
+ throttling = false;
29
+ start = 0;
30
+ }
31
+ const wrap = function () {
32
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
33
+ args[_key] = arguments[_key];
34
+ }
35
+ clearTimeout(timeoutId);
36
+ const now = Date.now();
37
+ if (!start) start = now;
38
+ const elapsed = now - Math.max(start, lastExec);
39
+ function invoke() {
40
+ lastExec = Date.now();
41
+ timeoutId = setTimeout(clear, delay);
42
+ fn(...args);
43
+ }
44
+ if (!throttling) {
45
+ throttling = true;
46
+ if (options.leading) {
47
+ invoke();
48
+ }
49
+ } else if (elapsed >= delay) {
50
+ invoke();
51
+ } else if (options.trailing) {
52
+ timeoutId = setTimeout(invoke, delay - elapsed);
53
+ }
54
+ };
55
+ wrap.clear = clear;
56
+ wrap.immediate = fn;
57
+ return wrap;
58
+ }
59
+
60
+ /**
61
+ *
62
+ * @param {import("../types").FiltersConfig} filtersConfig
63
+ */
64
+ const createSubtitleProperty = (filtersConfig) => {
65
+ /**
66
+ * @param {Record<string, any>} item
67
+ */ // should be dynamic based on a prop
68
+ return (item) => {
69
+ let subtitle = "";
70
+ filtersConfig.forEach((filter) => {
71
+ const property = filter.property;
72
+ if ((!filter.icon && !filter.unitLabel) || !item.properties[property]) {
73
+ return;
74
+ }
75
+ if (filter.icon) {
76
+ subtitle += filter.icon;
77
+ }
78
+
79
+ let value = item.properties[property];
80
+ if (typeof value === "number" && !Number.isInteger(value)) {
81
+ value = value.toFixed(1);
82
+ }
83
+
84
+ subtitle += value;
85
+
86
+ if (filter.unitLabel) {
87
+ subtitle += filter.unitLabel;
88
+ }
89
+ });
90
+ return subtitle;
91
+ };
92
+ };
93
+
94
+ /**
95
+ * @param {Array<{
96
+ * property: string,
97
+ * type: "range" | "multiselect" | "select",
98
+ * title?: string,
99
+ * min?: number,
100
+ * max?: number,
101
+ * step?: number,
102
+ * filterKeys?: string[],
103
+ * state?: Record<string, boolean>,
104
+ * placeholder?: string,
105
+ * format?: string,
106
+ * }>} filtersConfig
107
+ * @param {boolean} datetimeFilter
108
+ */
109
+ // Transform simple filter configs into eox-itemfilter format
110
+ const createFilterProperties = (filtersConfig, datetimeFilter) => {
111
+ const store = useSTAcStore();
112
+ const customDatetimeConfig = filtersConfig.find(
113
+ (f) => f.property === "datetime",
114
+ );
115
+ const baseFilters = [
116
+ {
117
+ key: "collection",
118
+ title: "Collections",
119
+ type: "multiselect",
120
+ placeholder: "Select collections",
121
+ inline: false,
122
+ filterKeys:
123
+ store.stac?.filter((col) => col.id).map((col) => col.id) || [],
124
+ ...(indicator.value && { state: { [indicator.value]: true } }),
125
+ },
126
+ ...(datetimeFilter || customDatetimeConfig
127
+ ? [
128
+ {
129
+ key: "datetime",
130
+ title: "Date",
131
+ type: "range",
132
+ format: "date",
133
+ ...(customDatetimeConfig || {}),
134
+ },
135
+ ]
136
+ : []),
137
+ ];
138
+
139
+ const dynamicFilters = filtersConfig
140
+ .filter((f) => f.property !== "datetime")
141
+ .map((filter) => {
142
+ const propertyKey = `properties.${filter.property}`;
143
+
144
+ if (filter.type === "range") {
145
+ return {
146
+ key: propertyKey,
147
+ title: filter.title || filter.property,
148
+ type: "range",
149
+ expanded: true,
150
+ min: filter.min,
151
+ max: filter.max,
152
+ step: filter.step,
153
+ format: filter.format,
154
+ filterKeys: [filter.min || 0, filter.max || 100],
155
+ state: filter.state ?? {
156
+ min: filter.min ?? 0,
157
+ max: filter.max ?? 100,
158
+ },
159
+ };
160
+ } else if (filter.type === "multiselect") {
161
+ return {
162
+ key: propertyKey,
163
+ title: filter.title || filter.property,
164
+ type: "multiselect",
165
+ placeholder: filter.placeholder || `Select ${filter.property}`,
166
+ inline: false,
167
+ filterKeys: filter.filterKeys || [],
168
+ state: filter.state,
169
+ };
170
+ } else if (filter.type === "select") {
171
+ return {
172
+ key: propertyKey,
173
+ title: filter.title || filter.property,
174
+ type: "select",
175
+ placeholder:
176
+ filter.placeholder || `Select ${filter.title || filter.property}`,
177
+ filterKeys: filter.filterKeys || [],
178
+ state: filter.state,
179
+ };
180
+ }
181
+
182
+ return null;
183
+ })
184
+ .filter(Boolean);
185
+
186
+ return [...baseFilters, ...dynamicFilters];
187
+ };
188
+
189
+ /**
190
+ * Build search URL with proper STAC API parameters
191
+ * @param {import("@/types").ItemFilterFilters} filters
192
+ * @param {boolean} bboxFilter
193
+ * @param {boolean} datetimeFilter
194
+ * @param {Number} searchLimit
195
+ * @param {string} [sortBy]
196
+ * @param {string | null} [stacEndpoint]
197
+ * @returns {string}
198
+ */
199
+ const buildSearchUrl = (
200
+ filters,
201
+ bboxFilter,
202
+ datetimeFilter,
203
+ searchLimit,
204
+ sortBy,
205
+ stacEndpoint,
206
+ ) => {
207
+ const store = useSTAcStore();
208
+ const endpoint = stacEndpoint || store.stacEndpoint;
209
+ const params = new URLSearchParams();
210
+
211
+ if (filters.collection?.stringifiedState) {
212
+ params.append(
213
+ "collections",
214
+ filters.collection.stringifiedState.replaceAll(" ", ""),
215
+ );
216
+ }
217
+
218
+ if (mapEl.value?.lonLatExtent && bboxFilter) {
219
+ params.append(
220
+ "bbox",
221
+ sanitizeBbox([...mapEl.value.lonLatExtent]).join(","),
222
+ );
223
+ }
224
+
225
+ if (datetimeFilter) {
226
+ const datetime = formatDatetimeParam(
227
+ /** @type {import("@/types").ItemFilterRange} */ (filters.datetime),
228
+ );
229
+ if (datetime) {
230
+ params.append("datetime", datetime);
231
+ }
232
+ }
233
+
234
+ const cqlFilter = buildCqlFilter(filters);
235
+ if (cqlFilter) {
236
+ params.append("filter", cqlFilter);
237
+ }
238
+ if (sortBy) {
239
+ params.append("sortby", sortBy);
240
+ }
241
+
242
+ params.append("limit", searchLimit.toString());
243
+
244
+ return `${endpoint}/search?${params.toString()}`;
245
+ };
246
+
247
+ /**
248
+ *
249
+ * @param {import("../types").FiltersConfig} propsFilters
250
+ * @param {boolean} bboxFilter
251
+ * @param {boolean} datetimeFilter
252
+ * @param {import("vue").Ref<import("@/types").GeoJsonFeature[]>} currentItems
253
+ * @param {import("vue").Ref<string>} sortBy
254
+ * @param {Number} searchLimit
255
+ * @param {import("vue").Ref<import("stac-ts").StacItem | null>} [selectedItemRef]
256
+ * @param {import("vue").Ref<string | null> | string | null} [stacEndpoint]
257
+ */
258
+ const createExternalFilter = (
259
+ propsFilters,
260
+ bboxFilter,
261
+ datetimeFilter,
262
+ currentItems,
263
+ sortBy,
264
+ searchLimit,
265
+ selectedItemRef,
266
+ stacEndpoint,
267
+ ) => {
268
+ const hasCustomDatetime = propsFilters.some((f) => f.property === "datetime");
269
+ const effectiveDatetimeFilter = datetimeFilter || hasCustomDatetime;
270
+ let controller = new AbortController();
271
+ /**
272
+ * @param {Array<any>} _items
273
+ * @param {Record<string,any>} filters
274
+ */
275
+ return (_items, filters) => ({
276
+ url: buildSearchUrl(
277
+ filters,
278
+ bboxFilter,
279
+ effectiveDatetimeFilter,
280
+ searchLimit,
281
+ sortBy.value,
282
+ typeof stacEndpoint === "object" ? stacEndpoint?.value : stacEndpoint,
283
+ ),
284
+ /** @param {string} url */
285
+ fetchFn: async (url) => {
286
+ controller.abort();
287
+ controller = new AbortController();
288
+ const signal = controller.signal;
289
+ return await axios
290
+ .get(url, { signal })
291
+ .then((res) => {
292
+ /** @type {import("@/types").GeoJsonFeature[]} */
293
+ const results = res.data.features;
294
+ const selected = selectedItemRef?.value;
295
+ if (selected && !results.some((r) => r.id === selected.id)) {
296
+ return [selected, ...results];
297
+ }
298
+ return results;
299
+ })
300
+ .catch((e) => {
301
+ // return previous items if aborted
302
+ if (e.name === "AbortError" || e.name === "CanceledError") {
303
+ return currentItems.value;
304
+ }
305
+ console.error(e);
306
+ return [];
307
+ });
308
+ },
309
+ });
310
+ };
311
+ /**
312
+ *
313
+ * @param {import("@/types").ItemFilterRange} datetimeFilter
314
+ */
315
+ function formatDatetimeParam(datetimeFilter) {
316
+ if (!datetimeFilter) {
317
+ return null;
318
+ }
319
+ const min = datetimeFilter?.min ? new Date(datetimeFilter.min) : null;
320
+ const max = datetimeFilter?.max ? new Date(datetimeFilter.max) : null;
321
+ const start = datetimeFilter.state?.min
322
+ ? new Date(datetimeFilter.state.min)
323
+ : null;
324
+ const end = datetimeFilter.state?.max
325
+ ? new Date(datetimeFilter.state.max)
326
+ : null;
327
+ const includeStart = start && (!min || start > min);
328
+ const includeEnd = end && (!max || end < max);
329
+ if (!includeStart && !includeEnd) {
330
+ return null;
331
+ }
332
+ return `${includeStart ? start.toISOString() : ".."}/${includeEnd ? end.toISOString() : ".."}`;
333
+ }
334
+
335
+ /**
336
+ * Build a compact signature from filter key + stringified state.
337
+ * @param {import("@/types").ItemFilterFilters | undefined} filters
338
+ * @returns {string}
339
+ */
340
+ const getFiltersSignature = (filters) => {
341
+ if (!filters) return "";
342
+ return Object.keys(filters)
343
+ .sort()
344
+ .map((key) => `${key}:${filters[key]?.stringifiedState ?? ""}`)
345
+ .join("|");
346
+ };
347
+
348
+ /**
349
+ * @param {{
350
+ * currentItems: import("vue").Ref<import("@/types").GeoJsonFeature[]>,
351
+ * mapElement: import("vue").Ref<import("@eox/map").EOxMap | null>,
352
+ * hoverProperties: string[] | undefined,
353
+ * stacItemsStyle?: object,
354
+ * stacItemsInteractionStyle?: object,
355
+ * itemfilterEl?: import("vue").Ref<any>,
356
+ * selectedItemRef?: import("vue").Ref<import("stac-ts").StacItem | null>,
357
+ * mosaicOptions?: {
358
+ * isMosaicEnabled: import("vue").ComputedRef<boolean>,
359
+ * getMosaicEndpoint: () => string | null | undefined,
360
+ * scheduleMosaicUpdate: (mosaicEndpoint: string | null | undefined, timeRange?: [string,string], filters?: import("@/types").ItemFilterFilters) => void,
361
+ * } | null
362
+ * }} params
363
+ */
364
+ const createOnFilterHandler = ({
365
+ currentItems,
366
+ mapElement,
367
+ hoverProperties,
368
+ stacItemsStyle,
369
+ stacItemsInteractionStyle,
370
+ itemfilterEl,
371
+ selectedItemRef,
372
+ mosaicOptions = null,
373
+ }) => {
374
+ let lastScheduledFiltersKey = "";
375
+
376
+ /** @param {CustomEvent} evt */
377
+ return (evt) => {
378
+ currentItems.value = evt.detail.results;
379
+ renderItemsFeatures(
380
+ currentItems.value,
381
+ mapElement,
382
+ hoverProperties,
383
+ stacItemsStyle,
384
+ stacItemsInteractionStyle,
385
+ );
386
+
387
+ const selected = selectedItemRef?.value;
388
+ if (selected && itemfilterEl?.value) {
389
+ itemfilterEl.value.selectedResult = selected;
390
+ }
391
+
392
+ if (mosaicOptions?.isMosaicEnabled.value && !selected) {
393
+ const nextFiltersKey = getFiltersSignature(evt.detail.filters);
394
+ if (nextFiltersKey === lastScheduledFiltersKey) {
395
+ return;
396
+ }
397
+
398
+ lastScheduledFiltersKey = nextFiltersKey;
399
+ mosaicOptions.scheduleMosaicUpdate(
400
+ mosaicOptions.getMosaicEndpoint(),
401
+ undefined,
402
+ evt.detail.filters,
403
+ );
404
+ }
405
+ };
406
+ };
407
+ /**
408
+ *
409
+ * @param {ReturnType<typeof import("@/store/stac.js").useSTAcStore>} store
410
+ * @param {boolean} enableCompare
411
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
412
+ * @returns
413
+ */
414
+ const createOnSelectHandler = (store, enableCompare, mapElement) => {
415
+ /** @param {CustomEvent} evt */
416
+ return async (evt) => {
417
+ const item = /** @type {import("stac-ts").StacItem} */ (evt.detail);
418
+ if (!item) {
419
+ return;
420
+ }
421
+ if (enableCompare) {
422
+ if (item.id === store.selectedCompareItem?.id) {
423
+ store.selectedCompareItem = null;
424
+ return;
425
+ }
426
+ if (store.selectedCompareStac?.id === item.collection) {
427
+ store.selectedCompareItem = item;
428
+ } else {
429
+ await store.loadSelectedCompareSTAC(item.collection, false, item);
430
+ }
431
+ } else {
432
+ if (item.id === store.selectedItem?.id) {
433
+ store.selectedItem = null;
434
+ return;
435
+ }
436
+ if (store.selectedStac?.id === item.collection) {
437
+ store.selectedItem = item;
438
+ } else {
439
+ await store.loadSelectedSTAC(item.collection, false, item);
440
+ }
441
+ }
442
+
443
+ mapElement.value?.selectInteractions["stac-items"]?.highlightById(
444
+ [item.id],
445
+ {
446
+ padding: [100, 100, 100, 100],
447
+ duration: 1200,
448
+ easing: inAndOut,
449
+ },
450
+ );
451
+ };
452
+ };
453
+
454
+ /**
455
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
456
+ */
457
+ const createOnMouseEnterResult = (mapElement) => {
458
+ /**
459
+ * @param {CustomEvent} evt
460
+ */
461
+ return (evt) => {
462
+ mapElement.value?.selectInteractions["stac-item-hover"]?.highlightById([
463
+ evt.detail.id,
464
+ ]);
465
+ };
466
+ };
467
+
468
+ /**
469
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
470
+ */
471
+ const createOnMouseLeaveResult = (mapElement) => {
472
+ return () => {
473
+ mapElement.value?.selectInteractions["stac-item-hover"]?.highlightById([]);
474
+ };
475
+ };
476
+
477
+ /**
478
+ *
479
+ * @param {import("@/types").GeoJsonFeature[]} features
480
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
481
+ * @param {string[] | undefined} hoverProperties
482
+ * @param {object} [stacItemsStyle]
483
+ * @param {object} [stacItemsInteractionStyle]
484
+ */
485
+ function renderItemsFeatures(
486
+ features,
487
+ mapElement,
488
+ hoverProperties,
489
+ stacItemsStyle,
490
+ stacItemsInteractionStyle,
491
+ ) {
492
+ const currentMap = mapElement.value;
493
+ let analysisLayers =
494
+ /** @type {import("@eox/map/src/layers").EOxLayerTypeGroup} */ (
495
+ currentMap?.layers?.find((l) => l.properties?.id === "AnalysisGroup")
496
+ );
497
+ if (!currentMap || !features) {
498
+ return;
499
+ }
500
+ if (!analysisLayers) {
501
+ analysisLayers = {
502
+ type: "Group",
503
+ properties: {
504
+ id: "AnalysisGroup",
505
+ title: "Data Layers",
506
+ },
507
+ layers: [],
508
+ };
509
+ currentMap.layers = [...currentMap.layers, analysisLayers];
510
+ }
511
+ /** @type {import("@eox/map/src/layers").EOxLayerType<"Vector","Vector">} */
512
+ const stacItemsLayer = {
513
+ type: "Vector",
514
+ properties: {
515
+ id: "stac-items",
516
+ title: "STAC Items",
517
+ },
518
+ source: {
519
+ type: "Vector",
520
+ url:
521
+ "data:application/geo+json," +
522
+ encodeURIComponent(
523
+ JSON.stringify({ type: "FeatureCollection", features }),
524
+ ),
525
+ format: "GeoJSON",
526
+ projection: "EPSG:4326",
527
+ },
528
+ style: stacItemsStyle || {
529
+ "fill-color": "transparent",
530
+ "stroke-color": "#003170",
531
+ },
532
+ interactions: [
533
+ {
534
+ type: "select",
535
+ options: {
536
+ id: "stac-item-hover",
537
+ condition: "pointermove",
538
+ tooltip: hoverProperties?.length,
539
+ style: stacItemsInteractionStyle || {
540
+ "stroke-color": "white",
541
+ "stroke-width": 3,
542
+ },
543
+ },
544
+ },
545
+ {
546
+ type: "select",
547
+ options: {
548
+ id: "stac-items",
549
+ condition: "click",
550
+ tooltip: false,
551
+ style: stacItemsInteractionStyle || {
552
+ "stroke-color": "#003047",
553
+ "stroke-width": 3.2,
554
+ },
555
+ },
556
+ },
557
+ ],
558
+ };
559
+ const exists = analysisLayers.layers.some(
560
+ (l) => l.properties?.id === stacItemsLayer.properties?.id,
561
+ );
562
+ if (exists) {
563
+ currentMap.addOrUpdateLayer(stacItemsLayer);
564
+ return;
565
+ } else {
566
+ analysisLayers.layers.unshift(stacItemsLayer);
567
+ currentMap.layers = [...currentMap.layers];
568
+ }
569
+ }
570
+
571
+ /**
572
+ * @param {import("vue").Ref<any>} itemFilter
573
+ * @param {boolean} bboxFilter
574
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
575
+ */
576
+ const useSearchOnMapMove = (itemFilter, bboxFilter, mapElement) => {
577
+ if (!bboxFilter) {
578
+ return;
579
+ }
580
+ /** @type {NodeJS.Timeout} */
581
+ let timeout;
582
+ const handler = () => {
583
+ clearTimeout(timeout);
584
+ timeout = setTimeout(() => {
585
+ if (mapElement.value?.projection == "globe") {
586
+ return;
587
+ }
588
+ itemFilter.value?.search();
589
+ }, 500);
590
+ };
591
+ onMounted(() => {
592
+ mapElement.value?.map.on("moveend", handler);
593
+ });
594
+ onUnmounted(() => {
595
+ mapElement.value?.map.un("moveend", handler);
596
+ });
597
+ };
598
+ /**
599
+ *
600
+ * @param {import("vue").Ref<import("@/types").GeoJsonFeature[]>} currentItems
601
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
602
+ * @param {string[] | undefined} hoverProperties
603
+ * @param {object} [stacItemsStyle]
604
+ * @param {object} [stacItemsInteractionStyle]
605
+ */
606
+ const useRenderItemsFeatures = (
607
+ currentItems,
608
+ mapElement,
609
+ hoverProperties,
610
+ stacItemsStyle,
611
+ stacItemsInteractionStyle,
612
+ ) => {
613
+ onMounted(() => {
614
+ renderItemsFeatures(
615
+ currentItems.value,
616
+ mapElement,
617
+ hoverProperties,
618
+ stacItemsStyle,
619
+ stacItemsInteractionStyle,
620
+ );
621
+ });
622
+
623
+ useOnLayersUpdate(() => {
624
+ // consider cases where this is not needed
625
+ renderItemsFeatures(
626
+ currentItems.value,
627
+ mapElement,
628
+ hoverProperties,
629
+ stacItemsStyle,
630
+ stacItemsInteractionStyle,
631
+ );
632
+ });
633
+ };
634
+ /**
635
+ * Registers a tooltip adapter that displays item properties when hovering over map features.
636
+ * @param {string[]} [hoverProperties]
637
+ */
638
+ function useHoverTooltip(hoverProperties) {
639
+ /**
640
+ * @param {*} val
641
+ */
642
+ const formatValue = (val) => {
643
+ if (typeof val === "number") {
644
+ return val.toFixed(0);
645
+ }
646
+
647
+ if (typeof val === "string") {
648
+ // Attempt to parse as date if string looks like ISO date
649
+ if (val.length > 9 && /\d{4}-\d{2}-\d{2}/.test(val)) {
650
+ const timestamp = Date.parse(val);
651
+ if (!isNaN(timestamp)) {
652
+ return new Date(timestamp).toUTCString();
653
+ }
654
+ }
655
+ }
656
+ return val;
657
+ };
658
+ /**
659
+ * @param {string} key
660
+ */
661
+ const formatKey = (key) => {
662
+ let formattedKey = key;
663
+ if (key.includes(":")) {
664
+ formattedKey = key.split(":")[1];
665
+ }
666
+ return formattedKey
667
+ .replace(/_/g, " ")
668
+ .replace(/\b\w/g, (c) => c.toUpperCase());
669
+ };
670
+ onMounted(() => {
671
+ tooltipAdapter.value = ({ key, value }) => {
672
+ if (hoverProperties && hoverProperties.includes(key)) {
673
+ return { key: formatKey(key), value: formatValue(value) };
674
+ }
675
+ return undefined;
676
+ };
677
+ });
678
+ onUnmounted(() => {
679
+ tooltipAdapter.value = null;
680
+ });
681
+ }
682
+
683
+ /**
684
+ *
685
+ * @param {import("vue").Ref<any>} itemfilterEl
686
+ * @param {ReturnType< typeof import("@/store/stac").useSTAcStore>} store
687
+ * @param {import("vue").Ref<import("@eox/map").EOxMap | null>} mapElement
688
+ * @param {boolean} enableCompare
689
+ */
690
+ function useRenderOnFeatureClick(
691
+ itemfilterEl,
692
+ store,
693
+ mapElement,
694
+ enableCompare,
695
+ ) {
696
+ const onSelectItem = createOnSelectHandler(store, enableCompare, mapElement);
697
+ /**
698
+ *
699
+ * @param {CustomEvent} evt
700
+ */
701
+ const handler = (evt) => {
702
+ if (evt.detail.originalEvent.type !== "click") {
703
+ return;
704
+ }
705
+ const itemId = evt.detail?.feature?.getId();
706
+ if (!itemId) {
707
+ return;
708
+ }
709
+ const item = itemfilterEl.value.results?.find(
710
+ //@ts-expect-error todo
711
+ (r) => r?.id === itemId,
712
+ );
713
+
714
+ if (item) {
715
+ onSelectItem(new CustomEvent("select", { detail: item }));
716
+ }
717
+ };
718
+
719
+ onMounted(() => {
720
+ //@ts-expect-error todo
721
+ mapElement.value?.addEventListener("select", handler);
722
+ });
723
+ onUnmounted(() => {
724
+ //@ts-expect-error todo
725
+ mapElement.value?.removeEventListener("select", handler);
726
+ });
727
+ }
728
+
729
+ const _style_0 = ".itemfilter-scroll[data-v-5a4d8ea3]{flex:1;min-height:0;overflow:hidden;padding:.5rem;--eox-itemfilter-results-color: var(--v-theme-primary) !important}.title[data-v-5a4d8ea3]{padding:1em;margin:.2em;flex-shrink:0}";
730
+
731
+ // Utilities
732
+
733
+ // List
734
+ const ListKey = Symbol.for('vuetify:list');
735
+ function createList() {
736
+ let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
737
+ filterable: false
738
+ };
739
+ const parent = inject(ListKey, {
740
+ filterable: false,
741
+ hasPrepend: shallowRef(false),
742
+ updateHasPrepend: () => null,
743
+ trackingIndex: shallowRef(-1),
744
+ navigationStrategy: shallowRef('focus'),
745
+ uid: ''
746
+ });
747
+ const {
748
+ filterable,
749
+ trackingIndex = parent.trackingIndex,
750
+ navigationStrategy = parent.navigationStrategy,
751
+ uid = parent.uid || useId()
752
+ } = options;
753
+ const data = {
754
+ filterable: parent.filterable || filterable,
755
+ hasPrepend: shallowRef(false),
756
+ updateHasPrepend: value => {
757
+ if (value) data.hasPrepend.value = value;
758
+ },
759
+ trackingIndex,
760
+ navigationStrategy,
761
+ uid
762
+ };
763
+ provide(ListKey, data);
764
+ return parent;
765
+ }
766
+ function useList() {
767
+ return inject(ListKey, null);
768
+ }
769
+
770
+ /* eslint-disable sonarjs/no-identical-functions */
771
+ // Utilities
772
+ const independentActiveStrategy = mandatory => {
773
+ const strategy = {
774
+ activate: _ref => {
775
+ let {
776
+ id,
777
+ value,
778
+ activated
779
+ } = _ref;
780
+ id = toRaw(id);
781
+
782
+ // When mandatory and we're trying to deselect when id
783
+ // is the only currently selected item then do nothing
784
+ if (mandatory && !value && activated.size === 1 && activated.has(id)) return activated;
785
+ if (value) {
786
+ activated.add(id);
787
+ } else {
788
+ activated.delete(id);
789
+ }
790
+ return activated;
791
+ },
792
+ in: (v, children, parents) => {
793
+ let set = new Set();
794
+ if (v != null) {
795
+ for (const id of wrapInArray(v)) {
796
+ set = strategy.activate({
797
+ id,
798
+ value: true,
799
+ activated: new Set(set),
800
+ children,
801
+ parents
802
+ });
803
+ }
804
+ }
805
+ return set;
806
+ },
807
+ out: v => {
808
+ return Array.from(v);
809
+ }
810
+ };
811
+ return strategy;
812
+ };
813
+ const independentSingleActiveStrategy = mandatory => {
814
+ const parentStrategy = independentActiveStrategy(mandatory);
815
+ const strategy = {
816
+ activate: _ref2 => {
817
+ let {
818
+ activated,
819
+ id,
820
+ ...rest
821
+ } = _ref2;
822
+ id = toRaw(id);
823
+ const singleSelected = activated.has(id) ? new Set([id]) : new Set();
824
+ return parentStrategy.activate({
825
+ ...rest,
826
+ id,
827
+ activated: singleSelected
828
+ });
829
+ },
830
+ in: (v, children, parents) => {
831
+ let set = new Set();
832
+ if (v != null) {
833
+ const arr = wrapInArray(v);
834
+ if (arr.length) {
835
+ set = parentStrategy.in(arr.slice(0, 1), children, parents);
836
+ }
837
+ }
838
+ return set;
839
+ },
840
+ out: (v, children, parents) => {
841
+ return parentStrategy.out(v, children, parents);
842
+ }
843
+ };
844
+ return strategy;
845
+ };
846
+ const leafActiveStrategy = mandatory => {
847
+ const parentStrategy = independentActiveStrategy(mandatory);
848
+ const strategy = {
849
+ activate: _ref3 => {
850
+ let {
851
+ id,
852
+ activated,
853
+ children,
854
+ ...rest
855
+ } = _ref3;
856
+ id = toRaw(id);
857
+ if (children.has(id)) return activated;
858
+ return parentStrategy.activate({
859
+ id,
860
+ activated,
861
+ children,
862
+ ...rest
863
+ });
864
+ },
865
+ in: parentStrategy.in,
866
+ out: parentStrategy.out
867
+ };
868
+ return strategy;
869
+ };
870
+ const leafSingleActiveStrategy = mandatory => {
871
+ const parentStrategy = independentSingleActiveStrategy(mandatory);
872
+ const strategy = {
873
+ activate: _ref4 => {
874
+ let {
875
+ id,
876
+ activated,
877
+ children,
878
+ ...rest
879
+ } = _ref4;
880
+ id = toRaw(id);
881
+ if (children.has(id)) return activated;
882
+ return parentStrategy.activate({
883
+ id,
884
+ activated,
885
+ children,
886
+ ...rest
887
+ });
888
+ },
889
+ in: parentStrategy.in,
890
+ out: parentStrategy.out
891
+ };
892
+ return strategy;
893
+ };
894
+
895
+ const singleOpenStrategy = {
896
+ open: _ref => {
897
+ let {
898
+ id,
899
+ value,
900
+ opened,
901
+ parents
902
+ } = _ref;
903
+ if (value) {
904
+ const newOpened = new Set();
905
+ newOpened.add(id);
906
+ let parent = parents.get(id);
907
+ while (parent != null) {
908
+ newOpened.add(parent);
909
+ parent = parents.get(parent);
910
+ }
911
+ return newOpened;
912
+ } else {
913
+ opened.delete(id);
914
+ return opened;
915
+ }
916
+ },
917
+ select: () => null
918
+ };
919
+ const multipleOpenStrategy = {
920
+ open: _ref2 => {
921
+ let {
922
+ id,
923
+ value,
924
+ opened,
925
+ parents
926
+ } = _ref2;
927
+ if (value) {
928
+ let parent = parents.get(id);
929
+ opened.add(id);
930
+ while (parent != null && parent !== id) {
931
+ opened.add(parent);
932
+ parent = parents.get(parent);
933
+ }
934
+ return opened;
935
+ } else {
936
+ opened.delete(id);
937
+ }
938
+ return opened;
939
+ },
940
+ select: () => null
941
+ };
942
+ const listOpenStrategy = {
943
+ open: multipleOpenStrategy.open,
944
+ select: _ref3 => {
945
+ let {
946
+ id,
947
+ value,
948
+ opened,
949
+ parents
950
+ } = _ref3;
951
+ if (!value) return opened;
952
+ const path = [];
953
+ let parent = parents.get(id);
954
+ while (parent != null) {
955
+ path.push(parent);
956
+ parent = parents.get(parent);
957
+ }
958
+ return new Set(path);
959
+ }
960
+ };
961
+
962
+ /* eslint-disable sonarjs/no-identical-functions */
963
+ // Utilities
964
+ const independentSelectStrategy = mandatory => {
965
+ const strategy = {
966
+ select: _ref => {
967
+ let {
968
+ id,
969
+ value,
970
+ selected
971
+ } = _ref;
972
+ id = toRaw(id);
973
+
974
+ // When mandatory and we're trying to deselect when id
975
+ // is the only currently selected item then do nothing
976
+ if (mandatory && !value) {
977
+ const on = Array.from(selected.entries()).reduce((arr, _ref2) => {
978
+ let [key, value] = _ref2;
979
+ if (value === 'on') arr.push(key);
980
+ return arr;
981
+ }, []);
982
+ if (on.length === 1 && on[0] === id) return selected;
983
+ }
984
+ selected.set(id, value ? 'on' : 'off');
985
+ return selected;
986
+ },
987
+ in: (v, children, parents, disabled) => {
988
+ const map = new Map();
989
+ for (const id of v || []) {
990
+ strategy.select({
991
+ id,
992
+ value: true,
993
+ selected: map,
994
+ children,
995
+ parents,
996
+ disabled
997
+ });
998
+ }
999
+ return map;
1000
+ },
1001
+ out: v => {
1002
+ const arr = [];
1003
+ for (const [key, value] of v.entries()) {
1004
+ if (value === 'on') arr.push(key);
1005
+ }
1006
+ return arr;
1007
+ }
1008
+ };
1009
+ return strategy;
1010
+ };
1011
+ const independentSingleSelectStrategy = mandatory => {
1012
+ const parentStrategy = independentSelectStrategy(mandatory);
1013
+ const strategy = {
1014
+ select: _ref3 => {
1015
+ let {
1016
+ selected,
1017
+ id,
1018
+ ...rest
1019
+ } = _ref3;
1020
+ id = toRaw(id);
1021
+ const singleSelected = selected.has(id) ? new Map([[id, selected.get(id)]]) : new Map();
1022
+ return parentStrategy.select({
1023
+ ...rest,
1024
+ id,
1025
+ selected: singleSelected
1026
+ });
1027
+ },
1028
+ in: (v, children, parents, disabled) => {
1029
+ if (v?.length) {
1030
+ return parentStrategy.in(v.slice(0, 1), children, parents, disabled);
1031
+ }
1032
+ return new Map();
1033
+ },
1034
+ out: (v, children, parents) => {
1035
+ return parentStrategy.out(v, children, parents);
1036
+ }
1037
+ };
1038
+ return strategy;
1039
+ };
1040
+ const leafSelectStrategy = mandatory => {
1041
+ const parentStrategy = independentSelectStrategy(mandatory);
1042
+ const strategy = {
1043
+ select: _ref4 => {
1044
+ let {
1045
+ id,
1046
+ selected,
1047
+ children,
1048
+ ...rest
1049
+ } = _ref4;
1050
+ id = toRaw(id);
1051
+ if (children.has(id)) return selected;
1052
+ return parentStrategy.select({
1053
+ id,
1054
+ selected,
1055
+ children,
1056
+ ...rest
1057
+ });
1058
+ },
1059
+ in: parentStrategy.in,
1060
+ out: parentStrategy.out
1061
+ };
1062
+ return strategy;
1063
+ };
1064
+ const leafSingleSelectStrategy = mandatory => {
1065
+ const parentStrategy = independentSingleSelectStrategy(mandatory);
1066
+ const strategy = {
1067
+ select: _ref5 => {
1068
+ let {
1069
+ id,
1070
+ selected,
1071
+ children,
1072
+ ...rest
1073
+ } = _ref5;
1074
+ id = toRaw(id);
1075
+ if (children.has(id)) return selected;
1076
+ return parentStrategy.select({
1077
+ id,
1078
+ selected,
1079
+ children,
1080
+ ...rest
1081
+ });
1082
+ },
1083
+ in: parentStrategy.in,
1084
+ out: parentStrategy.out
1085
+ };
1086
+ return strategy;
1087
+ };
1088
+ const classicSelectStrategy = mandatory => {
1089
+ const strategy = {
1090
+ select: _ref6 => {
1091
+ let {
1092
+ id,
1093
+ value,
1094
+ selected,
1095
+ children,
1096
+ parents,
1097
+ disabled
1098
+ } = _ref6;
1099
+ id = toRaw(id);
1100
+ const original = new Map(selected);
1101
+ const items = [id];
1102
+ while (items.length) {
1103
+ const item = items.shift();
1104
+ if (!disabled.has(item)) {
1105
+ selected.set(toRaw(item), value ? 'on' : 'off');
1106
+ }
1107
+ if (children.has(item)) {
1108
+ items.push(...children.get(item));
1109
+ }
1110
+ }
1111
+ let parent = toRaw(parents.get(id));
1112
+ while (parent) {
1113
+ let everySelected = true;
1114
+ let noneSelected = true;
1115
+ for (const child of children.get(parent)) {
1116
+ const cid = toRaw(child);
1117
+ if (disabled.has(cid)) continue;
1118
+ if (selected.get(cid) !== 'on') everySelected = false;
1119
+ if (selected.has(cid) && selected.get(cid) !== 'off') noneSelected = false;
1120
+ if (!everySelected && !noneSelected) break;
1121
+ }
1122
+ selected.set(parent, everySelected ? 'on' : noneSelected ? 'off' : 'indeterminate');
1123
+ parent = toRaw(parents.get(parent));
1124
+ }
1125
+
1126
+ // If mandatory and planned deselect results in no selected
1127
+ // items then we can't do it, so return original state
1128
+ if (mandatory && !value) {
1129
+ const on = Array.from(selected.entries()).reduce((arr, _ref7) => {
1130
+ let [key, value] = _ref7;
1131
+ if (value === 'on') arr.push(key);
1132
+ return arr;
1133
+ }, []);
1134
+ if (on.length === 0) return original;
1135
+ }
1136
+ return selected;
1137
+ },
1138
+ in: (v, children, parents) => {
1139
+ let map = new Map();
1140
+ for (const id of v || []) {
1141
+ map = strategy.select({
1142
+ id,
1143
+ value: true,
1144
+ selected: map,
1145
+ children,
1146
+ parents,
1147
+ disabled: new Set()
1148
+ });
1149
+ }
1150
+ return map;
1151
+ },
1152
+ out: (v, children) => {
1153
+ const arr = [];
1154
+ for (const [key, value] of v.entries()) {
1155
+ if (value === 'on' && !children.has(key)) arr.push(key);
1156
+ }
1157
+ return arr;
1158
+ }
1159
+ };
1160
+ return strategy;
1161
+ };
1162
+ const trunkSelectStrategy = mandatory => {
1163
+ const parentStrategy = classicSelectStrategy(mandatory);
1164
+ const strategy = {
1165
+ select: parentStrategy.select,
1166
+ in: parentStrategy.in,
1167
+ out: (v, children, parents) => {
1168
+ const arr = [];
1169
+ for (const [key, value] of v.entries()) {
1170
+ if (value === 'on') {
1171
+ if (parents.has(key)) {
1172
+ const parent = parents.get(key);
1173
+ if (v.get(parent) === 'on') continue;
1174
+ }
1175
+ arr.push(key);
1176
+ }
1177
+ }
1178
+ return arr;
1179
+ }
1180
+ };
1181
+ return strategy;
1182
+ };
1183
+ const branchSelectStrategy = mandatory => {
1184
+ const parentStrategy = classicSelectStrategy(mandatory);
1185
+ const strategy = {
1186
+ select: parentStrategy.select,
1187
+ in: (v, children, parents, disabled) => {
1188
+ let map = new Map();
1189
+ for (const id of v || []) {
1190
+ if (children.has(id)) continue;
1191
+ map = strategy.select({
1192
+ id,
1193
+ value: true,
1194
+ selected: map,
1195
+ children,
1196
+ parents,
1197
+ disabled
1198
+ });
1199
+ }
1200
+ return map;
1201
+ },
1202
+ out: v => {
1203
+ const arr = [];
1204
+ for (const [key, value] of v.entries()) {
1205
+ if (value === 'on' || value === 'indeterminate') {
1206
+ arr.push(key);
1207
+ }
1208
+ }
1209
+ return arr;
1210
+ }
1211
+ };
1212
+ return strategy;
1213
+ };
1214
+
1215
+ // Composables
1216
+ const VNestedSymbol = Symbol.for('vuetify:nested');
1217
+ const emptyNested = {
1218
+ id: shallowRef(),
1219
+ root: {
1220
+ itemsRegistration: ref('render'),
1221
+ register: () => null,
1222
+ unregister: () => null,
1223
+ updateDisabled: () => null,
1224
+ children: ref(new Map()),
1225
+ parents: ref(new Map()),
1226
+ disabled: ref(new Set()),
1227
+ open: () => null,
1228
+ openOnSelect: () => null,
1229
+ activate: () => null,
1230
+ select: () => null,
1231
+ activatable: ref(false),
1232
+ scrollToActive: ref(false),
1233
+ selectable: ref(false),
1234
+ opened: ref(new Set()),
1235
+ activated: ref(new Set()),
1236
+ selected: ref(new Map()),
1237
+ selectedValues: ref([]),
1238
+ getPath: () => []
1239
+ }
1240
+ };
1241
+ const makeNestedProps = propsFactory({
1242
+ activatable: Boolean,
1243
+ selectable: Boolean,
1244
+ activeStrategy: [String, Function, Object],
1245
+ selectStrategy: [String, Function, Object],
1246
+ openStrategy: [String, Object],
1247
+ opened: null,
1248
+ activated: null,
1249
+ selected: null,
1250
+ mandatory: Boolean,
1251
+ itemsRegistration: {
1252
+ type: String,
1253
+ default: 'render'
1254
+ }
1255
+ }, 'nested');
1256
+ const useNested = (props, _ref) => {
1257
+ let {
1258
+ items,
1259
+ returnObject,
1260
+ scrollToActive
1261
+ } = _ref;
1262
+ let isUnmounted = false;
1263
+ const children = shallowRef(new Map());
1264
+ const parents = shallowRef(new Map());
1265
+ const disabled = shallowRef(new Set());
1266
+ const opened = useProxiedModel(props, 'opened', props.opened, v => new Set(Array.isArray(v) ? v.map(i => toRaw(i)) : v), v => [...v.values()]);
1267
+ const activeStrategy = computed(() => {
1268
+ if (typeof props.activeStrategy === 'object') return props.activeStrategy;
1269
+ if (typeof props.activeStrategy === 'function') return props.activeStrategy(props.mandatory);
1270
+ switch (props.activeStrategy) {
1271
+ case 'leaf':
1272
+ return leafActiveStrategy(props.mandatory);
1273
+ case 'single-leaf':
1274
+ return leafSingleActiveStrategy(props.mandatory);
1275
+ case 'independent':
1276
+ return independentActiveStrategy(props.mandatory);
1277
+ case 'single-independent':
1278
+ default:
1279
+ return independentSingleActiveStrategy(props.mandatory);
1280
+ }
1281
+ });
1282
+ const selectStrategy = computed(() => {
1283
+ if (typeof props.selectStrategy === 'object') return props.selectStrategy;
1284
+ if (typeof props.selectStrategy === 'function') return props.selectStrategy(props.mandatory);
1285
+ switch (props.selectStrategy) {
1286
+ case 'single-leaf':
1287
+ return leafSingleSelectStrategy(props.mandatory);
1288
+ case 'leaf':
1289
+ return leafSelectStrategy(props.mandatory);
1290
+ case 'independent':
1291
+ return independentSelectStrategy(props.mandatory);
1292
+ case 'single-independent':
1293
+ return independentSingleSelectStrategy(props.mandatory);
1294
+ case 'trunk':
1295
+ return trunkSelectStrategy(props.mandatory);
1296
+ case 'branch':
1297
+ return branchSelectStrategy(props.mandatory);
1298
+ case 'classic':
1299
+ default:
1300
+ return classicSelectStrategy(props.mandatory);
1301
+ }
1302
+ });
1303
+ const openStrategy = computed(() => {
1304
+ if (typeof props.openStrategy === 'object') return props.openStrategy;
1305
+ switch (props.openStrategy) {
1306
+ case 'list':
1307
+ return listOpenStrategy;
1308
+ case 'single':
1309
+ return singleOpenStrategy;
1310
+ case 'multiple':
1311
+ default:
1312
+ return multipleOpenStrategy;
1313
+ }
1314
+ });
1315
+ const activated = useProxiedModel(props, 'activated', props.activated, v => activeStrategy.value.in(v, children.value, parents.value), v => activeStrategy.value.out(v, children.value, parents.value));
1316
+ const selected = useProxiedModel(props, 'selected', props.selected, v => selectStrategy.value.in(v, children.value, parents.value, disabled.value), v => selectStrategy.value.out(v, children.value, parents.value));
1317
+ onBeforeUnmount(() => {
1318
+ isUnmounted = true;
1319
+ });
1320
+ function getPath(id) {
1321
+ const path = [];
1322
+ let parent = toRaw(id);
1323
+ while (parent !== undefined) {
1324
+ path.unshift(parent);
1325
+ parent = parents.value.get(parent);
1326
+ }
1327
+ return path;
1328
+ }
1329
+ const vm = getCurrentInstance('nested');
1330
+ const nodeIds = new Set();
1331
+ const itemsUpdatePropagation = throttle(() => {
1332
+ nextTick(() => {
1333
+ children.value = new Map(children.value);
1334
+ parents.value = new Map(parents.value);
1335
+ });
1336
+ }, 100);
1337
+ watch(() => [items.value, toValue(returnObject)], () => {
1338
+ if (props.itemsRegistration === 'props') {
1339
+ updateInternalMaps();
1340
+ }
1341
+ }, {
1342
+ immediate: true
1343
+ });
1344
+ function updateInternalMaps() {
1345
+ const _parents = new Map();
1346
+ const _children = new Map();
1347
+ const _disabled = new Set();
1348
+ const getValue = toValue(returnObject) ? item => toRaw(item.raw) : item => item.value;
1349
+ const stack = [...items.value];
1350
+ let i = 0;
1351
+ while (i < stack.length) {
1352
+ const item = stack[i++];
1353
+ const itemValue = getValue(item);
1354
+ if (item.children) {
1355
+ const childValues = [];
1356
+ for (const child of item.children) {
1357
+ const childValue = getValue(child);
1358
+ _parents.set(childValue, itemValue);
1359
+ childValues.push(childValue);
1360
+ stack.push(child);
1361
+ }
1362
+ _children.set(itemValue, childValues);
1363
+ }
1364
+ if (item.props.disabled) {
1365
+ _disabled.add(itemValue);
1366
+ }
1367
+ }
1368
+ children.value = _children;
1369
+ parents.value = _parents;
1370
+ disabled.value = _disabled;
1371
+ }
1372
+ const nested = {
1373
+ id: shallowRef(),
1374
+ root: {
1375
+ opened,
1376
+ activatable: toRef(() => props.activatable),
1377
+ scrollToActive: toRef(() => toValue(scrollToActive)),
1378
+ selectable: toRef(() => props.selectable),
1379
+ activated,
1380
+ selected,
1381
+ selectedValues: computed(() => {
1382
+ const arr = [];
1383
+ for (const [key, value] of selected.value.entries()) {
1384
+ if (value === 'on') arr.push(key);
1385
+ }
1386
+ return arr;
1387
+ }),
1388
+ itemsRegistration: toRef(() => props.itemsRegistration),
1389
+ register: (id, parentId, isDisabled, isGroup) => {
1390
+ if (nodeIds.has(id)) {
1391
+ const path = getPath(id).map(String).join(' -> ');
1392
+ const newPath = getPath(parentId).concat(id).map(String).join(' -> ');
1393
+ consoleError(`Multiple nodes with the same ID\n\t${path}\n\t${newPath}`);
1394
+ return;
1395
+ } else {
1396
+ nodeIds.add(id);
1397
+ }
1398
+ parentId && id !== parentId && parents.value.set(id, parentId);
1399
+ isDisabled && disabled.value.add(id);
1400
+ isGroup && children.value.set(id, []);
1401
+ if (parentId != null) {
1402
+ children.value.set(parentId, [...(children.value.get(parentId) || []), id]);
1403
+ }
1404
+ itemsUpdatePropagation();
1405
+ },
1406
+ unregister: id => {
1407
+ if (isUnmounted) return;
1408
+ nodeIds.delete(id);
1409
+ children.value.delete(id);
1410
+ disabled.value.delete(id);
1411
+ const parent = parents.value.get(id);
1412
+ if (parent) {
1413
+ const list = children.value.get(parent) ?? [];
1414
+ children.value.set(parent, list.filter(child => child !== id));
1415
+ }
1416
+ parents.value.delete(id);
1417
+ itemsUpdatePropagation();
1418
+ },
1419
+ updateDisabled: (id, isDisabled) => {
1420
+ if (isDisabled) {
1421
+ disabled.value.add(id);
1422
+ } else {
1423
+ disabled.value.delete(id);
1424
+ }
1425
+ // classic selection requires refresh to re-evaluate on/off/indeterminate but
1426
+ // currently it is only run for selection interactions, so it will set new disabled
1427
+ // to "off" and the visual state becomes out of sync
1428
+ // -- selected.value = new Map(selected.value)
1429
+ // it is not clear if the framework should un-select when disabled changed to true
1430
+ // more discussion is needed
1431
+ },
1432
+ open: (id, value, event) => {
1433
+ vm.emit('click:open', {
1434
+ id,
1435
+ value,
1436
+ path: getPath(id),
1437
+ event
1438
+ });
1439
+ const newOpened = openStrategy.value.open({
1440
+ id,
1441
+ value,
1442
+ opened: new Set(opened.value),
1443
+ children: children.value,
1444
+ parents: parents.value,
1445
+ event
1446
+ });
1447
+ newOpened && (opened.value = newOpened);
1448
+ },
1449
+ openOnSelect: (id, value, event) => {
1450
+ const newOpened = openStrategy.value.select({
1451
+ id,
1452
+ value,
1453
+ selected: new Map(selected.value),
1454
+ opened: new Set(opened.value),
1455
+ children: children.value,
1456
+ parents: parents.value,
1457
+ event
1458
+ });
1459
+ newOpened && (opened.value = newOpened);
1460
+ },
1461
+ select: (id, value, event) => {
1462
+ vm.emit('click:select', {
1463
+ id,
1464
+ value,
1465
+ path: getPath(id),
1466
+ event
1467
+ });
1468
+ const newSelected = selectStrategy.value.select({
1469
+ id,
1470
+ value,
1471
+ selected: new Map(selected.value),
1472
+ children: children.value,
1473
+ parents: parents.value,
1474
+ disabled: disabled.value,
1475
+ event
1476
+ });
1477
+ newSelected && (selected.value = newSelected);
1478
+ nested.root.openOnSelect(id, value, event);
1479
+ },
1480
+ activate: (id, value, event) => {
1481
+ if (!props.activatable) {
1482
+ return nested.root.select(id, true, event);
1483
+ }
1484
+ vm.emit('click:activate', {
1485
+ id,
1486
+ value,
1487
+ path: getPath(id),
1488
+ event
1489
+ });
1490
+ const newActivated = activeStrategy.value.activate({
1491
+ id,
1492
+ value,
1493
+ activated: new Set(activated.value),
1494
+ children: children.value,
1495
+ parents: parents.value,
1496
+ event
1497
+ });
1498
+ if (newActivated.size !== activated.value.size) {
1499
+ activated.value = newActivated;
1500
+ } else {
1501
+ for (const value of newActivated) {
1502
+ if (!activated.value.has(value)) {
1503
+ activated.value = newActivated;
1504
+ return;
1505
+ }
1506
+ }
1507
+ for (const value of activated.value) {
1508
+ if (!newActivated.has(value)) {
1509
+ activated.value = newActivated;
1510
+ return;
1511
+ }
1512
+ }
1513
+ }
1514
+ },
1515
+ children,
1516
+ parents,
1517
+ disabled,
1518
+ getPath
1519
+ }
1520
+ };
1521
+ provide(VNestedSymbol, nested);
1522
+ return nested.root;
1523
+ };
1524
+ const useNestedItem = (id, isDisabled, isGroup) => {
1525
+ const parent = inject(VNestedSymbol, emptyNested);
1526
+ const uidSymbol = Symbol('nested item');
1527
+ const computedId = computed(() => {
1528
+ const idValue = toRaw(toValue(id));
1529
+ return idValue !== undefined ? idValue : uidSymbol;
1530
+ });
1531
+ const item = {
1532
+ ...parent,
1533
+ id: computedId,
1534
+ open: (open, e) => parent.root.open(computedId.value, open, e),
1535
+ openOnSelect: (open, e) => parent.root.openOnSelect(computedId.value, open, e),
1536
+ isOpen: computed(() => parent.root.opened.value.has(computedId.value)),
1537
+ parent: computed(() => parent.root.parents.value.get(computedId.value)),
1538
+ activate: (activated, e) => parent.root.activate(computedId.value, activated, e),
1539
+ isActivated: computed(() => parent.root.activated.value.has(computedId.value)),
1540
+ scrollToActive: parent.root.scrollToActive,
1541
+ select: (selected, e) => parent.root.select(computedId.value, selected, e),
1542
+ isSelected: computed(() => parent.root.selected.value.get(computedId.value) === 'on'),
1543
+ isIndeterminate: computed(() => parent.root.selected.value.get(computedId.value) === 'indeterminate'),
1544
+ isLeaf: computed(() => !parent.root.children.value.get(computedId.value)),
1545
+ isGroupActivator: parent.isGroupActivator
1546
+ };
1547
+ onBeforeMount(() => {
1548
+ if (parent.isGroupActivator || parent.root.itemsRegistration.value === 'props') return;
1549
+ nextTick(() => {
1550
+ parent.root.register(computedId.value, parent.id.value, toValue(isDisabled), isGroup);
1551
+ });
1552
+ });
1553
+ onBeforeUnmount(() => {
1554
+ if (parent.isGroupActivator || parent.root.itemsRegistration.value === 'props') return;
1555
+ parent.root.unregister(computedId.value);
1556
+ });
1557
+ watch(computedId, (val, oldVal) => {
1558
+ if (parent.isGroupActivator || parent.root.itemsRegistration.value === 'props') return;
1559
+ parent.root.unregister(oldVal);
1560
+ nextTick(() => {
1561
+ parent.root.register(val, parent.id.value, toValue(isDisabled), isGroup);
1562
+ });
1563
+ });
1564
+ watch(() => toValue(isDisabled), val => {
1565
+ parent.root.updateDisabled(computedId.value, val);
1566
+ });
1567
+ isGroup && provide(VNestedSymbol, item);
1568
+ return item;
1569
+ };
1570
+ const useNestedGroupActivator = () => {
1571
+ const parent = inject(VNestedSymbol, emptyNested);
1572
+ provide(VNestedSymbol, {
1573
+ ...parent,
1574
+ isGroupActivator: true
1575
+ });
1576
+ };
1577
+
1578
+ const VListGroupActivator = defineComponent({
1579
+ name: 'VListGroupActivator',
1580
+ setup(_, _ref) {
1581
+ let {
1582
+ slots
1583
+ } = _ref;
1584
+ useNestedGroupActivator();
1585
+ return () => slots.default?.();
1586
+ }
1587
+ });
1588
+ const makeVListGroupProps = propsFactory({
1589
+ /* @deprecated */
1590
+ activeColor: String,
1591
+ baseColor: String,
1592
+ color: String,
1593
+ collapseIcon: {
1594
+ type: IconValue,
1595
+ default: '$collapse'
1596
+ },
1597
+ disabled: Boolean,
1598
+ expandIcon: {
1599
+ type: IconValue,
1600
+ default: '$expand'
1601
+ },
1602
+ rawId: [String, Number],
1603
+ prependIcon: IconValue,
1604
+ appendIcon: IconValue,
1605
+ fluid: Boolean,
1606
+ subgroup: Boolean,
1607
+ title: String,
1608
+ value: null,
1609
+ ...makeComponentProps(),
1610
+ ...makeTagProps()
1611
+ }, 'VListGroup');
1612
+ const VListGroup = genericComponent()({
1613
+ name: 'VListGroup',
1614
+ props: makeVListGroupProps(),
1615
+ setup(props, _ref2) {
1616
+ let {
1617
+ slots
1618
+ } = _ref2;
1619
+ const {
1620
+ isOpen,
1621
+ open,
1622
+ id: _id
1623
+ } = useNestedItem(() => props.value, () => props.disabled, true);
1624
+ const id = computed(() => `v-list-group--id-${String(props.rawId ?? _id.value)}`);
1625
+ const list = useList();
1626
+ const {
1627
+ isBooted
1628
+ } = useSsrBoot();
1629
+ const parent = inject(VNestedSymbol);
1630
+ const renderWhenClosed = toRef(() => parent?.root?.itemsRegistration.value === 'render');
1631
+ function onClick(e) {
1632
+ if (['INPUT', 'TEXTAREA'].includes(e.target?.tagName)) return;
1633
+ open(!isOpen.value, e);
1634
+ }
1635
+ const activatorProps = computed(() => ({
1636
+ onClick,
1637
+ class: 'v-list-group__header',
1638
+ id: id.value
1639
+ }));
1640
+ const toggleIcon = computed(() => isOpen.value ? props.collapseIcon : props.expandIcon);
1641
+ const activatorDefaults = computed(() => ({
1642
+ VListItem: {
1643
+ activeColor: props.activeColor,
1644
+ baseColor: props.baseColor,
1645
+ color: props.color,
1646
+ prependIcon: props.prependIcon || props.subgroup && toggleIcon.value,
1647
+ appendIcon: props.appendIcon || !props.subgroup && toggleIcon.value,
1648
+ title: props.title,
1649
+ value: props.value
1650
+ }
1651
+ }));
1652
+ useRender(() => createVNode(props.tag, {
1653
+ "class": normalizeClass(['v-list-group', {
1654
+ 'v-list-group--prepend': list?.hasPrepend.value,
1655
+ 'v-list-group--fluid': props.fluid,
1656
+ 'v-list-group--subgroup': props.subgroup,
1657
+ 'v-list-group--open': isOpen.value
1658
+ }, props.class]),
1659
+ "style": normalizeStyle(props.style)
1660
+ }, {
1661
+ default: () => [slots.activator && createVNode(VDefaultsProvider, {
1662
+ "defaults": activatorDefaults.value
1663
+ }, {
1664
+ default: () => [createVNode(VListGroupActivator, null, {
1665
+ default: () => [slots.activator({
1666
+ props: activatorProps.value,
1667
+ isOpen: isOpen.value
1668
+ })]
1669
+ })]
1670
+ }), createVNode(MaybeTransition, {
1671
+ "transition": {
1672
+ component: VExpandTransition
1673
+ },
1674
+ "disabled": !isBooted.value
1675
+ }, {
1676
+ default: () => [renderWhenClosed.value ? withDirectives(createElementVNode("div", {
1677
+ "class": "v-list-group__items",
1678
+ "role": "group",
1679
+ "aria-labelledby": id.value
1680
+ }, [slots.default?.()]), [[vShow, isOpen.value]]) : isOpen.value && createElementVNode("div", {
1681
+ "class": "v-list-group__items",
1682
+ "role": "group",
1683
+ "aria-labelledby": id.value
1684
+ }, [slots.default?.()])]
1685
+ })]
1686
+ }));
1687
+ return {
1688
+ isOpen
1689
+ };
1690
+ }
1691
+ });
1692
+
1693
+ const makeVListItemSubtitleProps = propsFactory({
1694
+ opacity: [Number, String],
1695
+ ...makeComponentProps(),
1696
+ ...makeTagProps()
1697
+ }, 'VListItemSubtitle');
1698
+ const VListItemSubtitle = genericComponent()({
1699
+ name: 'VListItemSubtitle',
1700
+ props: makeVListItemSubtitleProps(),
1701
+ setup(props, _ref) {
1702
+ let {
1703
+ slots
1704
+ } = _ref;
1705
+ useRender(() => createVNode(props.tag, {
1706
+ "class": normalizeClass(['v-list-item-subtitle', props.class]),
1707
+ "style": normalizeStyle([{
1708
+ '--v-list-item-subtitle-opacity': props.opacity
1709
+ }, props.style])
1710
+ }, slots));
1711
+ return {};
1712
+ }
1713
+ });
1714
+
1715
+ // Utilities
1716
+ const VListItemTitle = createSimpleFunctional('v-list-item-title');
1717
+
1718
+ const makeVListItemProps = propsFactory({
1719
+ active: {
1720
+ type: Boolean,
1721
+ default: undefined
1722
+ },
1723
+ activeClass: String,
1724
+ /* @deprecated */
1725
+ activeColor: String,
1726
+ appendAvatar: String,
1727
+ appendIcon: IconValue,
1728
+ baseColor: String,
1729
+ disabled: Boolean,
1730
+ lines: [Boolean, String],
1731
+ link: {
1732
+ type: Boolean,
1733
+ default: undefined
1734
+ },
1735
+ nav: Boolean,
1736
+ prependAvatar: String,
1737
+ prependIcon: IconValue,
1738
+ ripple: {
1739
+ type: [Boolean, Object],
1740
+ default: true
1741
+ },
1742
+ slim: Boolean,
1743
+ prependGap: [Number, String],
1744
+ subtitle: {
1745
+ type: [String, Number, Boolean],
1746
+ default: undefined
1747
+ },
1748
+ title: {
1749
+ type: [String, Number, Boolean],
1750
+ default: undefined
1751
+ },
1752
+ value: null,
1753
+ index: Number,
1754
+ tabindex: [Number, String],
1755
+ onClick: EventProp(),
1756
+ onClickOnce: EventProp(),
1757
+ ...makeBorderProps(),
1758
+ ...makeComponentProps(),
1759
+ ...makeDensityProps(),
1760
+ ...makeDimensionProps(),
1761
+ ...makeElevationProps(),
1762
+ ...makeRoundedProps(),
1763
+ ...makeRouterProps(),
1764
+ ...makeTagProps(),
1765
+ ...makeThemeProps(),
1766
+ ...makeVariantProps({
1767
+ variant: 'text'
1768
+ })
1769
+ }, 'VListItem');
1770
+ const VListItem = genericComponent()({
1771
+ name: 'VListItem',
1772
+ directives: {
1773
+ vRipple: Ripple
1774
+ },
1775
+ props: makeVListItemProps(),
1776
+ emits: {
1777
+ click: e => true
1778
+ },
1779
+ setup(props, _ref) {
1780
+ let {
1781
+ attrs,
1782
+ slots,
1783
+ emit
1784
+ } = _ref;
1785
+ const link = useLink(props, attrs);
1786
+ const rootEl = ref();
1787
+ const id = computed(() => props.value === undefined ? link.href.value : props.value);
1788
+ const {
1789
+ activate,
1790
+ isActivated,
1791
+ select,
1792
+ isOpen,
1793
+ isSelected,
1794
+ isIndeterminate,
1795
+ isGroupActivator,
1796
+ root,
1797
+ parent,
1798
+ openOnSelect,
1799
+ scrollToActive,
1800
+ id: uid
1801
+ } = useNestedItem(id, () => props.disabled, false);
1802
+ const list = useList();
1803
+ const isActive = computed(() => props.active !== false && (props.active || link.isActive?.value || (root.activatable.value ? isActivated.value : isSelected.value)));
1804
+ const isLink = toRef(() => props.link !== false && link.isLink.value);
1805
+ const isSelectable = computed(() => !!list && (root.selectable.value || root.activatable.value || props.value != null));
1806
+ const isClickable = computed(() => !props.disabled && props.link !== false && (props.link || link.isClickable.value || isSelectable.value));
1807
+ const isTracked = computed(() => list && list.navigationStrategy.value === 'track' && props.index !== undefined && list.trackingIndex.value === props.index);
1808
+ const role = computed(() => list ? isLink.value ? 'link' : isSelectable.value ? 'option' : 'listitem' : undefined);
1809
+ const ariaSelected = computed(() => {
1810
+ if (!isSelectable.value) return undefined;
1811
+ return root.activatable.value ? isActivated.value : root.selectable.value ? isSelected.value : isActive.value;
1812
+ });
1813
+ const roundedProps = toRef(() => props.rounded || props.nav);
1814
+ const color = toRef(() => props.color ?? props.activeColor);
1815
+ const variantProps = toRef(() => ({
1816
+ color: isActive.value ? color.value ?? props.baseColor : props.baseColor,
1817
+ variant: props.variant
1818
+ }));
1819
+
1820
+ // useNestedItem doesn't call register until beforeMount,
1821
+ // so this can't be an immediate watcher as we don't know parent yet
1822
+ watch(() => link.isActive?.value, val => {
1823
+ if (!val) return;
1824
+ handleActiveLink();
1825
+ });
1826
+ watch(isActivated, val => {
1827
+ if (!val || !scrollToActive) return;
1828
+ rootEl.value?.scrollIntoView({
1829
+ block: 'nearest',
1830
+ behavior: 'instant'
1831
+ });
1832
+ });
1833
+ watch(isTracked, val => {
1834
+ if (!val) return;
1835
+ rootEl.value?.scrollIntoView({
1836
+ block: 'nearest',
1837
+ behavior: 'instant'
1838
+ });
1839
+ });
1840
+ onBeforeMount(() => {
1841
+ if (link.isActive?.value) {
1842
+ nextTick(() => handleActiveLink());
1843
+ }
1844
+ });
1845
+ function handleActiveLink() {
1846
+ if (parent.value != null) {
1847
+ root.open(parent.value, true);
1848
+ }
1849
+ openOnSelect(true);
1850
+ }
1851
+ const {
1852
+ themeClasses
1853
+ } = provideTheme(props);
1854
+ const {
1855
+ borderClasses
1856
+ } = useBorder(props);
1857
+ const {
1858
+ colorClasses,
1859
+ colorStyles,
1860
+ variantClasses
1861
+ } = useVariant(variantProps);
1862
+ const {
1863
+ densityClasses
1864
+ } = useDensity(props);
1865
+ const {
1866
+ dimensionStyles
1867
+ } = useDimension(props);
1868
+ const {
1869
+ elevationClasses
1870
+ } = useElevation(props);
1871
+ const {
1872
+ roundedClasses
1873
+ } = useRounded(roundedProps);
1874
+ const lineClasses = toRef(() => props.lines ? `v-list-item--${props.lines}-line` : undefined);
1875
+ const rippleOptions = toRef(() => props.ripple !== undefined && !!props.ripple && list?.filterable ? {
1876
+ keys: ['Enter']
1877
+ } : props.ripple);
1878
+ const slotProps = computed(() => ({
1879
+ isActive: isActive.value,
1880
+ select,
1881
+ isOpen: isOpen.value,
1882
+ isSelected: isSelected.value,
1883
+ isIndeterminate: isIndeterminate.value,
1884
+ isDisabled: props.disabled
1885
+ }));
1886
+ function onClick(e) {
1887
+ emit('click', e);
1888
+ if (['INPUT', 'TEXTAREA'].includes(e.target?.tagName)) return;
1889
+ if (!isClickable.value) return;
1890
+ link.navigate.value?.(e);
1891
+ if (isGroupActivator) return;
1892
+ if (root.activatable.value) {
1893
+ activate(!isActivated.value, e);
1894
+ } else if (root.selectable.value) {
1895
+ select(!isSelected.value, e);
1896
+ } else if (props.value != null && !isLink.value) {
1897
+ select(!isSelected.value, e);
1898
+ }
1899
+ }
1900
+ function onKeyDown(e) {
1901
+ const target = e.target;
1902
+ if (['INPUT', 'TEXTAREA'].includes(target.tagName)) return;
1903
+ if (e.key === 'Enter' || e.key === ' ' && !list?.filterable) {
1904
+ e.preventDefault();
1905
+ e.stopPropagation();
1906
+ e.target.dispatchEvent(new MouseEvent('click', e));
1907
+ }
1908
+ }
1909
+ useRender(() => {
1910
+ const Tag = isLink.value ? 'a' : props.tag;
1911
+ const hasTitle = slots.title || props.title != null;
1912
+ const hasSubtitle = slots.subtitle || props.subtitle != null;
1913
+ const hasAppendMedia = !!(props.appendAvatar || props.appendIcon);
1914
+ const hasAppend = !!(hasAppendMedia || slots.append);
1915
+ const hasPrependMedia = !!(props.prependAvatar || props.prependIcon);
1916
+ const hasPrepend = !!(hasPrependMedia || slots.prepend);
1917
+ list?.updateHasPrepend(hasPrepend);
1918
+ if (props.activeColor) {
1919
+ deprecate('active-color', ['color', 'base-color']);
1920
+ }
1921
+ return withDirectives(createVNode(Tag, mergeProps(link.linkProps, {
1922
+ "ref": rootEl,
1923
+ "id": props.index !== undefined && list ? `v-list-item-${list.uid}-${props.index}` : undefined,
1924
+ "class": ['v-list-item', {
1925
+ 'v-list-item--active': isActive.value,
1926
+ 'v-list-item--disabled': props.disabled,
1927
+ 'v-list-item--link': isClickable.value,
1928
+ 'v-list-item--nav': props.nav,
1929
+ 'v-list-item--prepend': !hasPrepend && list?.hasPrepend.value,
1930
+ 'v-list-item--slim': props.slim,
1931
+ 'v-list-item--focus-visible': isTracked.value,
1932
+ [`${props.activeClass}`]: props.activeClass && isActive.value
1933
+ }, themeClasses.value, borderClasses.value, colorClasses.value, densityClasses.value, elevationClasses.value, lineClasses.value, roundedClasses.value, variantClasses.value, props.class],
1934
+ "style": [{
1935
+ '--v-list-prepend-gap': convertToUnit(props.prependGap)
1936
+ }, colorStyles.value, dimensionStyles.value, props.style],
1937
+ "tabindex": props.tabindex ?? (isClickable.value ? list ? -2 : 0 : undefined),
1938
+ "aria-selected": ariaSelected.value,
1939
+ "role": role.value,
1940
+ "onClick": onClick,
1941
+ "onKeydown": isClickable.value && !isLink.value && onKeyDown
1942
+ }), {
1943
+ default: () => [genOverlays(isClickable.value || isActive.value, 'v-list-item'), hasPrepend && createElementVNode("div", {
1944
+ "key": "prepend",
1945
+ "class": "v-list-item__prepend"
1946
+ }, [!slots.prepend ? createElementVNode(Fragment, null, [props.prependAvatar && createVNode(VAvatar, {
1947
+ "key": "prepend-avatar",
1948
+ "density": props.density,
1949
+ "image": props.prependAvatar
1950
+ }, null), props.prependIcon && createVNode(VIcon, {
1951
+ "key": "prepend-icon",
1952
+ "density": props.density,
1953
+ "icon": props.prependIcon
1954
+ }, null)]) : createVNode(VDefaultsProvider, {
1955
+ "key": "prepend-defaults",
1956
+ "defaults": {
1957
+ VAvatar: {
1958
+ density: props.density,
1959
+ image: props.prependAvatar
1960
+ },
1961
+ VIcon: {
1962
+ density: props.density,
1963
+ icon: props.prependIcon
1964
+ },
1965
+ VListItemAction: {
1966
+ start: true
1967
+ },
1968
+ VCheckboxBtn: {
1969
+ density: props.density
1970
+ }
1971
+ }
1972
+ }, {
1973
+ default: () => [slots.prepend?.(slotProps.value)]
1974
+ }), createElementVNode("div", {
1975
+ "class": "v-list-item__spacer"
1976
+ }, null)]), createElementVNode("div", {
1977
+ "class": "v-list-item__content",
1978
+ "data-no-activator": ""
1979
+ }, [hasTitle && createVNode(VListItemTitle, {
1980
+ "key": "title"
1981
+ }, {
1982
+ default: () => [slots.title?.({
1983
+ title: props.title
1984
+ }) ?? toDisplayString(props.title)]
1985
+ }), hasSubtitle && createVNode(VListItemSubtitle, {
1986
+ "key": "subtitle"
1987
+ }, {
1988
+ default: () => [slots.subtitle?.({
1989
+ subtitle: props.subtitle
1990
+ }) ?? toDisplayString(props.subtitle)]
1991
+ }), slots.default?.(slotProps.value)]), hasAppend && createElementVNode("div", {
1992
+ "key": "append",
1993
+ "class": "v-list-item__append"
1994
+ }, [!slots.append ? createElementVNode(Fragment, null, [props.appendIcon && createVNode(VIcon, {
1995
+ "key": "append-icon",
1996
+ "density": props.density,
1997
+ "icon": props.appendIcon
1998
+ }, null), props.appendAvatar && createVNode(VAvatar, {
1999
+ "key": "append-avatar",
2000
+ "density": props.density,
2001
+ "image": props.appendAvatar
2002
+ }, null)]) : createVNode(VDefaultsProvider, {
2003
+ "key": "append-defaults",
2004
+ "defaults": {
2005
+ VAvatar: {
2006
+ density: props.density,
2007
+ image: props.appendAvatar
2008
+ },
2009
+ VIcon: {
2010
+ density: props.density,
2011
+ icon: props.appendIcon
2012
+ },
2013
+ VListItemAction: {
2014
+ end: true
2015
+ },
2016
+ VCheckboxBtn: {
2017
+ density: props.density
2018
+ }
2019
+ }
2020
+ }, {
2021
+ default: () => [slots.append?.(slotProps.value)]
2022
+ }), createElementVNode("div", {
2023
+ "class": "v-list-item__spacer"
2024
+ }, null)])]
2025
+ }), [[Ripple, isClickable.value && rippleOptions.value]]);
2026
+ });
2027
+ return {
2028
+ activate,
2029
+ isActivated,
2030
+ isGroupActivator,
2031
+ isSelected,
2032
+ list,
2033
+ select,
2034
+ root,
2035
+ id: uid,
2036
+ link
2037
+ };
2038
+ }
2039
+ });
2040
+
2041
+ const makeVListSubheaderProps = propsFactory({
2042
+ color: String,
2043
+ inset: Boolean,
2044
+ sticky: Boolean,
2045
+ title: String,
2046
+ ...makeComponentProps(),
2047
+ ...makeTagProps()
2048
+ }, 'VListSubheader');
2049
+ const VListSubheader = genericComponent()({
2050
+ name: 'VListSubheader',
2051
+ props: makeVListSubheaderProps(),
2052
+ setup(props, _ref) {
2053
+ let {
2054
+ slots
2055
+ } = _ref;
2056
+ const {
2057
+ textColorClasses,
2058
+ textColorStyles
2059
+ } = useTextColor(() => props.color);
2060
+ useRender(() => {
2061
+ const hasText = !!(slots.default || props.title);
2062
+ return createVNode(props.tag, {
2063
+ "class": normalizeClass(['v-list-subheader', {
2064
+ 'v-list-subheader--inset': props.inset,
2065
+ 'v-list-subheader--sticky': props.sticky
2066
+ }, textColorClasses.value, props.class]),
2067
+ "style": normalizeStyle([{
2068
+ textColorStyles
2069
+ }, props.style])
2070
+ }, {
2071
+ default: () => [hasText && createElementVNode("div", {
2072
+ "class": "v-list-subheader__text"
2073
+ }, [slots.default?.() ?? props.title])]
2074
+ });
2075
+ });
2076
+ return {};
2077
+ }
2078
+ });
2079
+
2080
+ const makeVListChildrenProps = propsFactory({
2081
+ items: Array,
2082
+ returnObject: Boolean
2083
+ }, 'VListChildren');
2084
+ const VListChildren = genericComponent()({
2085
+ name: 'VListChildren',
2086
+ props: makeVListChildrenProps(),
2087
+ setup(props, _ref) {
2088
+ let {
2089
+ slots
2090
+ } = _ref;
2091
+ createList();
2092
+ return () => slots.default?.() ?? props.items?.map((_ref2, index) => {
2093
+ let {
2094
+ children,
2095
+ props: itemProps,
2096
+ type,
2097
+ raw: item
2098
+ } = _ref2;
2099
+ if (type === 'divider') {
2100
+ return slots.divider?.({
2101
+ props: itemProps
2102
+ }) ?? createVNode(VDivider, itemProps, null);
2103
+ }
2104
+ if (type === 'subheader') {
2105
+ return slots.subheader?.({
2106
+ props: itemProps
2107
+ }) ?? createVNode(VListSubheader, itemProps, null);
2108
+ }
2109
+ const slotsWithItem = {
2110
+ subtitle: slots.subtitle ? slotProps => slots.subtitle?.({
2111
+ ...slotProps,
2112
+ item
2113
+ }) : undefined,
2114
+ prepend: slots.prepend ? slotProps => slots.prepend?.({
2115
+ ...slotProps,
2116
+ item
2117
+ }) : undefined,
2118
+ append: slots.append ? slotProps => slots.append?.({
2119
+ ...slotProps,
2120
+ item
2121
+ }) : undefined,
2122
+ title: slots.title ? slotProps => slots.title?.({
2123
+ ...slotProps,
2124
+ item
2125
+ }) : undefined
2126
+ };
2127
+ const listGroupProps = VListGroup.filterProps(itemProps);
2128
+ return children ? createVNode(VListGroup, mergeProps(listGroupProps, {
2129
+ "value": props.returnObject ? item : itemProps?.value,
2130
+ "rawId": itemProps?.value
2131
+ }), {
2132
+ activator: _ref3 => {
2133
+ let {
2134
+ props: activatorProps
2135
+ } = _ref3;
2136
+ const listItemProps = mergeProps(itemProps, activatorProps, {
2137
+ value: props.returnObject ? item : itemProps.value
2138
+ });
2139
+ return slots.header ? slots.header({
2140
+ props: listItemProps
2141
+ }) : createVNode(VListItem, mergeProps(listItemProps, {
2142
+ "index": index
2143
+ }), slotsWithItem);
2144
+ },
2145
+ default: () => createVNode(VListChildren, {
2146
+ "items": children,
2147
+ "returnObject": props.returnObject
2148
+ }, slots)
2149
+ }) : slots.item ? slots.item({
2150
+ props: {
2151
+ ...itemProps,
2152
+ index
2153
+ }
2154
+ }) : createVNode(VListItem, mergeProps(itemProps, {
2155
+ "index": index,
2156
+ "value": props.returnObject ? item : itemProps.value
2157
+ }), slotsWithItem);
2158
+ });
2159
+ }
2160
+ });
2161
+
2162
+ // Utilities
2163
+ // Composables
2164
+ const makeItemsProps = propsFactory({
2165
+ items: {
2166
+ type: Array,
2167
+ default: () => []
2168
+ },
2169
+ itemTitle: {
2170
+ type: [String, Array, Function],
2171
+ default: 'title'
2172
+ },
2173
+ itemValue: {
2174
+ type: [String, Array, Function],
2175
+ default: 'value'
2176
+ },
2177
+ itemChildren: {
2178
+ type: [Boolean, String, Array, Function],
2179
+ default: 'children'
2180
+ },
2181
+ itemProps: {
2182
+ type: [Boolean, String, Array, Function],
2183
+ default: 'props'
2184
+ },
2185
+ itemType: {
2186
+ type: [Boolean, String, Array, Function],
2187
+ default: 'type'
2188
+ },
2189
+ returnObject: Boolean,
2190
+ valueComparator: Function
2191
+ }, 'list-items');
2192
+
2193
+ const itemTypes = new Set(['item', 'divider', 'subheader']);
2194
+ function transformItem(props, item) {
2195
+ const title = isPrimitive(item) ? item : getPropertyFromItem(item, props.itemTitle);
2196
+ const value = isPrimitive(item) ? item : getPropertyFromItem(item, props.itemValue, undefined);
2197
+ const children = getPropertyFromItem(item, props.itemChildren);
2198
+ const itemProps = props.itemProps === true ? omit(item, ['children']) : getPropertyFromItem(item, props.itemProps);
2199
+ let type = getPropertyFromItem(item, props.itemType, 'item');
2200
+ if (!itemTypes.has(type)) {
2201
+ type = 'item';
2202
+ }
2203
+ const _props = {
2204
+ title,
2205
+ value,
2206
+ ...itemProps
2207
+ };
2208
+ return {
2209
+ type,
2210
+ title: _props.title,
2211
+ value: _props.value,
2212
+ props: _props,
2213
+ children: type === 'item' && children ? transformItems(props, children) : undefined,
2214
+ raw: item
2215
+ };
2216
+ }
2217
+ function transformItems(props, items) {
2218
+ const array = [];
2219
+ for (const item of items) {
2220
+ array.push(transformItem(props, item));
2221
+ }
2222
+ return array;
2223
+ }
2224
+ function useListItems(props) {
2225
+ const items = computed(() => transformItems(props, props.items));
2226
+ return {
2227
+ items
2228
+ };
2229
+ }
2230
+ const makeVListProps = propsFactory({
2231
+ baseColor: String,
2232
+ /* @deprecated */
2233
+ activeColor: String,
2234
+ activeClass: String,
2235
+ bgColor: String,
2236
+ disabled: Boolean,
2237
+ filterable: Boolean,
2238
+ expandIcon: IconValue,
2239
+ collapseIcon: IconValue,
2240
+ lines: {
2241
+ type: [Boolean, String],
2242
+ default: 'one'
2243
+ },
2244
+ slim: Boolean,
2245
+ prependGap: [Number, String],
2246
+ indent: [Number, String],
2247
+ nav: Boolean,
2248
+ navigationStrategy: {
2249
+ type: String,
2250
+ default: 'focus'
2251
+ },
2252
+ navigationIndex: Number,
2253
+ 'onClick:open': EventProp(),
2254
+ 'onClick:select': EventProp(),
2255
+ 'onUpdate:opened': EventProp(),
2256
+ ...makeNestedProps({
2257
+ selectStrategy: 'single-leaf',
2258
+ openStrategy: 'list'
2259
+ }),
2260
+ ...makeBorderProps(),
2261
+ ...makeComponentProps(),
2262
+ ...makeDensityProps(),
2263
+ ...makeDimensionProps(),
2264
+ ...makeElevationProps(),
2265
+ ...makeItemsProps(),
2266
+ ...makeRoundedProps(),
2267
+ ...makeTagProps(),
2268
+ ...makeThemeProps(),
2269
+ ...makeVariantProps({
2270
+ variant: 'text'
2271
+ })
2272
+ }, 'VList');
2273
+ const VList = genericComponent()({
2274
+ name: 'VList',
2275
+ props: makeVListProps(),
2276
+ emits: {
2277
+ 'update:selected': value => true,
2278
+ 'update:activated': value => true,
2279
+ 'update:opened': value => true,
2280
+ 'update:navigationIndex': value => true,
2281
+ 'click:open': value => true,
2282
+ 'click:activate': value => true,
2283
+ 'click:select': value => true
2284
+ },
2285
+ setup(props, _ref) {
2286
+ let {
2287
+ attrs,
2288
+ slots} = _ref;
2289
+ const {
2290
+ items
2291
+ } = useListItems(props);
2292
+ const {
2293
+ themeClasses
2294
+ } = provideTheme(props);
2295
+ const {
2296
+ backgroundColorClasses,
2297
+ backgroundColorStyles
2298
+ } = useBackgroundColor(() => props.bgColor);
2299
+ const {
2300
+ borderClasses
2301
+ } = useBorder(props);
2302
+ const {
2303
+ densityClasses
2304
+ } = useDensity(props);
2305
+ const {
2306
+ dimensionStyles
2307
+ } = useDimension(props);
2308
+ const {
2309
+ elevationClasses
2310
+ } = useElevation(props);
2311
+ const {
2312
+ roundedClasses
2313
+ } = useRounded(props);
2314
+ const {
2315
+ children,
2316
+ open,
2317
+ parents,
2318
+ select,
2319
+ getPath
2320
+ } = useNested(props, {
2321
+ items,
2322
+ returnObject: toRef(() => props.returnObject),
2323
+ scrollToActive: toRef(() => props.navigationStrategy === 'track')
2324
+ });
2325
+ const lineClasses = toRef(() => props.lines ? `v-list--${props.lines}-line` : undefined);
2326
+ const activeColor = toRef(() => props.activeColor);
2327
+ const baseColor = toRef(() => props.baseColor);
2328
+ const color = toRef(() => props.color);
2329
+ const isSelectable = toRef(() => props.selectable || props.activatable);
2330
+ const navigationIndex = useProxiedModel(props, 'navigationIndex', -1, v => v ?? -1);
2331
+ const uid = useId();
2332
+ createList({
2333
+ filterable: props.filterable,
2334
+ trackingIndex: navigationIndex,
2335
+ navigationStrategy: toRef(() => props.navigationStrategy),
2336
+ uid
2337
+ });
2338
+ watch(items, () => {
2339
+ if (props.navigationStrategy === 'track') {
2340
+ navigationIndex.value = -1;
2341
+ }
2342
+ });
2343
+ provideDefaults({
2344
+ VListGroup: {
2345
+ activeColor,
2346
+ baseColor,
2347
+ color,
2348
+ expandIcon: toRef(() => props.expandIcon),
2349
+ collapseIcon: toRef(() => props.collapseIcon)
2350
+ },
2351
+ VListItem: {
2352
+ activeClass: toRef(() => props.activeClass),
2353
+ activeColor,
2354
+ baseColor,
2355
+ color,
2356
+ density: toRef(() => props.density),
2357
+ disabled: toRef(() => props.disabled),
2358
+ lines: toRef(() => props.lines),
2359
+ nav: toRef(() => props.nav),
2360
+ slim: toRef(() => props.slim),
2361
+ variant: toRef(() => props.variant),
2362
+ tabindex: toRef(() => props.navigationStrategy === 'track' ? -1 : undefined)
2363
+ }
2364
+ });
2365
+ const isFocused = shallowRef(false);
2366
+ const contentRef = ref();
2367
+ function onFocusin(e) {
2368
+ isFocused.value = true;
2369
+ }
2370
+ function onFocusout(e) {
2371
+ isFocused.value = false;
2372
+ }
2373
+ function onFocus(e) {
2374
+ if (props.navigationStrategy === 'track') {
2375
+ if (!~navigationIndex.value) {
2376
+ navigationIndex.value = getNextIndex('first');
2377
+ }
2378
+ } else if (!isFocused.value && !(e.relatedTarget && contentRef.value?.contains(e.relatedTarget))) focus();
2379
+ }
2380
+ function onBlur() {
2381
+ if (props.navigationStrategy === 'track') {
2382
+ navigationIndex.value = -1;
2383
+ }
2384
+ }
2385
+ function getNavigationDirection(key) {
2386
+ switch (key) {
2387
+ case 'ArrowDown':
2388
+ return 'next';
2389
+ case 'ArrowUp':
2390
+ return 'prev';
2391
+ case 'Home':
2392
+ return 'first';
2393
+ case 'End':
2394
+ return 'last';
2395
+ default:
2396
+ return null;
2397
+ }
2398
+ }
2399
+ function getNextIndex(direction) {
2400
+ const itemCount = items.value.length;
2401
+ if (itemCount === 0) return -1;
2402
+ let nextIndex;
2403
+ if (direction === 'first') {
2404
+ nextIndex = 0;
2405
+ } else if (direction === 'last') {
2406
+ nextIndex = itemCount - 1;
2407
+ } else {
2408
+ nextIndex = navigationIndex.value + (direction === 'next' ? 1 : -1);
2409
+ if (nextIndex < 0) nextIndex = itemCount - 1;
2410
+ if (nextIndex >= itemCount) nextIndex = 0;
2411
+ }
2412
+ const startIndex = nextIndex;
2413
+ let attempts = 0;
2414
+ while (attempts < itemCount) {
2415
+ const item = items.value[nextIndex];
2416
+ if (item && item.type !== 'divider' && item.type !== 'subheader') {
2417
+ return nextIndex;
2418
+ }
2419
+ nextIndex += direction === 'next' || direction === 'first' ? 1 : -1;
2420
+ if (nextIndex < 0) nextIndex = itemCount - 1;
2421
+ if (nextIndex >= itemCount) nextIndex = 0;
2422
+ if (nextIndex === startIndex) return -1;
2423
+ attempts++;
2424
+ }
2425
+ return -1;
2426
+ }
2427
+ function onKeydown(e) {
2428
+ const target = e.target;
2429
+ if (!contentRef.value || target.tagName === 'INPUT' && ['Home', 'End'].includes(e.key) || target.tagName === 'TEXTAREA') {
2430
+ return;
2431
+ }
2432
+ const direction = getNavigationDirection(e.key);
2433
+ if (direction !== null) {
2434
+ e.preventDefault();
2435
+ if (props.navigationStrategy === 'track') {
2436
+ const nextIndex = getNextIndex(direction);
2437
+ if (nextIndex !== -1) {
2438
+ navigationIndex.value = nextIndex;
2439
+ }
2440
+ } else {
2441
+ focus(direction);
2442
+ }
2443
+ }
2444
+ }
2445
+ function onMousedown(e) {
2446
+ isFocused.value = true;
2447
+ }
2448
+ function focus(location, options) {
2449
+ if (contentRef.value) {
2450
+ return focusChild(contentRef.value, location, options);
2451
+ }
2452
+ }
2453
+ useRender(() => {
2454
+ const indent = props.indent ?? (props.prependGap ? Number(props.prependGap) + 24 : undefined);
2455
+ const ariaMultiselectable = isSelectable.value ? attrs.ariaMultiselectable ?? !String(props.selectStrategy).startsWith('single-') : undefined;
2456
+ return createVNode(props.tag, {
2457
+ "ref": contentRef,
2458
+ "class": normalizeClass(['v-list', {
2459
+ 'v-list--disabled': props.disabled,
2460
+ 'v-list--nav': props.nav,
2461
+ 'v-list--slim': props.slim
2462
+ }, themeClasses.value, backgroundColorClasses.value, borderClasses.value, densityClasses.value, elevationClasses.value, lineClasses.value, roundedClasses.value, props.class]),
2463
+ "style": normalizeStyle([{
2464
+ '--v-list-indent': convertToUnit(indent),
2465
+ '--v-list-group-prepend': indent ? '0px' : undefined,
2466
+ '--v-list-prepend-gap': convertToUnit(props.prependGap)
2467
+ }, backgroundColorStyles.value, dimensionStyles.value, props.style]),
2468
+ "tabindex": props.disabled ? -1 : 0,
2469
+ "role": isSelectable.value ? 'listbox' : 'list',
2470
+ "aria-activedescendant": props.navigationStrategy === 'track' && navigationIndex.value >= 0 ? `v-list-item-${uid}-${navigationIndex.value}` : undefined,
2471
+ "aria-multiselectable": ariaMultiselectable,
2472
+ "onFocusin": onFocusin,
2473
+ "onFocusout": onFocusout,
2474
+ "onFocus": onFocus,
2475
+ "onBlur": onBlur,
2476
+ "onKeydown": onKeydown,
2477
+ "onMousedown": onMousedown
2478
+ }, {
2479
+ default: () => [createVNode(VListChildren, {
2480
+ "items": items.value,
2481
+ "returnObject": props.returnObject
2482
+ }, slots)]
2483
+ });
2484
+ });
2485
+ return {
2486
+ open,
2487
+ select,
2488
+ focus,
2489
+ children,
2490
+ parents,
2491
+ getPath,
2492
+ navigationIndex
2493
+ };
2494
+ }
2495
+ });
2496
+
2497
+ const makeVMenuProps = propsFactory({
2498
+ // TODO
2499
+ // disableKeys: Boolean,
2500
+ id: String,
2501
+ submenu: Boolean,
2502
+ ...omit(makeVOverlayProps({
2503
+ captureFocus: true,
2504
+ closeDelay: 250,
2505
+ closeOnContentClick: true,
2506
+ locationStrategy: 'connected',
2507
+ location: undefined,
2508
+ openDelay: 300,
2509
+ scrim: false,
2510
+ scrollStrategy: 'reposition',
2511
+ transition: {
2512
+ component: VDialogTransition
2513
+ }
2514
+ }), ['absolute'])
2515
+ }, 'VMenu');
2516
+ const VMenu = genericComponent()({
2517
+ name: 'VMenu',
2518
+ props: makeVMenuProps(),
2519
+ emits: {
2520
+ 'update:modelValue': value => true
2521
+ },
2522
+ setup(props, _ref) {
2523
+ let {
2524
+ slots
2525
+ } = _ref;
2526
+ const isActive = useProxiedModel(props, 'modelValue');
2527
+ const {
2528
+ scopeId
2529
+ } = useScopeId();
2530
+ const {
2531
+ isRtl
2532
+ } = useRtl();
2533
+ const uid = useId();
2534
+ const id = toRef(() => props.id || `v-menu-${uid}`);
2535
+ const overlay = ref();
2536
+ const parent = inject(VMenuSymbol, null);
2537
+ const openChildren = shallowRef(new Set());
2538
+ provide(VMenuSymbol, {
2539
+ register() {
2540
+ openChildren.value.add(uid);
2541
+ },
2542
+ unregister() {
2543
+ openChildren.value.delete(uid);
2544
+ },
2545
+ closeParents(e) {
2546
+ setTimeout(() => {
2547
+ if (!openChildren.value.size && !props.persistent && (e == null || overlay.value?.contentEl && !isClickInsideElement(e, overlay.value.contentEl))) {
2548
+ isActive.value = false;
2549
+ parent?.closeParents();
2550
+ }
2551
+ }, 40);
2552
+ }
2553
+ });
2554
+ onBeforeUnmount(() => parent?.unregister());
2555
+ onDeactivated(() => isActive.value = false);
2556
+ watch(isActive, val => {
2557
+ val ? parent?.register() : parent?.unregister();
2558
+ }, {
2559
+ immediate: true
2560
+ });
2561
+ function onClickOutside(e) {
2562
+ parent?.closeParents(e);
2563
+ }
2564
+ function onKeydown(e) {
2565
+ if (props.disabled) return;
2566
+ if (e.key === 'Tab' || e.key === 'Enter' && !props.closeOnContentClick) {
2567
+ if (e.key === 'Enter' && (e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLInputElement && !!e.target.closest('form'))) return;
2568
+ if (e.key === 'Enter') e.preventDefault();
2569
+ const nextElement = getNextElement(focusableChildren(overlay.value?.contentEl, false), e.shiftKey ? 'prev' : 'next', el => el.tabIndex >= 0);
2570
+ if (!nextElement && !props.retainFocus) {
2571
+ isActive.value = false;
2572
+ overlay.value?.activatorEl?.focus();
2573
+ }
2574
+ } else if (props.submenu && e.key === (isRtl.value ? 'ArrowRight' : 'ArrowLeft')) {
2575
+ isActive.value = false;
2576
+ overlay.value?.activatorEl?.focus();
2577
+ }
2578
+ }
2579
+ function onActivatorKeydown(e) {
2580
+ if (props.disabled) return;
2581
+ const el = overlay.value?.contentEl;
2582
+ if (el && isActive.value) {
2583
+ if (e.key === 'ArrowDown') {
2584
+ e.preventDefault();
2585
+ e.stopImmediatePropagation();
2586
+ focusChild(el, 'next');
2587
+ } else if (e.key === 'ArrowUp') {
2588
+ e.preventDefault();
2589
+ e.stopImmediatePropagation();
2590
+ focusChild(el, 'prev');
2591
+ } else if (props.submenu) {
2592
+ if (e.key === (isRtl.value ? 'ArrowRight' : 'ArrowLeft')) {
2593
+ isActive.value = false;
2594
+ } else if (e.key === (isRtl.value ? 'ArrowLeft' : 'ArrowRight')) {
2595
+ e.preventDefault();
2596
+ focusChild(el, 'first');
2597
+ }
2598
+ }
2599
+ } else if (props.submenu ? e.key === (isRtl.value ? 'ArrowLeft' : 'ArrowRight') : ['ArrowDown', 'ArrowUp'].includes(e.key)) {
2600
+ isActive.value = true;
2601
+ e.preventDefault();
2602
+ setTimeout(() => setTimeout(() => onActivatorKeydown(e)));
2603
+ }
2604
+ }
2605
+ const activatorProps = computed(() => mergeProps({
2606
+ 'aria-haspopup': 'menu',
2607
+ 'aria-expanded': String(isActive.value),
2608
+ 'aria-controls': id.value,
2609
+ 'aria-owns': id.value,
2610
+ onKeydown: onActivatorKeydown
2611
+ }, props.activatorProps));
2612
+ useRender(() => {
2613
+ const overlayProps = VOverlay.filterProps(props);
2614
+ return createVNode(VOverlay, mergeProps({
2615
+ "ref": overlay,
2616
+ "id": id.value,
2617
+ "class": ['v-menu', props.class],
2618
+ "style": props.style
2619
+ }, overlayProps, {
2620
+ "modelValue": isActive.value,
2621
+ "onUpdate:modelValue": $event => isActive.value = $event,
2622
+ "absolute": true,
2623
+ "activatorProps": activatorProps.value,
2624
+ "location": props.location ?? (props.submenu ? 'end' : 'bottom'),
2625
+ "onClick:outside": onClickOutside,
2626
+ "onKeydown": onKeydown
2627
+ }, scopeId), {
2628
+ activator: slots.activator,
2629
+ default: function () {
2630
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
2631
+ args[_key] = arguments[_key];
2632
+ }
2633
+ return createVNode(VDefaultsProvider, {
2634
+ "root": "VMenu"
2635
+ }, {
2636
+ default: () => [slots.default?.(...args)]
2637
+ });
2638
+ }
2639
+ });
2640
+ });
2641
+ return forwardRefs({
2642
+ id,
2643
+ ΨopenChildren: openChildren
2644
+ }, overlay);
2645
+ }
2646
+ });
2647
+
2648
+ const _hoisted_1 = { class: "d-flex flex-column" };
2649
+ const _hoisted_2 = { class: "d-flex align-center" };
2650
+ const _hoisted_3 = [".imageProperty", ".subTitleProperty", ".filterProperties", ".items", "externalFilter"];
2651
+ const _hoisted_4 = {
2652
+ slot: "filterstitle",
2653
+ style: {"margin":"14px 8px"}
2654
+ };
2655
+ const _hoisted_5 = {
2656
+ slot: "resultstitle",
2657
+ style: {"margin":"14px 8px"}
2658
+ };
2659
+
2660
+ const itemfilterStyleOverride = `
2661
+ li.highlighted {
2662
+ background-color: rgb(var(--v-theme-primary)) !important;
2663
+ color: rgb(var(--v-theme-on-primary)) !important;
2664
+ }
2665
+ li.highlighted .subtitle {
2666
+ opacity: 0.85;
2667
+ }
2668
+ .title-container.truncate,
2669
+ .title.truncate,
2670
+ .subtitle.truncate {
2671
+ white-space: normal !important;
2672
+ overflow: visible !important;
2673
+ text-overflow: unset !important;
2674
+ }
2675
+ .title {
2676
+ display: block;
2677
+ line-height: 1.25;
2678
+ overflow-wrap: anywhere;
2679
+ word-break: break-word;
2680
+ }
2681
+ nav.responsive i.small {
2682
+ width: 72px !important;
2683
+ height: 72px !important;
2684
+ min-width: 72px;
2685
+ min-height: 72px;
2686
+ flex: 0 0 72px;
2687
+ }
2688
+ nav.responsive i.small .image {
2689
+ width: 100%;
2690
+ height: 100%;
2691
+ object-fit: cover;
2692
+ border-radius: 8px;
2693
+ padding:4px;
2694
+ }
2695
+ `;
2696
+
2697
+ const _sfc_main = {
2698
+ __name: 'index',
2699
+ props: {
2700
+ title: {
2701
+ type: String,
2702
+ default: "Explore Catalog",
2703
+ },
2704
+ hoverProperties: {
2705
+ /** @type {import("vue").PropType<string[]>} */
2706
+ type: Array,
2707
+ default: () => ["datetime", "eo:cloud_cover"],
2708
+ },
2709
+ sortBy: {
2710
+ /** @type {import("vue").PropType<{ property: string, label: string }[]>} */
2711
+ type: Array,
2712
+ default: () => [
2713
+ { property: "datetime", label: "Date" },
2714
+ { property: "eo:cloud_cover", label: "Cloud Cover" },
2715
+ ],
2716
+ },
2717
+ layoutTarget: {
2718
+ type: String,
2719
+ default: "lite",
2720
+ },
2721
+ layoutIcon: {
2722
+ type: String,
2723
+ default: mdiViewDashboard,
2724
+ },
2725
+ filtersTitle: {
2726
+ type: String,
2727
+ default: "Filters:",
2728
+ },
2729
+ resultsTitle: {
2730
+ type: String,
2731
+ default: "Items:",
2732
+ },
2733
+ datetimeFilter: {
2734
+ type: Boolean,
2735
+ default: false,
2736
+ },
2737
+ bboxFilter: {
2738
+ type: Boolean,
2739
+ default: true,
2740
+ },
2741
+ imageProperty: {
2742
+ type: String,
2743
+ default: "assets.thumbnail.href",
2744
+ },
2745
+ showTitleBlock: {
2746
+ type: Boolean,
2747
+ default: true,
2748
+ },
2749
+ filters: {
2750
+ /** @type {import("vue").PropType<import("./types").FiltersConfig>} */
2751
+ type: Array,
2752
+ default: () => [
2753
+ {
2754
+ property: "eo:cloud_cover",
2755
+ type: "range",
2756
+ title: "Cloud Cover (%)",
2757
+ min: 0,
2758
+ max: 100,
2759
+ step: 1,
2760
+ state: {
2761
+ min: 0,
2762
+ max: 30,
2763
+ },
2764
+ icon: `<svg style="height: 1rem; transform: translateY(-2px); fill: currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>weather-cloudy</title><path d="M6,19A5,5 0 0,1 1,14A5,5 0 0,1 6,9C7,6.65 9.3,5 12,5C15.43,5 18.24,7.66 18.5,11.03L19,11A4,4 0 0,1 23,15A4,4 0 0,1 19,19H6M19,13H17V12A5,5 0 0,0 12,7C9.5,7 7.45,8.82 7.06,11.19C6.73,11.07 6.37,11 6,11A3,3 0 0,0 3,14A3,3 0 0,0 6,17H19A2,2 0 0,0 21,15A2,2 0 0,0 19,13Z" /></svg>`,
2765
+ unitLabel: "%",
2766
+ },
2767
+ ],
2768
+ },
2769
+ enableCompare: {
2770
+ type: Boolean,
2771
+ default: false,
2772
+ },
2773
+ useMosaic: {
2774
+ type: Boolean,
2775
+ default: false,
2776
+ },
2777
+ mosaicIndicators: {
2778
+ /** @type {import("vue").PropType<string[]>} */
2779
+ type: Array,
2780
+ required: false,
2781
+ },
2782
+ stacEndpoint: {
2783
+ type: String,
2784
+ default: null,
2785
+ },
2786
+ searchLimit: {
2787
+ type: Number,
2788
+ default: 100,
2789
+ },
2790
+ stacItemsStyle: {
2791
+ type: Object,
2792
+ required: false,
2793
+ },
2794
+ stacItemsInteractionStyle: {
2795
+ type: Object,
2796
+ required: false,
2797
+ },
2798
+ },
2799
+ async setup(__props) {
2800
+
2801
+ let __temp, __restore;
2802
+
2803
+ if (!customElements.get("eox-itemfilter")) {
2804
+ (
2805
+ ([__temp,__restore] = withAsyncContext(() => import('@eox/itemfilter'))),
2806
+ await __temp,
2807
+ __restore()
2808
+ );
2809
+ }
2810
+
2811
+ // Props definition
2812
+ const props = __props;
2813
+
2814
+ const itemfilterEl = useTemplateRef("itemfilter");
2815
+
2816
+ // Sorting states
2817
+ const sortMenu = ref(false);
2818
+ const sortOrder = ref("-");
2819
+ const selectedSort = ref(props.sortBy?.[0] ?? "");
2820
+ const sortByParam = ref("-datetime");
2821
+
2822
+ function updateSortByParam() {
2823
+ sortByParam.value = `${sortOrder.value === "+" ? "" : "-"}${
2824
+ selectedSort.value.property
2825
+ }`;
2826
+ }
2827
+ /**
2828
+ * Handle sort option selection
2829
+ * @param {{ property: string, label: string }} option
2830
+ */
2831
+ function selectSort(option) {
2832
+ if (selectedSort.value.property === option.property) {
2833
+ // Flip order if same property
2834
+ sortOrder.value = sortOrder.value === "-" ? "+" : "-";
2835
+ } else {
2836
+ selectedSort.value = option;
2837
+ // default to descending when changing property
2838
+ sortOrder.value = "-";
2839
+ }
2840
+ updateSortByParam();
2841
+ sortMenu.value = false;
2842
+ if (itemfilterEl.value) {
2843
+ itemfilterEl.value.search();
2844
+ }
2845
+ }
2846
+
2847
+ const store = useSTAcStore();
2848
+ const { selectedItem, selectedCompareItem } = storeToRefs(store);
2849
+
2850
+ const catalogEndpoint = computed(
2851
+ () => props.stacEndpoint || store.stacEndpoint,
2852
+ );
2853
+ const {
2854
+ isItemView,
2855
+ latestLayer,
2856
+ visibilityThreshold,
2857
+ returnToOverview,
2858
+ mosaicEndpoint,
2859
+ } = useMosaicState();
2860
+
2861
+ const isMosaicEnabled = computed(
2862
+ () => props.useMosaic && !!mosaicEndpoint.value,
2863
+ );
2864
+
2865
+ const activeSelectedItem =
2866
+ /** @type {import("vue").Ref<import("stac-ts").StacItem | null>} */ (
2867
+ props.enableCompare ? selectedCompareItem : selectedItem
2868
+ );
2869
+
2870
+ if (props.useMosaic) {
2871
+ const unsubscribeReturn = returnToOverview.on(() => {
2872
+ activeSelectedItem.value = null;
2873
+ });
2874
+ onUnmounted(() => {
2875
+ unsubscribeReturn();
2876
+ isItemView.value = false;
2877
+ });
2878
+ }
2879
+
2880
+ onUnmounted(() => {
2881
+ activeSelectedItem.value = null;
2882
+ });
2883
+
2884
+ // Reactive state
2885
+ /** @type {import("vue").Ref<import("@/types").GeoJsonFeature[]>} */
2886
+ const currentItems = ref([]);
2887
+
2888
+ const items = currentItems.value;
2889
+
2890
+ // Initial data fetch
2891
+
2892
+ if (catalogEndpoint.value) {
2893
+ (
2894
+ ([__temp,__restore] = withAsyncContext(() => axios
2895
+ .get(catalogEndpoint.value + `/search?limit=${props.searchLimit}`)
2896
+ .then((res) => (currentItems.value = res.data.features)))),
2897
+ await __temp,
2898
+ __restore()
2899
+ );
2900
+ }
2901
+
2902
+ const filterProperties = createFilterProperties(
2903
+ props.filters,
2904
+ props.datetimeFilter,
2905
+ );
2906
+
2907
+ const subTitleProperty = createSubtitleProperty(props.filters);
2908
+
2909
+ const externalFilterHandler = createExternalFilter(
2910
+ props.filters,
2911
+ props.bboxFilter,
2912
+ props.datetimeFilter,
2913
+ currentItems,
2914
+ sortByParam,
2915
+ props.searchLimit,
2916
+ activeSelectedItem,
2917
+ catalogEndpoint,
2918
+ );
2919
+
2920
+ watch(activeSelectedItem, (item) => {
2921
+ if (itemfilterEl.value) {
2922
+ itemfilterEl.value.selectedResult = item ?? null;
2923
+ }
2924
+
2925
+ if (isMosaicEnabled.value) {
2926
+ isItemView.value = !!item;
2927
+ }
2928
+
2929
+ // restore mosaic when item is deselected
2930
+ if (!isMosaicEnabled.value || item) return;
2931
+ if (latestLayer.value) {
2932
+ renderLatestMosaic();
2933
+ renderItemsFeatures(
2934
+ currentItems.value,
2935
+ props.enableCompare ? mapCompareEl : mapEl,
2936
+ props.hoverProperties,
2937
+ props.stacItemsStyle,
2938
+ props.stacItemsInteractionStyle,
2939
+ );
2940
+ nextTick(() => {
2941
+ const z = mapPosition.value[2] ?? 0;
2942
+ const layer = mapEl.value?.getLayerById(
2943
+ /** @type {string} */ (latestLayer.value?.properties?.id),
2944
+ );
2945
+ layer?.setVisible(z >= visibilityThreshold.value);
2946
+ });
2947
+ }
2948
+ });
2949
+
2950
+ // re-add items features after mosaic layer is rendered
2951
+ watch(
2952
+ () => latestLayer.value,
2953
+ (layer) => {
2954
+ if (!isMosaicEnabled.value || !layer) return;
2955
+ renderItemsFeatures(
2956
+ currentItems.value,
2957
+ props.enableCompare ? mapCompareEl : mapEl,
2958
+ props.hoverProperties,
2959
+ props.stacItemsStyle,
2960
+ props.stacItemsInteractionStyle,
2961
+ );
2962
+ },
2963
+ );
2964
+
2965
+ const scheduleMosaicUpdate = useScheduleMosaicUpdate();
2966
+
2967
+ // Event handlers
2968
+ const onFilter = createOnFilterHandler({
2969
+ currentItems,
2970
+ mapElement: props.enableCompare ? mapCompareEl : mapEl,
2971
+ hoverProperties: props.hoverProperties,
2972
+ stacItemsStyle: props.stacItemsStyle,
2973
+ stacItemsInteractionStyle: props.stacItemsInteractionStyle,
2974
+ itemfilterEl,
2975
+ selectedItemRef: activeSelectedItem,
2976
+ mosaicOptions: isMosaicEnabled.value
2977
+ ? {
2978
+ isMosaicEnabled,
2979
+ getMosaicEndpoint: () => mosaicEndpoint.value,
2980
+ scheduleMosaicUpdate,
2981
+ }
2982
+ : null,
2983
+ });
2984
+
2985
+ /**
2986
+ * @param {CustomEvent} evt
2987
+ */
2988
+ const onSelectItem = createOnSelectHandler(
2989
+ store,
2990
+ props.enableCompare,
2991
+ props.enableCompare ? mapCompareEl : mapEl,
2992
+ );
2993
+
2994
+ // composables
2995
+
2996
+ // Render items features on the map
2997
+ useRenderItemsFeatures(
2998
+ currentItems,
2999
+ props.enableCompare ? mapCompareEl : mapEl,
3000
+ props.hoverProperties,
3001
+ props.stacItemsStyle,
3002
+ props.stacItemsInteractionStyle,
3003
+ );
3004
+ // Search on map move logic
3005
+ useSearchOnMapMove(
3006
+ itemfilterEl,
3007
+ props.bboxFilter,
3008
+ props.enableCompare ? mapCompareEl : mapEl,
3009
+ );
3010
+ // Render on feature click
3011
+ useRenderOnFeatureClick(
3012
+ itemfilterEl,
3013
+ store,
3014
+ props.enableCompare ? mapCompareEl : mapEl,
3015
+ props.enableCompare,
3016
+ );
3017
+ // initialize mosaic and keep in sync with map state
3018
+ useInitMosaic(
3019
+ props.useMosaic ? mosaicEndpoint.value : null,
3020
+ undefined,
3021
+ props.mosaicIndicators,
3022
+ );
3023
+
3024
+ // highlight on feature hover
3025
+ useHoverTooltip(props.hoverProperties);
3026
+ const onMouseEnterResult = createOnMouseEnterResult(
3027
+ props.enableCompare ? mapCompareEl : mapEl,
3028
+ );
3029
+ const onMouseLeaveResult = createOnMouseLeaveResult(
3030
+ props.enableCompare ? mapCompareEl : mapEl,
3031
+ );
3032
+
3033
+
3034
+ return (_ctx, _cache) => {
3035
+
3036
+
3037
+
3038
+
3039
+
3040
+
3041
+
3042
+
3043
+
3044
+ return (openBlock(), createElementBlock("div", _hoisted_1, [
3045
+ (__props.showTitleBlock)
3046
+ ? (openBlock(), createBlock(VRow, {
3047
+ key: 0,
3048
+ class: "title align-center justify-space-between flex-shrink-0"
3049
+ }, {
3050
+ default: withCtx(() => [
3051
+ _cache[7] || (_cache[7] = createElementVNode("h4", null, "Catalog Items", -1 /* CACHED */)),
3052
+ createElementVNode("div", _hoisted_2, [
3053
+ (__props.sortBy?.length)
3054
+ ? (openBlock(), createBlock(VMenu, {
3055
+ key: 0,
3056
+ modelValue: sortMenu.value,
3057
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((sortMenu).value = $event)),
3058
+ "offset-y": ""
3059
+ }, {
3060
+ activator: withCtx(({ props: menuProps }) => [
3061
+ createVNode(VTooltip, { location: "bottom" }, {
3062
+ activator: withCtx(({ props: tooltipProps }) => [
3063
+ createVNode(VBtn, mergeProps({ ...menuProps, ...tooltipProps }, {
3064
+ icon: "",
3065
+ size: "small",
3066
+ color: "primary",
3067
+ class: "mr-2",
3068
+ "aria-label": "Sort",
3069
+ variant: "text"
3070
+ }), {
3071
+ default: withCtx(() => [
3072
+ createVNode(VIcon, null, {
3073
+ default: withCtx(() => [...(_cache[5] || (_cache[5] = [
3074
+ createTextVNode("mdi-sort", -1 /* CACHED */)
3075
+ ]))]),
3076
+ _: 1 /* STABLE */
3077
+ })
3078
+ ]),
3079
+ _: 1 /* STABLE */
3080
+ }, 16 /* FULL_PROPS */)
3081
+ ]),
3082
+ default: withCtx(() => [
3083
+ _cache[6] || (_cache[6] = createElementVNode("span", null, "Sort items", -1 /* CACHED */))
3084
+ ]),
3085
+ _: 2 /* DYNAMIC */
3086
+ }, 1024 /* DYNAMIC_SLOTS */)
3087
+ ]),
3088
+ default: withCtx(() => [
3089
+ createVNode(VList, {
3090
+ density: "compact",
3091
+ color: "primary"
3092
+ }, {
3093
+ default: withCtx(() => [
3094
+ (openBlock(true), createElementBlock(Fragment, null, renderList(props.sortBy, (option) => {
3095
+ return (openBlock(), createBlock(VListItem, {
3096
+ key: option.property,
3097
+ onClick: $event => (selectSort(option)),
3098
+ active: selectedSort.value.property === option.property
3099
+ }, {
3100
+ default: withCtx(() => [
3101
+ createVNode(VListItemTitle, null, {
3102
+ default: withCtx(() => [
3103
+ createTextVNode(toDisplayString(option.label) + " ", 1 /* TEXT */),
3104
+ (selectedSort.value.property === option.property)
3105
+ ? (openBlock(), createBlock(VIcon, {
3106
+ key: 0,
3107
+ size: "x-small",
3108
+ class: "ml-1"
3109
+ }, {
3110
+ default: withCtx(() => [
3111
+ createTextVNode(toDisplayString(sortOrder.value === "+" ? "mdi-arrow-up" : "mdi-arrow-down"), 1 /* TEXT */)
3112
+ ]),
3113
+ _: 1 /* STABLE */
3114
+ }))
3115
+ : createCommentVNode("v-if", true)
3116
+ ]),
3117
+ _: 2 /* DYNAMIC */
3118
+ }, 1024 /* DYNAMIC_SLOTS */)
3119
+ ]),
3120
+ _: 2 /* DYNAMIC */
3121
+ }, 1032 /* PROPS, DYNAMIC_SLOTS */, ["onClick", "active"]))
3122
+ }), 128 /* KEYED_FRAGMENT */))
3123
+ ]),
3124
+ _: 1 /* STABLE */
3125
+ })
3126
+ ]),
3127
+ _: 1 /* STABLE */
3128
+ }, 8 /* PROPS */, ["modelValue"]))
3129
+ : createCommentVNode("v-if", true),
3130
+ (__props.layoutTarget && __props.layoutIcon)
3131
+ ? (openBlock(), createBlock(_sfc_main$1, {
3132
+ key: 1,
3133
+ target: __props.layoutTarget,
3134
+ icon: __props.layoutIcon
3135
+ }, null, 8 /* PROPS */, ["target", "icon"]))
3136
+ : createCommentVNode("v-if", true)
3137
+ ])
3138
+ ]),
3139
+ _: 1 /* STABLE */
3140
+ }))
3141
+ : createCommentVNode("v-if", true),
3142
+ createElementVNode("eox-itemfilter", {
3143
+ class: "itemfilter-scroll",
3144
+ ref: "itemfilter",
3145
+ titleProperty: "id",
3146
+ ".imageProperty": __props.imageProperty,
3147
+ ".subTitleProperty": unref(subTitleProperty),
3148
+ ".filterProperties": unref(filterProperties),
3149
+ ".items": unref(items),
3150
+ ".styleOverride": itemfilterStyleOverride,
3151
+ onSelect: _cache[1] || (_cache[1] = (...args) => (unref(onSelectItem) && unref(onSelectItem)(...args))),
3152
+ onFilter: _cache[2] || (_cache[2] = (...args) => (unref(onFilter) && unref(onFilter)(...args))),
3153
+ "onMouseenter:result": _cache[3] || (_cache[3] = (...args) => (unref(onMouseEnterResult) && unref(onMouseEnterResult)(...args))),
3154
+ "onMouseleave:result": _cache[4] || (_cache[4] = (...args) => (unref(onMouseLeaveResult) && unref(onMouseLeaveResult)(...args))),
3155
+ externalFilter: unref(externalFilterHandler)
3156
+ }, [
3157
+ createElementVNode("h4", _hoisted_4, toDisplayString(__props.filtersTitle), 1 /* TEXT */),
3158
+ createElementVNode("h4", _hoisted_5, toDisplayString(__props.resultsTitle), 1 /* TEXT */)
3159
+ ], 40 /* PROPS, NEED_HYDRATION */, _hoisted_3)
3160
+ ]))
3161
+ }
3162
+ }
3163
+
3164
+ };
3165
+ const index = /*#__PURE__*/_export_sfc(_sfc_main, [['styles',[_style_0]],['__scopeId',"data-v-5a4d8ea3"]]);
3166
+
3167
+ export { index as default };