@opengeoweb/time-series 14.0.1 → 14.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.esm.js CHANGED
@@ -4,7 +4,7 @@ import React__default, { useState, useMemo, useEffect, useCallback, memo } from
4
4
  import { useDispatch, useSelector } from 'react-redux';
5
5
  import { mapUtils, selectorMemoizationOptions, genericSelectors, uiSelectors, uiTypes, uiActions, useSetupDialog, mapSelectors, mapListener, serviceListener, layersListener, syncGroupsListener, genericListener, openlayersListener, uiReducer, syncGroupsReducer, layerReducer, serviceReducer, mapReducer, loadingIndicatorActions, syncGroupsActions, genericActions, mapActions, layerActions } from '@opengeoweb/store';
6
6
  import { snackbarActions, snackbarTypes, snackbarListener, snackbarReducer } from '@opengeoweb/snackbar';
7
- import { dateUtils, CustomTooltip, CustomToggleButton, TooltipSelect, CustomIconButton, CustomAccordion, ToolContainerDraggable, SearchHighlight, SearchField, ToggleMenu, CustomDialog, AlertBanner, useConfirmationDialog, SwitchButton } from '@opengeoweb/shared';
7
+ import { dateUtils, CustomTooltip, CustomToggleButton, TooltipSelect, CustomIconButton, CustomAccordion, ToolContainerDraggable, SearchHighlight, SearchField, ToggleMenu, CustomDialog, AlertBanner, useConfirmationDialog, SwitchButton, useResizeObserver } from '@opengeoweb/shared';
8
8
  import { MenuItem, useTheme, LinearProgress, Grid, Box, Typography, ClickAwayListener, Input, Collapse, ListItem, ListItemButton, ListItemText, List, Button, InputAdornment, Card, styled, Stack, Tooltip, ListItemIcon, Checkbox } from '@mui/material';
9
9
  import { isString, trim, trimStart, compact, chain, groupBy, sortBy, uniqBy, isEqual } from 'lodash';
10
10
  import { ReactSortable } from 'react-sortablejs';
@@ -42,6 +42,9 @@ var en = {
42
42
  "timeseries-show": "Show",
43
43
  "timeseries-hide": "Hide",
44
44
  "timeseries-collection": "Collection",
45
+ "timeseries-collection-id": "Collection ID",
46
+ "timeseries-collection-title": "Collection title",
47
+ "timeseries-collection-description": "Collection description",
45
48
  "timeseries-parameter": "Parameter",
46
49
  "timeseries-parameter-info": "Timeseries parameter info",
47
50
  "timeseries-style": "Style",
@@ -371,6 +374,9 @@ var nl = {
371
374
  "timeseries-show": "Tonen",
372
375
  "timeseries-hide": "Verbergen",
373
376
  "timeseries-collection": "Collectie",
377
+ "timeseries-collection-id": "Collectie ID",
378
+ "timeseries-collection-title": "Collectie titel",
379
+ "timeseries-collection-description": "Collectie beschrijving",
374
380
  "timeseries-parameter": "Parameter",
375
381
  "timeseries-parameter-info": "Tijdreeksen parameter info",
376
382
  "timeseries-style": "Stijl",
@@ -1009,15 +1015,20 @@ var constructParameterObject = function constructParameterObject(serviceId, coll
1009
1015
  });
1010
1016
  };
1011
1017
  var getCollectionDetails = function getCollectionDetails(service, collection, instances) {
1012
- var _collection$extent2;
1018
+ var _collection$extent$cu, _collection$extent2, _collection$data_quer, _collection$extent3;
1013
1019
  return Object.assign({
1014
1020
  serviceId: service.id,
1015
1021
  serviceName: service.name || service.id,
1016
- collectionId: collection.id,
1022
+ collectionId: collection.id
1023
+ }, collection.title !== undefined ? {
1024
+ collectionTitle: collection.title
1025
+ } : {}, collection.description !== undefined ? {
1026
+ collectionDescription: collection.description
1027
+ } : {}, {
1017
1028
  parameters: constructParameterObject(service.id, collection.id, collection.parameter_names),
1018
- customDimensions: ((_collection$extent2 = collection.extent) == null ? void 0 : _collection$extent2.custom) || [],
1019
- dataQueries: collection.data_queries || {},
1020
- extent: collection.extent || {}
1029
+ customDimensions: (_collection$extent$cu = (_collection$extent2 = collection.extent) == null ? void 0 : _collection$extent2.custom) != null ? _collection$extent$cu : [],
1030
+ dataQueries: (_collection$data_quer = collection.data_queries) != null ? _collection$data_quer : {},
1031
+ extent: (_collection$extent3 = collection.extent) != null ? _collection$extent3 : {}
1021
1032
  }, {});
1022
1033
  };
