@opengeoweb/soundings 14.5.2 → 15.1.0
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/index.esm.js +661 -163
- package/package.json +8 -8
- package/src/index.d.ts +2 -0
- package/src/lib/api/edr-api/hooks.d.ts +2 -2
- package/src/lib/components/MenuBar/LocationsMenu.d.ts +1 -1
- package/src/lib/components/MenuBar/MenuBar.d.ts +3 -1
- package/src/lib/components/MenuBar/SoundingsMarkersSwitch.d.ts +6 -0
- package/src/lib/components/MenuBar/SoundingsMarkersSwitch.spec.d.ts +1 -0
- package/src/lib/components/MenuBar/SoundingsSourceMenu.d.ts +1 -1
- package/src/lib/components/Soundings/SortableAccordions.d.ts +8 -1
- package/src/lib/components/Soundings/SoundingsConnect.d.ts +3 -3
- package/src/lib/components/SoundingsManager/SoundingManagerConnect.intergration.spec.d.ts +1 -0
- package/src/lib/components/SoundingsManager/SoundingManagerListItem.d.ts +4 -0
- package/src/lib/components/SoundingsManager/SoundingsManager.d.ts +9 -1
- package/src/lib/components/SoundingsManager/SoundingsManagerConnect.d.ts +1 -0
- package/src/lib/components/SoundingsManager/SoundingsManagerListItem.spec.d.ts +1 -0
- package/src/lib/components/SoundingsTimeSlider/SoundingsTimeSliderConnect.d.ts +5 -1
- package/src/lib/store/reducer.d.ts +8 -1
- package/src/lib/store/reducer.spec.d.ts +1 -0
- package/src/lib/store/selectors.d.ts +90 -5
- package/src/lib/store/store.d.ts +3 -0
- package/src/lib/store/types.d.ts +34 -15
- package/src/lib/utils/soundingsLookUp.d.ts +2 -3
package/index.esm.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
2
|
import { Menu, ListSubheader, Typography, MenuItem, Box, Card, Stack, IconButton, Tooltip, LinearProgress, List, ListItem, ListItemIcon, Checkbox, ListItemText, Grid, ListItemButton } from '@mui/material';
|
|
3
|
+
import { range, minBy, isEqual } from 'lodash';
|
|
3
4
|
import React, { useRef, useEffect, useMemo, useState, Fragment as Fragment$1 } from 'react';
|
|
4
|
-
import { useDispatch, useSelector } from 'react-redux';
|
|
5
|
-
import { safelyInitI18nForTestsAndStories, sharedTranslations, SHARED_NAMESPACE, CustomToggleButton, dateUtils, CustomSlider, AlertBanner, ToolContainerDraggable, StyledSelect, SearchField, CustomAccordion } from '@opengeoweb/shared';
|
|
5
|
+
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
|
|
6
|
+
import { safelyInitI18nForTestsAndStories, sharedTranslations, SHARED_NAMESPACE, CustomToggleButton, dateUtils, SwitchButton, CustomSlider, AlertBanner, ToolContainerDraggable, StyledSelect, SearchField, CustomAccordion } from '@opengeoweb/shared';
|
|
6
7
|
import { selectorMemoizationOptions, uiSelectors, uiTypes, uiActions, genericSelectors, mapSelectors, genericActions, syncGroupsActions, mapActions, useSetupDialog, storeReducerMap, storeMiddlewares } from '@opengeoweb/store';
|
|
7
8
|
import { ParseISOTimeRangeDuration, parseISO8601IntervalToDateInterval, webmapUtils } from '@opengeoweb/webmap';
|
|
8
9
|
import { MapFeatureClass } from '@opengeoweb/webmap-react';
|
|
9
10
|
import { ReactSortable } from 'react-sortablejs';
|
|
10
|
-
import { ArrowDropDownDown, DragHandle, Layers, Sources } from '@opengeoweb/theme';
|
|
11
|
+
import { ArrowDropDownDown, DragHandle, Layers, Sources, Visibility, VisibilityOff } from '@opengeoweb/theme';
|
|
11
12
|
import * as d3 from 'd3';
|
|
12
13
|
import i18n from 'i18next';
|
|
13
14
|
export { default as i18n } from 'i18next';
|
|
@@ -16,7 +17,6 @@ import { timesliderTranslations, TIME_SLIDER_NAMESPACE, BackwardForwardStepButto
|
|
|
16
17
|
import { useResizable } from 'react-resizable-layout';
|
|
17
18
|
import { useQueries } from '@tanstack/react-query';
|
|
18
19
|
import { getHeaders, handleResponse } from '@opengeoweb/api';
|
|
19
|
-
import { range, minBy } from 'lodash';
|
|
20
20
|
import { createSlice, createSelector, combineReducers, configureStore } from '@reduxjs/toolkit';
|
|
21
21
|
import { FixedSizeList } from 'react-window';
|
|
22
22
|
|
|
@@ -30,7 +30,9 @@ var en = {
|
|
|
30
30
|
options: "Options",
|
|
31
31
|
"diagram-SKEWT": "Skew-T",
|
|
32
32
|
"diagram-BIJVOET": "Bijvoet",
|
|
33
|
-
"diagram-EMAGRAM": "Emagram"
|
|
33
|
+
"diagram-EMAGRAM": "Emagram",
|
|
34
|
+
"soundings-toggle-location-markers-label": "Markers",
|
|
35
|
+
"soundings-toggle-location-markers-tooltip": "Show/hide location markers"
|
|
34
36
|
};
|
|
35
37
|
var no = {
|
|
36
38
|
sources: "Kilder",
|
|
@@ -42,7 +44,9 @@ var no = {
|
|
|
42
44
|
options: "Valg",
|
|
43
45
|
"diagram-SKEWT": "Skew-T",
|
|
44
46
|
"diagram-BIJVOET": "Bijvoet",
|
|
45
|
-
"diagram-EMAGRAM": "Emagram"
|
|
47
|
+
"diagram-EMAGRAM": "Emagram",
|
|
48
|
+
"soundings-toggle-location-markers-label": "ikke oversatt",
|
|
49
|
+
"soundings-toggle-location-markers-tooltip": "ikke oversatt"
|
|
46
50
|
};
|
|
47
51
|
var nl = {
|
|
48
52
|
sources: "Bronnen",
|
|
@@ -54,7 +58,9 @@ var nl = {
|
|
|
54
58
|
options: "Opties",
|
|
55
59
|
"diagram-SKEWT": "Skew-T",
|
|
56
60
|
"diagram-BIJVOET": "Bijvoet",
|
|
57
|
-
"diagram-EMAGRAM": "Emagram"
|
|
61
|
+
"diagram-EMAGRAM": "Emagram",
|
|
62
|
+
"soundings-toggle-location-markers-label": "Kaart locaties",
|
|
63
|
+
"soundings-toggle-location-markers-tooltip": "Toon/Verberg locatie punten"
|
|
58
64
|
};
|
|
59
65
|
var fi = {
|
|
60
66
|
sources: "Lähteet",
|
|
@@ -66,7 +72,9 @@ var fi = {
|
|
|
66
72
|
options: "Valinnat",
|
|
67
73
|
"diagram-SKEWT": "Skew-T",
|
|
68
74
|
"diagram-BIJVOET": "Bijvoet",
|
|
69
|
-
"diagram-EMAGRAM": "Emagram"
|
|
75
|
+
"diagram-EMAGRAM": "Emagram",
|
|
76
|
+
"soundings-toggle-location-markers-label": "Merkit",
|
|
77
|
+
"soundings-toggle-location-markers-tooltip": "Näytä/piilota kohdemerkit"
|
|
70
78
|
};
|
|
71
79
|
var soundingsTranslations = {
|
|
72
80
|
en: en,
|
|
@@ -683,8 +691,7 @@ const drawSoundingsTooltip = ({
|
|
|
683
691
|
return getParamHtml(dataSet.hoveredDewpoint, index, '°C', index === interpolatedData.length - 1, dataSet.color);
|
|
684
692
|
}).join(''));
|
|
685
693
|
// Wind
|
|
686
|
-
windRefd3?.style('display', 'block').style('left', `${width + margin.left + margin.right
|
|
687
|
-
.style('top', `${pressureY}px`).html(() => {
|
|
694
|
+
windRefd3?.style('display', 'block').style('left', `${width + margin.left + margin.right}px`).style('top', `${pressureY}px`).html(() => {
|
|
688
695
|
const windSpeedStrings = interpolatedData.map((dataSet, index) => {
|
|
689
696
|
return getParamHtml(dataSet.hoveredWindSpeed, index, 'm/s', index === interpolatedData.length - 1, dataSet.color, true);
|
|
690
697
|
});
|
|
@@ -1870,6 +1877,19 @@ const soundingsStyle = {
|
|
|
1870
1877
|
}
|
|
1871
1878
|
};
|
|
1872
1879
|
const WIND_BARB_OFFSET_X = 35;
|
|
1880
|
+
/**
|
|
1881
|
+
* Check if the sounding data is effectively empty.
|
|
1882
|
+
* Data can be empty in two ways:
|
|
1883
|
+
* 1. No sources selected: data = []
|
|
1884
|
+
* 2. No locations selected: data = [[], []] (sources exist but all have empty arrays)
|
|
1885
|
+
*/
|
|
1886
|
+
const isDataEmpty = data => {
|
|
1887
|
+
if (!data || data.length === 0) {
|
|
1888
|
+
return true;
|
|
1889
|
+
}
|
|
1890
|
+
// Check if all inner arrays are empty (no locations selected)
|
|
1891
|
+
return data.every(features => features.length === 0);
|
|
1892
|
+
};
|
|
1873
1893
|
const WindBarbs = ({
|
|
1874
1894
|
d3GraphId,
|
|
1875
1895
|
data,
|
|
@@ -1951,7 +1971,8 @@ const WindBarbs = ({
|
|
|
1951
1971
|
columnOffsetX: index * WIND_BARB_OFFSET_X
|
|
1952
1972
|
});
|
|
1953
1973
|
});
|
|
1954
|
-
|
|
1974
|
+
// Clear or update tooltip data based on whether we have actual data
|
|
1975
|
+
tooltipDataRef.current = isDataEmpty(data) ? null : constructTooltipData(data);
|
|
1955
1976
|
// Update previous inner height after drawing so animation uses the prior value
|
|
1956
1977
|
prevInnerHeightRef.current = height;
|
|
1957
1978
|
// eslint-disable-next-line consistent-return, @typescript-eslint/explicit-function-return-type
|
|
@@ -1964,6 +1985,11 @@ const WindBarbs = ({
|
|
|
1964
1985
|
windRef.current && (windRef.current.style.display = 'none');
|
|
1965
1986
|
return;
|
|
1966
1987
|
}
|
|
1988
|
+
// Hide tooltip when data is empty (all sources/locations removed)
|
|
1989
|
+
if (isDataEmpty(data)) {
|
|
1990
|
+
windRef.current.style.display = 'none';
|
|
1991
|
+
return;
|
|
1992
|
+
}
|
|
1967
1993
|
const rect = svgRectRef.current ?? svgRef.current.getBoundingClientRect();
|
|
1968
1994
|
if (clientY == null || clientY < rect.top || clientY > rect.bottom) {
|
|
1969
1995
|
windRef.current.style.display = 'none';
|
|
@@ -1991,15 +2017,12 @@ const WindBarbs = ({
|
|
|
1991
2017
|
tooltipData: tooltipDataRef.current,
|
|
1992
2018
|
diagramType
|
|
1993
2019
|
});
|
|
1994
|
-
}, [clientY, showTooltip, diagramType]);
|
|
1995
|
-
useEffect(() => {
|
|
1996
|
-
// Reset the tooltips when filteredData changes
|
|
1997
|
-
if (windRef.current) {
|
|
1998
|
-
windRef.current.style.display = 'none';
|
|
1999
|
-
}
|
|
2000
|
-
}, [data]);
|
|
2020
|
+
}, [clientY, showTooltip, diagramType, data]);
|
|
2001
2021
|
return jsxs(Box, {
|
|
2002
2022
|
ref: canvasRef,
|
|
2023
|
+
sx: {
|
|
2024
|
+
position: 'relative'
|
|
2025
|
+
},
|
|
2003
2026
|
children: [jsx("svg", {
|
|
2004
2027
|
ref: svgRef
|
|
2005
2028
|
}), jsx(Box, {
|
|
@@ -2258,21 +2281,19 @@ const SoundingsAccordion = React.forwardRef(({
|
|
|
2258
2281
|
});
|
|
2259
2282
|
|
|
2260
2283
|
const SortableAccordions = ({
|
|
2284
|
+
soundingPanelsInOrder,
|
|
2261
2285
|
data,
|
|
2262
2286
|
size,
|
|
2263
2287
|
chosenDiagramType,
|
|
2264
2288
|
setChosenDiagramType,
|
|
2265
|
-
viewRef
|
|
2289
|
+
viewRef,
|
|
2290
|
+
onOrderChange
|
|
2266
2291
|
}) => {
|
|
2267
|
-
const [accordions, setAccordions] = React.useState([{
|
|
2268
|
-
id: 'soundings'
|
|
2269
|
-
}, {
|
|
2270
|
-
id: 'windBarbs'
|
|
2271
|
-
}]);
|
|
2272
2292
|
const [soundingsIsClosed, setSoundingsIsClosed] = React.useState(false);
|
|
2273
2293
|
const [previousHeight, setPreviousHeight] = React.useState(size);
|
|
2274
2294
|
const createPanel = React.useCallback(id => {
|
|
2275
|
-
|
|
2295
|
+
const normalizedId = id.toLowerCase().replace(/\s+/g, '');
|
|
2296
|
+
switch (normalizedId) {
|
|
2276
2297
|
case 'soundings':
|
|
2277
2298
|
return jsx(SoundingsAccordion, {
|
|
2278
2299
|
accordionIsOpen: true,
|
|
@@ -2286,7 +2307,7 @@ const SortableAccordions = ({
|
|
|
2286
2307
|
setSoundingsIsClosed: setSoundingsIsClosed,
|
|
2287
2308
|
size: size,
|
|
2288
2309
|
children: jsx(Soundings, {
|
|
2289
|
-
d3GraphId:
|
|
2310
|
+
d3GraphId: normalizedId,
|
|
2290
2311
|
data: data,
|
|
2291
2312
|
diagramType: chosenDiagramType,
|
|
2292
2313
|
svgDimensions: {
|
|
@@ -2301,13 +2322,13 @@ const SortableAccordions = ({
|
|
|
2301
2322
|
}
|
|
2302
2323
|
})
|
|
2303
2324
|
});
|
|
2304
|
-
case '
|
|
2325
|
+
case 'windbarbs':
|
|
2305
2326
|
return jsx(SoundingsAccordion, {
|
|
2306
2327
|
maxWidth: 200,
|
|
2307
2328
|
name: "Wind Barbs",
|
|
2308
2329
|
children: jsx(WindBarbs, {
|
|
2309
2330
|
animateResize: Boolean(soundingsIsClosed && previousHeight),
|
|
2310
|
-
d3GraphId:
|
|
2331
|
+
d3GraphId: normalizedId,
|
|
2311
2332
|
data: data,
|
|
2312
2333
|
diagramType: chosenDiagramType,
|
|
2313
2334
|
svgDimensions: {
|
|
@@ -2339,18 +2360,31 @@ const SortableAccordions = ({
|
|
|
2339
2360
|
direction: "horizontal",
|
|
2340
2361
|
draggable: ".sortable-item",
|
|
2341
2362
|
handle: ".drag-handle",
|
|
2342
|
-
list:
|
|
2343
|
-
|
|
2363
|
+
list: soundingPanelsInOrder.map(panel => ({
|
|
2364
|
+
...panel
|
|
2365
|
+
})),
|
|
2366
|
+
setList: list => onOrderChange(list.map(({
|
|
2367
|
+
id,
|
|
2368
|
+
name,
|
|
2369
|
+
isDisplayed
|
|
2370
|
+
}) => ({
|
|
2371
|
+
id,
|
|
2372
|
+
name,
|
|
2373
|
+
isDisplayed
|
|
2374
|
+
}))),
|
|
2344
2375
|
style: {
|
|
2345
2376
|
display: 'flex',
|
|
2346
2377
|
gap: 4,
|
|
2347
2378
|
width: '100%'
|
|
2348
2379
|
},
|
|
2349
2380
|
tag: "div",
|
|
2350
|
-
children:
|
|
2381
|
+
children: soundingPanelsInOrder.filter(entry => entry.isDisplayed).flatMap(({
|
|
2351
2382
|
id
|
|
2352
2383
|
}) => {
|
|
2353
2384
|
const panel = createPanel(id);
|
|
2385
|
+
if (!panel) {
|
|
2386
|
+
return [];
|
|
2387
|
+
}
|
|
2354
2388
|
return panel ? jsx(React.Fragment, {
|
|
2355
2389
|
children: React.cloneElement(panel, {
|
|
2356
2390
|
accordionId: id,
|
|
@@ -2824,10 +2858,28 @@ const useMultipleSources = (sources, locations, datetime) => {
|
|
|
2824
2858
|
parameterMappings,
|
|
2825
2859
|
verticalRange
|
|
2826
2860
|
}, index) => {
|
|
2827
|
-
//
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2861
|
+
// Find the best matching timestep for this source
|
|
2862
|
+
let dateToUse = '';
|
|
2863
|
+
if (datetime && timesteps[index] && timesteps[index].length > 0) {
|
|
2864
|
+
const requestedTime = new Date(datetime).getTime();
|
|
2865
|
+
const availableTimesteps = timesteps[index];
|
|
2866
|
+
// Check for exact match first
|
|
2867
|
+
if (availableTimesteps.includes(new Date(datetime).toISOString())) {
|
|
2868
|
+
dateToUse = datetime;
|
|
2869
|
+
} else {
|
|
2870
|
+
// Find nearest timestep (prefer earlier timesteps if equidistant)
|
|
2871
|
+
let nearestTimestep = availableTimesteps[0];
|
|
2872
|
+
let minDiff = Math.abs(new Date(availableTimesteps[0]).getTime() - requestedTime);
|
|
2873
|
+
for (const timestep of availableTimesteps) {
|
|
2874
|
+
const diff = Math.abs(new Date(timestep).getTime() - requestedTime);
|
|
2875
|
+
if (diff < minDiff) {
|
|
2876
|
+
minDiff = diff;
|
|
2877
|
+
nearestTimestep = timestep;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
dateToUse = nearestTimestep;
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2831
2883
|
const queryParams = constructQueryParams(collectionId, url, instanceId, locations[collectionId] || '', dateToUse || '', parameterMappings || defaultParameterMappings, verticalRange || '');
|
|
2832
2884
|
return queryParams;
|
|
2833
2885
|
});
|
|
@@ -3029,12 +3081,40 @@ const useGetCollectionLocations = (collections, enabled) => {
|
|
|
3029
3081
|
const initialSoundingsState = {
|
|
3030
3082
|
selectedSources: {},
|
|
3031
3083
|
selectedLocations: {},
|
|
3032
|
-
selectedTime: new Date().toISOString()
|
|
3084
|
+
selectedTime: new Date().toISOString(),
|
|
3085
|
+
diagramType: 'SKEWT',
|
|
3086
|
+
soundingPanels: {
|
|
3087
|
+
order: ['soundings', 'windbarbs'],
|
|
3088
|
+
entities: {
|
|
3089
|
+
soundings: {
|
|
3090
|
+
id: 'soundings',
|
|
3091
|
+
name: 'Soundings',
|
|
3092
|
+
isDisplayed: true
|
|
3093
|
+
},
|
|
3094
|
+
windbarbs: {
|
|
3095
|
+
id: 'windbarbs',
|
|
3096
|
+
name: 'Wind Barbs',
|
|
3097
|
+
isDisplayed: true
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
3100
|
+
},
|
|
3101
|
+
shouldHideLocationMarkers: false
|
|
3033
3102
|
};
|
|
3034
3103
|
const slice = createSlice({
|
|
3035
3104
|
initialState: initialSoundingsState,
|
|
3036
3105
|
name: 'soundings',
|
|
3037
3106
|
reducers: {
|
|
3107
|
+
registerSoundingsPreset: (draft, payloadAction) => {
|
|
3108
|
+
const preset = payloadAction.payload;
|
|
3109
|
+
if (preset) {
|
|
3110
|
+
draft.selectedSources = preset.selectedSources ?? {};
|
|
3111
|
+
draft.selectedLocations = preset.selectedLocations ?? {};
|
|
3112
|
+
draft.selectedTime = preset.selectedTime ?? new Date().toISOString();
|
|
3113
|
+
draft.diagramType = preset.diagramType ?? 'SKEWT';
|
|
3114
|
+
draft.soundingPanels = preset.soundingPanels ?? initialSoundingsState.soundingPanels;
|
|
3115
|
+
draft.shouldHideLocationMarkers = preset.shouldHideLocationMarkers ?? false;
|
|
3116
|
+
}
|
|
3117
|
+
},
|
|
3038
3118
|
updateSelectedSource: (draft, payloadAction) => {
|
|
3039
3119
|
const {
|
|
3040
3120
|
sourceId,
|
|
@@ -3057,6 +3137,29 @@ const slice = createSlice({
|
|
|
3057
3137
|
selectedTime
|
|
3058
3138
|
} = payloadAction.payload;
|
|
3059
3139
|
draft.selectedTime = selectedTime;
|
|
3140
|
+
},
|
|
3141
|
+
updateDiagramType: (draft, payloadAction) => {
|
|
3142
|
+
draft.diagramType = payloadAction.payload.diagramType;
|
|
3143
|
+
},
|
|
3144
|
+
toggleLocationMarkers: (draft, payloadAction) => {
|
|
3145
|
+
const {
|
|
3146
|
+
shouldHideLocationMarkers
|
|
3147
|
+
} = payloadAction.payload;
|
|
3148
|
+
draft.shouldHideLocationMarkers = shouldHideLocationMarkers;
|
|
3149
|
+
},
|
|
3150
|
+
addSoundingPanelsFromPreset: (draft, payloadAction) => {
|
|
3151
|
+
const {
|
|
3152
|
+
order,
|
|
3153
|
+
entities
|
|
3154
|
+
} = payloadAction.payload;
|
|
3155
|
+
draft.soundingPanels.order = order;
|
|
3156
|
+
draft.soundingPanels.entities = entities;
|
|
3157
|
+
},
|
|
3158
|
+
changeSoundingPanelOrder: (draft, payloadAction) => {
|
|
3159
|
+
const {
|
|
3160
|
+
newOrderOfIds
|
|
3161
|
+
} = payloadAction.payload;
|
|
3162
|
+
draft.soundingPanels.order = newOrderOfIds;
|
|
3060
3163
|
}
|
|
3061
3164
|
}
|
|
3062
3165
|
});
|
|
@@ -3087,7 +3190,9 @@ const getSourceSelections = createSelector(getSoundingsStore, store => {
|
|
|
3087
3190
|
return store.selectedSources;
|
|
3088
3191
|
}, selectorMemoizationOptions);
|
|
3089
3192
|
const getLocationSelections = createSelector(getSoundingsStore, store => store.selectedLocations, selectorMemoizationOptions);
|
|
3090
|
-
const getSelectedTime = createSelector(getSoundingsStore, store => store.selectedTime, selectorMemoizationOptions);
|
|
3193
|
+
const getSelectedTime = createSelector(getSoundingsStore, store => store.selectedTime ?? new Date().toISOString(), selectorMemoizationOptions);
|
|
3194
|
+
const getDiagramType = createSelector(getSoundingsStore, store => store.diagramType ?? 'SKEWT', selectorMemoizationOptions);
|
|
3195
|
+
const getShouldHideLocationMarkers = createSelector(getSoundingsStore, store => store.shouldHideLocationMarkers ?? false, selectorMemoizationOptions);
|
|
3091
3196
|
const getSelectedSources = createSelector(getSoundingsStore, store => {
|
|
3092
3197
|
const sources = store.selectedSources;
|
|
3093
3198
|
const activeSources = Object.keys(sources).reduce((list, key) => {
|
|
@@ -3095,11 +3200,11 @@ const getSelectedSources = createSelector(getSoundingsStore, store => {
|
|
|
3095
3200
|
if (value.isChecked === true) {
|
|
3096
3201
|
return list.concat({
|
|
3097
3202
|
collectionId: key,
|
|
3098
|
-
url: value.url,
|
|
3203
|
+
url: value.url ?? '',
|
|
3099
3204
|
instanceId: value.instanceId,
|
|
3100
3205
|
verticalRange: value.verticalRange,
|
|
3101
3206
|
parameterMappings: value.parameterMappings,
|
|
3102
|
-
name: value.name
|
|
3207
|
+
name: value.name ?? ''
|
|
3103
3208
|
});
|
|
3104
3209
|
}
|
|
3105
3210
|
return list;
|
|
@@ -3112,6 +3217,135 @@ const getSelectedSourcesAmount = createSelector(getSelectedSources, activeSource
|
|
|
3112
3217
|
const getSelectedSourcesIds = createSelector(getSelectedSources, activeSources => {
|
|
3113
3218
|
return activeSources.map(source => source.collectionId);
|
|
3114
3219
|
}, selectorMemoizationOptions);
|
|
3220
|
+
const getSoundingsPreset = createSelector(getSoundingsStore, soundingsStore => {
|
|
3221
|
+
const {
|
|
3222
|
+
selectedTime,
|
|
3223
|
+
selectedSources,
|
|
3224
|
+
...rest
|
|
3225
|
+
} = soundingsStore;
|
|
3226
|
+
const filteredSources = Object.entries(selectedSources).reduce((acc, [key, value]) => {
|
|
3227
|
+
const {
|
|
3228
|
+
instanceId,
|
|
3229
|
+
...sourceWithoutInstance
|
|
3230
|
+
} = value;
|
|
3231
|
+
return {
|
|
3232
|
+
...acc,
|
|
3233
|
+
[key]: sourceWithoutInstance
|
|
3234
|
+
};
|
|
3235
|
+
}, {});
|
|
3236
|
+
return {
|
|
3237
|
+
...rest,
|
|
3238
|
+
selectedSources: filteredSources
|
|
3239
|
+
};
|
|
3240
|
+
}, selectorMemoizationOptions);
|
|
3241
|
+
const getSoundingPanelsInOrder = createSelector(getSoundingsStore, store => {
|
|
3242
|
+
const {
|
|
3243
|
+
order = [],
|
|
3244
|
+
entities = {}
|
|
3245
|
+
} = store.soundingPanels || {
|
|
3246
|
+
order: [],
|
|
3247
|
+
entities: {}
|
|
3248
|
+
};
|
|
3249
|
+
return order.map(id => entities[id]);
|
|
3250
|
+
}, selectorMemoizationOptions);
|
|
3251
|
+
|
|
3252
|
+
var selectors = /*#__PURE__*/Object.freeze({
|
|
3253
|
+
__proto__: null,
|
|
3254
|
+
getDiagramType: getDiagramType,
|
|
3255
|
+
getLocationSelections: getLocationSelections,
|
|
3256
|
+
getSelectedSources: getSelectedSources,
|
|
3257
|
+
getSelectedSourcesAmount: getSelectedSourcesAmount,
|
|
3258
|
+
getSelectedSourcesIds: getSelectedSourcesIds,
|
|
3259
|
+
getSelectedTime: getSelectedTime,
|
|
3260
|
+
getShouldHideLocationMarkers: getShouldHideLocationMarkers,
|
|
3261
|
+
getSoundingPanelsInOrder: getSoundingPanelsInOrder,
|
|
3262
|
+
getSoundingsPreset: getSoundingsPreset,
|
|
3263
|
+
getSourceSelections: getSourceSelections
|
|
3264
|
+
});
|
|
3265
|
+
|
|
3266
|
+
/* *
|
|
3267
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
3268
|
+
* you may not use this file except in compliance with the License.
|
|
3269
|
+
* You may obtain a copy of the License at
|
|
3270
|
+
*
|
|
3271
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
3272
|
+
*
|
|
3273
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
3274
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
3275
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
3276
|
+
* See the License for the specific language governing permissions and
|
|
3277
|
+
* limitations under the License.
|
|
3278
|
+
*
|
|
3279
|
+
* Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
|
|
3280
|
+
* Copyright 2025 - Finnish Meteorological Institute (FMI)
|
|
3281
|
+
* Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
|
|
3282
|
+
* */
|
|
3283
|
+
var SoundingsActionOrigin;
|
|
3284
|
+
(function (SoundingsActionOrigin) {
|
|
3285
|
+
SoundingsActionOrigin["soundingsManager"] = "soundingsManager";
|
|
3286
|
+
SoundingsActionOrigin["soundingsComponent"] = "soundingsComponent";
|
|
3287
|
+
})(SoundingsActionOrigin || (SoundingsActionOrigin = {}));
|
|
3288
|
+
|
|
3289
|
+
var types = /*#__PURE__*/Object.freeze({
|
|
3290
|
+
__proto__: null,
|
|
3291
|
+
get SoundingsActionOrigin () { return SoundingsActionOrigin; }
|
|
3292
|
+
});
|
|
3293
|
+
|
|
3294
|
+
const SoundingsMarkersSwitch = ({
|
|
3295
|
+
shouldHideMarkers,
|
|
3296
|
+
toggleMarkers
|
|
3297
|
+
}) => {
|
|
3298
|
+
const {
|
|
3299
|
+
t
|
|
3300
|
+
} = useSoundingsTranslation();
|
|
3301
|
+
return jsxs(Box, {
|
|
3302
|
+
sx: {
|
|
3303
|
+
display: 'flex',
|
|
3304
|
+
flexWrap: 'wrap',
|
|
3305
|
+
alignItems: 'flex-start',
|
|
3306
|
+
'label:first-of-type': {
|
|
3307
|
+
marginLeft: '5px',
|
|
3308
|
+
marginRight: '-5px'
|
|
3309
|
+
},
|
|
3310
|
+
'label:last-of-type': {
|
|
3311
|
+
marginLeft: '-5px'
|
|
3312
|
+
},
|
|
3313
|
+
'.switch-button label': {
|
|
3314
|
+
display: 'none'
|
|
3315
|
+
},
|
|
3316
|
+
'.markers-label': {
|
|
3317
|
+
display: 'none'
|
|
3318
|
+
},
|
|
3319
|
+
'@media (min-width: 720px)': {
|
|
3320
|
+
minWidth: '146px',
|
|
3321
|
+
'.markers-label, .switch-button label': {
|
|
3322
|
+
display: 'initial'
|
|
3323
|
+
}
|
|
3324
|
+
}
|
|
3325
|
+
},
|
|
3326
|
+
children: [jsx(Typography, {
|
|
3327
|
+
className: "markers-label",
|
|
3328
|
+
sx: {
|
|
3329
|
+
marginTop: '4px'
|
|
3330
|
+
},
|
|
3331
|
+
variant: "body2",
|
|
3332
|
+
children: t('soundings-toggle-location-markers-label')
|
|
3333
|
+
}), jsx(Box, {
|
|
3334
|
+
sx: {
|
|
3335
|
+
marginTop: '-5px'
|
|
3336
|
+
},
|
|
3337
|
+
children: jsx(SwitchButton, {
|
|
3338
|
+
activeLabel: "ON",
|
|
3339
|
+
checked: !shouldHideMarkers,
|
|
3340
|
+
inActiveLabel: "OFF",
|
|
3341
|
+
onChange: event => {
|
|
3342
|
+
toggleMarkers(!event.target.checked);
|
|
3343
|
+
},
|
|
3344
|
+
tooltip: t('soundings-toggle-location-markers-tooltip')
|
|
3345
|
+
})
|
|
3346
|
+
})]
|
|
3347
|
+
});
|
|
3348
|
+
};
|
|
3115
3349
|
|
|
3116
3350
|
var MenuOptions;
|
|
3117
3351
|
(function (MenuOptions) {
|
|
@@ -3126,7 +3360,9 @@ const buttonStyle = {
|
|
|
3126
3360
|
const SoundingsMenuBar = ({
|
|
3127
3361
|
handleClick,
|
|
3128
3362
|
isOpen,
|
|
3129
|
-
selectedSourcesAmount = 0
|
|
3363
|
+
selectedSourcesAmount = 0,
|
|
3364
|
+
shouldHideMarkers,
|
|
3365
|
+
toggleMarkers
|
|
3130
3366
|
}) => {
|
|
3131
3367
|
const {
|
|
3132
3368
|
t
|
|
@@ -3165,6 +3401,9 @@ const SoundingsMenuBar = ({
|
|
|
3165
3401
|
sx: buttonStyle,
|
|
3166
3402
|
variant: "tool",
|
|
3167
3403
|
children: t('locations')
|
|
3404
|
+
}), jsx(SoundingsMarkersSwitch, {
|
|
3405
|
+
shouldHideMarkers: shouldHideMarkers,
|
|
3406
|
+
toggleMarkers: toggleMarkers
|
|
3168
3407
|
})]
|
|
3169
3408
|
});
|
|
3170
3409
|
};
|
|
@@ -3181,6 +3420,14 @@ const MenuBarConnect = ({
|
|
|
3181
3420
|
const isLocationsOpenInStore = useSelector(store => uiSelectors.getisDialogOpen(store, uiTypes.DialogTypes.SoundingsLocations));
|
|
3182
3421
|
const isSoundingsManagerOpenInStore = useSelector(store => uiSelectors.getisDialogOpen(store, uiTypes.DialogTypes.SoundingsManager));
|
|
3183
3422
|
const sourcesAmount = useSelector(getSelectedSourcesAmount);
|
|
3423
|
+
const shouldHideLocationMarkers = useSelector(getShouldHideLocationMarkers);
|
|
3424
|
+
const toggleLocationMarkers = React.useCallback(shouldHide => {
|
|
3425
|
+
dispatch(soundingsActions.toggleLocationMarkers({
|
|
3426
|
+
shouldHideLocationMarkers: shouldHide,
|
|
3427
|
+
panelId: viewId,
|
|
3428
|
+
origin: SoundingsActionOrigin.soundingsComponent
|
|
3429
|
+
}));
|
|
3430
|
+
}, [dispatch, viewId]);
|
|
3184
3431
|
const dispatchActiveMapId = React.useCallback((dialogType, isOpen, activeMapId) => void dispatch(uiActions.setActiveMapIdForDialog({
|
|
3185
3432
|
type: dialogType,
|
|
3186
3433
|
mapId: viewId,
|
|
@@ -3208,7 +3455,9 @@ const MenuBarConnect = ({
|
|
|
3208
3455
|
return jsx(SoundingsMenuBar, {
|
|
3209
3456
|
handleClick: handleClick,
|
|
3210
3457
|
isOpen: isOpen,
|
|
3211
|
-
selectedSourcesAmount: sourcesAmount
|
|
3458
|
+
selectedSourcesAmount: sourcesAmount,
|
|
3459
|
+
shouldHideMarkers: shouldHideLocationMarkers,
|
|
3460
|
+
toggleMarkers: toggleLocationMarkers
|
|
3212
3461
|
});
|
|
3213
3462
|
};
|
|
3214
3463
|
|
|
@@ -3348,24 +3597,33 @@ const roundToClosestTimestep = (timeToRound, availableSteps) => {
|
|
|
3348
3597
|
}
|
|
3349
3598
|
return timeToRound;
|
|
3350
3599
|
};
|
|
3351
|
-
const SoundingsTimeSliderConnect = (
|
|
3600
|
+
const SoundingsTimeSliderConnect = ({
|
|
3601
|
+
viewId
|
|
3602
|
+
}) => {
|
|
3352
3603
|
const selectedTime = useSelector(getSelectedTime);
|
|
3353
3604
|
const sourceSelections = useSelector(getSelectedSources);
|
|
3354
3605
|
const dispatch = useDispatch();
|
|
3355
|
-
const updateSelectedTime = React.useCallback(selectedTime => {
|
|
3606
|
+
const updateSelectedTime = React.useCallback((selectedTime, isInitialCorrection = false) => {
|
|
3356
3607
|
dispatch(soundingsActions.updateSelectedTime({
|
|
3357
|
-
selectedTime
|
|
3608
|
+
selectedTime,
|
|
3609
|
+
panelId: viewId,
|
|
3610
|
+
origin: isInitialCorrection ? SoundingsActionOrigin.soundingsManager : SoundingsActionOrigin.soundingsComponent
|
|
3358
3611
|
}));
|
|
3359
|
-
}, [dispatch]);
|
|
3612
|
+
}, [dispatch, viewId]);
|
|
3360
3613
|
const {
|
|
3361
3614
|
data: timesteps
|
|
3362
3615
|
} = useTimesteps(sourceSelections);
|
|
3363
3616
|
const mergedTimesteps = [...new Set(timesteps.flat())].sort();
|
|
3617
|
+
const hasInitializedTime = React.useRef(false);
|
|
3364
3618
|
// Ensure that the selected time is always one of the available timesteps
|
|
3365
3619
|
const validSelectedTime = roundToClosestTimestep(selectedTime, mergedTimesteps);
|
|
3366
3620
|
useEffect(() => {
|
|
3367
3621
|
if (selectedTime !== validSelectedTime) {
|
|
3368
|
-
|
|
3622
|
+
const isInitial = !hasInitializedTime.current;
|
|
3623
|
+
updateSelectedTime(validSelectedTime, isInitial);
|
|
3624
|
+
if (isInitial) {
|
|
3625
|
+
hasInitializedTime.current = true;
|
|
3626
|
+
}
|
|
3369
3627
|
}
|
|
3370
3628
|
}, [selectedTime, updateSelectedTime, validSelectedTime]);
|
|
3371
3629
|
return jsx(SoundingsTimeSlider, {
|
|
@@ -3404,33 +3662,80 @@ const useGetSize = viewRef => {
|
|
|
3404
3662
|
};
|
|
3405
3663
|
const SoundingsConnect = ({
|
|
3406
3664
|
viewId,
|
|
3407
|
-
|
|
3665
|
+
soundingsPreset
|
|
3408
3666
|
}) => {
|
|
3409
3667
|
const viewRef = React.useRef(null);
|
|
3410
3668
|
const size = useGetSize(viewRef);
|
|
3411
|
-
const [chosenDiagramType, setChosenDiagramType] = React.useState(diagramType);
|
|
3412
3669
|
const geojsonLayerId = React.useRef(webmapUtils.generateLayerId()).current;
|
|
3413
3670
|
const dispatch = useDispatch();
|
|
3671
|
+
const soundingsPresetRef = React.useRef();
|
|
3672
|
+
const presetJustLoadedRef = React.useRef(false);
|
|
3673
|
+
const initialLocationsRef = React.useRef({});
|
|
3674
|
+
const previousLocationsRef = React.useRef({});
|
|
3675
|
+
React.useLayoutEffect(() => {
|
|
3676
|
+
if (soundingsPreset && !isEqual(soundingsPreset, soundingsPresetRef.current)) {
|
|
3677
|
+
soundingsPresetRef.current = soundingsPreset;
|
|
3678
|
+
presetJustLoadedRef.current = true;
|
|
3679
|
+
initialLocationsRef.current = soundingsPreset.selectedLocations || {};
|
|
3680
|
+
previousLocationsRef.current = {};
|
|
3681
|
+
dispatch(soundingsActions.registerSoundingsPreset(soundingsPreset));
|
|
3682
|
+
setTimeout(() => {
|
|
3683
|
+
presetJustLoadedRef.current = false;
|
|
3684
|
+
initialLocationsRef.current = {};
|
|
3685
|
+
}, 500);
|
|
3686
|
+
} else if (!soundingsPreset) {
|
|
3687
|
+
soundingsPresetRef.current = undefined;
|
|
3688
|
+
initialLocationsRef.current = {};
|
|
3689
|
+
previousLocationsRef.current = {};
|
|
3690
|
+
}
|
|
3691
|
+
}, [dispatch, soundingsPreset, viewId]);
|
|
3414
3692
|
const selectedTime = useSelector(getSelectedTime);
|
|
3415
|
-
const sourceSelections = useSelector(getSelectedSources);
|
|
3416
|
-
const locationSelections = useSelector(getLocationSelections);
|
|
3417
|
-
const
|
|
3418
|
-
const
|
|
3693
|
+
const sourceSelections = useSelector(getSelectedSources, shallowEqual);
|
|
3694
|
+
const locationSelections = useSelector(getLocationSelections, shallowEqual);
|
|
3695
|
+
const diagramType = useSelector(getDiagramType);
|
|
3696
|
+
const shouldHideLocationMarkers = useSelector(getShouldHideLocationMarkers);
|
|
3697
|
+
const setChosenDiagramType = React.useCallback(diagramType => {
|
|
3698
|
+
dispatch(soundingsActions.updateDiagramType({
|
|
3699
|
+
diagramType,
|
|
3700
|
+
panelId: viewId,
|
|
3701
|
+
origin: SoundingsActionOrigin.soundingsComponent
|
|
3702
|
+
}));
|
|
3703
|
+
}, [dispatch, viewId]);
|
|
3704
|
+
const selectedFeature = useSelector(state => genericSelectors.getSelectedFeature(state.syncGroups, viewId), shallowEqual);
|
|
3705
|
+
const selectedMapIds = useSelector(state => genericSelectors.getLinkedMaps(state, viewId), shallowEqual);
|
|
3706
|
+
const firstMapId = selectedMapIds[0];
|
|
3419
3707
|
const selectedMapPin = useSelector(state =>
|
|
3420
3708
|
// mapPins are synced so we can arbitrarily choose the first linked map
|
|
3421
|
-
mapSelectors.getPinLocation(state,
|
|
3422
|
-
const
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3709
|
+
mapSelectors.getPinLocation(state, firstMapId), shallowEqual);
|
|
3710
|
+
const collectionsRef = React.useRef();
|
|
3711
|
+
const collections = useMemo(() => {
|
|
3712
|
+
const newCollections = sourceSelections.map(({
|
|
3713
|
+
collectionId,
|
|
3714
|
+
url,
|
|
3715
|
+
name
|
|
3716
|
+
}) => ({
|
|
3717
|
+
id: collectionId,
|
|
3718
|
+
url,
|
|
3719
|
+
name: name || collectionId
|
|
3720
|
+
}));
|
|
3721
|
+
if (isEqual(newCollections, collectionsRef.current)) {
|
|
3722
|
+
return collectionsRef.current;
|
|
3723
|
+
}
|
|
3724
|
+
collectionsRef.current = newCollections;
|
|
3725
|
+
return newCollections;
|
|
3726
|
+
}, [sourceSelections]);
|
|
3431
3727
|
const {
|
|
3432
|
-
data:
|
|
3728
|
+
data: rawLocationData
|
|
3433
3729
|
} = useGetCollectionLocations(collections, true);
|
|
3730
|
+
// Stabilize locationData to prevent infinite loops caused by new array references
|
|
3731
|
+
const locationDataRef = React.useRef();
|
|
3732
|
+
const locationData = useMemo(() => {
|
|
3733
|
+
if (isEqual(rawLocationData, locationDataRef.current)) {
|
|
3734
|
+
return locationDataRef.current;
|
|
3735
|
+
}
|
|
3736
|
+
locationDataRef.current = rawLocationData;
|
|
3737
|
+
return rawLocationData;
|
|
3738
|
+
}, [rawLocationData]);
|
|
3434
3739
|
/* Make sure the map pin is visible when linking new maps */
|
|
3435
3740
|
useEffect(() => {
|
|
3436
3741
|
const toggleMapPinVisibility = displayMapPin => {
|
|
@@ -3448,25 +3753,26 @@ const SoundingsConnect = ({
|
|
|
3448
3753
|
};
|
|
3449
3754
|
}, [dispatch, selectedMapIds]);
|
|
3450
3755
|
// construct and send geojson to map for drawing locations
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3756
|
+
const geoJsonForMap = useMemo(() => ({
|
|
3757
|
+
type: 'FeatureCollection',
|
|
3758
|
+
features: (locationData || []).flatMap(({
|
|
3759
|
+
id: serviceId,
|
|
3760
|
+
features
|
|
3761
|
+
}) => features.map(feature => ({
|
|
3762
|
+
type: 'Feature',
|
|
3763
|
+
geometry: feature.geometry,
|
|
3764
|
+
id: feature.id,
|
|
3765
|
+
properties: {
|
|
3766
|
+
name: feature.properties.name,
|
|
3767
|
+
mapFeatureClass: MapFeatureClass.ObservationStation,
|
|
3768
|
+
highlightSelectedFeature: selectedFeature?.id === feature.id,
|
|
3460
3769
|
id: feature.id,
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
}
|
|
3468
|
-
})))
|
|
3469
|
-
};
|
|
3770
|
+
serviceId,
|
|
3771
|
+
hidden: shouldHideLocationMarkers
|
|
3772
|
+
}
|
|
3773
|
+
})))
|
|
3774
|
+
}), [locationData, selectedFeature?.id, shouldHideLocationMarkers]);
|
|
3775
|
+
useEffect(() => {
|
|
3470
3776
|
dispatch(genericActions.addSharedData({
|
|
3471
3777
|
panelId: viewId,
|
|
3472
3778
|
data: {
|
|
@@ -3476,7 +3782,7 @@ const SoundingsConnect = ({
|
|
|
3476
3782
|
}]
|
|
3477
3783
|
}
|
|
3478
3784
|
}));
|
|
3479
|
-
}, [dispatch, geojsonLayerId,
|
|
3785
|
+
}, [dispatch, geojsonLayerId, geoJsonForMap, viewId]);
|
|
3480
3786
|
useEffect(() => () => {
|
|
3481
3787
|
if (viewId) {
|
|
3482
3788
|
dispatch(syncGroupsActions.deleteSharedData({
|
|
@@ -3487,31 +3793,145 @@ const SoundingsConnect = ({
|
|
|
3487
3793
|
// Map location selection has changed. Update soundings location
|
|
3488
3794
|
// since the location menu may be out of sync
|
|
3489
3795
|
useEffect(() => {
|
|
3796
|
+
if (presetJustLoadedRef.current) {
|
|
3797
|
+
return;
|
|
3798
|
+
}
|
|
3490
3799
|
const serviceId = selectedFeature?.serviceId;
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3800
|
+
const featureId = selectedFeature?.id;
|
|
3801
|
+
if (serviceId && featureId) {
|
|
3802
|
+
const currentLocation = locationSelectionsRef.current[serviceId];
|
|
3803
|
+
const initialLocation = initialLocationsRef.current[serviceId];
|
|
3804
|
+
if (currentLocation !== featureId && initialLocation !== featureId) {
|
|
3805
|
+
dispatch(soundingsActions.updateSelectedLocation({
|
|
3806
|
+
sourceId: serviceId,
|
|
3807
|
+
location: featureId,
|
|
3808
|
+
panelId: viewId,
|
|
3809
|
+
origin: SoundingsActionOrigin.soundingsComponent
|
|
3810
|
+
}));
|
|
3811
|
+
}
|
|
3496
3812
|
}
|
|
3497
|
-
}, [dispatch, selectedFeature?.id, selectedFeature?.serviceId]);
|
|
3498
|
-
//
|
|
3813
|
+
}, [dispatch, selectedFeature?.id, selectedFeature?.serviceId, viewId]);
|
|
3814
|
+
// Use ref to avoid circular dependency when reading locationSelections
|
|
3815
|
+
const locationSelectionsRef = React.useRef(locationSelections);
|
|
3816
|
+
React.useEffect(() => {
|
|
3817
|
+
locationSelectionsRef.current = locationSelections;
|
|
3818
|
+
}, [locationSelections]);
|
|
3819
|
+
// Makes position requests for all user-selected sources for the current map pin position.
|
|
3820
|
+
// Map pin change on its own implies a new position selection.
|
|
3499
3821
|
useEffect(() => {
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
if (
|
|
3504
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3822
|
+
if (presetJustLoadedRef.current) {
|
|
3823
|
+
return;
|
|
3824
|
+
}
|
|
3825
|
+
if (!selectedFeature?.id && !selectedMapPin?.id && selectedMapPin?.lon && selectedMapPin.lat) {
|
|
3826
|
+
const newLocation = [selectedMapPin.lon, selectedMapPin.lat];
|
|
3827
|
+
// Update location for all selected sources
|
|
3828
|
+
sourceSelections.forEach(source => {
|
|
3829
|
+
if (source?.collectionId) {
|
|
3830
|
+
const currentLocation = locationSelectionsRef.current[source.collectionId];
|
|
3831
|
+
const isSameLocation = Array.isArray(currentLocation) && currentLocation[0] === newLocation[0] && currentLocation[1] === newLocation[1];
|
|
3832
|
+
if (!isSameLocation) {
|
|
3833
|
+
dispatch(soundingsActions.updateSelectedLocation({
|
|
3834
|
+
sourceId: source.collectionId,
|
|
3835
|
+
location: newLocation,
|
|
3836
|
+
panelId: viewId,
|
|
3837
|
+
origin: SoundingsActionOrigin.soundingsComponent
|
|
3838
|
+
}));
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
3841
|
+
});
|
|
3842
|
+
}
|
|
3843
|
+
}, [selectedMapPin, selectedFeature?.id, sourceSelections, dispatch, viewId]);
|
|
3844
|
+
useEffect(() => {
|
|
3845
|
+
if (!locationData || locationData.length === 0) {
|
|
3846
|
+
return;
|
|
3847
|
+
}
|
|
3848
|
+
if (selectedMapIds.length === 0) {
|
|
3849
|
+
return;
|
|
3850
|
+
}
|
|
3851
|
+
let changedSourceId;
|
|
3852
|
+
for (const source of sourceSelections) {
|
|
3853
|
+
if (source?.collectionId) {
|
|
3854
|
+
const currentLocation = locationSelections[source.collectionId];
|
|
3855
|
+
const previousLocation = previousLocationsRef.current[source.collectionId];
|
|
3856
|
+
if (currentLocation !== previousLocation) {
|
|
3857
|
+
changedSourceId = source.collectionId;
|
|
3858
|
+
if (!presetJustLoadedRef.current) {
|
|
3859
|
+
break;
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3508
3863
|
}
|
|
3509
|
-
|
|
3864
|
+
if (!changedSourceId) {
|
|
3865
|
+
return;
|
|
3866
|
+
}
|
|
3867
|
+
const selectedLocation = locationSelections[changedSourceId];
|
|
3868
|
+
if (!selectedLocation) {
|
|
3869
|
+
return;
|
|
3870
|
+
}
|
|
3871
|
+
previousLocationsRef.current = {
|
|
3872
|
+
...previousLocationsRef.current,
|
|
3873
|
+
[changedSourceId]: selectedLocation
|
|
3874
|
+
};
|
|
3875
|
+
const serviceData = locationData.find(service => service.id === changedSourceId);
|
|
3876
|
+
if (!serviceData) {
|
|
3877
|
+
return;
|
|
3878
|
+
}
|
|
3879
|
+
if (typeof selectedLocation === 'string') {
|
|
3880
|
+
const foundFeature = serviceData.features.find(feature => feature.id === selectedLocation);
|
|
3881
|
+
if (foundFeature?.geometry.type === 'Point') {
|
|
3882
|
+
const [lon, lat] = foundFeature.geometry.coordinates;
|
|
3883
|
+
const mapPinNeedsUpdate = selectedMapPin?.id !== selectedLocation || selectedMapPin?.lon !== lon || selectedMapPin?.lat !== lat;
|
|
3884
|
+
const featureNeedsUpdate = selectedFeature?.id !== selectedLocation;
|
|
3885
|
+
if (mapPinNeedsUpdate) {
|
|
3886
|
+
selectedMapIds.forEach(mapId => {
|
|
3887
|
+
dispatch(mapActions.setMapPinLocation({
|
|
3888
|
+
mapId,
|
|
3889
|
+
mapPinLocation: {
|
|
3890
|
+
lon,
|
|
3891
|
+
lat,
|
|
3892
|
+
id: selectedLocation
|
|
3893
|
+
}
|
|
3894
|
+
}));
|
|
3895
|
+
});
|
|
3896
|
+
}
|
|
3897
|
+
if (featureNeedsUpdate) {
|
|
3898
|
+
dispatch(genericActions.addSharedData({
|
|
3899
|
+
panelId: viewId,
|
|
3900
|
+
data: {
|
|
3901
|
+
selectedFeatureId: selectedLocation
|
|
3902
|
+
}
|
|
3903
|
+
}));
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
} else if (Array.isArray(selectedLocation)) {
|
|
3907
|
+
const [lon, lat] = selectedLocation;
|
|
3908
|
+
const needsUpdate = selectedMapPin?.lon !== lon || selectedMapPin?.lat !== lat;
|
|
3909
|
+
if (needsUpdate) {
|
|
3910
|
+
selectedMapIds.forEach(mapId => {
|
|
3911
|
+
dispatch(mapActions.setMapPinLocation({
|
|
3912
|
+
mapId,
|
|
3913
|
+
mapPinLocation: {
|
|
3914
|
+
lon,
|
|
3915
|
+
lat
|
|
3916
|
+
}
|
|
3917
|
+
}));
|
|
3918
|
+
});
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3922
|
+
}, [locationData, locationSelections, sourceSelections, selectedMapIds, dispatch, viewId]);
|
|
3510
3923
|
const {
|
|
3511
3924
|
error,
|
|
3512
3925
|
data,
|
|
3513
3926
|
isPending
|
|
3514
3927
|
} = useMultipleSources(sourceSelections, locationSelections, selectedTime);
|
|
3928
|
+
const soundingPanelsInOrder = useSelector(store => getSoundingPanelsInOrder(store));
|
|
3929
|
+
const changeOrder = newOrder => {
|
|
3930
|
+
const orderedIds = newOrder.map(collection => collection.id);
|
|
3931
|
+
dispatch(soundingsActions.changeSoundingPanelOrder({
|
|
3932
|
+
newOrderOfIds: orderedIds
|
|
3933
|
+
}));
|
|
3934
|
+
};
|
|
3515
3935
|
return jsxs(Fragment, {
|
|
3516
3936
|
children: [!!error && jsx(AlertBanner, {
|
|
3517
3937
|
shouldClose: true,
|
|
@@ -3566,10 +3986,12 @@ const SoundingsConnect = ({
|
|
|
3566
3986
|
},
|
|
3567
3987
|
children: jsx(SharedMouseYProvider, {
|
|
3568
3988
|
children: jsx(SortableAccordions, {
|
|
3569
|
-
chosenDiagramType:
|
|
3989
|
+
chosenDiagramType: diagramType,
|
|
3570
3990
|
data: data,
|
|
3991
|
+
onOrderChange: changeOrder,
|
|
3571
3992
|
setChosenDiagramType: setChosenDiagramType,
|
|
3572
3993
|
size: size,
|
|
3994
|
+
soundingPanelsInOrder: soundingPanelsInOrder,
|
|
3573
3995
|
viewRef: viewRef
|
|
3574
3996
|
})
|
|
3575
3997
|
})
|
|
@@ -3579,7 +4001,9 @@ const SoundingsConnect = ({
|
|
|
3579
4001
|
flex: '0 0 auto',
|
|
3580
4002
|
height: `${TIMESLIDER_HEIGHT}px`
|
|
3581
4003
|
},
|
|
3582
|
-
children: jsx(SoundingsTimeSliderConnect, {
|
|
4004
|
+
children: jsx(SoundingsTimeSliderConnect, {
|
|
4005
|
+
viewId: viewId
|
|
4006
|
+
})
|
|
3583
4007
|
})]
|
|
3584
4008
|
})
|
|
3585
4009
|
})]
|
|
@@ -3655,6 +4079,7 @@ const SoundingsSourceMenu = ({
|
|
|
3655
4079
|
instanceId,
|
|
3656
4080
|
url
|
|
3657
4081
|
} = sourceSelections[collection.id] || {};
|
|
4082
|
+
const sourceUrl = url || collection.url;
|
|
3658
4083
|
const hasInstances = collection.instances.length > 0;
|
|
3659
4084
|
const selectedInstanceId = instanceId || collection.instances[0]?.id || undefined;
|
|
3660
4085
|
return jsxs(ListItem, {
|
|
@@ -3669,7 +4094,7 @@ const SoundingsSourceMenu = ({
|
|
|
3669
4094
|
onChange: event => {
|
|
3670
4095
|
const verticalRange = selectedInstanceId !== undefined ? extractVerticalRange(collection, selectedInstanceId) : '';
|
|
3671
4096
|
updateCollection(collection.id, {
|
|
3672
|
-
url,
|
|
4097
|
+
url: sourceUrl,
|
|
3673
4098
|
isChecked: event.target.checked,
|
|
3674
4099
|
instanceId: selectedInstanceId,
|
|
3675
4100
|
verticalRange
|
|
@@ -3685,7 +4110,7 @@ const SoundingsSourceMenu = ({
|
|
|
3685
4110
|
const instanceId = event.target.value;
|
|
3686
4111
|
const verticalRange = extractVerticalRange(collection, instanceId);
|
|
3687
4112
|
updateCollection(collection.id, {
|
|
3688
|
-
url,
|
|
4113
|
+
url: sourceUrl,
|
|
3689
4114
|
instanceId,
|
|
3690
4115
|
verticalRange
|
|
3691
4116
|
});
|
|
@@ -3748,30 +4173,39 @@ const SoundingsSourceDialogConnect = ({
|
|
|
3748
4173
|
dialogOrder,
|
|
3749
4174
|
setDialogOrder
|
|
3750
4175
|
} = useSetupDialog(uiTypes.DialogTypes.SoundingsSources);
|
|
4176
|
+
const panelId = useSelector(store => uiSelectors.getDialogMapId(store, uiTypes.DialogTypes.SoundingsSources));
|
|
3751
4177
|
const updateCollection = React.useCallback((sourceId, value) => {
|
|
3752
4178
|
dispatch(soundingsActions.updateSelectedSource({
|
|
3753
4179
|
sourceId,
|
|
3754
|
-
value
|
|
4180
|
+
value,
|
|
4181
|
+
panelId,
|
|
4182
|
+
origin: SoundingsActionOrigin.soundingsComponent
|
|
3755
4183
|
}));
|
|
3756
|
-
}, [dispatch]);
|
|
4184
|
+
}, [dispatch, panelId]);
|
|
3757
4185
|
const sourceSelections = useSelector(getSourceSelections);
|
|
3758
4186
|
// Set mappings for each collection
|
|
3759
4187
|
React.useEffect(() => {
|
|
3760
4188
|
soundingsCollections?.forEach(collection => {
|
|
4189
|
+
const currentSelection = sourceSelections[collection.id];
|
|
4190
|
+
if (currentSelection?.url && currentSelection?.parameterMappings) {
|
|
4191
|
+
return;
|
|
4192
|
+
}
|
|
4193
|
+
const newValue = {
|
|
4194
|
+
url: collection.url,
|
|
4195
|
+
isChecked: currentSelection?.isChecked || false,
|
|
4196
|
+
parameterMappings: collection.parameterMappings,
|
|
4197
|
+
...(collection.name && collection.name !== '' ? {
|
|
4198
|
+
name: collection.name
|
|
4199
|
+
} : {})
|
|
4200
|
+
};
|
|
3761
4201
|
dispatch(soundingsActions.updateSelectedSource({
|
|
3762
4202
|
sourceId: collection.id,
|
|
3763
|
-
value:
|
|
3764
|
-
|
|
3765
|
-
isChecked: sourceSelections[collection.id]?.isChecked || false,
|
|
3766
|
-
parameterMappings: collection.parameterMappings,
|
|
3767
|
-
...(collection.name && collection.name !== '' ? {
|
|
3768
|
-
name: collection.name
|
|
3769
|
-
} : {})
|
|
3770
|
-
}
|
|
4203
|
+
value: newValue,
|
|
4204
|
+
origin: SoundingsActionOrigin.soundingsManager
|
|
3771
4205
|
}));
|
|
3772
4206
|
});
|
|
3773
4207
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3774
|
-
}, [dispatch, soundingsCollections]);
|
|
4208
|
+
}, [dispatch, soundingsCollections, panelId]);
|
|
3775
4209
|
const {
|
|
3776
4210
|
data,
|
|
3777
4211
|
isPending,
|
|
@@ -3941,8 +4375,6 @@ const LocationsMenuConnect = ({
|
|
|
3941
4375
|
const locationSelections = useSelector(getLocationSelections);
|
|
3942
4376
|
const selectedSourceIds = useSelector(getSelectedSourcesIds);
|
|
3943
4377
|
const sourcesAmount = useSelector(getSelectedSourcesAmount);
|
|
3944
|
-
const panelId = useSelector(store => uiSelectors.getDialogMapId(store, uiTypes.DialogTypes.SoundingsLocations));
|
|
3945
|
-
const selectedMapIds = useSelector(state => genericSelectors.getLinkedMaps(state, panelId));
|
|
3946
4378
|
const {
|
|
3947
4379
|
data,
|
|
3948
4380
|
isPending,
|
|
@@ -3951,22 +4383,6 @@ const LocationsMenuConnect = ({
|
|
|
3951
4383
|
const services = useMemo(() => {
|
|
3952
4384
|
return data?.filter(service => selectedSourceIds.includes(service.id)) || [];
|
|
3953
4385
|
}, [data, selectedSourceIds]);
|
|
3954
|
-
const handleFeatureSelect = (panelId, featureId) => {
|
|
3955
|
-
dispatch(genericActions.addSharedData({
|
|
3956
|
-
panelId,
|
|
3957
|
-
data: {
|
|
3958
|
-
selectedFeatureId: featureId
|
|
3959
|
-
}
|
|
3960
|
-
}));
|
|
3961
|
-
};
|
|
3962
|
-
const setMapPinLocation = mapPinLocation => {
|
|
3963
|
-
selectedMapIds.forEach(mapId => {
|
|
3964
|
-
dispatch(mapActions.setMapPinLocation({
|
|
3965
|
-
mapId,
|
|
3966
|
-
mapPinLocation
|
|
3967
|
-
}));
|
|
3968
|
-
});
|
|
3969
|
-
};
|
|
3970
4386
|
return jsx(LocationsMenu, {
|
|
3971
4387
|
error: error,
|
|
3972
4388
|
isLoading: isPending,
|
|
@@ -3982,50 +4398,118 @@ const LocationsMenuConnect = ({
|
|
|
3982
4398
|
sourceId,
|
|
3983
4399
|
location: locationId
|
|
3984
4400
|
}));
|
|
3985
|
-
// Use first source. Multiple sources will be implemented later.
|
|
3986
|
-
// https://gitlab.com/opengeoweb/geoweb-assets/-/issues/4254
|
|
3987
|
-
const isFirstSource = sourceId === selectedSourceIds[0];
|
|
3988
|
-
if (!isFirstSource) {
|
|
3989
|
-
return;
|
|
3990
|
-
}
|
|
3991
|
-
if (locationId) {
|
|
3992
|
-
handleFeatureSelect(panelId, locationId);
|
|
3993
|
-
}
|
|
3994
|
-
const foundFeature = services[0].features.find(feature => feature.id === locationId);
|
|
3995
|
-
// check type for typescript
|
|
3996
|
-
if (foundFeature?.geometry.type === 'Point') {
|
|
3997
|
-
const [lon, lat] = foundFeature.geometry.coordinates;
|
|
3998
|
-
setMapPinLocation({
|
|
3999
|
-
lon,
|
|
4000
|
-
lat,
|
|
4001
|
-
id: locationId
|
|
4002
|
-
});
|
|
4003
|
-
}
|
|
4004
4401
|
}
|
|
4005
4402
|
});
|
|
4006
4403
|
};
|
|
4007
4404
|
|
|
4405
|
+
var SoundingManagerDummyPreset = {
|
|
4406
|
+
order: [
|
|
4407
|
+
"soundings",
|
|
4408
|
+
"windbarbs"
|
|
4409
|
+
],
|
|
4410
|
+
entities: {
|
|
4411
|
+
soundings: {
|
|
4412
|
+
id: "soundings",
|
|
4413
|
+
name: "Soundings",
|
|
4414
|
+
isDisplayed: true
|
|
4415
|
+
},
|
|
4416
|
+
windbarbs: {
|
|
4417
|
+
id: "windbarbs",
|
|
4418
|
+
name: "Wind Barbs",
|
|
4419
|
+
isDisplayed: true
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
};
|
|
4423
|
+
var preset = {
|
|
4424
|
+
SoundingManagerDummyPreset: SoundingManagerDummyPreset
|
|
4425
|
+
};
|
|
4426
|
+
|
|
4427
|
+
const SoundingManagerListItem = ({
|
|
4428
|
+
name,
|
|
4429
|
+
isDisplayed
|
|
4430
|
+
}) => {
|
|
4431
|
+
return jsx(Card, {
|
|
4432
|
+
className: "sortable-item",
|
|
4433
|
+
sx: {
|
|
4434
|
+
margin: '4px 0 2px',
|
|
4435
|
+
backgroundColor: isDisplayed ? 'white' : 'rgb(232, 232, 232)'
|
|
4436
|
+
},
|
|
4437
|
+
children: jsxs(Grid, {
|
|
4438
|
+
alignItems: "center",
|
|
4439
|
+
container: true,
|
|
4440
|
+
direction: "row",
|
|
4441
|
+
justifyContent: "flex-start",
|
|
4442
|
+
sx: {
|
|
4443
|
+
height: '32px'
|
|
4444
|
+
},
|
|
4445
|
+
children: [jsx(IconButton, {
|
|
4446
|
+
"aria-label": "drag-handle",
|
|
4447
|
+
className: "drag-handle",
|
|
4448
|
+
size: "small",
|
|
4449
|
+
sx: {
|
|
4450
|
+
width: '24px',
|
|
4451
|
+
height: '24px',
|
|
4452
|
+
paddingRight: '8px',
|
|
4453
|
+
paddingLeft: '8px'
|
|
4454
|
+
},
|
|
4455
|
+
children: jsx(DragHandle, {
|
|
4456
|
+
fontSize: "small"
|
|
4457
|
+
})
|
|
4458
|
+
}), jsx(Box, {
|
|
4459
|
+
sx: {
|
|
4460
|
+
display: 'flex',
|
|
4461
|
+
alignItems: 'center'
|
|
4462
|
+
},
|
|
4463
|
+
children: isDisplayed ? jsx(Visibility, {}) : jsx(VisibilityOff, {})
|
|
4464
|
+
}), jsxs(Box, {
|
|
4465
|
+
sx: {
|
|
4466
|
+
paddingRight: '8px',
|
|
4467
|
+
paddingLeft: '8px'
|
|
4468
|
+
},
|
|
4469
|
+
children: [" ", name, " "]
|
|
4470
|
+
})]
|
|
4471
|
+
})
|
|
4472
|
+
});
|
|
4473
|
+
};
|
|
4474
|
+
|
|
4008
4475
|
const SoundingsManager = ({
|
|
4009
4476
|
isOpen,
|
|
4010
4477
|
onClose,
|
|
4011
4478
|
onMouseDown,
|
|
4012
|
-
order
|
|
4479
|
+
order,
|
|
4480
|
+
soundingPanelsInOrder,
|
|
4481
|
+
onOrderChange
|
|
4013
4482
|
}) => {
|
|
4483
|
+
const sortableList = React.useMemo(() => soundingPanelsInOrder.map(collection => ({
|
|
4484
|
+
...collection
|
|
4485
|
+
})), [soundingPanelsInOrder]);
|
|
4014
4486
|
return jsx(ToolContainerDraggable, {
|
|
4015
4487
|
isOpen: isOpen,
|
|
4016
4488
|
minWidth: 300,
|
|
4017
4489
|
onClose: onClose,
|
|
4018
4490
|
onMouseDown: onMouseDown,
|
|
4019
4491
|
order: order,
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4492
|
+
title: "Soundings Manager",
|
|
4493
|
+
children: jsx(ReactSortable, {
|
|
4494
|
+
animation: 200,
|
|
4495
|
+
direction: "vertical",
|
|
4496
|
+
draggable: ".sortable-item",
|
|
4497
|
+
handle: ".drag-handle",
|
|
4498
|
+
list: sortableList,
|
|
4499
|
+
setList: onOrderChange,
|
|
4500
|
+
style: {
|
|
4501
|
+
padding: '5px'
|
|
4502
|
+
},
|
|
4503
|
+
tag: "div",
|
|
4504
|
+
children: sortableList.map(({
|
|
4505
|
+
id,
|
|
4506
|
+
name,
|
|
4507
|
+
isDisplayed
|
|
4508
|
+
}) => jsx(SoundingManagerListItem, {
|
|
4509
|
+
isDisplayed: isDisplayed,
|
|
4510
|
+
name: name
|
|
4511
|
+
}, id))
|
|
4512
|
+
})
|
|
4029
4513
|
});
|
|
4030
4514
|
};
|
|
4031
4515
|
|
|
@@ -4036,18 +4520,32 @@ const SoundingsManagerConnect = () => {
|
|
|
4036
4520
|
dialogOrder,
|
|
4037
4521
|
setDialogOrder
|
|
4038
4522
|
} = useSetupDialog(uiTypes.DialogTypes.SoundingsManager);
|
|
4523
|
+
const dispatch = useDispatch();
|
|
4524
|
+
React.useEffect(() => {
|
|
4525
|
+
const data = preset.SoundingManagerDummyPreset;
|
|
4526
|
+
dispatch(soundingsActions.addSoundingPanelsFromPreset(data));
|
|
4527
|
+
}, [dispatch]);
|
|
4528
|
+
const soundingPanelsInOrder = useSelector(store => getSoundingPanelsInOrder(store));
|
|
4529
|
+
const changeOrder = newOrder => {
|
|
4530
|
+
const orderedIds = newOrder.map(collection => collection.id);
|
|
4531
|
+
dispatch(soundingsActions.changeSoundingPanelOrder({
|
|
4532
|
+
newOrderOfIds: orderedIds
|
|
4533
|
+
}));
|
|
4534
|
+
};
|
|
4039
4535
|
return jsx(SoundingsManager, {
|
|
4040
4536
|
isOpen: isDialogOpen,
|
|
4041
4537
|
onClose: onCloseDialog,
|
|
4042
4538
|
onMouseDown: setDialogOrder,
|
|
4043
|
-
|
|
4539
|
+
onOrderChange: changeOrder,
|
|
4540
|
+
order: dialogOrder,
|
|
4541
|
+
soundingPanelsInOrder: soundingPanelsInOrder
|
|
4044
4542
|
});
|
|
4045
4543
|
};
|
|
4046
4544
|
|
|
4047
4545
|
const soundingsLookUp = payload => {
|
|
4048
4546
|
if (payload.componentType === 'Soundings') {
|
|
4049
4547
|
return jsx(SoundingsConnect, {
|
|
4050
|
-
|
|
4548
|
+
soundingsPreset: payload.initialProps,
|
|
4051
4549
|
viewId: payload.id
|
|
4052
4550
|
});
|
|
4053
4551
|
}
|
|
@@ -4082,4 +4580,4 @@ const createMockStore = mockState => configureStore({
|
|
|
4082
4580
|
middleware: getDefaultMiddleware => getDefaultMiddleware().concat(storeMiddlewares)
|
|
4083
4581
|
});
|
|
4084
4582
|
|
|
4085
|
-
export { LocationsMenuConnect, SOUNDINGS_NAMESPACE, SoundingsConnect, SoundingsManagerConnect, SoundingsSourceDialogConnect, createMockStore, initSoundingsI18n, soundingDiagrams, soundingsLookUp, soundingsReducersMap, soundingsTranslations, translateInTestsAndStories, useSoundingsTranslation };
|
|
4583
|
+
export { LocationsMenuConnect, SOUNDINGS_NAMESPACE, SoundingsActionOrigin, SoundingsConnect, SoundingsManagerConnect, SoundingsSourceDialogConnect, types as SoundingsTypes, createMockStore, initSoundingsI18n, soundingDiagrams, soundingsActions, soundingsLookUp, soundingsReducer, soundingsReducersMap, selectors as soundingsSelectors, soundingsTranslations, translateInTestsAndStories, useSoundingsTranslation };
|