@eodash/eodash 5.6.1 → 5.7.1

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