1023
1034
  var getServiceCollectionDetails = function getServiceCollectionDetails(service, collections) {
@@ -1028,9 +1039,9 @@ var getServiceCollectionDetails = function getServiceCollectionDetails(service,
1028
1039
  }
1029
1040
  // Only use parameters from collections that support /locations or /position
1030
1041
  for (var _iterator = _createForOfIteratorHelperLoose(collections), _step; !(_step = _iterator()).done;) {
1031
- var _collection$data_quer, _collection$data_quer2;
1042
+ var _collection$data_quer2, _collection$data_quer3;
1032
1043
  var collection = _step.value;
1033
- if ((_collection$data_quer = collection.data_queries) != null && _collection$data_quer.locations || (_collection$data_quer2 = collection.data_queries) != null && _collection$data_quer2.position) {
1044
+ if ((_collection$data_quer2 = collection.data_queries) != null && _collection$data_quer2.locations || (_collection$data_quer3 = collection.data_queries) != null && _collection$data_quer3.position) {
1034
1045
  collectionsParameters.push(getCollectionDetails(service, collection));
1035
1046
  }
1036
1047
  }
@@ -1063,15 +1074,16 @@ var clipEdrInterval = function clipEdrInterval(interval, maxHoursBefore,
1063
1074
  maxHoursAfter,
1064
1075
  // 10 days to the future
1065
1076
  date) {
1077
+ var _dateUtils$isoStringT, _dateUtils$isoStringT2;
1066
1078
  if (date === void 0) {
1067
1079
  date = Date.now();
1068
1080
  }
1069
1081
  var startTimeFromEdr = (interval == null ? void 0 : interval.length) && interval[0];
1070
1082
  var endTimeFromEdr = (interval == null ? void 0 : interval.length) && interval[1];
1071
- var startDate = (dateUtils.isoStringToDate(String(startTimeFromEdr), false) || dateUtils.getNowUtc()).getTime();
1072
- var endDate = (dateUtils.isoStringToDate(String(endTimeFromEdr), false) || dateUtils.getNowUtc()).getTime();
1073
- var clippedStartDate = new Date(Math.max(startDate || date, date - maxHoursBefore * HOUR_IN_MILLISECONDS));
1074
- var clippedEndDate = new Date(Math.min(endDate || date, date + maxHoursAfter * HOUR_IN_MILLISECONDS));
1083
+ var startDate = ((_dateUtils$isoStringT = dateUtils.isoStringToDate(String(startTimeFromEdr), false)) != null ? _dateUtils$isoStringT : dateUtils.getNowUtc()).getTime();
1084
+ var endDate = ((_dateUtils$isoStringT2 = dateUtils.isoStringToDate(String(endTimeFromEdr), false)) != null ? _dateUtils$isoStringT2 : dateUtils.getNowUtc()).getTime();
1085
+ var clippedStartDate = new Date(Math.max(startDate != null ? startDate : date, date - maxHoursBefore * HOUR_IN_MILLISECONDS));
1086
+ var clippedEndDate = new Date(Math.min(endDate != null ? endDate : date, date + maxHoursAfter * HOUR_IN_MILLISECONDS));
1075
1087
  // Round the dates to minutes to make caching more likely.
1076
1088
  clippedStartDate.setMilliseconds(0);
1077
1089
  clippedStartDate.setSeconds(0);
@@ -1084,8 +1096,8 @@ date) {
1084
1096
  // 1 day to past, 10 days to future
1085
1097
  var clipInterval = [1 * 24, 10 * 24];
1086
1098
  var constructTimeInterval = function constructTimeInterval(collection, instance, parameter) {
1087
- var _instance$extent2, _collection$extent3;
1088
- var interval = ((_instance$extent2 = instance.extent) == null || (_instance$extent2 = _instance$extent2.temporal) == null ? void 0 : _instance$extent2.interval[0]) || (collection == null || (_collection$extent3 = collection.extent) == null || (_collection$extent3 = _collection$extent3.temporal) == null ? void 0 : _collection$extent3.interval[0]);
1099
+ var _instance$extent$temp, _instance$extent2, _collection$extent4;
1100
+ var interval = (_instance$extent$temp = (_instance$extent2 = instance.extent) == null || (_instance$extent2 = _instance$extent2.temporal) == null ? void 0 : _instance$extent2.interval[0]) != null ? _instance$extent$temp : collection == null || (_collection$extent4 = collection.extent) == null || (_collection$extent4 = _collection$extent4.temporal) == null ? void 0 : _collection$extent4.interval[0];
1089
1101
  var _clipEdrInterval = clipEdrInterval(interval, clipInterval[0], clipInterval[1]),
1090
1102
  start = _clipEdrInterval[0],
1091
1103
  end = _clipEdrInterval[1];
@@ -1103,14 +1115,14 @@ var EARTH_RADIUS = 6371;
1103
1115
  * @returns feature nearest to the target location or null if none are within the maximum distance
1104
1116
  */
1105
1117
  var findNearestPoint = function findNearestPoint(targetLocation, pointFeatures, maxDistanceKm) {
1106
- var _pointFeatures$reduce;
1118
+ var _pointFeatures$reduce, _pointFeatures$reduce2;
1107
1119
  if (maxDistanceKm === void 0) {
1108
1120
  maxDistanceKm = 2;
1109
1121
  }
1110
1122
  if (!pointFeatures.length) {
1111
1123
  return null;
1112
1124
  }
1113
- return ((_pointFeatures$reduce = pointFeatures.reduce(function (nearestPoint, currentFeature) {
1125
+ return (_pointFeatures$reduce = (_pointFeatures$reduce2 = pointFeatures.reduce(function (nearestPoint, currentFeature) {
1114
1126
  var _getFeatureCenterPoin = getFeatureCenterPoint(currentFeature),
1115
1127
  featureLon = _getFeatureCenterPoin[0],
1116
1128
  featureLat = _getFeatureCenterPoin[1];
@@ -1131,7 +1143,7 @@ var findNearestPoint = function findNearestPoint(targetLocation, pointFeatures,
1131
1143
  };
1132
1144
  }
1133
1145
  return nearestPoint;
1134
- }, null)) == null ? void 0 : _pointFeatures$reduce.feature) || null;
1146
+ }, null)) == null ? void 0 : _pointFeatures$reduce2.feature) != null ? _pointFeatures$reduce : null;
1135
1147
  };
1136
1148
  var getFeatureCenterPoint = function getFeatureCenterPoint(feature) {
1137
1149
  return feature.geometry.type === 'Point' ? feature.geometry.coordinates : turf.center(feature).geometry.coordinates;
@@ -4121,12 +4133,13 @@ var filterCollectionsParametersFromService = function filterCollectionsParameter
4121
4133
  return filteredCollectionList;
4122
4134
  }
4123
4135
  var collectionContainsSearch = searchStringArray.every(function (search) {
4124
- var _collection$collectio, _collection$serviceId, _collection$serviceNa;
4136
+ var _collection$collectio, _collection$collectio2, _collection$serviceId, _collection$serviceNa;
4125
4137
  var searchLowercase = search.toLowerCase();
4126
4138
  var isSearchTextInCollectionId = (_collection$collectio = collection.collectionId) == null ? void 0 : _collection$collectio.toLowerCase().includes(searchLowercase);
4139
+ var isSearchTextInCollectionTitle = (_collection$collectio2 = collection.collectionTitle) == null ? void 0 : _collection$collectio2.toLowerCase().includes(searchLowercase);
4127
4140
  var isSearchTextInServiceId = (_collection$serviceId = collection.serviceId) == null ? void 0 : _collection$serviceId.toLowerCase().includes(searchLowercase);
4128
4141
  var isSearchTextInServiceName = (_collection$serviceNa = collection.serviceName) == null ? void 0 : _collection$serviceNa.toLowerCase().includes(searchLowercase);
4129
- return isSearchTextInCollectionId || isSearchTextInServiceId || isSearchTextInServiceName;
4142
+ return isSearchTextInCollectionId || isSearchTextInCollectionTitle || isSearchTextInServiceId || isSearchTextInServiceName;
4130
4143
  });
4131
4144
  // Not found in collection - search in parameters
4132
4145
  var filteredParamters = collection.parameters.reduce(function (filteredParameterList, parameter) {
@@ -4168,6 +4181,7 @@ var fontStyling = function fontStyling(primary) {
4168
4181
  lineHeight: '14px',
4169
4182
  fontWeight: primary ? 500 : 'normal',
4170
4183
  textOverflow: 'ellipsis',
4184
+ whiteSpace: 'nowrap',
4171
4185
  overflow: 'hidden'
4172
4186
  };
4173
4187
  };
@@ -4274,10 +4288,11 @@ var TimeSeriesSelectList = function TimeSeriesSelectList(_ref) {
4274
4288
  alignItems: 'center'
4275
4289
  },
4276
4290
  children: [jsxs(Grid, {
4291
+ size: "grow",
4277
4292
  children: [jsx(Box, {
4278
4293
  sx: fontStyling(),
4279
4294
  children: jsx(SearchHighlight, {
4280
- text: collectionListItem.collectionId,
4295
+ text: collectionListItem.collectionTitle || collectionListItem.collectionId,
4281
4296
  search: searchFilter
4282
4297
  })
4283
4298
  }), jsx(Box, {
@@ -4325,6 +4340,7 @@ var TimeSeriesSelectList = function TimeSeriesSelectList(_ref) {
4325
4340
  ' .MuiListItemText-primary': {
4326
4341
  fontSize: '12px',
4327
4342
  fontWeight: 500,
4343
+ whiteSpace: 'nowrap',
4328
4344
  textOverflow: 'ellipsis',
4329
4345
  overflow: 'hidden'
4330
4346
  }
@@ -4373,6 +4389,7 @@ var TimeSeriesSelectList = function TimeSeriesSelectList(_ref) {
4373
4389
  alignItems: 'center'
4374
4390
  },
4375
4391
  children: [jsx(Grid, {
4392
+ size: "grow",
4376
4393
  children: jsx(Box, {
4377
4394
  sx: fontStyling(),
4378
4395
  children: jsx(SearchHighlight, {
@@ -5544,7 +5561,7 @@ var ParameterInfoDialog = function ParameterInfoDialog(_ref) {
5544
5561
  onClose: onClose,
5545
5562
  startSize: {
5546
5563
  width: 288,
5547
- height: dialogHeight || 650
5564
+ height: dialogHeight != null ? dialogHeight : 650
5548
5565
  },
5549
5566
  startPosition: {
5550
5567
  top: 150,
@@ -5580,8 +5597,14 @@ var ParameterInfoDialog = function ParameterInfoDialog(_ref) {
5580
5597
  value: relatedService.type
5581
5598
  })]
5582
5599
  }), jsx(ParameterInfoText$1, {
5583
- label: t('timeseries-collection'),
5600
+ label: t('timeseries-collection-id'),
5584
5601
  value: currentParameterInfo.collectionId
5602
+ }), (collectionInformation == null ? void 0 : collectionInformation.collectionTitle) && (collectionInformation == null ? void 0 : collectionInformation.collectionTitle) !== '' && jsx(ParameterInfoText$1, {
5603
+ label: t('timeseries-collection-title'),
5604
+ value: collectionInformation == null ? void 0 : collectionInformation.collectionTitle
5605
+ }), (collectionInformation == null ? void 0 : collectionInformation.collectionDescription) && (collectionInformation == null ? void 0 : collectionInformation.collectionDescription) !== '' && jsx(ParameterInfoText$1, {
5606
+ label: t('timeseries-collection-description'),
5607
+ value: collectionInformation == null ? void 0 : collectionInformation.collectionDescription
5585
5608
  }), jsx(QueryInfoElement, {
5586
5609
  queryInformation: collectionInformation,
5587
5610
  isLoadingCollectionInformation: isLoadingCollectionInformation
@@ -7807,7 +7830,26 @@ var LocationListItem = function LocationListItem(_ref) {
7807
7830
  var theme = useTheme();
7808
7831
  var _useTimeseriesTransla = useTimeseriesTranslation();
7809
7832
  _useTimeseriesTransla.t;
7833
+ // Wrapper ref used for hover detection AND restoring keyboard focus after selection.
7810
7834
  var mouseMoveRef = React__default.useRef(null);
7835
+ // Listen for global selection events so we can re-focus the item that was activated via keyboard.
7836
+ React__default.useEffect(function () {
7837
+ var handleSelection = function handleSelection(e) {
7838
+ var _custom$detail;
7839
+ var custom = e;
7840
+ if (((_custom$detail = custom.detail) == null ? void 0 : _custom$detail.location) === location && mouseMoveRef.current) {
7841
+ // Delay to allow parent state updates / virtualization re-render.
7842
+ requestAnimationFrame(function () {
7843
+ var _mouseMoveRef$current;
7844
+ (_mouseMoveRef$current = mouseMoveRef.current) == null || _mouseMoveRef$current.focus();
7845
+ });
7846
+ }
7847
+ };
7848
+ window.addEventListener('location-list-selection', handleSelection);
7849
+ return function () {
7850
+ window.removeEventListener('location-list-selection', handleSelection);
7851
+ };
7852
+ }, [location]);
7811
7853
  var textContainerRef = React__default.useRef(null);
7812
7854
  var _React$useState = React__default.useState(false),
7813
7855
  isHovering = _React$useState[0],
@@ -7843,6 +7885,23 @@ var LocationListItem = function LocationListItem(_ref) {
7843
7885
  setTextContainerWidth(textContainerRef.current.clientWidth);
7844
7886
  }
7845
7887
  }, [textContainerRef]);
7888
+ // If selection state changes and this item is now the selected one, ensure it regains focus.
7889
+ React__default.useEffect(function () {
7890
+ if (selectedLocation === location && mouseMoveRef.current) {
7891
+ // If focus already on this item, do nothing.
7892
+ if (mouseMoveRef.current === document.activeElement) {
7893
+ return;
7894
+ }
7895
+ // If focus is outside the list item, re-focus after render settles.
7896
+ // Use double rAF for virtualization/layout stability.
7897
+ requestAnimationFrame(function () {
7898
+ requestAnimationFrame(function () {
7899
+ var _mouseMoveRef$current2;
7900
+ (_mouseMoveRef$current2 = mouseMoveRef.current) == null || _mouseMoveRef$current2.focus();
7901
+ });
7902
+ });
7903
+ }
7904
+ }, [selectedLocation, location]);
7846
7905
  return jsx(OverflowTooltip, {
7847
7906
  text: location,
7848
7907
  textContainerWidth: textContainerWidth,
@@ -7852,6 +7911,29 @@ var LocationListItem = function LocationListItem(_ref) {
7852
7911
  onMouseLeave: handleMouseLeave,
7853
7912
  onFocus: handleMouseEnter,
7854
7913
  onBlur: handleMouseLeave,
7914
+ tabIndex: 0,
7915
+ role: "option",
7916
+ "aria-selected": selectedLocation === location,
7917
+ onKeyDown: function onKeyDown(e) {
7918
+ if (isDisabled) {
7919
+ return;
7920
+ }
7921
+ if (e.key === 'Enter' || e.key === ' ') {
7922
+ e.preventDefault();
7923
+ handleChange(location);
7924
+ // Dispatch custom event to remove focus styling from outer list container
7925
+ window.dispatchEvent(new CustomEvent('location-list-selection', {
7926
+ detail: {
7927
+ location: location
7928
+ }
7929
+ }));
7930
+ // Explicitly retain focus immediately (backup before effect runs)
7931
+ requestAnimationFrame(function () {
7932
+ var _mouseMoveRef$current3;
7933
+ (_mouseMoveRef$current3 = mouseMoveRef.current) == null || _mouseMoveRef$current3.focus();
7934
+ });
7935
+ }
7936
+ },
7855
7937
  children: jsxs(ListItem, Object.assign({
7856
7938
  "data-testid": "locationDropdownListItem",
7857
7939
  role: "listitem"
@@ -7886,7 +7968,19 @@ var LocationListItem = function LocationListItem(_ref) {
7886
7968
  backgroundColor: 'transparent'
7887
7969
  }
7888
7970
  },
7889
- disabled: isDisabled
7971
+ disabled: isDisabled,
7972
+ // Remove from tab order so row wrapper is the single focus target
7973
+ tabIndex: -1,
7974
+ onChange: function onChange() {
7975
+ if (!isDisabled) {
7976
+ handleChange(location);
7977
+ window.dispatchEvent(new CustomEvent('location-list-selection', {
7978
+ detail: {
7979
+ location: location
7980
+ }
7981
+ }));
7982
+ }
7983
+ }
7890
7984
  })
7891
7985
  }), jsx(Typography, {
7892
7986
  ref: textContainerRef,
@@ -7926,6 +8020,121 @@ var renderRow = function renderRow(features, handleChange, selectedLocation, isD
7926
8020
  };
7927
8021
  };
7928
8022
 
8023
+ var FocusableListOuter = /*#__PURE__*/React.forwardRef(function (props, ref) {
8024
+ // Track last interaction modality to conditionally show focus ring only for keyboard navigation.
8025
+ var lastInteractionWasKeyboard = React.useRef(false);
8026
+ React.useEffect(function () {
8027
+ var handleKey = function handleKey(e) {
8028
+ // Ignore modifier-only keys; any navigation key sets keyboard modality.
8029
+ if (e.key) {
8030
+ lastInteractionWasKeyboard.current = true;
8031
+ }
8032
+ };
8033
+ var handlePointer = function handlePointer() {
8034
+ lastInteractionWasKeyboard.current = false;
8035
+ };
8036
+ window.addEventListener('keydown', handleKey, {
8037
+ capture: true
8038
+ });
8039
+ window.addEventListener('mousedown', handlePointer, {
8040
+ capture: true
8041
+ });
8042
+ window.addEventListener('pointerdown', handlePointer, {
8043
+ capture: true
8044
+ });
8045
+ window.addEventListener('touchstart', handlePointer, {
8046
+ capture: true
8047
+ });
8048
+ // Listen for custom selection event to remove focus styling after keyboard activation.
8049
+ var handleSelection = function handleSelection() {
8050
+ setIsFocusVisible(false);
8051
+ };
8052
+ window.addEventListener('location-list-selection', handleSelection);
8053
+ return function () {
8054
+ window.removeEventListener('keydown', handleKey, {
8055
+ capture: true
8056
+ });
8057
+ window.removeEventListener('mousedown', handlePointer, {
8058
+ capture: true
8059
+ });
8060
+ window.removeEventListener('pointerdown', handlePointer, {
8061
+ capture: true
8062
+ });
8063
+ window.removeEventListener('touchstart', handlePointer, {
8064
+ capture: true
8065
+ });
8066
+ window.removeEventListener('location-list-selection', handleSelection);
8067
+ };
8068
+ }, []);
8069
+ var handleKeyDown = function handleKeyDown(e) {
8070
+ var el = ref && typeof ref === 'object' && 'current' in ref ? ref.current : null;
8071
+ if (!el) {
8072
+ return;
8073
+ }
8074
+ var lineHeight = 40; // matches itemSize
8075
+ var pageHeight = el.clientHeight;
8076
+ switch (e.key) {
8077
+ case 'ArrowDown':
8078
+ e.preventDefault();
8079
+ el.scrollTop += lineHeight;
8080
+ break;
8081
+ case 'ArrowUp':
8082
+ e.preventDefault();
8083
+ el.scrollTop -= lineHeight;
8084
+ break;
8085
+ case 'PageDown':
8086
+ e.preventDefault();
8087
+ el.scrollTop += pageHeight;
8088
+ break;
8089
+ case 'PageUp':
8090
+ e.preventDefault();
8091
+ el.scrollTop -= pageHeight;
8092
+ break;
8093
+ case 'Home':
8094
+ e.preventDefault();
8095
+ el.scrollTop = 0;
8096
+ break;
8097
+ case 'End':
8098
+ e.preventDefault();
8099
+ el.scrollTop = el.scrollHeight;
8100
+ break;
8101
+ }
8102
+ };
8103
+ // merge any existing style prop with our outline override
8104
+ var mergedStyle = Object.assign({}, props.style, {
8105
+ outline: 'none',
8106
+ transition: 'box-shadow 120ms ease, background-color 120ms ease'
8107
+ });
8108
+ var _React$useState = React.useState(false),
8109
+ isFocusVisible = _React$useState[0],
8110
+ setIsFocusVisible = _React$useState[1];
8111
+ var onFocus = function onFocus() {
8112
+ if (lastInteractionWasKeyboard.current) {
8113
+ setIsFocusVisible(true);
8114
+ }
8115
+ };
8116
+ var onBlur = function onBlur() {
8117
+ return setIsFocusVisible(false);
8118
+ };
8119
+ if (isFocusVisible) {
8120
+ mergedStyle.boxShadow = '0 0 0 2px var(--focus-ring-color, #1976d2)';
8121
+ mergedStyle.backgroundColor = 'rgba(25,118,210,0.06)';
8122
+ }
8123
+ return jsx("div", Object.assign({}, props, {
8124
+ ref: ref,
8125
+ tabIndex: 0,
8126
+ role: "listbox",
8127
+ "aria-label": "Locations list",
8128
+ onKeyDown: handleKeyDown,
8129
+ onFocus: onFocus,
8130
+ onBlur: onBlur,
8131
+ // Remove focus styling immediately on mouse interaction
8132
+ onMouseDown: function onMouseDown() {
8133
+ setIsFocusVisible(false);
8134
+ },
8135
+ style: mergedStyle
8136
+ }));
8137
+ });
7929
8138
  var LocationDropdown = function LocationDropdown(_ref) {
7930
8139
  var _combinedSelectedLoca;
7931
8140
  var geoJson = _ref.geoJson,
@@ -7933,10 +8142,11 @@ var LocationDropdown = function LocationDropdown(_ref) {
7933
8142
  handleChange = _ref.handleChange,
7934
8143
  selectedLocation = _ref.selectedLocation,
7935
8144
  _ref$isDisabled = _ref.isDisabled,
7936
- isDisabled = _ref$isDisabled === void 0 ? false : _ref$isDisabled;
7937
- var _React$useState = React.useState(''),
7938
- searchTerm = _React$useState[0],
7939
- setSearchTerm = _React$useState[1];
8145
+ isDisabled = _ref$isDisabled === void 0 ? false : _ref$isDisabled,
8146
+ defaultExpanded = _ref.defaultExpanded;
8147
+ var _React$useState2 = React.useState(''),
8148
+ searchTerm = _React$useState2[0],
8149
+ setSearchTerm = _React$useState2[1];
7940
8150
  var combinedSelectedLocation = geoJson == null ? void 0 : geoJson.features.find(function (feature) {
7941
8151
  var _feature$properties;
7942
8152
  return selectedLocation === ((_feature$properties = feature.properties) == null ? void 0 : _feature$properties.name);
@@ -7955,8 +8165,11 @@ var LocationDropdown = function LocationDropdown(_ref) {
7955
8165
  });
7956
8166
  });
7957
8167
  }, [searchTerm, splitGeoJson]);
8168
+ var listContainerRef = React.useRef(null);
8169
+ var heightObserver = useResizeObserver(listContainerRef, filteredResults);
7958
8170
  return jsx(Box, {
7959
8171
  "data-testid": "locationDropdownContainer",
8172
+ ref: listContainerRef,
7960
8173
  sx: {
7961
8174
  width: '100%',
7962
8175
  height: '100%',
@@ -8000,16 +8213,17 @@ var LocationDropdown = function LocationDropdown(_ref) {
8000
8213
  title: collectionName,
8001
8214
  title2: serviceName,
8002
8215
  amountOfItems: collectionGeoJson.features.length || 0,
8003
- defaultExpanded: false,
8216
+ defaultExpanded: defaultExpanded || false,
8004
8217
  sx: {
8005
8218
  mx: 1,
8006
8219
  mb: 1
8007
8220
  },
8008
8221
  children: jsx(FixedSizeList, {
8009
- height: 350,
8222
+ height: heightObserver,
8010
8223
  width: "100%",
8011
8224
  itemSize: 40,
8012
8225
  itemCount: collectionGeoJson.features.length,
8226
+ outerElementType: FocusableListOuter,
8013
8227
  children: renderRow(collectionGeoJson.features, handleChange, collectionSelectedLocation == null || (_collectionSelectedLo = collectionSelectedLocation.properties) == null ? void 0 : _collectionSelectedLo.name, isDisabled)
8014
8228
  })
8015
8229
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/time-series",
3
- "version": "14.0.1",
3
+ "version": "14.2.1",
4
4
  "description": "GeoWeb time-series library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -9,26 +9,26 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "immer": "^10.0.3",
12
- "@opengeoweb/shared": "14.0.1",
12
+ "@opengeoweb/shared": "14.2.1",
13
13
  "@reduxjs/toolkit": "^2.6.1",
14
14
  "react-redux": "^9.2.0",
15
- "@opengeoweb/webmap": "14.0.1",
16
- "@opengeoweb/webmap-react": "14.0.1",
15
+ "@opengeoweb/webmap": "14.2.1",
16
+ "@opengeoweb/webmap-react": "14.2.1",
17
17
  "lodash": "^4.17.21",
18
- "@opengeoweb/api": "14.0.1",
19
- "@opengeoweb/theme": "14.0.1",
20
- "@opengeoweb/store": "14.0.1",
18
+ "@opengeoweb/api": "14.2.1",
19
+ "@opengeoweb/theme": "14.2.1",
20
+ "@opengeoweb/store": "14.2.1",
21
21
  "echarts-for-react": "^3.0.2",
22
22
  "echarts": "^5.4.3",
23
23
  "react-sortablejs": "^6.1.4",
24
- "@opengeoweb/snackbar": "14.0.1",
24
+ "@opengeoweb/snackbar": "14.2.1",
25
25
  "react-window": "^1.8.10",
26
26
  "react-virtualized-auto-sizer": "^1.0.21",
27
27
  "msw": "^2.7.3",
28
28
  "i18next": "^25.0.1",
29
29
  "react-i18next": "^15.1.1",
30
- "@opengeoweb/core": "14.0.1",
31
- "@opengeoweb/form-fields": "14.0.1",
30
+ "@opengeoweb/core": "14.2.1",
31
+ "@opengeoweb/form-fields": "14.2.1",
32
32
  "@mui/material": "^7.0.1",
33
33
  "react-hook-form": "^7.61.1",
34
34
  "uuid": "^10.0.0",
@@ -7,6 +7,7 @@ interface LocationDropdownProps {
7
7
  selectedLocation: string;
8
8
  geoJson?: FeatureCollection<GeometryObject>;
9
9
  isDisabled?: boolean;
10
+ defaultExpanded?: boolean;
10
11
  }
11
12
  export declare const LocationDropdown: React.FC<LocationDropdownProps>;
12
13
  export {};
@@ -6,4 +6,6 @@ type Story = StoryObj<typeof LocationDropdown>;
6
6
  export declare const Component: Story;
7
7
  export declare const ComponentDark: Story;
8
8
  export declare const LocationDropdownDemoLight: Story;
9
+ export declare const LocationDropdownExpandedDemoLight: Story;
9
10
  export declare const LocationDropdownDemoDark: Story;
11
+ export declare const LocationDropdownExpandedDemoDark: Story;
@@ -3,6 +3,8 @@ import { ServiceFilterChipsObjectEntities } from '../../store/types';
3
3
  import { CustomDimension, EDRDataQueriesObject, EDRExtentObject, SelectParameter } from '../TimeSeries/types';
4
4
  export interface TimeseriesCollectionDetail {
5
5
  collectionId: string;
6
+ collectionTitle?: string;
7
+ collectionDescription?: string;
6
8
  serviceId: string;
7
9
  serviceName: string;
8
10
  parameters: SelectParameter[];