@eodash/eodash 5.0.0-alpha.1.12 → 5.0.0-alpha.1.14

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
@@ -127,13 +129,15 @@ async function configureServer(server) {
127
129
 
128
130
  return () => {
129
131
  server.middlewares.use(async (req, res, next) => {
130
- if (req.originalUrl === '/@fs/config.js' && existsSync(runtimeConfigPath)) {
131
- await readFile(runtimeConfigPath).then(runtimeConfig => {
132
- res.statusCode = 200
133
- res.setHeader('Content-Type', 'text/javascript')
134
- res.write(runtimeConfig)
135
- res.end()
136
- })
132
+ if (req.originalUrl === '/@fs/config.js') {
133
+ res.statusCode = 200
134
+ res.setHeader('Content-Type', 'text/javascript')
135
+ if (existsSync(runtimeConfigPath)) {
136
+ await readFile(runtimeConfigPath).then(runtimeConfig => {
137
+ res.write(runtimeConfig)
138
+ })
139
+ }
140
+ res.end()
137
141
  return
138
142
  }
139
143
 
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
  }
@@ -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,7 +91,7 @@ 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
97
  importedWidget.props = reactive(/** @type {import("@/types").InternalComponentWidget} **/(config)?.widget.props ?? {})
@@ -2,10 +2,11 @@
2
2
  // setup functions or vue composition api components
3
3
 
4
4
  import { reactive } from "vue"
5
- import { currentUrl } from "@/store/States"
5
+ import { currentUrl, datetime, mapInstance, indicator } from "@/store/States"
6
6
  import eodashConfig from "@/eodash"
7
7
  import { useTheme } from "vuetify/lib/framework.mjs"
8
-
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`
@@ -145,3 +146,50 @@ export const useUpdateTheme = (themeName, themeDefinition = {}) => {
145
146
  })
146
147
  return theme
147
148
  }
149
+
150
+
151
+ /**
152
+ * Composable that initiates route query params to store
153
+ * STAC related values
154
+ */
155
+ export const useRouteParams = () => {
156
+ const router = useRouter()
157
+ /**
158
+ * @type {import("openlayers").EventsListenerFunctionType}
159
+ */
160
+ const handleMoveEnd = (evt) => {
161
+ const map = /** @type {import("openlayers").Map | undefined} */ (/** @type {*} */ (evt).map)
162
+ const [x, y] = map?.getView().getCenter() ?? [0, 0]
163
+ const z = map?.getView().getZoom()
164
+ const currentQuery = router.currentRoute.value.query
165
+ router.push({
166
+ query: {
167
+ ...currentQuery,
168
+ x, y, z
169
+ }
170
+ })
171
+
172
+ }
173
+ onMounted(() => {
174
+ watch([datetime, mapInstance, currentUrl, indicator], ([updatedDate, updatedMap, updatedUrl, updatedIndicator]) => {
175
+ const [x, y] = updatedMap?.getView().getCenter() ?? [0, 0]
176
+ const currentQuery = router.currentRoute.value.query
177
+ router.push({
178
+ query: {
179
+ ...currentQuery,
180
+ indicator: updatedIndicator,
181
+ x,
182
+ y,
183
+ z: updatedMap?.getView().getZoom(),
184
+ datetime: updatedDate,
185
+ url: updatedUrl,
186
+ }
187
+ })
188
+ updatedMap?.on("moveend", handleMoveEnd)
189
+ })
190
+ })
191
+
192
+ onUnmounted(() => {
193
+ mapInstance.value?.un("moveend", handleMoveEnd)
194
+ })
195
+ }
package/core/eodash.js CHANGED
@@ -1,9 +1,5 @@
1
1
  import { reactive } from "vue";
2
- import { currentUrl, mapInstance } from './store/States';
3
- /**
4
- * @type {Function | null}
5
- */
6
- let handleMoveEnd = null;
2
+ import { currentUrl } from './store/States';
7
3
 
8
4
  /**
9
5
  * Reactive Edoash Instance Object. provided globally in the app,
@@ -25,39 +21,16 @@ const eodash = reactive({
25
21
  secondary: '#00417044',
26
22
  surface: "#f0f0f0f0",
27
23
  }
28
- }
24
+ },
25
+ meta: {}
29
26
  },
30
27
  template: {
31
28
  background: {
32
29
  id: Symbol(),
30
+ type: "internal",
33
31
  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'
32
+ name: 'EodashMap'
33
+ }
61
34
  },
62
35
  widgets: [
63
36
  {
package/core/main.js CHANGED
@@ -1,4 +1,2 @@
1
- // Plugins
2
1
  export { createEodash } from '@/composables/DefineEodash';
3
-
4
-
2
+ export { default as store } from "@/store"
@@ -11,8 +11,10 @@ import { createPinia } from 'pinia';
11
11
  import eodash from '@/eodash';
12
12
  import { eodashKey } from '@/store/Keys';
13
13
  import store from '../store';
14
+ import { createHead } from '@unhead/vue'
14
15
 
15
16
  export const pinia = createPinia();
17
+ const head = createHead()
16
18
 
17
19
  /**
18
20
  * @param {import('vue').App} app
@@ -21,6 +23,7 @@ export function registerPlugins(app) {
21
23
  window.eodashStore = store;
22
24
 
23
25
  app.use(vuetify)
26
+ .use(head)
24
27
  .use(router)
25
28
  .use(pinia)
26
29
  .provide(eodashKey, eodash);
@@ -1,28 +0,0 @@
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
- };
@@ -10,3 +10,13 @@ export const currentUrl = ref('');
10
10
  * @type {import("vue").Ref<import('openlayers').Map | null>}
11
11
  */
12
12
  export const mapInstance = ref(null);
13
+
14
+ /**
15
+ * currently selected datetime
16
+ */
17
+ export const datetime = ref(new Date().toISOString());
18
+
19
+ /**
20
+ * Currently selected indicator
21
+ */
22
+ export const indicator = ref("")
@@ -3,6 +3,7 @@ import { inject, ref } from 'vue';
3
3
  import axios from 'axios';
4
4
  import { useAbsoluteUrl } from '@/composables/index';
5
5
  import { eodashKey } from '@/store/Keys';
6
+ import { assignIndicator } from '@/utils';
6
7
 
7
8
  export const useSTAcStore = defineStore('stac', () => {
8
9
  /**
@@ -14,7 +15,7 @@ export const useSTAcStore = defineStore('stac', () => {
14
15
  /**
15
16
  * selected STAC object.
16
17
  * @type {import('vue').Ref<import('stac-ts').StacCatalog |
17
- * import('stac-ts').StacCollection |import('stac-ts').StacItem
18
+ * import('stac-ts').StacCollection |import('stac-ts').StacItem
18
19
  * | null>}
19
20
  */
20
21
  const selectedStac = ref(null);
@@ -54,6 +55,7 @@ export const useSTAcStore = defineStore('stac', () => {
54
55
 
55
56
  await axios.get(absoluteUrl.value).then(resp => {
56
57
  selectedStac.value = resp.data;
58
+ assignIndicator(selectedStac.value)
57
59
  }).catch(err => console.error(err));
58
60
  }
59
61
 
package/core/types.d.ts CHANGED
@@ -4,26 +4,41 @@ import type { StacCatalog, StacCollection, StacItem } from "stac-ts";
4
4
  import type { Ref } from "vue"
5
5
  import type { ThemeDefinition } from "vuetify/lib/index.mjs";
6
6
  import type { Map } from "openlayers";
7
+
7
8
  /**
8
- * Web Component configuration
9
+ * @group Eodash
9
10
  */
10
11
  export interface WebComponentProps<T extends ExecutionTime = "compiletime"> {
11
- /** Web component definition file URL*/
12
+ /**
13
+ * Imports web component file, either using a URL or an import funtion.
14
+ * @example
15
+ * importing `eox-itemfilter` web component, after installing `@eox/itemfilter` it can be
16
+ * referenced:
17
+ * ```js
18
+ * link: async() => import("@eox/itemfilter")
19
+ * ```
20
+ *
21
+ * ::: warning
22
+ * import maps are not available in runtime config
23
+ * :::
24
+ **/
12
25
  link: T extends 'runtime' ? string : (string | (() => Promise<unknown>));
13
- /** Exported Constructor, needs to be provided if the web component is not registered by the `link` provided */
26
+ /**
27
+ * Exported Constructor, needs to be provided if the web component is not registered in by the
28
+ * [link](#link) provided
29
+ **/
14
30
  constructorProp?: string
15
- /** Custom tag name */
16
31
  tagName: `${string}-${string}`
17
32
  /** Object defining all the properties and attributes of the web component */
18
33
  properties?: Record<string, any>
19
34
  /**
20
- * Function that is triggered when the web component is mounted in the DOM.
35
+ * Triggered when the web component is mounted in the DOM.
21
36
  * @param el - web component
22
37
  * @param store - return value of the core STAC pinia store in `/core/store/stac.ts`
23
38
  */
24
39
  onMounted?: (el: Element | null, store: ReturnType<typeof useSTAcStore>, router: Router) => (Promise<void> | void)
25
40
  /**
26
- * Function that is triggered when the web component is unmounted from the DOM.
41
+ * Triggered when the web component is unmounted from the DOM.
27
42
  * @param el - web component
28
43
  * @param store - return value of the core STAC pinia store in `/core/store/stac.ts`
29
44
  */
@@ -38,85 +53,70 @@ export interface WidgetsContainerProps {
38
53
 
39
54
  // eodash types:
40
55
  /**
41
- * Widget type: `web-component` specification. The web component definition is imported using the `widget.link` property either from
42
- * an external endpoint, or an installed node_module.
43
- * Installed node_module web components import should be mapped in `/core/modulesMap.ts`,
44
- * then setting `widget.link`:`(import-map-key)` and `node_module`:`true`
56
+ * Widget type: `web-component` API
57
+ * @group Eodash
45
58
  */
46
59
  export interface WebComponentWidget<T extends ExecutionTime = "compiletime"> {
47
- /**
48
- * Unique Identifier, triggers rerender when using `defineWidget`
49
- **/
50
60
  id: number | string | symbol
51
- /**
52
- * Widget title
53
- */
54
61
  title: string
55
62
  /**
56
63
  * Widget position and size.
57
64
  */
58
65
  layout: {
59
66
  /**
60
- * Horizontal start position. Integer (1 - 12)
67
+ * Horizontal start position. Integer between 1 and 12
61
68
  */
62
69
  x: number
63
70
  /**
64
- * Vertical start position. Integer (1 - 12)
71
+ * Vertical start position. Integer between 1 and 12
65
72
  */
66
73
  y: number
67
74
  /**
68
- * Width. Integer (1 - 12)
75
+ * Width. Integer between 1 and 12
69
76
  */
70
77
  w: number
71
78
  /**
72
- * Height. Integer (1 - 12)
79
+ * Height. Integer between 1 and 12
73
80
  */
74
81
  h: number
75
82
  }
76
83
  widget: WebComponentProps<T>
77
- /**
78
- * Widget type
79
- */
80
84
  type: 'web-component'
81
85
  }
82
86
 
83
87
  /**
84
- * Widget type: `internal` specification.
85
- * Internal widgets are Vue components inside the `/widgets` directory.
88
+ * Widget type: `internal` API.
89
+ * Internal widgets are Vue components provided by eodash.
90
+ * @group Eodash
86
91
  */
87
92
  export interface InternalComponentWidget {
88
- /**
89
- * Unique Identifier, triggers rerender when using `defineWidget`
90
- **/
91
93
  id: number | string | symbol
92
- /**
93
- * Widget title
94
- */
95
94
  title: string
96
95
  /**
97
96
  * Widget position and size.
98
97
  */
99
98
  layout: {
100
99
  /**
101
- * Horizontal start position. Integer (1 - 12)
100
+ * Horizontal start position. Integer between 1 and 12
102
101
  */
103
102
  x: number
104
103
  /**
105
- * Vertical start position. Integer (1 - 12)
104
+ * Vertical start position. Integer between 1 and 12
106
105
  */
107
106
  y: number
108
107
  /**
109
- * Width. Integer (1 - 12)
108
+ * Width. Integer between 1 and 12
110
109
  */
111
110
  w: number
112
111
  /**
113
- * Height. Integer (1 - 12)
112
+ * Height. Integer between 1 and 12
114
113
  */
115
114
  h: number
116
115
  }
117
116
  widget: {
118
117
  /**
119
- * Internal Vue Component file name without the extention .vue
118
+ * Internal Vue Components inside the [widgets](https://github.com/eodash/eodash/tree/main/widgets) folder can be referenced
119
+ * using their name without the extention .vue
120
120
  */
121
121
  name: string;
122
122
  /**
@@ -124,43 +124,37 @@ export interface InternalComponentWidget {
124
124
  */
125
125
  props?: Record<string, unknown>
126
126
  }
127
- /**
128
- * Widget type.
129
- */
130
127
  type: 'internal'
131
128
  }
132
129
 
133
130
  /**
134
- * Widget type: `iframe` specification.
131
+ * Widget type: `iframe` API
135
132
  * Renders an external HTML file as a widget.
136
133
  */
134
+ /**
135
+ * @group Eodash
136
+ */
137
137
  export interface IFrameWidget {
138
- /**
139
- * Unique Identifier, triggers rerender when using `defineWidget`
140
- **/
141
138
  id: number | string | symbol
142
- /**
143
- * Widget title
144
- */
145
139
  title: string
146
140
  /**
147
141
  * Widget position and size.
148
142
  */
149
143
  layout: {
150
144
  /**
151
- * Horizontal start position. Integer (1 - 12)
145
+ * Horizontal start position. Integer between 1 and 12
152
146
  */
153
147
  x: number
154
148
  /**
155
- * Vertical start position. Integer (1 - 12)
149
+ * Vertical start position. Integer between 1 and 12
156
150
  */
157
151
  y: number
158
152
  /**
159
- * Width. Integer (1 - 12)
153
+ * Width. Integer between 1 and 12
160
154
  */
161
155
  w: number
162
156
  /**
163
- * Height. Integer (1 - 12)
157
+ * Height. Integer between 1 and 12
164
158
  */
165
159
  h: number
166
160
  }
@@ -170,11 +164,11 @@ export interface IFrameWidget {
170
164
  */
171
165
  src: string
172
166
  }
173
- /**
174
- * Widget type
175
- */
176
167
  type: 'iframe'
177
168
  }
169
+ /**
170
+ * @group Eodash
171
+ */
178
172
  export interface FunctionalWidget<T extends ExecutionTime = "compiletime"> {
179
173
  /**
180
174
  * Provides a functional definition of the widget,
@@ -184,32 +178,42 @@ export interface FunctionalWidget<T extends ExecutionTime = "compiletime"> {
184
178
  defineWidget: (selectedSTAC: StacCatalog | StacCollection | StacItem | null) => Omit<StaticWidget<T>, 'layout'>
185
179
  layout: {
186
180
  /**
187
- * Horizontal start position. Integer (1 - 12)
181
+ * Horizontal start position. Integer between 1 and 12
188
182
  */
189
183
  x: number
190
184
  /**
191
- * Vertical start position. Integer (1 - 12)
185
+ * Vertical start position. Integer between 1 and 12
192
186
  */
193
187
  y: number
194
188
  /**
195
- * Width. Integer (1 - 12)
189
+ * Width. Integer between 1 and 12
196
190
  */
197
191
  w: number
198
192
  /**
199
- * Height. Integer (1 - 12)
193
+ * Height. Integer between 1 and 12
200
194
  */
201
195
  h: number
202
196
  }
203
197
  }
198
+ /**
199
+ * @group Eodash
200
+ */
204
201
  export type StaticWidget<T extends ExecutionTime = "compiletime"> = WebComponentWidget<T> | InternalComponentWidget | IFrameWidget
202
+ /**
203
+ * @group Eodash
204
+ */
205
205
  export type Widget<T extends ExecutionTime = "compiletime"> = StaticWidget<T> | FunctionalWidget<T>
206
206
 
207
207
 
208
+ /**
209
+ * @group Eodash
210
+ */
208
211
  export type BackgroundWidget<T extends ExecutionTime = "compiletime"> = Omit<WebComponentWidget<T>, 'layout' | 'title'> | Omit<InternalComponentWidget, 'layout' | 'title'> | Omit<IFrameWidget, 'layout' | 'title'> | Omit<FunctionalWidget, 'layout'>
209
212
  /**
210
213
  * Dashboard rendered widgets specification.
211
214
  * 3 types of widgets are supported: `"iframe"`, `"internal"`, and `"web-component"`.
212
215
  * A specific object should be provided based on the type of the widget.
216
+ * @group Eodash
213
217
  */
214
218
  export interface Template<T extends ExecutionTime = "compiletime"> {
215
219
  /**
@@ -226,15 +230,21 @@ export interface Template<T extends ExecutionTime = "compiletime"> {
226
230
  */
227
231
  widgets: Widget<T>[]
228
232
  }
229
-
233
+ /** @ignore */
230
234
  export type ExternalURL = `${'https://' | 'http://'}${string}`;
235
+ /** @ignore */
231
236
  export type InternalRoute = `/${string}`
237
+ /** @ignore */
232
238
  export type StacEndpoint = `${'https://' | 'http://'}${string}/catalog.json`
233
239
 
240
+ /**
241
+ * @group Eodash
242
+ */
234
243
  export type ExecutionTime = "runtime" | "compiletime";
235
244
 
236
245
  /**
237
- * Eodash instance specification.
246
+ * Eodash instance API
247
+ * @group Eodash
238
248
  */
239
249
  export interface Eodash<T extends ExecutionTime = "compiletime"> {
240
250
  /**
@@ -249,13 +259,7 @@ export interface Eodash<T extends ExecutionTime = "compiletime"> {
249
259
  * Renderes to navigation buttons on the app header.
250
260
  **/
251
261
  routes?: Array<{
252
- /**
253
- * button title
254
- **/
255
262
  title: string,
256
- /**
257
- * external URL or inner path to navigate to.
258
- **/
259
263
  to: ExternalURL | InternalRoute
260
264
  }>
261
265
  /**
@@ -263,7 +267,7 @@ export interface Eodash<T extends ExecutionTime = "compiletime"> {
263
267
  */
264
268
  brand: {
265
269
  /**
266
- * Automatically fetches the specified font family from google fonts. if the `link` property is specified
270
+ * Automatically fetches the specified font family from google fonts. if the [link](#font-link) property is specified
267
271
  * the font family will be fetched from the provided source instead.
268
272
  */
269
273
  font?: {
@@ -272,7 +276,7 @@ export interface Eodash<T extends ExecutionTime = "compiletime"> {
272
276
  */
273
277
  link?: string;
274
278
  /**
275
- * Font family. Use FVD notation to include families https://github.com/typekit/fvd
279
+ * Font family. Use FVD notation to include families. see https://github.com/typekit/fvd
276
280
  */
277
281
  family: string
278
282
  }
@@ -292,6 +296,8 @@ export interface Eodash<T extends ExecutionTime = "compiletime"> {
292
296
  * Dashboard theme as a custom vuetifyJs theme.
293
297
  */
294
298
  theme?: ThemeDefinition
299
+
300
+ meta?: import("@unhead/vue").UseSeoMetaInput
295
301
  }
296
302
  /**
297
303
  * Template configuration
@@ -301,6 +307,9 @@ export interface Eodash<T extends ExecutionTime = "compiletime"> {
301
307
  /////////
302
308
 
303
309
  /// eodash store types
310
+ /**
311
+ * @group EodashStore
312
+ */
304
313
  export interface EodashStore {
305
314
  /**
306
315
  * Stateful Reactive variables
@@ -314,11 +323,16 @@ export interface EodashStore {
314
323
  * OpenLayers map instance
315
324
  */
316
325
  mapInstance: Ref<Map | null>
326
+ /**
327
+ * currently selected datetime
328
+ */
329
+ datetime: Ref<string>
330
+ /**
331
+ * Currently selected indicator
332
+ */
333
+ indicator: Ref<string>
317
334
  }
318
- // consider removing the actions ?
319
- actions: {
320
- loadFont: (family?: string, link?: string) => Promise<string>;
321
- };
335
+ actions: {};
322
336
  /**
323
337
  * Pinia store definition used to navigate the root STAC catalog.
324
338
  */
@@ -327,23 +341,54 @@ export interface EodashStore {
327
341
  }
328
342
  }
329
343
  ///////
344
+ /**
345
+ * Eodash server, build and setup configuration
346
+ * @group EodashConfig
347
+ */
330
348
  export interface EodashConfig {
331
349
  dev?: {
350
+ /** serving port */
332
351
  port?: string | number
333
352
  host?: string | boolean
353
+ /** open default browser when the server starts */
334
354
  open?: boolean
335
355
  }
336
356
  preview?: {
357
+ /** serving port */
337
358
  port?: string | number
338
359
  host?: string | boolean
360
+ /** open default browser when the server starts */
339
361
  open?: boolean
340
362
  }
363
+ /**
364
+ * base public path
365
+ */
341
366
  base?: string;
367
+ /**
368
+ * build target folder path
369
+ */
342
370
  outDir?: string;
371
+ /** path to statically served assets folder, can be set to `false`
372
+ * to disable serving assets statically
373
+ **/
343
374
  publicDir?: string | false;
375
+ /**
376
+ * cache folder
377
+ */
344
378
  cacheDir?: string
379
+ /** specifies main entry file, exporting `createEodash`*/
345
380
  entryPoint?: string
381
+ /**
382
+ * file exporting eodash client runtime config
383
+ */
346
384
  runtime?: string
385
+ widgets?: string
347
386
  }
348
-
349
- export declare const createEodash: (configCallback: (store: EodashStore) => Eodash | Promise<Eodash>) => Eodash
387
+ /**
388
+ * project entry point should export this function as a default
389
+ * to instantiate eodash
390
+ *
391
+ * @param configCallback
392
+ */
393
+ export declare const createEodash: (configCallback: (store: EodashStore) => Eodash | Promise<Eodash>) => Promise<Eodash>
394
+ export declare const store: EodashStore
@@ -0,0 +1,129 @@
1
+ import { Collection, Item } from 'stac-js';
2
+ import { toAbsolute } from 'stac-js/src/http.js';
3
+
4
+ export class EodashCollection {
5
+ /** @type {string | undefined} */
6
+ #collectionUrl = undefined;
7
+ #collectionStac = undefined;
8
+ /** @type {*} */
9
+ selectedItem = null;
10
+
11
+ /**
12
+ * @param {string} collectionUrl
13
+ */
14
+ constructor(collectionUrl) {
15
+ this.#collectionUrl = collectionUrl;
16
+ }
17
+ /**
18
+ * @param {*} item
19
+ * @returns
20
+ * @async
21
+ */
22
+ createLayersJson = async (item = null) => {
23
+ let stacItem;
24
+ if (item instanceof Date) {
25
+ // if collectionStac not yet initialized we do it here
26
+ if (!this.#collectionStac) {
27
+ const response = await fetch(this.#collectionUrl);
28
+ const stac = await response.json();
29
+ this.#collectionStac = new Collection(stac);
30
+ }
31
+ stacItem = this.getItems().sort((a, b) => {
32
+ //@ts-expect-error
33
+ const distanceA = Math.abs(new Date(a.datetime) - item);
34
+ //@ts-expect-error
35
+ const distanceB = Math.abs(new Date(b.datetime) - item);
36
+ return distanceA - distanceB;
37
+ })[0];
38
+ this.selectedItem = stacItem;
39
+ } else {
40
+ stacItem = item;
41
+ }
42
+ const response = await fetch(
43
+ stacItem
44
+ ? toAbsolute(stacItem.href, this.#collectionUrl)
45
+ : this.#collectionUrl
46
+ );
47
+ const stac = await response.json();
48
+
49
+ // TODO get auxiliary layers from collection
50
+ let layersJson = [
51
+ {
52
+ type: 'Tile',
53
+ properties: {
54
+ id: 'OSM',
55
+ },
56
+ source: {
57
+ type: 'OSM',
58
+ },
59
+ },
60
+ ];
61
+
62
+ if (!stacItem) {
63
+ // no specific item was requested; render last item
64
+ this.#collectionStac = new Collection(stac);
65
+ const items = this.getItems();
66
+ this.selectedItem = items[items.length - 1];
67
+ layersJson = await this.createLayersJson(this.selectedItem);
68
+ return layersJson;
69
+ } else {
70
+ // specific item was requested
71
+ const item = new Item(stac);
72
+ this.selectedItem = item;
73
+ //@ts-expect-error
74
+ layersJson.unshift(this.buildJson(item));
75
+ return layersJson;
76
+ }
77
+ }
78
+
79
+ /**
80
+ * @param {*} item
81
+ */
82
+ buildJson(item) {
83
+ let json;
84
+ // TODO implement other types, such as COG
85
+ if (/** @type {import('stac-ts').StacLink[]} */(item.links).find((l) => l.rel === 'wms' || l.rel === 'wmts')) {
86
+ json = {
87
+ type: 'STAC',
88
+ displayWebMapLink: true,
89
+ displayFootprint: false,
90
+ data: item,
91
+ };
92
+ } else {
93
+ // fall back to rendering the feature
94
+ json = {
95
+ type: 'Vector',
96
+ source: {
97
+ type: 'Vector',
98
+ url: 'data:,' + encodeURIComponent(JSON.stringify(item.geometry)),
99
+ format: 'GeoJSON',
100
+ },
101
+ };
102
+ }
103
+
104
+ return json;
105
+ }
106
+
107
+ getItems() {
108
+ return (
109
+ //@ts-expect-error
110
+ // eslint-disable-next-line
111
+ /** @type {import('stac-ts').StacLink[]}*/(this.#collectionStac?.links)
112
+ .filter((i) => i.rel === 'item')
113
+ // sort by `datetime`, where oldest is first in array
114
+ .sort((a, b) => ( /** @type {number} */(a.datetime) < /** @type {number} */(b.datetime) ? -1 : 1))
115
+ );
116
+ }
117
+
118
+ getDates() {
119
+ return (
120
+ //@ts-expect-error
121
+ // eslint-disable-next-line
122
+ /** @type {import('stac-ts').StacLink[]}*/ (this.#collectionStac?.links)
123
+ .filter((i) => i.rel === 'item')
124
+ // sort by `datetime`, where oldest is first in array
125
+ .sort((a, b) => ( /** @type {number} */(a.datetime) < /** @type {number} */(b.datetime) ? -1 : 1))
126
+ .map((i) => new Date( /** @type {number} */(i.datetime)))
127
+ );
128
+ }
129
+ }
@@ -0,0 +1,48 @@
1
+ import { indicator } from '@/store/States';
2
+
3
+ /**
4
+ * loads font in the app using `webfontloader`
5
+ * @param {string} [family="Roboto"]
6
+ * @param {string} [link]
7
+ * @returns {Promise<string>} - font family name
8
+ * @see {@link "https://github.com/typekit/webfontloader "}
9
+ */
10
+ export const loadFont = async (family = "Roboto", link) => {
11
+ const WebFontLoader = (await import('webfontloader')).default;
12
+ if (link) {
13
+ WebFontLoader.load({
14
+ custom: {
15
+ // Use FVD notation to include families https://github.com/typekit/fvd
16
+ families: [family],
17
+ // Path to stylesheet that defines font-face
18
+ urls: [link],
19
+ },
20
+ });
21
+ }
22
+ else {
23
+ WebFontLoader.load({
24
+ google: {
25
+ families: [family]
26
+ }
27
+ });
28
+ }
29
+ return family;
30
+ };
31
+
32
+
33
+ /**
34
+ * @param {import('stac-ts').StacCatalog | import('stac-ts').StacCollection | import('stac-ts').StacItem | null} selectedSTAC
35
+ * @see {@link indicator}
36
+ */
37
+ export const assignIndicator = (selectedSTAC) => {
38
+ const code = /** @type {string | undefined} */ (selectedSTAC?.code);
39
+ const subcode = /** @type {string | string[] | undefined} */ (selectedSTAC?.subcode);
40
+
41
+ if (selectedSTAC?.code || selectedSTAC?.subcode) {
42
+ indicator.value = code ? code :
43
+ Array.isArray(subcode) ? subcode[0] : subcode ?? ""
44
+ } else {
45
+ indicator.value = ""
46
+ }
47
+ console.log(indicator.value,/** @type {string} */(selectedSTAC?.code)),/** @type {string[]} */(selectedSTAC?.subcode)
48
+ }
@@ -6,19 +6,22 @@
6
6
 
7
7
  <script setup>
8
8
  import { useEodashRuntime } from "@/composables/DefineEodash";
9
- import { useUpdateTheme } from "@/composables";
9
+ import { useRouteParams, useUpdateTheme } from "@/composables";
10
10
  import { useSTAcStore } from '@/store/stac';
11
11
  import { defineAsyncComponent } from "vue";
12
12
  import { useDisplay, useLayout } from "vuetify/lib/framework.mjs";
13
- import { loadFont } from '@/store/Actions'
13
+ import { loadFont } from '@/utils'
14
+ import { useSeoMeta } from "@unhead/vue"
14
15
 
15
16
 
16
- const eodashConfig = await useEodashRuntime()
17
+ const eodash = await useEodashRuntime()
17
18
 
18
- const theme = useUpdateTheme('dashboardTheme', eodashConfig.brand?.theme)
19
+ useRouteParams()
20
+
21
+ const theme = useUpdateTheme('dashboardTheme', eodash.brand?.theme)
19
22
  theme.global.name.value = 'dashboardTheme'
20
23
 
21
- const fontFamily = await loadFont(eodashConfig.brand?.font?.family, eodashConfig.brand?.font?.link)
24
+ const fontFamily = await loadFont(eodash.brand?.font?.family, eodash.brand?.font?.link)
22
25
 
23
26
  const { loadSTAC } = useSTAcStore()
24
27
  await loadSTAC()
@@ -30,9 +33,15 @@ const TemplateComponent = smAndDown.value ?
30
33
  const HeaderComponent = defineAsyncComponent(() => import(`@/components/Header.vue`))
31
34
  const FooterComponent = defineAsyncComponent(() => import(`@/components/Footer.vue`))
32
35
  const { mainRect } = useLayout()
36
+
37
+ useSeoMeta(eodash.brand.meta ?? {})
33
38
  </script>
34
39
 
35
40
  <style scoped lang="scss">
41
+ html {
42
+ overflow: hidden;
43
+ }
44
+
36
45
  * {
37
46
  font-family: v-bind('fontFamily');
38
47
  }
@@ -11,7 +11,18 @@ declare interface Window {
11
11
  declare module '@eox/stacinfo' {
12
12
  export const EOxStacInfo: CustomElementConstructor
13
13
  }
14
+ declare module '@eox/map' {
15
+ export const EOxMap: CustomElementConstructor
16
+ }
14
17
  declare module 'user:config' {
15
18
  const eodash: import("@/types").Eodash | Promise<import("@/types").Eodash>;
16
19
  export default eodash
17
20
  }
21
+ declare module "stac-js" {
22
+ const STAC: any, Collection: any, Item: any
23
+ export { STAC, Collection, Item }
24
+ }
25
+ declare module "stac-js/src/http.js" {
26
+ const toAbsolute: any
27
+ export { toAbsolute }
28
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eodash/eodash",
3
- "version": "5.0.0-alpha.1.12",
3
+ "version": "5.0.0-alpha.1.14",
4
4
  "type": "module",
5
5
  "types": "./core/types.d.ts",
6
6
  "files": [
@@ -17,25 +17,33 @@
17
17
  "browser": "./core/main.js",
18
18
  "scripts": {
19
19
  "dev": "npx eodash dev --entryPoint core/eodash.js",
20
- "build": "npx eodash build",
20
+ "build": "npx eodash build --entryPoint core/eodash.js",
21
21
  "check": "vue-tsc --noEmit --skipLibCheck && eslint .",
22
22
  "check:lint": "eslint .",
23
23
  "check:types": "vue-tsc --noEmit --skipLibCheck",
24
24
  "preview": "vite preview",
25
25
  "lint": "eslint . --fix",
26
- "docs:generate": "typedoc --plugin typedoc-plugin-markdown core/store/Types.ts --tsconfig tsconfig.json --out docs --readme none"
26
+ "docs:generate": "typedoc --options typedoc.config.json",
27
+ "docs:dev": "vitepress dev docs",
28
+ "docs:build": "npm run docs:generate && vitepress build docs",
29
+ "docs:preview": "vitepress preview docs"
27
30
  },
28
31
  "dependencies": {
29
32
  "@eox/layout": "^0.1.0",
33
+ "@eox/map": "^1.4.0",
30
34
  "@eox/stacinfo": "^0.3.0",
31
35
  "@mdi/font": "7.0.96",
36
+ "@unhead/vue": "^1.8.20",
32
37
  "@vitejs/plugin-vue": "^5.0.0",
38
+ "@web-comp/core": "^1.0.2",
39
+ "@web-comp/date-picker": "^1.0.8",
33
40
  "animated-details": "gist:2912bb049fa906671807415eb0e87188",
34
41
  "axios": "^1.6.2",
35
42
  "commander": "^12.0.0",
36
43
  "core-js": "^3.29.0",
37
44
  "pinia": "^2.0.0",
38
45
  "sass": "^1.60.0",
46
+ "stac-js": "^0.0.9",
39
47
  "stac-ts": "^1.0.3",
40
48
  "vite": "^5.1.5",
41
49
  "vite-plugin-vuetify": "^2.0.0",
@@ -52,10 +60,12 @@
52
60
  "eslint": "^8.56.0",
53
61
  "eslint-plugin-vue": "^9.19.2",
54
62
  "typedoc": "^0.25.7",
55
- "typedoc-plugin-markdown": "^3.17.1",
63
+ "typedoc-plugin-markdown": "^4.0.0-next.54",
64
+ "typedoc-plugin-vue": "^1.1.0",
65
+ "typedoc-vitepress-theme": "^1.0.0-next.0",
56
66
  "typescript": "^5.0.0",
57
67
  "unplugin-fonts": "^1.0.3",
58
- "vite-plugin-dts": "^3.7.3",
68
+ "vitepress": "^1.0.0",
59
69
  "vue-eslint-parser": "^9.3.2",
60
70
  "vue-tsc": "^1.2.0"
61
71
  },
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <span class="fill-height fill-width align-center justify-center">
3
+ <div v-if="inline" class="fill-height fill-width">
4
+ <!-- <p class="text-subtitle-1 ma-1 pa-2">currently selected date is {{ currentDate }}</p> -->
5
+ <v-text-field base-color="primary" density="comfortable" type="date" bg-color="surface" color="primary"
6
+ validate-on="input" min="1980-01-01" :max="new Date().toISOString().split(`T`)[0]" label="Select Date"
7
+ :value="currentDate" v-model="currentDate" />
8
+ </div>
9
+
10
+ <v-date-picker v-else v-model="currentDate" color="primary" elevation="0" bg-color="surface" location="center"
11
+ class="overflow-auto fill-height fill-width" :max="new Date()" position="relative"
12
+ show-adjacent-months></v-date-picker>
13
+ </span>
14
+ </template>
15
+ <script setup>
16
+ import { computed } from "vue";
17
+ import { datetime } from "@/store/States"
18
+
19
+ const props = defineProps(["inline"])
20
+ const currentDate = computed({
21
+ get() {
22
+ return props.inline ? datetime.value.split("T")[0] : new Date(datetime.value) ?? new Date()
23
+ },
24
+ /** @param {Date | string} updatedDate */
25
+ set(updatedDate) {
26
+ if (props.inline) {
27
+ updatedDate = new Date(updatedDate) ?? new Date()
28
+ }
29
+ datetime.value = /** @type {Date} */ (updatedDate).toISOString()
30
+ }
31
+ })
32
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <DynamicWebComponent :link="link" tag-name="eox-map" :properties="properties" :on-mounted="onMounted"
3
+ :on-unmounted="onUnmounted" />
4
+ </template>
5
+ <script setup>
6
+ import { inject, watch } from "vue";
7
+ import { toAbsolute } from 'stac-js/src/http.js';
8
+ import { EodashCollection } from "@/utils/eodashSTAC";
9
+ import { eodashKey } from "@/store/Keys";
10
+ import { mapInstance, datetime } from "@/store/States";
11
+ import DynamicWebComponent from "@/components/DynamicWebComponent.vue";
12
+ import { storeToRefs } from "pinia";
13
+ import '@eox/map/dist/eox-map-advanced-layers-and-sources.js';
14
+
15
+ const eodashConfig = /** @type {import("@/types").Eodash} */ inject(eodashKey)
16
+
17
+ const properties = {
18
+ class: "fill-height fill-width overflow-none",
19
+ center: [15, 48],
20
+ layers: [{ type: "Tile", source: { type: "OSM" } }],
21
+ }
22
+ const link = () => import("@eox/map")
23
+
24
+ /** @type {import("openlayers").EventsListenerFunctionType}*/
25
+ let handleMoveEnd;
26
+
27
+ /** @type {import("@/types").WebComponentProps["onMounted"]} */
28
+ const onMounted = (el, store, router) => {
29
+ datetime.value = /** @type {string} */ (router.currentRoute.value.query["datetime"] ?? "");
30
+ mapInstance.value = /** @type {any} */(el).map;
31
+
32
+ const { selectedStac } = storeToRefs(store)
33
+
34
+ watch([selectedStac, datetime], async ([updatedStac, updatedTime]) => {
35
+ if (updatedStac) {
36
+ const parentCollUrl = toAbsolute(updatedStac.links[1].href, eodashConfig.stacEndpoint);
37
+ const childCollUrl = toAbsolute(updatedStac.links[1].href, parentCollUrl);
38
+ const eodash = new EodashCollection(childCollUrl);
39
+ if (updatedTime) {
40
+ /** @type {any} */(el).layers = await eodash.createLayersJson(new Date(updatedTime));
41
+ } else {
42
+ /** @type {any} */(el).layers = await eodash.createLayersJson();
43
+ }
44
+ }
45
+ }, { immediate: true })
46
+ }
47
+ /** @type {import("@/types").WebComponentProps["onUnmounted"]} */
48
+ const onUnmounted = (_el, _store, _router) => {
49
+ mapInstance.value?.un('moveend', handleMoveEnd);
50
+ }
51
+
52
+
53
+
54
+ </script>