@eodash/eodash 5.0.0-alpha → 5.0.0-alpha.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.
- package/bin/app.js +16 -147
- package/bin/cli.js +58 -0
- package/bin/serverConfig.js +130 -0
- package/bin/update.js +19 -0
- package/bin/utils.js +28 -0
- package/core/App.vue +21 -0
- package/core/components/DashboardLayout.vue +58 -0
- package/core/components/DynamicWebComponent.vue +68 -0
- package/core/components/Footer.vue +31 -0
- package/core/components/Header.vue +38 -0
- package/core/components/IframeWrapper.vue +11 -0
- package/core/components/MobileLayout.vue +79 -0
- package/core/composables/DefineConfig.js +44 -0
- package/core/composables/DefineWidgets.js +118 -0
- package/core/composables/index.js +147 -0
- package/core/eodashConfig.js +136 -0
- package/core/main.js +4 -0
- package/core/plugins/index.js +27 -0
- package/core/plugins/router.js +20 -0
- package/core/plugins/vuetify.js +27 -0
- package/core/render.js +13 -0
- package/core/store/Actions.js +28 -0
- package/core/store/Keys.js +6 -0
- package/core/store/States.js +12 -0
- package/core/store/index.js +23 -0
- package/core/store/stac.js +61 -0
- package/core/types.d.ts +333 -0
- package/core/views/Dashboard.vue +49 -0
- package/core/vite-env.d.ts +35 -0
- package/package.json +20 -21
- package/widgets/WidgetsContainer.vue +46 -0
- package/dist/.gitkeep +0 -0
- package/dist/assets/Dashboard-4233cc52.css +0 -1
- package/dist/assets/Dashboard-d7d438a6.js +0 -1
- package/dist/assets/DashboardLayout-5ab6c4a5.js +0 -29
- package/dist/assets/DashboardLayout-739135c5.css +0 -1
- package/dist/assets/DynamicWebComponent-1b7b527a.js +0 -1
- package/dist/assets/Footer-0fe1887a.css +0 -1
- package/dist/assets/Footer-7e1ca735.js +0 -1
- package/dist/assets/Header-9cac598d.css +0 -1
- package/dist/assets/Header-ae17296c.js +0 -1
- package/dist/assets/IframeWrapper-b2c05ffd.js +0 -1
- package/dist/assets/MobileLayout-1b3cb89b.js +0 -1
- package/dist/assets/MobileLayout-ad647682.css +0 -1
- package/dist/assets/VMain-d6216866.css +0 -1
- package/dist/assets/VMain-f6b3f120.js +0 -1
- package/dist/assets/WidgetsContainer-cdad3d50.js +0 -1
- package/dist/assets/color-6039ab34.js +0 -1
- package/dist/assets/eox-chart-3b122ba4.js +0 -142
- package/dist/assets/eox-itemfilter-a63da0da.js +0 -981
- package/dist/assets/eox-jsonform-98c2af62.js +0 -239
- package/dist/assets/eox-layercontrol-f5e35702.js +0 -1126
- package/dist/assets/eox-map-7f82b0d4.js +0 -178
- package/dist/assets/eox-stacinfo-65a8d8f3.js +0 -388
- package/dist/assets/eox-timecontrol-4be63aa2.js +0 -237
- package/dist/assets/index-52c9d989.js +0 -34
- package/dist/assets/index-5d2400e5.css +0 -5
- package/dist/assets/materialdesignicons-webfont-48d3eec6.woff +0 -0
- package/dist/assets/materialdesignicons-webfont-861aea05.eot +0 -0
- package/dist/assets/materialdesignicons-webfont-bd725a7a.ttf +0 -0
- package/dist/assets/materialdesignicons-webfont-e52d60f6.woff2 +0 -0
- package/dist/assets/ssrBoot-0b8754a9.css +0 -1
- package/dist/assets/ssrBoot-d383eb61.js +0 -1
- package/dist/assets/webfontloader-523643f5.js +0 -1
- package/dist/index.html +0 -17
- package/dist/types/index.d.ts +0 -124
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-main class="overflow-none" style="height: 91dvh;">
|
|
3
|
+
|
|
4
|
+
<component :is="bgWidget.component" v-bind="bgWidget.props"></component>
|
|
5
|
+
|
|
6
|
+
<v-row no-gutters class="d-flex justify-center align-end">
|
|
7
|
+
<v-col v-for="(importedWidget, idx) in importedWidgets" :key="idx" :cols="cols"
|
|
8
|
+
class="flex-column fill-height fill-width elevation-1 align-start ma-0 justify-center">
|
|
9
|
+
<span class="d-flex pa-2 justify-center ma-0 panel-header align-center fill-width" @click="handleSelection(idx)">
|
|
10
|
+
{{ importedWidget.value.title }}
|
|
11
|
+
</span>
|
|
12
|
+
<div v-show="activeIdx === idx" class="overlay align-self-end overflow-auto pa-2">
|
|
13
|
+
<v-btn icon variant="text" class="close-btn" @click="activeIdx = -1">✕</v-btn>
|
|
14
|
+
<component :key="importedWidget.value.id" :is="importedWidget.value.component" v-show="activeIdx === idx"
|
|
15
|
+
v-bind="importedWidget.value.props" />
|
|
16
|
+
</div>
|
|
17
|
+
</v-col>
|
|
18
|
+
</v-row>
|
|
19
|
+
</v-main>
|
|
20
|
+
</template>
|
|
21
|
+
<script setup>
|
|
22
|
+
import { eodashConfigKey } from '@/store/Keys';
|
|
23
|
+
import { inject } from 'vue';
|
|
24
|
+
import { useDefineWidgets } from '@/composables/DefineWidgets'
|
|
25
|
+
import { ref } from 'vue';
|
|
26
|
+
|
|
27
|
+
const eodashConfig = /** @type {EodashConfig} */(inject(eodashConfigKey));
|
|
28
|
+
|
|
29
|
+
//import widgets
|
|
30
|
+
const widgetsConfig = eodashConfig.template.widgets
|
|
31
|
+
const importedWidgets = useDefineWidgets(widgetsConfig)
|
|
32
|
+
const [bgWidget] = useDefineWidgets([eodashConfig.template?.background])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* number of flex columns
|
|
38
|
+
*/
|
|
39
|
+
const cols = importedWidgets.length / 12
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* index of the active tab
|
|
43
|
+
*/
|
|
44
|
+
const activeIdx = ref(-1)
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {number} idx
|
|
48
|
+
**/
|
|
49
|
+
const handleSelection = (idx) => {
|
|
50
|
+
activeIdx.value = activeIdx.value === idx ? -1 : idx
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
<style scoped lang='scss'>
|
|
54
|
+
.panel-header {
|
|
55
|
+
height: auto;
|
|
56
|
+
margin: 0;
|
|
57
|
+
width: 100%;
|
|
58
|
+
position: relative;
|
|
59
|
+
bottom: 64px;
|
|
60
|
+
z-index: 10;
|
|
61
|
+
background: rgb(var(--v-theme-background));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.overlay {
|
|
65
|
+
position: absolute;
|
|
66
|
+
width: 100%;
|
|
67
|
+
left: 0;
|
|
68
|
+
top: 64px;
|
|
69
|
+
bottom: 64px;
|
|
70
|
+
z-index: 1;
|
|
71
|
+
background: rgb(var(--v-theme-background));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.close-btn {
|
|
75
|
+
justify-self: end;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
//
|
|
79
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { eodashConfigKey } from "@/store/Keys"
|
|
2
|
+
import { inject } from "vue"
|
|
3
|
+
import store from '@/store'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sets user defined configuration on runtime.
|
|
7
|
+
* Consumes `/config.js` file from the base URL, and assign it to `eodashConfig`
|
|
8
|
+
* @async
|
|
9
|
+
* @returns {Promise<EodashConfig>}
|
|
10
|
+
* @see {@linkplain '@/eodashConfig.js'}
|
|
11
|
+
*/
|
|
12
|
+
export const useEodashRuntimeConfig = async () => {
|
|
13
|
+
const eodashConfig = /** @type {EodashConfig} */(inject(eodashConfigKey))
|
|
14
|
+
/**
|
|
15
|
+
* @param {EodashConfig} updatedConfig
|
|
16
|
+
*/
|
|
17
|
+
const assignConfig = (updatedConfig) => {
|
|
18
|
+
/** @type {(keyof EodashConfig)[]} */(Object.keys(eodashConfig))
|
|
19
|
+
.forEach((key) => {
|
|
20
|
+
//@ts-expect-error
|
|
21
|
+
eodashConfig[key] = updatedConfig[key]
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
assignConfig(
|
|
27
|
+
(await import( /* @vite-ignore */new URL('/config.js', import.meta.url).href)).default
|
|
28
|
+
)
|
|
29
|
+
} catch {
|
|
30
|
+
try {
|
|
31
|
+
assignConfig((await import("user:config")).default)
|
|
32
|
+
} catch {
|
|
33
|
+
console.error('no dashboard configuration assigned')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return eodashConfig
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {(store:EodashStore)=>EodashConfig} configCallback
|
|
41
|
+
*/
|
|
42
|
+
export const defineCompiletimeConfig = (configCallback) => {
|
|
43
|
+
return configCallback(store)
|
|
44
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { defineAsyncComponent, reactive, shallowRef, watch } from 'vue'
|
|
2
|
+
import { useSTAcStore } from '@/store/stac'
|
|
3
|
+
import { storeToRefs } from 'pinia'
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {{
|
|
9
|
+
* component:import('vue').Component | null;
|
|
10
|
+
* props: Record<string, unknown>;
|
|
11
|
+
* title :string;
|
|
12
|
+
* id:string|number|symbol;
|
|
13
|
+
* }} DefinedWidget
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @typedef {import('vue').ShallowRef<DefinedWidget>} ReactiveDefinedWidget
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* import map to all vue components inside `widgets` directory.
|
|
24
|
+
* @type {Record<string,() => Promise<import('vue').Component>>}
|
|
25
|
+
*/
|
|
26
|
+
const internalWidgets = import.meta.glob('^/**/*.vue')
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Composable that converts widgets Configurations to defined imported widgets
|
|
30
|
+
* @param { (WidgetConfig | BackgroundWidgetConfig | undefined)[] |
|
|
31
|
+
* WidgetsContainerProps['widgets'] | undefined} widgetConfigs
|
|
32
|
+
* @returns {Array<ReactiveDefinedWidget>}
|
|
33
|
+
**/
|
|
34
|
+
export const useDefineWidgets = (widgetConfigs) => {
|
|
35
|
+
/**
|
|
36
|
+
* @type {Array<ReactiveDefinedWidget>}
|
|
37
|
+
*/
|
|
38
|
+
const definedWidgets = []
|
|
39
|
+
|
|
40
|
+
for (const config of (widgetConfigs ?? [])) {
|
|
41
|
+
/**
|
|
42
|
+
* @type {ReactiveDefinedWidget}
|
|
43
|
+
*/
|
|
44
|
+
const definedWidget = shallowRef({
|
|
45
|
+
component: null,
|
|
46
|
+
props: {},
|
|
47
|
+
title: '',
|
|
48
|
+
id: Symbol(),
|
|
49
|
+
no: '4'
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if ('defineWidget' in (config ?? {})) {
|
|
53
|
+
const { selectedStac } = storeToRefs(useSTAcStore())
|
|
54
|
+
watch(selectedStac, (updatedStac) => {
|
|
55
|
+
const definedConfig = reactive(/** @type {FunctionalWidget} */
|
|
56
|
+
(config)?.defineWidget(updatedStac))
|
|
57
|
+
definedWidget.value = definedWidget.value.id === definedConfig.id ?
|
|
58
|
+
definedWidget.value : getWidgetDefinition(definedConfig);
|
|
59
|
+
}, { immediate: true })
|
|
60
|
+
} else {
|
|
61
|
+
definedWidget.value = getWidgetDefinition(/** @type {StaticWidget} */(config))
|
|
62
|
+
}
|
|
63
|
+
definedWidgets.push(definedWidget)
|
|
64
|
+
}
|
|
65
|
+
return definedWidgets
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Converts a static widget configuration to a defined imported widget
|
|
71
|
+
* @param {StaticWidget| Omit<StaticWidget, "layout">| undefined} config
|
|
72
|
+
* @returns {DefinedWidget}
|
|
73
|
+
**/
|
|
74
|
+
const getWidgetDefinition = (config) => {
|
|
75
|
+
/**
|
|
76
|
+
* @type {DefinedWidget}
|
|
77
|
+
*/
|
|
78
|
+
const importedWidget = {
|
|
79
|
+
component: null,
|
|
80
|
+
props: {},
|
|
81
|
+
title: '',
|
|
82
|
+
id: Symbol(),
|
|
83
|
+
}
|
|
84
|
+
switch (config?.type) {
|
|
85
|
+
case 'internal':
|
|
86
|
+
importedWidget.component = defineAsyncComponent({
|
|
87
|
+
loader: internalWidgets[`/widgets/${/** @type {InternalComponentConfig} **/(config)?.widget.name}.vue`],
|
|
88
|
+
suspensible: true
|
|
89
|
+
})
|
|
90
|
+
importedWidget.props = reactive(/** @type {InternalComponentConfig} **/(config)?.widget.props ?? {})
|
|
91
|
+
|
|
92
|
+
break;
|
|
93
|
+
|
|
94
|
+
case 'web-component':
|
|
95
|
+
importedWidget.component = defineAsyncComponent({
|
|
96
|
+
loader: () => import('@/components/DynamicWebComponent.vue'),
|
|
97
|
+
suspensible: true
|
|
98
|
+
})
|
|
99
|
+
importedWidget.props = reactive(config.widget)
|
|
100
|
+
|
|
101
|
+
break;
|
|
102
|
+
case 'iframe':
|
|
103
|
+
importedWidget.component = defineAsyncComponent({
|
|
104
|
+
loader: () => import('@/components/IframeWrapper.vue'),
|
|
105
|
+
suspensible: true
|
|
106
|
+
})
|
|
107
|
+
importedWidget.props = reactive(config.widget)
|
|
108
|
+
break;
|
|
109
|
+
|
|
110
|
+
default:
|
|
111
|
+
console.error('Widget type not found')
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
importedWidget.title = config?.title ?? ''
|
|
115
|
+
importedWidget.id = config?.id ?? importedWidget.id
|
|
116
|
+
return importedWidget
|
|
117
|
+
}
|
|
118
|
+
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// functions of this folder can only be consumed inside setup stores,
|
|
2
|
+
// setup functions or vue composition api components
|
|
3
|
+
|
|
4
|
+
import { reactive } from "vue"
|
|
5
|
+
import { currentUrl } from "@/store/States"
|
|
6
|
+
import eodashConfig from "@/eodashConfig"
|
|
7
|
+
import { useTheme } from "vuetify/lib/framework.mjs"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates an absolute URL from a relative link and assignes it to `currentUrl`
|
|
12
|
+
* @param {string} [rel = '']
|
|
13
|
+
* @param {string} [base = eodashConfig.stacEndpoint] - base URL, default value is the root stac catalog
|
|
14
|
+
* @returns {import('vue').Ref<string>} - returns `currentUrl`
|
|
15
|
+
* @see {@link '@/store/States.js'}
|
|
16
|
+
*/
|
|
17
|
+
export const useAbsoluteUrl = (rel = '', base = eodashConfig.stacEndpoint) => {
|
|
18
|
+
if (!rel || rel.includes('http')) {
|
|
19
|
+
currentUrl.value = base
|
|
20
|
+
return currentUrl
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const st = base.split("/");
|
|
24
|
+
const arr = rel.split("/");
|
|
25
|
+
st.pop();
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < arr.length; i++) {
|
|
28
|
+
if (arr[i] == ".")
|
|
29
|
+
continue;
|
|
30
|
+
if (arr[i] == "..")
|
|
31
|
+
st.pop();
|
|
32
|
+
else
|
|
33
|
+
st.push(arr[i]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
currentUrl.value = st.join("/");
|
|
37
|
+
return currentUrl
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Adds slide in and out functionality to Elements
|
|
42
|
+
* @param {import('vue').Ref<HTMLElement[]|null>} elements - elements to add the functionality to
|
|
43
|
+
* @param {WidgetConfig[]} configs
|
|
44
|
+
*/
|
|
45
|
+
export const useSlidePanels = (elements, configs) => {
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Sliding direction
|
|
49
|
+
*/
|
|
50
|
+
const slideDirs = configs.map(m => m.layout.x == 0 ? 'left' : m.layout.x == 12 - m.layout.w ? 'right' : m.layout.y < 6 ? 'up' : 'down')
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Array of sliding button's style and icons
|
|
54
|
+
*/
|
|
55
|
+
const slideBtns = slideDirs.map(d => {
|
|
56
|
+
const btn = reactive({ style: {}, icon: { in: '', out: '' }, active: false })
|
|
57
|
+
switch (d) {
|
|
58
|
+
case 'left':
|
|
59
|
+
btn.style = { 'top': '50%', 'right': '-11%' }
|
|
60
|
+
btn.icon.in = 'mdi-chevron-double-right'
|
|
61
|
+
btn.icon.out = 'mdi-chevron-double-left'
|
|
62
|
+
|
|
63
|
+
break;
|
|
64
|
+
case 'right':
|
|
65
|
+
btn.style = { 'top': '50%', 'left': '-11%' }
|
|
66
|
+
btn.icon.in = 'mdi-chevron-double-left'
|
|
67
|
+
btn.icon.out = 'mdi-chevron-double-right'
|
|
68
|
+
|
|
69
|
+
break;
|
|
70
|
+
case 'up':
|
|
71
|
+
btn.style = { 'right': '50%', 'bottom': '-17%' }
|
|
72
|
+
btn.icon.in = 'mdi-chevron-double-down'
|
|
73
|
+
btn.icon.out = 'mdi-chevron-double-up'
|
|
74
|
+
|
|
75
|
+
break;
|
|
76
|
+
case 'down':
|
|
77
|
+
btn.style = { 'right': '50%', 'top': '-17%' }
|
|
78
|
+
btn.icon.in = 'mdi-chevron-double-up'
|
|
79
|
+
btn.icon.out = 'mdi-chevron-double-down'
|
|
80
|
+
break;
|
|
81
|
+
|
|
82
|
+
default:
|
|
83
|
+
console.error('sliding error')
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
return btn
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Transforms the element's position based on the direction
|
|
91
|
+
* @param {number} idx - index of the pressed element
|
|
92
|
+
*/
|
|
93
|
+
const slideInOut = (idx) => {
|
|
94
|
+
const parentStyle = /** @type {CSSStyleDeclaration} */ (elements.value?.[idx].style)
|
|
95
|
+
if (parentStyle?.transform.length) {
|
|
96
|
+
slideBtns[idx].active = false
|
|
97
|
+
parentStyle.transform = "";
|
|
98
|
+
} else {
|
|
99
|
+
slideBtns[idx].active = true
|
|
100
|
+
parentStyle.transition = "transform 0.3s ease-in-out"
|
|
101
|
+
switch (slideDirs[idx]) {
|
|
102
|
+
case 'left':
|
|
103
|
+
parentStyle.transform = "translateX(-100%)"
|
|
104
|
+
break;
|
|
105
|
+
case 'right':
|
|
106
|
+
parentStyle.transform = "translateX(100%)"
|
|
107
|
+
break;
|
|
108
|
+
case 'up':
|
|
109
|
+
parentStyle.transform = `translateY(-${((configs[idx].layout.y / configs[idx].layout.h) * 100) + 100}%)`
|
|
110
|
+
break;
|
|
111
|
+
case 'down':
|
|
112
|
+
parentStyle.transform = `translateY(${((Math.max(0, 12 - configs[idx].layout.y - configs[idx].layout.h) / configs[idx].layout.h) * 100) + 100}%)`;
|
|
113
|
+
break;
|
|
114
|
+
|
|
115
|
+
default:
|
|
116
|
+
console.error('sliding error')
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { slideBtns, slideInOut }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Updates an existing Vuetify theme.
|
|
127
|
+
* updates only the values provided in the `ThemeDefinition`
|
|
128
|
+
* @param {string} themeName - Name of the theme to be updated
|
|
129
|
+
* @param {import('vuetify').ThemeDefinition} [themeDefinition = {}] - New defintion to be updated to
|
|
130
|
+
* @returns {import('vuetify').ThemeInstance}
|
|
131
|
+
*/
|
|
132
|
+
export const useUpdateTheme = (themeName, themeDefinition = {}) => {
|
|
133
|
+
const theme = useTheme();
|
|
134
|
+
|
|
135
|
+
/** @type {Array<keyof import('vuetify').ThemeDefinition>} */(Object.keys(themeDefinition)).forEach((key) => {
|
|
136
|
+
if (key === 'dark') {
|
|
137
|
+
theme.themes.value[themeName][key] = /** @type {Boolean} */ (themeDefinition[key])
|
|
138
|
+
} else {
|
|
139
|
+
//@ts-expect-error
|
|
140
|
+
theme.themes.value[themeName][key] = {
|
|
141
|
+
...theme.themes.value[themeName][key],
|
|
142
|
+
...themeDefinition[key]
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
return theme
|
|
147
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { reactive } from "vue";
|
|
2
|
+
import { currentUrl, mapInstance } from './store/States';
|
|
3
|
+
/**
|
|
4
|
+
* @type {Function | null}
|
|
5
|
+
*/
|
|
6
|
+
let handleMoveEnd = null;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reactive Edoash Config Object. provided globally in the app,
|
|
10
|
+
* and used as an intermediate object to make user defined configurations reactive.
|
|
11
|
+
* @type {EodashConfig}
|
|
12
|
+
*/
|
|
13
|
+
const eodashConfig = reactive({
|
|
14
|
+
id: 'demo',
|
|
15
|
+
stacEndpoint: 'https://eurodatacube.github.io/eodash-catalog/RACE/catalog.json',
|
|
16
|
+
routes: [],
|
|
17
|
+
brand: {
|
|
18
|
+
name: 'Demo',
|
|
19
|
+
font: {
|
|
20
|
+
family: 'Poppins'
|
|
21
|
+
},
|
|
22
|
+
theme: {
|
|
23
|
+
colors: {
|
|
24
|
+
primary: '#004170',
|
|
25
|
+
secondary: '#00417044',
|
|
26
|
+
surface: "#f0f0f0f0",
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
template: {
|
|
31
|
+
background: {
|
|
32
|
+
id: Symbol(),
|
|
33
|
+
widget: {
|
|
34
|
+
link: 'https://cdn.skypack.dev/@eox/map',
|
|
35
|
+
properties: {
|
|
36
|
+
class: "fill-height fill-width overflow-none",
|
|
37
|
+
center: [15, 48],
|
|
38
|
+
layers: [{ type: "Tile", source: { type: "OSM" } }],
|
|
39
|
+
},
|
|
40
|
+
tagName: 'eox-map',
|
|
41
|
+
onMounted(el, _, router) {
|
|
42
|
+
/** @type {any} */(el).zoom = router.currentRoute.value.query['z'];
|
|
43
|
+
|
|
44
|
+
mapInstance.value = /** @type {any} */(el).map;
|
|
45
|
+
|
|
46
|
+
mapInstance.value?.on('moveend', handleMoveEnd =
|
|
47
|
+
/** @param {any} evt */(evt) => {
|
|
48
|
+
router.push({
|
|
49
|
+
query: {
|
|
50
|
+
z: `${evt.map.getView().getZoom()}`
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
onUnmounted(_el, _store, _router) {
|
|
56
|
+
//@ts-expect-error
|
|
57
|
+
mapInstance.value?.un('moveend', handleMoveEnd);
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
type: 'web-component'
|
|
61
|
+
},
|
|
62
|
+
widgets: [
|
|
63
|
+
{
|
|
64
|
+
id: Symbol(),
|
|
65
|
+
title: 'Tools',
|
|
66
|
+
layout: { "x": 0, "y": 0, "w": 3, "h": 12 },
|
|
67
|
+
widget: {
|
|
68
|
+
link: 'https://cdn.skypack.dev/@eox/itemfilter',
|
|
69
|
+
properties: {
|
|
70
|
+
config: {
|
|
71
|
+
titleProperty: "title",
|
|
72
|
+
filterProperties: [
|
|
73
|
+
{
|
|
74
|
+
keys: ["title", "themes"],
|
|
75
|
+
title: "Search",
|
|
76
|
+
type: "text",
|
|
77
|
+
expanded: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
key: "themes",
|
|
81
|
+
title: "Theme",
|
|
82
|
+
type: "multiselect",
|
|
83
|
+
featured: true
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
aggregateResults: "themes",
|
|
87
|
+
enableHighlighting: true,
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
onMounted: async function (el, store, _) {
|
|
91
|
+
/**
|
|
92
|
+
* @typedef {object} Item
|
|
93
|
+
* @property {string} href
|
|
94
|
+
* */
|
|
95
|
+
|
|
96
|
+
/** @type {any} */(el).apply(store?.stac);
|
|
97
|
+
/** @type {any} */(el).config.onSelect =
|
|
98
|
+
/**
|
|
99
|
+
* @param {Item} item
|
|
100
|
+
* */
|
|
101
|
+
async (item) => {
|
|
102
|
+
console.log(item);
|
|
103
|
+
await store.loadSelectedSTAC(item.href);
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
tagName: 'eox-itemfilter'
|
|
107
|
+
},
|
|
108
|
+
type: 'web-component'
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: Symbol(),
|
|
112
|
+
title: 'Information',
|
|
113
|
+
layout: { "x": 9, "y": 0, "w": 3, "h": 12 },
|
|
114
|
+
widget: {
|
|
115
|
+
link: async () => await import('@eox/stacinfo'),
|
|
116
|
+
properties: {
|
|
117
|
+
for: currentUrl,
|
|
118
|
+
allowHtml: "true",
|
|
119
|
+
styleOverride: "#properties li > .value {font-weight: normal !important;}",
|
|
120
|
+
header: "[]",
|
|
121
|
+
|
|
122
|
+
subheader: "[]",
|
|
123
|
+
properties: '["description"]',
|
|
124
|
+
featured: "[]",
|
|
125
|
+
footer: "[]"
|
|
126
|
+
},
|
|
127
|
+
tagName: 'eox-stacinfo'
|
|
128
|
+
},
|
|
129
|
+
type: 'web-component'
|
|
130
|
+
},
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
export default eodashConfig;
|
package/core/main.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/index.ts
|
|
3
|
+
*
|
|
4
|
+
* Automatically included in `./src/main.ts`
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Plugins
|
|
8
|
+
import vuetify from './vuetify';
|
|
9
|
+
import router from './router';
|
|
10
|
+
import { createPinia } from 'pinia';
|
|
11
|
+
import eodashConfig from '@/eodashConfig';
|
|
12
|
+
import { eodashConfigKey } from '@/store/Keys';
|
|
13
|
+
import store from '../store';
|
|
14
|
+
|
|
15
|
+
export const pinia = createPinia();
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @param {import('vue').App} app
|
|
19
|
+
*/
|
|
20
|
+
export function registerPlugins(app) {
|
|
21
|
+
window.eodashStore = store;
|
|
22
|
+
|
|
23
|
+
app.use(vuetify)
|
|
24
|
+
.use(router)
|
|
25
|
+
.use(pinia)
|
|
26
|
+
.provide(eodashConfigKey, eodashConfig);
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createRouter, createWebHistory } from 'vue-router';
|
|
2
|
+
|
|
3
|
+
const routes = [
|
|
4
|
+
{
|
|
5
|
+
path: '/',
|
|
6
|
+
redirect: '/dashboard'
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
path: '/dashboard',
|
|
10
|
+
name: 'Dashboard',
|
|
11
|
+
component: () => import('@/views/Dashboard.vue'),
|
|
12
|
+
}
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const router = createRouter({
|
|
16
|
+
history: createWebHistory(process.env.BASE_URL),
|
|
17
|
+
routes,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export default router;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugins/vuetify.ts
|
|
3
|
+
*
|
|
4
|
+
* Framework documentation: https://vuetifyjs.com`
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Styles
|
|
8
|
+
import '@mdi/font/css/materialdesignicons.css';
|
|
9
|
+
import 'vuetify/styles';
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
import { createVuetify } from 'vuetify';
|
|
13
|
+
|
|
14
|
+
// https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
|
|
15
|
+
export default createVuetify({
|
|
16
|
+
theme: {
|
|
17
|
+
themes: {
|
|
18
|
+
dashboardTheme: {},
|
|
19
|
+
light: {
|
|
20
|
+
colors: {
|
|
21
|
+
primary: '#1867C0',
|
|
22
|
+
secondary: '#5CBBF6',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
});
|
package/core/render.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* loads font in the app using `webfontloader`
|
|
3
|
+
* @param {string} [family="Roboto"]
|
|
4
|
+
* @param {string} [link]
|
|
5
|
+
* @returns {Promise<string>} - font family name
|
|
6
|
+
* @see {@link "https://github.com/typekit/webfontloader "}
|
|
7
|
+
*/
|
|
8
|
+
export const loadFont = async (family = "Roboto", link) => {
|
|
9
|
+
const WebFontLoader = (await import('webfontloader')).default;
|
|
10
|
+
if (link) {
|
|
11
|
+
WebFontLoader.load({
|
|
12
|
+
custom: {
|
|
13
|
+
// Use FVD notation to include families https://github.com/typekit/fvd
|
|
14
|
+
families: [family],
|
|
15
|
+
// Path to stylesheet that defines font-face
|
|
16
|
+
urls: [link],
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
WebFontLoader.load({
|
|
22
|
+
google: {
|
|
23
|
+
families: [family]
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return family;
|
|
28
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ref } from "vue";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* currently selected STAC endpoint
|
|
5
|
+
*/
|
|
6
|
+
export const currentUrl = ref('');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ol map object. Updated by the config file.
|
|
10
|
+
* @type {import("vue").Ref<import('openlayers').Map | null>}
|
|
11
|
+
*/
|
|
12
|
+
export const mapInstance = ref(null);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//export all actions, states, and pinia stores
|
|
2
|
+
/**
|
|
3
|
+
* @template {keyof EodashStore} [K = keyof EodashStore]
|
|
4
|
+
* @typedef {Record<K,EodashStore[K]>} EodashStoreImports
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const storesImport = /**@type {EodashStoreImports} */(import.meta.glob('../store/**.js', { eager: true }))
|
|
8
|
+
/**
|
|
9
|
+
* @type {EodashStore}
|
|
10
|
+
*/
|
|
11
|
+
const store = (() => {
|
|
12
|
+
const stores = /** @type {EodashStore}*/({});
|
|
13
|
+
for (const [filePath, importedstore] of Object.entries(storesImport)) {
|
|
14
|
+
const storeType = filePath.split('/').at(-1)?.slice(0, -3).toLowerCase() ?? ''
|
|
15
|
+
if (!['keys'].includes(storeType)) {
|
|
16
|
+
//@ts-expect-error
|
|
17
|
+
stores[storeType] = importedstore;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return stores;
|
|
21
|
+
})();
|
|
22
|
+
|
|
23
|
+
export default store;
|