diff --git a/package-lock.json b/package-lock.json index 35ad4d9fc..c4e010aa8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "fews-weboc", "version": "1.1.0", "dependencies": { - "@deltares/fews-pi-requests": "^1.2.6", + "@deltares/fews-pi-requests": "^1.2.7", "@deltares/fews-ssd-requests": "^1.0.1", "@deltares/fews-ssd-webcomponent": "^1.0.1", "@deltares/fews-web-oc-charts": "^3.0.3-beta.6", @@ -122,9 +122,9 @@ } }, "node_modules/@deltares/fews-pi-requests": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@deltares/fews-pi-requests/-/fews-pi-requests-1.2.6.tgz", - "integrity": "sha512-fQRQb7KItCsK6BGzeQ29z9LzqcgtRvj9zyTpSb9gNz0pOg3IOay0ISAyRtohz8jsfOLobNQa2iSrXFflBzFeyQ==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@deltares/fews-pi-requests/-/fews-pi-requests-1.2.7.tgz", + "integrity": "sha512-H7irlul2mnn85R0RSkDBvKM9qMFpfRzZuLQFz82Q7aeNOZ3IuDOhhe5hVknLAdg3w2Z5iaDq6MLZ4D5ju9PMtQ==", "license": "MIT", "dependencies": { "@deltares/fews-web-oc-utils": "^1.0.0" diff --git a/package.json b/package.json index 01e59a9a5..f08a7ae45 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "test-ct": "playwright test -c playwright-ct.config.ts" }, "dependencies": { - "@deltares/fews-pi-requests": "^1.2.6", + "@deltares/fews-pi-requests": "^1.2.7", "@deltares/fews-ssd-requests": "^1.0.1", "@deltares/fews-ssd-webcomponent": "^1.0.1", "@deltares/fews-web-oc-charts": "^3.0.3-beta.6", diff --git a/src/components/spatialdisplay/SpatialDisplayComponent.vue b/src/components/spatialdisplay/SpatialDisplayComponent.vue index ac3591762..c7100aef2 100644 --- a/src/components/spatialdisplay/SpatialDisplayComponent.vue +++ b/src/components/spatialdisplay/SpatialDisplayComponent.vue @@ -39,7 +39,8 @@ />
- + + { emit('update:currentTime', currentTime.value) }) +const { tooltip } = useLocationTooltip(baseUrl, () => + props.filterIds.length && props.locationId + ? { + filterId: props.filterIds[0], + locationId: props.locationId, + } + : undefined, +) + function setLayerOptions(): void { if (props.layerName) { layerOptions.value = { @@ -489,4 +501,8 @@ function onCoordinateMoved(lat: number, lng: number): void { .maplibregl-ctrl-bottom-left { bottom: v-bind('offsetBottomControls') !important; } + +.control-group :deep(.v-slide-group__content) { + padding: 0 8px; +} diff --git a/src/components/spatialdisplay/SpatialTimeSeriesDisplay.vue b/src/components/spatialdisplay/SpatialTimeSeriesDisplay.vue index cfb9ce817..bad4309fb 100644 --- a/src/components/spatialdisplay/SpatialTimeSeriesDisplay.vue +++ b/src/components/spatialdisplay/SpatialTimeSeriesDisplay.vue @@ -45,6 +45,7 @@ :elevation-chart-config="elevationChartDisplayconfig" :current-time="props.currentTime" :displayType="displayType" + :information-content="tooltip" > systemTimeStore.endTime, ) +const { tooltip } = useLocationTooltip(baseUrl, () => + isFilterActionsFilter(props.filter) + ? { + filterId: props.filter.filterId, + locationId: props.filter.locationIds, + } + : undefined, +) + interface DisplayTypeItem { icon: string label: string @@ -159,6 +171,14 @@ const displayTypeItems = computed(() => { }) } + if (tooltip.value) { + displayItems.push({ + icon: 'mdi-information', + label: 'Information', + value: DisplayType.Information, + }) + } + return displayItems }) diff --git a/src/components/timeseries/TimeSeriesComponent.vue b/src/components/timeseries/TimeSeriesComponent.vue index 30ae3a114..2c88a750f 100644 --- a/src/components/timeseries/TimeSeriesComponent.vue +++ b/src/components/timeseries/TimeSeriesComponent.vue @@ -48,6 +48,12 @@ + + + + +
+ @@ -93,6 +99,7 @@ interface Props { elevationChartConfig?: DisplayConfig displayType: DisplayType currentTime?: Date + informationContent?: string } const props = withDefaults(defineProps(), { diff --git a/src/components/wms/TooltipPanel.vue b/src/components/wms/TooltipPanel.vue new file mode 100644 index 000000000..9481a47fa --- /dev/null +++ b/src/components/wms/TooltipPanel.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/src/lib/display/DisplayConfig.ts b/src/lib/display/DisplayConfig.ts index 7c3b5ef61..5129e9878 100644 --- a/src/lib/display/DisplayConfig.ts +++ b/src/lib/display/DisplayConfig.ts @@ -5,6 +5,7 @@ export enum DisplayType { TimeSeriesChart = 'TimeSeriesChart', TimeSeriesTable = 'TimeSeriesTable', ElevationChart = 'ElevationChart', + Information = 'Information', } export interface DisplayConfig { diff --git a/src/lib/filters/index.ts b/src/lib/filters/index.ts new file mode 100644 index 000000000..abd1ffa88 --- /dev/null +++ b/src/lib/filters/index.ts @@ -0,0 +1,18 @@ +import { + type filterActionsFilter, + type timeSeriesGridActionsFilter, +} from '@deltares/fews-pi-requests' + +export type Filter = timeSeriesGridActionsFilter | filterActionsFilter + +export function isFilterActionsFilter( + filter: Filter, +): filter is filterActionsFilter { + return (filter as filterActionsFilter).filterId !== undefined +} + +export function isTimeSeriesGridActionsFilter( + filter: Filter, +): filter is timeSeriesGridActionsFilter { + return (filter as timeSeriesGridActionsFilter).x !== undefined +} diff --git a/src/services/useDisplayConfig/index.ts b/src/services/useDisplayConfig/index.ts index 89a623f1d..a5334b337 100644 --- a/src/services/useDisplayConfig/index.ts +++ b/src/services/useDisplayConfig/index.ts @@ -1,8 +1,6 @@ import { PiWebserviceProvider, - type filterActionsFilter, type ActionsResponse, - type timeSeriesGridActionsFilter, } from '@deltares/fews-pi-requests' import { ref, toValue, watchEffect } from 'vue' import type { MaybeRefOrGetter, Ref } from 'vue' @@ -11,6 +9,11 @@ import { timeSeriesDisplayToChartConfig } from '../../lib/charts/timeSeriesDispl import { createTransformRequestFn } from '@/lib/requests/transformRequest.js' import { MD5 } from 'crypto-js' import { convertFewsPiDateTimeToJsDate } from '@/lib/date' +import { + type Filter, + isFilterActionsFilter, + isTimeSeriesGridActionsFilter, +} from '@/lib/filters' export interface UseDisplayConfigReturn { displayConfig: Ref @@ -22,18 +25,6 @@ export interface UseDisplayConfigOptions { useDisplayUnits?: boolean } -type Filter = timeSeriesGridActionsFilter | filterActionsFilter -// Guard functions, needed because it is not possible to use instanceof/typeof on interfaces -function isFilterActionsFilter(filter: Filter): filter is filterActionsFilter { - return (filter as filterActionsFilter).filterId !== undefined -} - -function isTimeSeriesGridActionsFilter( - filter: Filter, -): filter is timeSeriesGridActionsFilter { - return (filter as timeSeriesGridActionsFilter).x !== undefined -} - /** * Converts the actions response to an array of display configurations. * @param actionsResponse The actions response object. diff --git a/src/services/useLocationTooltip/index.ts b/src/services/useLocationTooltip/index.ts new file mode 100644 index 000000000..b076d88c0 --- /dev/null +++ b/src/services/useLocationTooltip/index.ts @@ -0,0 +1,67 @@ +import { createTransformRequestFn } from '@/lib/requests/transformRequest' +import { + LocationsTooltipFilter, + PiWebserviceProvider, +} from '@deltares/fews-pi-requests' +import type { MaybeRefOrGetter, Ref, ShallowRef } from 'vue' +import { ref, shallowRef, toValue, watchEffect } from 'vue' + +export interface UseLocationTooltipReturn { + error: Ref + tooltip: ShallowRef + isReady: Ref + isLoading: Ref +} + +export function useLocationTooltip( + baseUrl: string, + filter: MaybeRefOrGetter, +): UseLocationTooltipReturn { + const tooltip = shallowRef() + const isReady = ref(false) + const isLoading = ref(false) + const error = shallowRef() + + async function loadLocationTooltip() { + isLoading.value = true + isReady.value = false + + try { + const _filter = toValue(filter) + if (!_filter) throw new Error('Tooltip filter is undefined') + + const provider = new PiWebserviceProvider(baseUrl, { + transformRequestFn: createTransformRequestFn(), + }) + const response = await provider.getLocationsTooltip(_filter) + if (!response) throw new Error('Tooltip response is undefined') + + tooltip.value = isHTML(response) ? response : formatAsHTML(response) + } catch { + error.value = 'Error loading location tooltip' + tooltip.value = undefined + } finally { + isLoading.value = false + isReady.value = true + } + } + + watchEffect(loadLocationTooltip) + + return { + tooltip, + isReady, + isLoading, + error, + } +} + +function isHTML(str: string) { + const parser = new DOMParser() + const doc = parser.parseFromString(str, 'text/html') + return Array.from(doc.body.childNodes).some((node) => node.nodeType === 1) +} + +function formatAsHTML(str: string) { + return `
${str}
` +}