@eodash/eodash 5.0.0-alpha.1.13 → 5.0.0-alpha.1.15

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.
@@ -9,7 +9,8 @@ import {
9
9
  cachePath, publicPath, userConfig,
10
10
  buildTargetPath,
11
11
  logger,
12
- rootPath
12
+ rootPath,
13
+ internalWidgetsPath
13
14
  } from "./utils.js";
14
15
  import { readFile } from "fs/promises";
15
16
  import { defineConfig, searchForWorkspaceRoot } from "vite"
@@ -61,7 +62,8 @@ export const serverConfig = /** @type {import('vite').UserConfigFnPromise}*/(def
61
62
  alias: {
62
63
  '@': fileURLToPath(new URL('../core', import.meta.url)),
63
64
  '^': fileURLToPath(new URL('../widgets', import.meta.url)),
64
- "user:config": entryPath
65
+ "user:config": entryPath,
66
+ "user:widgets": internalWidgetsPath
65
67
  },
66
68
  extensions: ['.js', '.json', '.jsx', '.mjs', '.ts', '.tsx', '.vue'],
67
69
  },
@@ -78,7 +80,7 @@ export const serverConfig = /** @type {import('vite').UserConfigFnPromise}*/(def
78
80
  },
79
81
  root: fileURLToPath(new URL('..', import.meta.url)),
80
82
  optimizeDeps: mode === "development" ? {
81
- include: ["webfontloader", "vuetify", "vue", "pinia"],
83
+ include: ["webfontloader", "vuetify", "vue", "pinia", "stac-js", "urijs"],
82
84
  noDiscovery: true,
83
85
  } : {},
84
86
  /** @type {string|false} */
