@eodash/eodash 5.0.0-alpha.2.5 → 5.0.0-alpha.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -5
- package/core/client/App.vue +6 -7
- package/core/client/SuspensedDashboard.ce.vue +58 -40
- package/core/client/asWebComponent.d.ts +10 -5
- package/core/client/asWebComponent.js +6 -6
- package/core/client/components/DashboardLayout.vue +35 -16
- package/core/client/components/DynamicWebComponent.vue +44 -44
- package/core/client/components/ErrorAlert.vue +19 -7
- package/core/client/components/Footer.vue +28 -14
- package/core/client/components/Header.vue +4 -4
- package/core/client/components/IframeWrapper.vue +3 -3
- package/core/client/components/Loading.vue +17 -18
- package/core/client/components/MobileLayout.vue +48 -26
- package/core/client/composables/DefineEodash.js +38 -29
- package/core/client/composables/DefineWidgets.js +101 -81
- package/core/client/composables/index.js +43 -29
- package/core/client/eodash.js +59 -41
- package/core/client/main.js +2 -2
- package/core/client/plugins/index.js +9 -10
- package/core/client/plugins/vuetify.js +9 -10
- package/core/client/render.js +4 -5
- package/core/client/store/Actions.js +8 -0
- package/core/client/store/States.js +8 -13
- package/core/client/store/index.js +14 -11
- package/core/client/store/stac.js +51 -37
- package/core/client/types.d.ts +173 -200
- package/core/client/utils/eodashSTAC.js +130 -49
- package/core/client/utils/helpers.js +18 -20
- package/core/client/utils/index.js +29 -10
- package/core/client/utils/keys.js +2 -2
- package/core/client/views/Dashboard.vue +53 -36
- package/core/client/vite-env.d.ts +19 -17
- package/dist/client/{DashboardLayout-BYROtP-7.js → DashboardLayout-Cbci3g7o.js} +9 -9
- package/dist/client/{DynamicWebComponent-BQhxFPM0.js → DynamicWebComponent-DzmQ3Fr3.js} +3 -3
- package/dist/client/EodashDatePicker-SP5bYISd.js +252 -0
- package/dist/client/{EodashItemFilter-DIBDAHcc.js → EodashItemFilter-B9Tf2TBw.js} +4 -6
- package/dist/client/{EodashMap-C6jJ2Lb_.js → EodashMap-D_znzmX7.js} +13131 -14490
- package/dist/client/EodashMapBtns-BOKugQ88.js +37 -0
- package/dist/client/ExportState-D7m9s4T8.js +558 -0
- package/dist/client/{Footer-BVIZms1S.js → Footer-C2uV1-zu.js} +12 -12
- package/dist/client/Header-C2ROtxo_.js +350 -0
- package/dist/client/{IframeWrapper-XKChM78a.js → IframeWrapper-Wwou4pwf.js} +1 -1
- package/dist/client/{MobileLayout-BlGcMQra.js → MobileLayout-DR27Ctiz.js} +45 -57
- package/dist/client/PopUp-bPGAY43o.js +300 -0
- package/dist/client/VImg-swqiqth2.js +291 -0
- package/dist/client/{VMain-C9XV5Lyg.js → VMain-Bu1bPjvK.js} +2 -2
- package/dist/client/VOverlay-D_MKJ4vQ.js +967 -0
- package/dist/client/{WidgetsContainer-BQXHnZpa.js → WidgetsContainer-CpxYT8YI.js} +10 -3
- package/dist/client/{asWebComponent-CbdGxelK.js → asWebComponent-DeaU3QoK.js} +6332 -6159
- package/dist/client/{basedecoder-Qm25PwVp-CHo5Pomv.js → basedecoder-DHcBySSe-BmCFNFnw.js} +5 -6
- package/dist/client/{decoder-HRvnjnEI-CHAYOWhz.js → decoder-CP4lv0Kb-nokx54iM.js} +1 -1
- package/dist/client/deflate-BXt-9JA_-CWfClgpK.js +10 -0
- package/dist/client/eo-dash.js +2 -2
- package/dist/client/eodashSTAC-CFQuZ_cI.js +2788 -0
- package/dist/client/{eox-itemfilter-DcQkRD2l.js → eox-itemfilter-TaBxgqq_.js} +1002 -974
- package/dist/client/{eox-map-C3DL31fp.js → eox-map-L7abwKTR.js} +5677 -5695
- package/dist/client/{forwardRefs-CyCJOFsz.js → forwardRefs-D0a135Tc.js} +43 -50
- package/dist/client/{index-CabQjjQg.js → index-CoqcWt6E.js} +4 -4
- package/dist/client/{jpeg-DNfUpLwy-Fjan-04T.js → jpeg-BAgeD1d3-oeHbFPUL.js} +5 -6
- package/dist/client/{lerc-_E46UbWQ-TxBH4OeK.js → lerc-DzVumYtB-B3rx9xzz.js} +5 -7
- package/dist/client/{lzw-BOMhmEDy-Dboc93VO.js → lzw-LAGDNbSC-DkP96qO9.js} +1 -1
- package/dist/client/{packbits-DaUD6MLm-Bu1PoTGa.js → packbits-BlDR4Kj5-C66n1-zr.js} +1 -1
- package/dist/client/{pako.esm-C3kYPGGQ-BMki8cQY.js → pako.esm-CB1uQYY0-DB0PYm1P.js} +6 -12
- package/dist/client/{raw-CcGKjn8q-DFOt-i8n.js → raw-CMGvRjfu-BRi6E4i1.js} +1 -1
- package/dist/client/{ssrBoot-DWJ-z4I-.js → ssrBoot-C-inWOiD.js} +1 -1
- package/dist/client/style.css +2 -2
- package/dist/client/{transition-BlLt41wg.js → transition-C8le0YwQ.js} +3 -3
- package/dist/client/{webfontloader-CyOFAuFB.js → webfontloader-qotgY98I.js} +56 -92
- package/dist/client/{webimage-D2c098k3-DLj1LQxB.js → webimage-BM_pbLN3-L2cGWK5l.js} +1 -1
- package/dist/node/cli.js +1 -1
- package/dist/node/types.d.ts +32 -38
- package/package.json +12 -12
- package/widgets/EodashDatePicker.vue +94 -43
- package/widgets/EodashItemFilter.vue +13 -10
- package/widgets/EodashMap.vue +87 -20
- package/widgets/EodashMapBtns.vue +34 -0
- package/widgets/ExportState.vue +112 -0
- package/widgets/PopUp.vue +40 -0
- package/widgets/WidgetsContainer.vue +45 -27
- package/dist/client/EodashDatePicker-CFltnt5d.js +0 -1194
- package/dist/client/Header-TsTL1d2R.js +0 -633
- package/dist/client/deflate-Be2Arps5-hDqMz3RA.js +0 -10
- package/dist/client/http-ZWiLaAeR.js +0 -1337
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<DynamicWebComponent
|
|
2
|
+
<DynamicWebComponent
|
|
3
|
+
:link="link"
|
|
4
|
+
tag-name="eox-itemfilter"
|
|
5
|
+
:properties="properties"
|
|
6
|
+
:on-mounted="onMounted"
|
|
7
|
+
/>
|
|
3
8
|
</template>
|
|
4
9
|
<script setup>
|
|
5
10
|
import DynamicWebComponent from "@/components/DynamicWebComponent.vue";
|
|
@@ -31,10 +36,10 @@ const properties = {
|
|
|
31
36
|
},
|
|
32
37
|
};
|
|
33
38
|
|
|
34
|
-
/** @type {import("@/types").WebComponentProps["onMounted"]}*/
|
|
39
|
+
/** @type {import("@/types").WebComponentProps["onMounted"]} */
|
|
35
40
|
const onMounted = (el, store) => {
|
|
36
41
|
/** @type {any} */ (el).style.height = "100%";
|
|
37
|
-
|
|
42
|
+
|
|
38
43
|
const style = document.createElement("style");
|
|
39
44
|
style.innerHTML = `
|
|
40
45
|
section {
|
|
@@ -59,15 +64,13 @@ const onMounted = (el, store) => {
|
|
|
59
64
|
/**
|
|
60
65
|
* @typedef {object} Item
|
|
61
66
|
* @property {string} href
|
|
62
|
-
|
|
67
|
+
*/
|
|
63
68
|
/** @type {any} */ (el).apply(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
);
|
|
69
|
+
// Only list child elements in list
|
|
70
|
+
store.stac?.filter((item) => item.rel === "child"),
|
|
71
|
+
);
|
|
67
72
|
/** @type {any} */ (el).config.onSelect =
|
|
68
|
-
/**
|
|
69
|
-
* @param {Item} item
|
|
70
|
-
* */
|
|
73
|
+
/** @param {Item} item */
|
|
71
74
|
async (item) => {
|
|
72
75
|
console.log(item);
|
|
73
76
|
await store.loadSelectedSTAC(item.href);
|
package/widgets/EodashMap.vue
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<DynamicWebComponent
|
|
3
|
-
:
|
|
2
|
+
<DynamicWebComponent
|
|
3
|
+
:link="link"
|
|
4
|
+
tag-name="eox-map"
|
|
5
|
+
:properties="properties"
|
|
6
|
+
:on-mounted="onMounted"
|
|
7
|
+
:on-unmounted="onUnmounted"
|
|
8
|
+
/>
|
|
4
9
|
</template>
|
|
5
10
|
<script setup>
|
|
6
11
|
import { inject, watch } from "vue";
|
|
7
12
|
import { toAbsolute } from "stac-js/src/http.js";
|
|
8
|
-
import { EodashCollection } from "@/utils/eodashSTAC";
|
|
13
|
+
import { EodashCollection, extractCollectionUrls } from "@/utils/eodashSTAC";
|
|
9
14
|
import { eodashKey } from "@/utils/keys";
|
|
10
15
|
import { datetime, mapPosition } from "@/store/States";
|
|
11
16
|
import DynamicWebComponent from "@/components/DynamicWebComponent.vue";
|
|
@@ -14,26 +19,53 @@ import "@eox/map/dist/eox-map-advanced-layers-and-sources.js";
|
|
|
14
19
|
|
|
15
20
|
const eodashConfig = /** @type {import("@/types").Eodash} */ inject(eodashKey);
|
|
16
21
|
|
|
17
|
-
/** @type {Record<string,unknown>} */
|
|
22
|
+
/** @type {Record<string, unknown>} */
|
|
18
23
|
const properties = {
|
|
19
24
|
class: "fill-height fill-width overflow-none",
|
|
20
25
|
center: [15, 48],
|
|
21
|
-
|
|
26
|
+
zoom: 4,
|
|
27
|
+
// TODO: we should probably introduce some way of defining
|
|
28
|
+
layers: [
|
|
29
|
+
{
|
|
30
|
+
type: "Vector",
|
|
31
|
+
source: {
|
|
32
|
+
type: "Vector",
|
|
33
|
+
url: "https://openlayers.org/data/vector/ecoregions.json",
|
|
34
|
+
format: "GeoJSON",
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: "Tile",
|
|
39
|
+
properties: {
|
|
40
|
+
id: "osm",
|
|
41
|
+
title: "Background",
|
|
42
|
+
},
|
|
43
|
+
source: {
|
|
44
|
+
type: "OSM",
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
],
|
|
22
48
|
};
|
|
23
49
|
// Check if selected indicator was already set in store
|
|
24
50
|
if (mapPosition && mapPosition.value && mapPosition.value.length === 3) {
|
|
25
51
|
// TODO: do further checks for invalid values?
|
|
52
|
+
// TODO: can we expect the values to be in a specific projection
|
|
26
53
|
properties.center = [mapPosition.value?.[0], mapPosition.value[1]];
|
|
27
54
|
properties.zoom = mapPosition.value[2];
|
|
28
55
|
}
|
|
29
56
|
|
|
30
57
|
const link = () => import("@eox/map");
|
|
31
58
|
|
|
32
|
-
/** @type {import("openlayers").EventsListenerFunctionType}*/
|
|
59
|
+
/** @type {import("openlayers").EventsListenerFunctionType} */
|
|
33
60
|
const handleMoveEnd = (evt) => {
|
|
34
61
|
const map = /** @type {import("openlayers").Map | undefined} */ (
|
|
35
|
-
/** @type {
|
|
62
|
+
/** @type {any} */ (evt).map
|
|
36
63
|
);
|
|
64
|
+
/*
|
|
65
|
+
const currentProj = map?.getView().getProjection();
|
|
66
|
+
const transFunc = getTransform(currentProj?.getCode(), 'EPSG:4326');
|
|
67
|
+
const [x, y] = transFunc(map?.getView().getCenter() ?? [0, 0], undefined, undefined);
|
|
68
|
+
*/
|
|
37
69
|
const [x, y] = map?.getView().getCenter() ?? [0, 0];
|
|
38
70
|
const z = map?.getView().getZoom();
|
|
39
71
|
if (!Number.isNaN(x) && !Number.isNaN(y) && !Number.isNaN(z)) {
|
|
@@ -45,7 +77,6 @@ const handleMoveEnd = (evt) => {
|
|
|
45
77
|
const onMounted = (el, store) => {
|
|
46
78
|
/** @type {any} */
|
|
47
79
|
(el)?.map?.on("moveend", handleMoveEnd);
|
|
48
|
-
|
|
49
80
|
const { selectedStac } = storeToRefs(store);
|
|
50
81
|
|
|
51
82
|
watch(
|
|
@@ -54,23 +85,59 @@ const onMounted = (el, store) => {
|
|
|
54
85
|
if (updatedStac) {
|
|
55
86
|
const parentCollUrl = toAbsolute(
|
|
56
87
|
`./${updatedStac.id}/collection.json`,
|
|
57
|
-
eodashConfig.stacEndpoint
|
|
58
|
-
);
|
|
59
|
-
const childCollUrl = toAbsolute(
|
|
60
|
-
updatedStac.links[1].href,
|
|
61
|
-
parentCollUrl
|
|
88
|
+
eodashConfig.stacEndpoint,
|
|
62
89
|
);
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
new Date(updatedTime)
|
|
90
|
+
const collectionUrls = extractCollectionUrls(
|
|
91
|
+
selectedStac.value,
|
|
92
|
+
parentCollUrl,
|
|
67
93
|
);
|
|
68
|
-
|
|
69
|
-
|
|
94
|
+
/** @type {import("@/utils/eodashSTAC").EodashCollection[]} */
|
|
95
|
+
const eodashCollections = [];
|
|
96
|
+
collectionUrls.forEach((cu) => {
|
|
97
|
+
eodashCollections.push(new EodashCollection(cu));
|
|
98
|
+
});
|
|
99
|
+
const layersCollection = [];
|
|
100
|
+
for (let idx = 0; idx < eodashCollections.length; idx++) {
|
|
101
|
+
const ec = eodashCollections[idx];
|
|
102
|
+
let layers;
|
|
103
|
+
if (updatedTime) {
|
|
104
|
+
layers = await ec.createLayersJson(new Date(updatedTime));
|
|
105
|
+
} else {
|
|
106
|
+
layers = await ec.createLayersJson();
|
|
107
|
+
}
|
|
108
|
+
if (layers) {
|
|
109
|
+
layersCollection.push(...layers);
|
|
110
|
+
}
|
|
70
111
|
}
|
|
112
|
+
// TODO: add base layers and overlays as defined in the top collection / indicator
|
|
113
|
+
// Probably best also to introduce background and overlay groups
|
|
114
|
+
// For now adding OSM as background
|
|
115
|
+
layersCollection.push({
|
|
116
|
+
type: "Tile",
|
|
117
|
+
properties: {
|
|
118
|
+
id: "osm",
|
|
119
|
+
title: "Background",
|
|
120
|
+
},
|
|
121
|
+
source: {
|
|
122
|
+
type: "OSM",
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// TODO: we can check if the collection / indicator has a specific
|
|
127
|
+
// projection it wants to be displayed in the map we can register
|
|
128
|
+
// and set the attribute here, e.g. like following
|
|
129
|
+
/*
|
|
130
|
+
(el)?.registerProjection(
|
|
131
|
+
'EPSG:3031','+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs +type=crs'
|
|
132
|
+
);
|
|
133
|
+
(el)?.projection = "EPSG:3031";
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
/** @type {any} */
|
|
137
|
+
(el).layers = layersCollection;
|
|
71
138
|
}
|
|
72
139
|
},
|
|
73
|
-
{ immediate: true }
|
|
140
|
+
{ immediate: true },
|
|
74
141
|
);
|
|
75
142
|
};
|
|
76
143
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div ref="rootRef" class="d-flex align-end justify-end my-3 pa-2">
|
|
3
|
+
<v-btn
|
|
4
|
+
class="map-btn"
|
|
5
|
+
color="primary"
|
|
6
|
+
:icon="[mdiMapPlus]"
|
|
7
|
+
@click="showMapState = !showMapState"
|
|
8
|
+
/>
|
|
9
|
+
<ExportState :header="header" :code="code" v-model="showMapState" />
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
<script setup>
|
|
13
|
+
import { makePanelTransparent } from "@/composables";
|
|
14
|
+
import { mdiMapPlus } from "@mdi/js";
|
|
15
|
+
import ExportState from "^/ExportState.vue";
|
|
16
|
+
import { ref } from "vue";
|
|
17
|
+
const header = "Export Map";
|
|
18
|
+
const code = `<h2>
|
|
19
|
+
code example
|
|
20
|
+
</h2>`;
|
|
21
|
+
|
|
22
|
+
const showMapState = ref(false);
|
|
23
|
+
|
|
24
|
+
/** @type {import("vue").Ref<HTMLDivElement|null>} */
|
|
25
|
+
const rootRef = ref(null);
|
|
26
|
+
makePanelTransparent(rootRef);
|
|
27
|
+
</script>
|
|
28
|
+
<style scoped>
|
|
29
|
+
.map-btn {
|
|
30
|
+
width: 36px;
|
|
31
|
+
height: 36px;
|
|
32
|
+
border-radius: 25%;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<PopUp v-model="dialog">
|
|
3
|
+
<v-card>
|
|
4
|
+
<v-card-title class="bg-primary">
|
|
5
|
+
<h5 class="text-h5">Storytelling map configuration</h5>
|
|
6
|
+
</v-card-title>
|
|
7
|
+
|
|
8
|
+
<v-card-text class="py-5">
|
|
9
|
+
<p class="text-body-2">
|
|
10
|
+
Copy and paste this code into the map layers field of the storytelling
|
|
11
|
+
editor:
|
|
12
|
+
</p>
|
|
13
|
+
<div class="pa-3 code-block">
|
|
14
|
+
{{ getLayers(props.for) }}
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
<div style="position: absolute; bottom: 15px">
|
|
18
|
+
<v-expand-transition>
|
|
19
|
+
<div v-if="copySuccess" class="text-success mr-3">
|
|
20
|
+
<v-icon color="success" left :icon="[mdiClipboardCheckOutline]" />
|
|
21
|
+
<small>copied!</small>
|
|
22
|
+
</div>
|
|
23
|
+
</v-expand-transition>
|
|
24
|
+
</div>
|
|
25
|
+
<v-row class="d-flex pt-3 justify-end">
|
|
26
|
+
<v-col cols="6" class="flex-column align-center text-end">
|
|
27
|
+
<v-btn
|
|
28
|
+
v-for="btn in copyBtns"
|
|
29
|
+
class="text-body-2"
|
|
30
|
+
@click="btn.copyFn"
|
|
31
|
+
:key="btn.id"
|
|
32
|
+
small
|
|
33
|
+
variant="text"
|
|
34
|
+
:prepend-icon="[mdiContentCopy]"
|
|
35
|
+
>
|
|
36
|
+
copy as {{ btn.copyAs }}
|
|
37
|
+
</v-btn>
|
|
38
|
+
</v-col>
|
|
39
|
+
</v-row>
|
|
40
|
+
</v-card-text>
|
|
41
|
+
|
|
42
|
+
<v-divider></v-divider>
|
|
43
|
+
|
|
44
|
+
<v-card-actions>
|
|
45
|
+
<v-spacer></v-spacer>
|
|
46
|
+
<v-btn variant="text" @click="dialog = !dialog"> Close </v-btn>
|
|
47
|
+
</v-card-actions>
|
|
48
|
+
</v-card>
|
|
49
|
+
</PopUp>
|
|
50
|
+
</template>
|
|
51
|
+
<script setup>
|
|
52
|
+
import { mdiClipboardCheckOutline, mdiContentCopy } from "@mdi/js";
|
|
53
|
+
import PopUp from "./PopUp.vue";
|
|
54
|
+
import { copyToClipBoard } from "@/utils";
|
|
55
|
+
import { computed, ref } from "vue";
|
|
56
|
+
import { getLayers } from "@/store/Actions";
|
|
57
|
+
import { mapPosition } from "@/store/States";
|
|
58
|
+
|
|
59
|
+
const dialog = defineModel({ type: Boolean, required: true, default: false });
|
|
60
|
+
|
|
61
|
+
const props = defineProps({
|
|
62
|
+
for: {
|
|
63
|
+
type: String,
|
|
64
|
+
default: "eox-map",
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const copySuccess = ref(false);
|
|
69
|
+
|
|
70
|
+
const copyBtns = [
|
|
71
|
+
{
|
|
72
|
+
id: Symbol(),
|
|
73
|
+
copyFn: async () => await copyToClipBoard(mapEntryCode.value, copySuccess),
|
|
74
|
+
copyAs: "simple map",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: Symbol(),
|
|
78
|
+
copyFn: async () =>
|
|
79
|
+
await copyToClipBoard(JSON.stringify(getLayers(props?.for)), copySuccess),
|
|
80
|
+
copyAs: "layers configuration",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: Symbol(),
|
|
84
|
+
copyFn: async () => await copyToClipBoard(mapStepCode.value, copySuccess),
|
|
85
|
+
copyAs: "map tour section",
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
const mapStepCode = computed(() => {
|
|
90
|
+
const [x, y, z] = mapPosition.value;
|
|
91
|
+
const preTag = "### <!--{ layers=";
|
|
92
|
+
const endTag = `zoom="${z}" center=[${[x, y]}] animationOptions={duration:500}}-->
|
|
93
|
+
#### Tour step title
|
|
94
|
+
Text describing the current step of the tour and why it is interesting what the map shows currently
|
|
95
|
+
`;
|
|
96
|
+
return `${preTag}'${JSON.stringify(getLayers(props?.for))}' ${endTag}`;
|
|
97
|
+
});
|
|
98
|
+
const mapEntryCode = computed(() => {
|
|
99
|
+
const [x, y, z] = mapPosition.value;
|
|
100
|
+
const preTag =
|
|
101
|
+
'## Map Example <!--{as="eox-map" style="width: 100%; height: 500px;" layers=';
|
|
102
|
+
const endTag = `zoom="${z}" center=[${[x, y]}] }-->`;
|
|
103
|
+
return `${preTag}'${JSON.stringify(getLayers(props?.for))}' ${endTag}`;
|
|
104
|
+
});
|
|
105
|
+
</script>
|
|
106
|
+
<style scoped>
|
|
107
|
+
.code-block {
|
|
108
|
+
background-color: #ddd;
|
|
109
|
+
font-family: monospace;
|
|
110
|
+
font-size: small;
|
|
111
|
+
}
|
|
112
|
+
</style>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span>
|
|
3
|
+
<v-dialog
|
|
4
|
+
max-width="500px"
|
|
5
|
+
max-height="500px"
|
|
6
|
+
absolute
|
|
7
|
+
scrollable
|
|
8
|
+
scroll-strategy="block"
|
|
9
|
+
close-on-back
|
|
10
|
+
v-model="dialog"
|
|
11
|
+
>
|
|
12
|
+
<v-sheet>
|
|
13
|
+
<component
|
|
14
|
+
v-if="widget"
|
|
15
|
+
:is="definedWidget.component"
|
|
16
|
+
:key="definedWidget.id"
|
|
17
|
+
v-bind="definedWidget.props"
|
|
18
|
+
/>
|
|
19
|
+
<span v-if="$slots.default">
|
|
20
|
+
<slot />
|
|
21
|
+
</span>
|
|
22
|
+
</v-sheet>
|
|
23
|
+
</v-dialog>
|
|
24
|
+
</span>
|
|
25
|
+
</template>
|
|
26
|
+
<script setup>
|
|
27
|
+
import { useDefineWidgets } from "@/composables/DefineWidgets";
|
|
28
|
+
|
|
29
|
+
const dialog = defineModel({ type: Boolean, required: true, default: false });
|
|
30
|
+
|
|
31
|
+
const props = defineProps({
|
|
32
|
+
widget: {
|
|
33
|
+
/** @type {import("vue").PropType<import("@/types").Widget>} */
|
|
34
|
+
type: Object,
|
|
35
|
+
default: undefined,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const [definedWidget] = useDefineWidgets([props?.widget]);
|
|
40
|
+
</script>
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<details
|
|
3
|
-
|
|
2
|
+
<details
|
|
3
|
+
is="animated-details"
|
|
4
|
+
v-for="(mod, idx) in importedWidgets"
|
|
5
|
+
ref="detailsEls"
|
|
6
|
+
:key="idx"
|
|
7
|
+
class="overflow-auto"
|
|
8
|
+
exclusive
|
|
9
|
+
>
|
|
4
10
|
<summary ref="summaryEls">{{ mod.value.title }}</summary>
|
|
5
11
|
<span :style="{ height: widgetHeight }" class="d-flex flex-column">
|
|
6
12
|
<component :is="mod.value.component" v-bind="mod.value.props" />
|
|
@@ -8,41 +14,53 @@
|
|
|
8
14
|
</details>
|
|
9
15
|
</template>
|
|
10
16
|
<script setup>
|
|
11
|
-
import { useDefineWidgets } from
|
|
12
|
-
import { nextTick, onMounted } from
|
|
13
|
-
import { ref } from
|
|
14
|
-
import { useLayout } from
|
|
15
|
-
import
|
|
17
|
+
import { useDefineWidgets } from "@/composables/DefineWidgets";
|
|
18
|
+
import { nextTick, onMounted } from "vue";
|
|
19
|
+
import { ref } from "vue";
|
|
20
|
+
import { useLayout } from "vuetify/lib/framework.mjs";
|
|
21
|
+
import "animated-details";
|
|
16
22
|
|
|
17
23
|
const props = defineProps({
|
|
18
24
|
widgets: {
|
|
19
|
-
/**
|
|
25
|
+
/**
|
|
26
|
+
* @type {import("vue").PropType<
|
|
27
|
+
* Omit<import("@/types").Widget, "layout">[]
|
|
28
|
+
* >}
|
|
29
|
+
*/
|
|
20
30
|
type: Array,
|
|
21
31
|
required: true,
|
|
22
|
-
}
|
|
23
|
-
})
|
|
32
|
+
},
|
|
33
|
+
});
|
|
24
34
|
|
|
25
|
-
const importedWidgets = useDefineWidgets(props.widgets)
|
|
35
|
+
const importedWidgets = useDefineWidgets(props.widgets);
|
|
26
36
|
|
|
27
37
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
* Details elements template ref
|
|
39
|
+
*
|
|
40
|
+
* @type {import("vue").Ref<HTMLDetailsElement[]>}
|
|
41
|
+
*/
|
|
42
|
+
const detailsEls = ref([]);
|
|
32
43
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
const
|
|
44
|
+
* Summary elements template ref
|
|
45
|
+
*
|
|
46
|
+
* @type {import("vue").Ref<HTMLDetailsElement[]>}
|
|
47
|
+
*/
|
|
48
|
+
const summaryEls = ref([]);
|
|
49
|
+
const widgetHeight = ref("");
|
|
50
|
+
const summariesHeights = ref(0);
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
const { mainRect } = useLayout()
|
|
52
|
+
const { mainRect } = useLayout();
|
|
42
53
|
onMounted(async () => {
|
|
43
54
|
await nextTick(() => {
|
|
44
|
-
summariesHeights.value = summaryEls.value.reduce(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
summariesHeights.value = summaryEls.value.reduce(
|
|
56
|
+
(acc, el) => (acc += el.clientHeight),
|
|
57
|
+
0,
|
|
58
|
+
);
|
|
59
|
+
widgetHeight.value =
|
|
60
|
+
(detailsEls.value[0].parentElement?.scrollHeight ?? 0) -
|
|
61
|
+
summariesHeights.value -
|
|
62
|
+
mainRect.value["top"] +
|
|
63
|
+
"px";
|
|
64
|
+
});
|
|
65
|
+
});
|
|
48
66
|
</script>
|