@@ -100,7 +102,7 @@ export const serverConfig = /** @type {import('vite').UserConfigFnPromise}*/(def
100
102
  * @type {import("vite").ServerHook}
101
103
  */
102
104
  async function configureServer(server) {
103
- server.watcher.add([entryPath, runtimeConfigPath])
105
+ server.watcher.add([entryPath, runtimeConfigPath, path.join(internalWidgetsPath, "**/*.vue")])
104
106
 
105
107
  let updatedPath = ''
106
108
  const loggerInfo = logger.info
package/bin/utils.js CHANGED
@@ -21,6 +21,7 @@ const pkg = JSON.parse(
21
21
  * @property {string | false} publicDir
22
22
  * @property {string} outDir
23
23
  * @property {string} entryPoint
24
+ * @property {string} widgets
24
25
  * @property {string} cacheDir
25
26
  * @property {string} runtime
26
27
  * @property {string} base
@@ -33,7 +34,8 @@ cli.version(pkg.version, '-v, --version', 'output the current version')
33
34
  .option('--publicDir <path>', 'path to statically served assets folder')
34
35
  .option('--no-publicDir', 'stop serving static assets')
35
36
  .option('--outDir <path>', 'minified output folder')
36
- .option('-e, --entryPoint <path>', 'file exporting `defineConfig`')
37
+ .option('-e, --entryPoint <path>', 'file exporting `createEodash`')
38
+ .option('-w, --widgets <path>', 'folder that contains vue components as internal widgets')
37
39
  .option('--cacheDir <path>', 'cache folder')
38
40
  .option('-r, --runtime <path>', 'file exporting eodash client runtime config')
39
41
  .option('-b, --base <path>', 'base public path')
@@ -52,6 +54,7 @@ export const appPath = fileURLToPath(new URL("..", import.meta.url)),
52
54
  srcPath = path.join(rootPath, "/src"),
53
55
  runtimeConfigPath = userConfig.runtime ? path.resolve(rootPath, userConfig.runtime) : path.join(srcPath, "./runtime.js"),
54
56
  entryPath = userConfig.entryPoint ? path.resolve(rootPath, userConfig.entryPoint) : path.join(srcPath, "/main.js"),
57
+ internalWidgetsPath = userConfig.widgets ? path.resolve(rootPath, userConfig.widgets) : path.join(srcPath, "widgets"),
55
58
  dotEodashPath = path.join(rootPath, "/.eodash"),
56
59
  buildTargetPath = userConfig.outDir ? path.resolve(rootPath, userConfig.outDir) : path.join(dotEodashPath, '/dist'),
57
60
  cachePath = userConfig.cacheDir ? path.resolve(rootPath, userConfig.cacheDir) : path.join(dotEodashPath, 'cache');
@@ -84,6 +87,7 @@ async function getUserConfig(options, command) {
84
87
  entryPoint: options.entryPoint ?? config?.entryPoint,
85
88
  outDir: options.outDir ?? config?.outDir,
86
89
  publicDir: options.publicDir ?? config?.publicDir,
87
- runtime: options.runtime ?? config?.runtime
90
+ runtime: options.runtime ?? config?.runtime,
91
+ widgets: options.widgets ?? config?.widgets
88
92
  }
89
93
  }
package/core/App.vue CHANGED
@@ -8,7 +8,16 @@
8
8
 
9
9
  <!-- loading state -->
10
10
  <template #fallback>
11
- Loading...
11
+ <v-row class="d-flex justify-center align-center ">
12
+ <v-col class="flex-column justify-center align-center">
13
+ <Suspense>
14
+ <component v-if="loading.component" :is="loading.component" v-bind="loading.props"></component>
15
+ <div v-else class="text-center">
16
+ Loading...
17
+ </div>
18
+ </Suspense>
19
+ </v-col>
20
+ </v-row>
12
21
  </template>
13
22
  </Suspense>
14
23
  </template>
@@ -17,5 +26,11 @@
17
26
  </template>
18
27
 
19
28
  <script setup>
20
- //
29
+ import { inject } from 'vue';
30
+ import { eodashKey } from './store/Keys';
31
+ import { useDefineWidgets } from './composables/DefineWidgets';
32
+
33
+ const eodash = /** @type {import("@/types").Eodash} */ (inject(eodashKey))
34
+
35
+ const [loading] = useDefineWidgets([eodash.template.loading])
21
36
  </script>
@@ -7,8 +7,8 @@
7
7
  <eox-layout-item v-for="(config, idx) in widgetsConfig" ref="itemEls" :key="idx" class="custom-widget"
8
8
  :x="config.layout.x" :y="config.layout.y" :h="config.layout.h" :w="config.layout.w">
9
9
 
10
- <v-btn position="absolute" variant="tonal" :style="slideBtns[idx].style" class="slide-btn"
11
- @click="slideInOut(idx)">
10
+ <v-btn v-if="slideBtns[idx].enabled" position="absolute" variant="tonal" :style="slideBtns[idx].style"
11
+ class="slide-btn" @click="slideInOut(idx)">
12
12
  <v-icon :icon="slideBtns[idx].active ? slideBtns[idx].icon.in : slideBtns[idx].icon.out" />
13
13
  </v-btn>
14
14
  <component :key="importedWidgets[idx].value.id" :is="importedWidgets[idx].value.component"
@@ -43,6 +43,7 @@ const { slideBtns, slideInOut } = useSlidePanels(itemEls, widgetsConfig)
43
43
  </script>
44
44
  <style scoped>
45
45
  eox-layout-item {
46
+ border-radius: 0px;
46
47
  background: rgb(var(--v-theme-surface))
47
48
  }
48
49
 
@@ -2,10 +2,13 @@
2
2
  <v-footer ref="footer" :height="mdAndDown ? '48px' : 'auto'" color="secondary" app
3
3
  class="d-flex justify-space-between">
4
4
  <p class="pt-0 footer-text">
5
- Phasellus feugiat arcu sapien, et iaculis ipsum elementum sit amet. Mauris cursus commodo interdum.
5
+ {{ eodash.brand.footerText ?? "" }}
6
6
  </p>
7
7
  <div class="footer-text">
8
- {{ new Date().getFullYear() }} — <strong>{{ title }}</strong>
8
+ <a href="https://github.com/eodash/eodash" target="_blank">eodash</a> by
9
+ <a href="https://eox.at" target="_blank">
10
+ <img :src='`data:image/svg+xml;base64,${base64Logo}`' height="11px">
11
+ </a>
9
12
  </div>
10
13
  </v-footer>
11
14
  </template>
@@ -22,11 +25,16 @@ import { useDisplay } from 'vuetify/lib/framework.mjs';
22
25
  const footer = ref(null)
23
26
  const eodash = /** @type {import("@/types").Eodash} */(inject(eodashKey))
24
27
 
25
- const title = eodash.brand?.shortName ?? eodash.brand?.name
26
28
  const { mdAndDown } = useDisplay()
29
+ const eoxLogo = `<svg width="100%" height="100%" viewBox="0 0 355 85" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"><g id="ink_ext_XXXXXX"><path id="path5076" d="M152.986,40.52l-0.75,-0.019l-0.737,-0.056l-0.725,-0.093l-0.713,-0.127l-0.7,-0.162l-0.7,-0.196l-0.675,-0.228l-0.65,-0.261l-0.65,-0.292l-0.625,-0.319l-0.612,-0.352l-0.588,-0.377l-0.562,-0.407l-0.55,-0.431l-0.538,-0.457l-0.5,-0.481l-0.487,-0.505l-0.45,-0.528l-0.438,-0.55l-0.4,-0.57l-0.375,-0.59l-0.35,-0.609l-0.325,-0.627l-0.287,-0.645l-0.263,-0.66l-0.225,-0.676l-0.2,-0.69l-0.162,-0.704l-0.125,-0.717l-0.1,-0.728l-0.05,-0.739l-0.025,-0.748c0,-8.023 6.512,-14.532 14.537,-14.532c8.038,0 14.538,6.509 14.538,14.532c0,8.036 -6.5,14.544 -14.538,14.544Z" style="fill:none;"/><path id="path5078" d="M165.748,-0.002c-42.009,0 -76.06,18.985 -76.06,42.402c0,13.268 10.921,25.102 28.021,32.875c-8.081,-5.487 -13.403,-12.973 -14.584,-21.859c-2.685,-20.186 16.83,-40.82 45.148,-49.615c-14.687,6.665 -24.923,21.427 -24.923,38.599c0,23.218 18.648,42.046 41.773,42.383c-0.037,0.005 -0.087,0.015 -0.137,0.02c0.263,0 0.512,0.009 0.762,0.009c42.013,0 76.076,-18.984 76.076,-42.412c0,-13.261 -10.937,-25.097 -28.037,-32.871l0.75,0.522l0.736,0.531l0.713,0.543l0.7,0.555l0.687,0.566l0.664,0.578l0.649,0.588l0.625,0.6l0.613,0.609l0.588,0.621l0.574,0.631l0.562,0.643l0.538,0.652l0.513,0.662l0.5,0.672l0.475,0.684l0.449,0.693l0.438,0.703l0.425,0.713l0.387,0.723l0.375,0.73l0.352,0.74l0.336,0.75l0.3,0.76l0.287,0.77l0.264,0.777l0.25,0.787l0.211,0.795l0.188,0.805l0.175,0.812l0.151,0.82l0.125,0.831c2.675,20.19 -16.838,40.825 -45.151,49.619c14.688,-6.665 24.926,-21.426 24.926,-38.614c0,-23.202 -18.65,-42.026 -41.775,-42.367c0.05,-0.005 0.1,-0.015 0.15,-0.025c-0.262,0 -0.526,-0.01 -0.789,-0.01Zm-13.248,11.314c7.817,0 14.25,6.433 14.25,14.25c0,0.001 0,0.001 0,0.001c0,7.817 -6.433,14.249 -14.25,14.249c-7.817,0 -14.25,-6.432 -14.25,-14.249c0,-0.001 0,-0.001 0,-0.001c0,-7.817 6.433,-14.25 14.25,-14.25Z" style="fill:#fff;fill-rule:nonzero;"/><path id="path5080" d="M213.786,9.529l0.75,0.521l0.738,0.532l0.712,0.544l0.7,0.555l0.688,0.566l0.662,0.577l0.65,0.588l0.625,0.6l0.613,0.61l0.587,0.62l0.575,0.631l0.563,0.643l0.537,0.652l0.513,0.663l0.5,0.672l0.475,0.684l0.45,0.692l0.437,0.703l0.425,0.712l0.388,0.723l0.375,0.731l0.35,0.741l0.337,0.75l0.3,0.76l0.288,0.769l0.262,0.777l0.25,0.786l0.213,0.797l0.187,0.804l0.175,0.812l0.15,0.821l0.125,0.83c2.675,20.19 -16.837,40.825 -45.15,49.619c14.688,-6.665 24.925,-21.426 24.925,-38.614c0,-23.202 -18.65,-42.025 -41.775,-42.367c0.05,-0.005 0.1,-0.015 0.15,-0.025c-0.262,0 -0.525,-0.01 -0.787,-0.01c-42.01,0 -76.062,18.985 -76.062,42.402c0,13.268 10.922,25.103 28.022,32.876c-8.081,-5.487 -13.403,-12.973 -14.584,-21.859c-2.685,-20.187 16.831,-40.82 45.149,-49.615c-14.688,6.665 -24.925,21.426 -24.925,38.598c0,23.218 18.65,42.046 41.775,42.383c-0.038,0.005 -0.088,0.015 -0.138,0.02c0.263,0 0.513,0.01 0.763,0.01c42.012,-0.001 76.075,-18.985 76.075,-42.413c0,-13.261 -10.938,-25.097 -28.038,-32.871Z" style="fill:none;"/><path id="path5082" d="M354.323,82.586l-41.999,-42.002l38.524,-38.515l-29.913,0l-23.562,23.559l-23.563,-23.559l-29.924,0l38.525,38.515l-42,42.002l29.913,0l27.049,-27.046l27.05,27.046l29.9,0" style="fill:#fff;fill-rule:nonzero;"/><path id="path5084" d="M354.323,82.586l-41.999,-42.002l38.524,-38.515l-29.913,0l-23.562,23.559l-23.563,-23.559l-29.924,0l38.525,38.515l-42,42.002l29.913,0l27.049,-27.046l27.05,27.046l29.9,0Z" style="fill:none;"/><path id="path5086" d="M0,2.069l75.033,0l0,16.489l-51.629,0l0,14.931l40.258,0l0,16.495l-40.258,0l0,16.05l53.124,0l0,16.488l-76.528,0l0,-80.453" style="fill:#fff;fill-rule:nonzero;"/><path id="path5088" d="M0,2.069l75.033,0l0,16.489l-51.629,0l0,14.931l40.258,0l0,16.495l-40.258,0l0,16.05l53.124,0l0,16.488l-76.528,0l0,-80.453Z" style="fill:none;"/></g></svg>`
30
+ const base64Logo = window.btoa(eoxLogo)
27
31
  </script>
28
32
  <style scoped lang='scss'>
29
33
  .footer-text {
30
34
  font-size: 0.8rem;
31
35
  }
36
+
37
+ .footer-text a {
38
+ color: white;
39
+ }
32
40
  </style>
@@ -1,29 +1,29 @@
1
1
  <template>
2
- <v-main class="overflow-none" style="height: 91dvh;">
2
+ <v-main class="overflow-hidden" style="height: 91dvh;">
3
3
 
4
4
  <component :is="bgWidget.component" v-bind="bgWidget.props"></component>
5
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"
10
- @click="handleSelection(idx)">
11
- {{ importedWidget.value.title }}
12
- </span>
13
- <div v-show="activeIdx === idx" class="overlay align-self-end overflow-auto pa-2">
14
- <v-btn icon variant="text" class="close-btn" @click="activeIdx = -1">&#x2715;</v-btn>
15
- <component :key="importedWidget.value.id" :is="importedWidget.value.component" v-show="activeIdx === idx"
16
- v-bind="importedWidget.value.props" />
17
- </div>
18
- </v-col>
19
- </v-row>
6
+ <div v-show="activeIdx === idx" class="overlay pa-2" :style="{ bottom: tabsBottom }"
7
+ v-for="(importedWidget, idx) in importedWidgets" :key="idx">
8
+ <v-btn icon variant="text" class="close-btn" @click="activeIdx = -1">&#x2715;</v-btn>
9
+ <component :key="importedWidget.value.id" :is="importedWidget.value.component" v-show="activeIdx === idx"
10
+ v-bind="importedWidget.value.props" />
11
+ </div>
12
+
13
+ <v-tabs ref="tabs" align-tabs="center" bg-color="surface"
14
+ :style="{ position: 'relative', bottom: mainRect.bottom + 'px', zIndex: 10 }" show-arrows v-model="activeIdx">
15
+ <v-tab v-for="(importedWidget, idx) in importedWidgets" :key="idx" :value="idx">
16
+ {{ importedWidget.value.title }}
17
+ </v-tab>
18
+ </v-tabs>
19
+
20
20
  </v-main>
21
21
  </template>
22
22
  <script setup>
23
23
  import { eodashKey } from '@/store/Keys';
24
- import { inject } from 'vue';
24
+ import { inject, ref, onMounted } from 'vue';
25
25
  import { useDefineWidgets } from '@/composables/DefineWidgets'
26
- import { ref } from 'vue';
26
+ import { useLayout } from "vuetify"
27
27
 
28
28
  const eodash = /** @type {import("@/types").Eodash} */(inject(eodashKey));
29
29
 
@@ -32,47 +32,30 @@ const widgetsConfig = eodash.template.widgets
32
32
  const importedWidgets = useDefineWidgets(widgetsConfig)
33
33
  const [bgWidget] = useDefineWidgets([eodash.template?.background])
34
34
 
35
+ const { mainRect } = useLayout()
35
36
 
36
-
37
- /**
38
- * number of flex columns
39
- */
40
- const cols = importedWidgets.length / 12
41
-
42
- /**
43
- * index of the active tab
44
- */
45
37
  const activeIdx = ref(-1)
46
38
 
47
- /**
48
- * @param {number} idx
49
- **/
50
- const handleSelection = (idx) => {
51
- activeIdx.value = activeIdx.value === idx ? -1 : idx
52
- }
39
+ /** @type {import("vue").Ref<import("vuetify/components").VTabs|null>} */
40
+ const tabs = ref(null)
41
+ const tabsBottom = ref('')
42
+ onMounted(() => {
43
+ tabsBottom.value = mainRect.value.bottom + (/** @type {HTMLElement} */(tabs.value?.$el)?.clientHeight ?? 0) + "px"
44
+ })
53
45
  </script>
54
46
  <style scoped lang='scss'>
55
- .panel-header {
56
- height: auto;
57
- margin: 0;
58
- width: 100%;
59
- position: relative;
60
- bottom: 64px;
61
- z-index: 10;
62
- background: rgb(var(--v-theme-background));
63
- }
64
-
65
47
  .overlay {
66
48
  position: absolute;
67
49
  width: 100%;
68
50
  left: 0;
69
51
  top: 64px;
70
- bottom: 64px;
71
52
  z-index: 1;
72
- background: rgb(var(--v-theme-background));
53
+ background: rgb(var(--v-theme-surface));
73
54
  }
74
55
 
75
56
  .close-btn {
76
- justify-self: end;
57
+ position: relative;
58
+ height: 1rem;
59
+ font-size: 0.8rem;
77
60
  }
78
61
  </style>
@@ -2,8 +2,6 @@ import { defineAsyncComponent, reactive, shallowRef, watch } from 'vue'
2
2
  import { useSTAcStore } from '@/store/stac'
3
3
  import { storeToRefs } from 'pinia'
4
4
 
5
-
6
-
7
5
  /**
8
6
  * @typedef {{
9
7
  * component:import('vue').Component | null;
@@ -17,14 +15,24 @@ import { storeToRefs } from 'pinia'
17
15
  * @typedef {import('vue').ShallowRef<DefinedWidget>} ReactiveDefinedWidget
18
16
  */
19
17
 
18
+ const internalWidgets = (() => {
19
+ /**
20
+ * @type {Record<string,() => Promise<import('vue').Component>>}
21
+ */
22
+ const importMap = {
23
+ ...import.meta.glob('^/**/*.vue'),
24
+ ...import.meta.glob("user:widgets/**/*.vue")
25
+ }
26
+ for (const key in importMap) {
27
+ const newKey = /** @type {string} */(key.split('/').at(-1)).slice(0, -4)
28
+ Object.defineProperty(importMap, newKey,
29
+ /** @type {PropertyDescriptor} */(Object.getOwnPropertyDescriptor(importMap, key)));
30
+ delete importMap[key];
31
+ }
32
+ return importMap
33
+ })();
20
34
 
21
35
 
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
36
  /**
29
37
  * Composable that converts widgets Configurations to defined imported widgets
30
38
  * @param { (import("@/types").Widget | import("@/types").BackgroundWidget | undefined)[] |
@@ -46,7 +54,6 @@ export const useDefineWidgets = (widgetConfigs) => {
46
54
  props: {},
47
55
  title: '',
48
56
  id: Symbol(),
49
- no: '4'
50
57
  })
51
58
 
52
59
  if ('defineWidget' in (config ?? {})) {
@@ -84,10 +91,10 @@ const getWidgetDefinition = (config) => {
84
91
  switch (config?.type) {
85
92
  case 'internal':
86
93
  importedWidget.component = defineAsyncComponent({
87
- loader: internalWidgets[`/widgets/${/** @type {import("@/types").InternalComponentWidget} **/(config)?.widget.name}.vue`],
94
+ loader: internalWidgets[/** @type {import("@/types").InternalComponentWidget} **/(config)?.widget.name],
88
95
  suspensible: true
89
96
  })
90
- importedWidget.props = reactive(/** @type {import("@/types").InternalComponentWidget} **/(config)?.widget.props ?? {})
97
+ importedWidget.props = reactive(/** @type {import("@/types").InternalComponentWidget} **/(config)?.widget.properties ?? {})
91
98
 
92
99
  break;
93
100
 
@@ -1,11 +1,12 @@
1
1
  // functions of this folder can only be consumed inside setup stores,
2
2
  // setup functions or vue composition api components
3
3
 
4
- import { reactive } from "vue"
5
- import { currentUrl } from "@/store/States"
6
- import eodashConfig from "@/eodash"
7
- import { useTheme } from "vuetify/lib/framework.mjs"
8
-
4
+ import { reactive } from "vue";
5
+ import { currentUrl, datetime, mapInstance, indicator } from "@/store/States";
6
+ import eodashConfig from "@/eodash";
7
+ import { useTheme } from "vuetify/lib/framework.mjs";
8
+ import { useRouter } from "vue-router";
9
+ import { onMounted, onUnmounted, watch } from "vue";
9
10
 
10
11
  /**
11
12
  * Creates an absolute URL from a relative link and assignes it to `currentUrl`
@@ -14,10 +15,10 @@ import { useTheme } from "vuetify/lib/framework.mjs"
14
15
  * @returns {import('vue').Ref<string>} - returns `currentUrl`
15
16
  * @see {@link '@/store/States.js'}
16
17
  */
17
- export const useAbsoluteUrl = (rel = '', base = eodashConfig.stacEndpoint) => {
18
- if (!rel || rel.includes('http')) {
19
- currentUrl.value = base
20
- return currentUrl
18
+ export const useAbsoluteUrl = (rel = "", base = eodashConfig.stacEndpoint) => {
19
+ if (!rel || rel.includes("http")) {
20
+ currentUrl.value = base;
21
+ return currentUrl;
21
22
  }
22
23
 
23
24
  const st = base.split("/");
@@ -25,17 +26,14 @@ export const useAbsoluteUrl = (rel = '', base = eodashConfig.stacEndpoint) => {
25
26
  st.pop();
26
27
 
27
28
  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]);
29
+ if (arr[i] == ".") continue;
30
+ if (arr[i] == "..") st.pop();
31
+ else st.push(arr[i]);
34
32
  }
35
33
 
36
34
  currentUrl.value = st.join("/");
37
- return currentUrl
38
- }
35
+ return currentUrl;
36
+ };
39
37
 
40
38
  /**
41
39
  * Adds slide in and out functionality to Elements
@@ -43,84 +41,107 @@ export const useAbsoluteUrl = (rel = '', base = eodashConfig.stacEndpoint) => {
43
41
  * @param {import("@/types").Widget[]} configs
44
42
  */
45
43
  export const useSlidePanels = (elements, configs) => {
46
-
47
44
  /**
48
45
  * Sliding direction
49
46
  */
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')
47
+ const slideDirs = configs.map((m) =>
48
+ m.layout.x == 0
49
+ ? "left"
50
+ : m.layout.x == 12 - m.layout.w
51
+ ? "right"
52
+ : m.layout.y < 6
53
+ ? "up"
54
+ : "down"
55
+ );
51
56
 
52
57
  /**
53
58
  * Array of sliding button's style and icons
54
59
  */
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'
60
+ const slideBtns = slideDirs.map((dir, idx) => {
61
+ const btn = reactive({
62
+ style: {},
63
+ icon: { in: "", out: "" },
64
+ active: false,
65
+ enabled: true,
66
+ });
67
+ if (configs[idx].slidable === false) {
68
+ btn.enabled = false;
69
+ return btn;
70
+ }
71
+
72
+ switch (dir) {
73
+ case "left":
74
+ btn.style = { top: "50%", right: "-11%" };
75
+ btn.icon.in = "mdi-chevron-double-right";
76
+ btn.icon.out = "mdi-chevron-double-left";
62
77
 
63
78
  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'
79
+ case "right":
80
+ btn.style = { top: "50%", left: "-11%" };
81
+ btn.icon.in = "mdi-chevron-double-left";
82
+ btn.icon.out = "mdi-chevron-double-right";
68
83
 
69
84
  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'
85
+ case "up":
86
+ btn.style = { right: "50%", bottom: "-17%" };
87
+ btn.icon.in = "mdi-chevron-double-down";
88
+ btn.icon.out = "mdi-chevron-double-up";
74
89
 
75
90
  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'
91
+ case "down":
92
+ btn.style = { right: "50%", top: "-17%" };
93
+ btn.icon.in = "mdi-chevron-double-up";
94
+ btn.icon.out = "mdi-chevron-double-down";
80
95
  break;
81
96
 
82
97
  default:
83
- console.error('sliding error')
98
+ console.error("sliding error");
84
99
  break;
85
100
  }
86
- return btn
87
- })
101
+ return btn;
102
+ });
88
103
 
89
104
  /**
90
105
  * Transforms the element's position based on the direction
91
106
  * @param {number} idx - index of the pressed element
92
107
  */
93
108
  const slideInOut = (idx) => {
94
- const parentStyle = /** @type {CSSStyleDeclaration} */ (elements.value?.[idx].style)
109
+ const parentStyle = /** @type {CSSStyleDeclaration} */ (
110
+ elements.value?.[idx].style
111
+ );
95
112
  if (parentStyle?.transform.length) {
96
- slideBtns[idx].active = false
113
+ slideBtns[idx].active = false;
97
114
  parentStyle.transform = "";
98
115
  } else {
99
- slideBtns[idx].active = true
100
- parentStyle.transition = "transform 0.3s ease-in-out"
116
+ slideBtns[idx].active = true;
117
+ parentStyle.transition = "transform 0.3s ease-in-out";
101
118
  switch (slideDirs[idx]) {
102
- case 'left':
103
- parentStyle.transform = "translateX(-100%)"
119
+ case "left":
120
+ parentStyle.transform = "translateX(-100%)";
104
121
  break;
105
- case 'right':
106
- parentStyle.transform = "translateX(100%)"
122
+ case "right":
123
+ parentStyle.transform = "translateX(100%)";
107
124
  break;
108
- case 'up':
109
- parentStyle.transform = `translateY(-${((configs[idx].layout.y / configs[idx].layout.h) * 100) + 100}%)`
125
+ case "up":
126
+ parentStyle.transform = `translateY(-${(configs[idx].layout.y / configs[idx].layout.h) * 100 + 100
127
+ }%)`;
110
128
  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}%)`;
129
+ case "down":
130
+ parentStyle.transform = `translateY(${(Math.max(0, 12 - configs[idx].layout.y - configs[idx].layout.h) /
131
+ configs[idx].layout.h) *
132
+ 100 +
133
+ 100
134
+ }%)`;
113
135
  break;
114
136
 
115
137
  default:
116
- console.error('sliding error')
138
+ console.error("sliding error");
117
139
  break;
118
140
  }
119
141
  }
120
- }
121
- return { slideBtns, slideInOut }
122
- }
123
-
142
+ };
143
+ return { slideBtns, slideInOut };
144
+ };
124
145
 
125
146
  /**
126
147
  * Updates an existing Vuetify theme.
@@ -132,16 +153,82 @@ export const useSlidePanels = (elements, configs) => {
132
153
  export const useUpdateTheme = (themeName, themeDefinition = {}) => {
133
154
  const theme = useTheme();
134
155
 
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])
156
+ /** @type {Array<keyof import('vuetify').ThemeDefinition>} */ (
157
+ Object.keys(themeDefinition)
158
+ ).forEach((key) => {
159
+ if (key === "dark") {
160
+ theme.themes.value[themeName][key] = /** @type {Boolean} */ (
161
+ themeDefinition[key]
162
+ );
138
163
  } else {
139
164
  //@ts-expect-error
140
165
  theme.themes.value[themeName][key] = {
141
166
  ...theme.themes.value[themeName][key],
142
- ...themeDefinition[key]
143
- }
167
+ ...themeDefinition[key],
168
+ };
144
169
  }
145
- })
146
- return theme
147
- }
170
+ });
171
+ return theme;
172
+ };
173
+
174
+ /**
175
+ * Composable that initiates route query params to store
176
+ * STAC related values
177
+ */
178
+ export const useRouteParams = () => {
179
+ const router = useRouter();
180
+ /**
181
+ * @type {import("openlayers").EventsListenerFunctionType}
182
+ */
183
+ const handleMoveEnd = (evt) => {
184
+ const map = /** @type {import("openlayers").Map | undefined} */ (
185
+ /** @type {*} */ (evt).map
186
+ );
187
+ const [x, y] = map?.getView().getCenter() ?? [0, 0];
188
+ const z = map?.getView().getZoom();
189
+ const currentQuery = router.currentRoute.value.query;
190
+ router.push({
191
+ query: {
192
+ ...currentQuery,
193
+ x: x.toFixed(4),
194
+ y: y.toFixed(4),
195
+ z: z?.toFixed(4),
196
+ },
197
+ });
198
+ };
199
+ onMounted(() => {
200
+ // Set datetime based on kvp
201
+ if (
202
+ "datetime" in router.currentRoute.value.query &&
203
+ router.currentRoute.value.query["datetime"] !== ""
204
+ ) {
205
+ // @ts-ignore
206
+ datetime.value =
207
+ /** @type {string} */ router.currentRoute.value.query["datetime"];
208
+ }
209
+ watch(
210
+ [datetime, mapInstance, currentUrl, indicator],
211
+ ([updatedDate, updatedMap, _updatedUrl, updatedIndicator]) => {
212
+ const [x, y] = updatedMap?.getView().getCenter() ?? [0, 0];
213
+ // lets reduce unnecessary accuracy
214
+ const currentQuery = router.currentRoute.value.query;
215
+ router.push({
216
+ query: {
217
+ ...currentQuery,
218
+ indicator: updatedIndicator,
219
+ x: x.toFixed(4),
220
+ y: y.toFixed(4),
221
+ z: updatedMap?.getView().getZoom().toFixed(),
222
+ datetime: updatedDate,
223
+ // url: updatedUrl,
224
+ },
225
+ });
226
+ updatedMap?.on("moveend", handleMoveEnd);
227
+ }
228
+ );
229
+ });
230
+
231
+ onUnmounted(() => {
232
+ mapInstance.value?.un("moveend", handleMoveEnd);
233
+ });
234
+ };