@opengeoweb/time-slider 20.0.0 → 20.0.1-nightly-20261002

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/dist/index.esm.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { Grid, useTheme, Box, Typography, Menu, MenuItem, CardHeader, InputAdornment, RadioGroup, FormControlLabel, Radio, Divider, FormHelperText, FormControl, TextField, Select, Popper, Paper, Stack, ClickAwayListener } from '@mui/material';
2
+ import { Grid, useTheme, Box, InputAdornment, Typography, RadioGroup, FormControlLabel, Radio, Divider, FormHelperText, Popover, Menu, MenuItem, CardHeader, FormControl, TextField, Select, Popper, Paper, Stack, ClickAwayListener } from '@mui/material';
3
3
  import React, { useState, useRef, useEffect, useCallback, useContext, useMemo, createElement } from 'react';
4
4
  import { dateUtils, isTargetInteractiveInput, isDialogFocused, safelyInitI18nForTestsAndStories, CustomIconButton, CustomTooltip, CanvasComponent, NumberWithUnitField, DateTimePicker, renderCounter, pickerDateToUTC, CustomToggleButton } from '@opengeoweb/shared';
5
5
  import { debounce, throttle } from 'lodash';
6
- import { ChevronDown, ChevronUp, dragHandlePath, Edit, Animation, Clock, CalendarToday, AutoUpdateActive, AutoUpdateInActive, StepForward, StepBackward, Now, Pause, Play, Speed, FastForward, Options, ThemeWrapper, lightTheme, darkTheme } from '@opengeoweb/theme';
6
+ import { ChevronDown, ChevronUp, dragHandlePath, Clock, CalendarToday, Animation, AutoUpdateActive, AutoUpdateInActive, StepForward, StepBackward, Now, Pause, Play, Edit, Speed, FastForward, Options, ThemeWrapper, lightTheme, darkTheme } from '@opengeoweb/theme';
7
7
  import i18n from 'i18next';
8
8
  import { useTranslation, I18nextProvider } from 'react-i18next';
9
9
  import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns';
@@ -15,7 +15,6 @@ import { PickerDay } from '@mui/x-date-pickers/PickerDay';
15
15
  var en = {
16
16
  "timeslider-auto": "Auto",
17
17
  "timeslider-length": "length",
18
- "timeslider-animation": "Animation",
19
18
  "timeslider-auto-update-on": "Auto update on",
20
19
  "timeslider-auto-update-off": "Auto update off",
21
20
  "timeslider-backward": "Backward",
@@ -96,7 +95,6 @@ var en = {
96
95
  var fi = {
97
96
  "timeslider-auto": "Auto",
98
97
  "timeslider-length": "pituus",
99
- "timeslider-animation": "Animaatio",
100
98
  "timeslider-auto-update-on": "Auto update päällä",
101
99
  "timeslider-auto-update-off": "Auto update pois päältä",
102
100
  "timeslider-backward": "Taaksepäin",
@@ -175,7 +173,6 @@ var fi = {
175
173
  var no = {
176
174
  "timeslider-auto": "Auto",
177
175
  "timeslider-length": "lengde",
178
- "timeslider-animation": "Animasjon",
179
176
  "timeslider-auto-update-on": "Autooppdatering på",
180
177
  "timeslider-auto-update-off": "Autooppdatering av",
181
178
  "timeslider-backward": "Bakover",
@@ -254,7 +251,6 @@ var no = {
254
251
  var nl = {
255
252
  "timeslider-auto": "Auto",
256
253
  "timeslider-length": "lengte",
257
- "timeslider-animation": "Animatie",
258
254
  "timeslider-auto-update-on": "Auto update aan",
259
255
  "timeslider-auto-update-off": "Auto update uit",
260
256
  "timeslider-backward": "Achteruit",
@@ -390,17 +386,6 @@ var Scale;
390
386
  Scale[Scale["Months3"] = 8] = "Months3";
391
387
  Scale[Scale["Year"] = 9] = "Year";
392
388
  })(Scale || (Scale = {}));
393
- var AnimationLength;
394
- (function (AnimationLength) {
395
- AnimationLength[AnimationLength["Minutes15"] = 15] = "Minutes15";
396
- AnimationLength[AnimationLength["Minutes30"] = 30] = "Minutes30";
397
- AnimationLength[AnimationLength["Hours1"] = 60] = "Hours1";
398
- AnimationLength[AnimationLength["Hours2"] = 120] = "Hours2";
399
- AnimationLength[AnimationLength["Hours3"] = 180] = "Hours3";
400
- AnimationLength[AnimationLength["Hours6"] = 360] = "Hours6";
401
- AnimationLength[AnimationLength["Hours12"] = 720] = "Hours12";
402
- AnimationLength[AnimationLength["Hours24"] = 1440] = "Hours24";
403
- })(AnimationLength || (AnimationLength = {}));
404
389
  /**
405
390
  * In this Map collection all fundamental scales
406
391
  * (Scale.Minutes5 ... Scale.Year) are mapped with their corresponding
@@ -433,22 +418,6 @@ var timestampToPixel = function timestampToPixel(timestamp, centerTime, widthPx,
433
418
  var pixelToTimestamp = function pixelToTimestamp(timePx, centerTime, widthPx, secondsPerPx) {
434
419
  return centerTime - (widthPx / 2 - timePx) * secondsPerPx;
435
420
  };
436
- /**
437
- * This function creates and returns an array where there is secondsPerPx
438
- * number of the data set added for a data scale (secondsPerPx)
439
- * to secondsPerPxToScale.
440
- * @param secondsPerPx
441
- * @returns an array containing secondsPerPx numbers.
442
- * The secondsPerPx number for a data scale is included
443
- * contrary to secondsPerPxToScale.
444
- */
445
- var secondsPerPxValues = function secondsPerPxValues(secondsPerPx) {
446
- var sortedSecondsPerPx = [].concat(secondsPerPxToScale).map(function (value) {
447
- return Number(value[0]);
448
- });
449
- var full = [].concat(sortedSecondsPerPx, [secondsPerPx]);
450
- return full;
451
- };
452
421
  var secondsPerPxFromCanvasWidth = function secondsPerPxFromCanvasWidth(canvasWidth, spanInSeconds) {
453
422
  if (canvasWidth <= 0 || spanInSeconds <= 0) {
454
423
  return undefined;
@@ -459,14 +428,6 @@ var getNewCenterOfFixedPointZoom = function getNewCenterOfFixedPointZoom(fixedTi
459
428
  var centerToFixedPointPx = (fixedTimePoint - oldCenterTime) / oldSecondsPerPx;
460
429
  return fixedTimePoint - centerToFixedPointPx * newSecondsPerPx;
461
430
  };
462
- /**
463
- * Move the time slider such that a given time point (timePoint)
464
- * is at a given pixel width (timePointPx).
465
- * @returns a new center time for the resulting position
466
- */
467
- var moveRelativeToTimePoint = function moveRelativeToTimePoint(timePoint, timePointPx, secondsPerPx, canvasWidth) {
468
- return timePoint - secondsPerPx * (timePointPx - canvasWidth / 2);
469
- };
470
431
  /** This reusable custom hook tells whether given pointer event targets current canvas node.
471
432
  * Example:
472
433
  *
@@ -583,14 +544,15 @@ function getLargestTimeUnit(minutes) {
583
544
  }
584
545
  return TimeInMinutes.MINUTE;
585
546
  }
586
- // For a data scale an appropriate fundamental scale is chosen
587
- // depending on secondsPerPx of data. When a time range of
588
- // data is for example many months. the fundamental scale Scale.Year
589
- // is chosen here and the legend of the time slider is drawn in the
590
- // same as if otiginally a year scale would have been chose.
591
- //
592
- // If a time range of data is a few weeks, Scale.Month is chosen. So, all
593
- // the time range of data can be shown in the window.
547
+ /** For a data scale an appropriate fundamental scale is chosen
548
+ * depending on secondsPerPx of data. When a time range of
549
+ * data is for example many months. the fundamental scale Scale.Year
550
+ * is chosen here and the legend of the time slider is drawn in the
551
+ * same as if originally a year scale would have been chose.
552
+ *
553
+ * If a time range of data is a few weeks, Scale.Month is chosen. So, all
554
+ * the time range of data can be shown in the window.
555
+ */
594
556
  var getFundamentalScale = function getFundamentalScale(secondsPerPx) {
595
557
  if (secondsPerPx >= scaleToSecondsPerPx[Scale.Month]) {
596
558
  return Scale.Year;
@@ -631,17 +593,6 @@ var getScaleFromSpan = function getScaleFromSpan(span) {
631
593
  }
632
594
  return sortedScales[0][1];
633
595
  };
634
- var calculateMouseTimeAndCheckBounds = function calculateMouseTimeAndCheckBounds(x, startAnimationPosition, endAnimationPosition, DRAG_AREA_WIDTH, pixelsBetweenViewportAndTimesliderOnLeft, centerTime, canvasWidth, secondsPerPx, tooltipPosition) {
635
- var mousePosition = startAnimationPosition + x;
636
- var startAnimationMaxPosition = endAnimationPosition - DRAG_AREA_WIDTH * 2;
637
- var isDraggingWithinBounds = mousePosition >= startAnimationMaxPosition || mousePosition <= 0;
638
- if (isDraggingWithinBounds) {
639
- return undefined;
640
- }
641
- // eslint-disable-next-line no-param-reassign
642
- tooltipPosition.current = pixelsBetweenViewportAndTimesliderOnLeft && mousePosition + pixelsBetweenViewportAndTimesliderOnLeft;
643
- return pixelToTimestamp(mousePosition, centerTime, canvasWidth, secondsPerPx);
644
- };
645
596
  // default values
646
597
  var defaultAnimationDelayAtStart = 250;
647
598
  var defaultDelay = 1000; // [ms]
@@ -668,30 +619,12 @@ var roundWithTimeStep = function roundWithTimeStep(unixTime, timeStep, type) {
668
619
  var getSpeedDelay = function getSpeedDelay(speedFactor) {
669
620
  return defaultDelay / speedFactor;
670
621
  };
671
- var timeSliderLegendDefaultProps = {
672
- timeSliderWidth: 100,
673
- centerTime: dateUtils.unix(dateUtils.set(dateUtils.utc(), {
674
- hours: 12,
675
- minutes: 0,
676
- seconds: 0,
677
- milliseconds: 0
678
- })),
679
- secondsPerPx: defaultSecondsPerPx,
680
- selectedTime: dateUtils.unix(dateUtils.set(dateUtils.utc(), {
681
- hours: 12,
682
- minutes: 0,
683
- seconds: 0,
684
- milliseconds: 0
685
- }))
686
- };
687
622
 
688
623
  var timeSliderUtils = /*#__PURE__*/Object.freeze({
689
624
  __proto__: null,
690
625
  AUTO_MOVE_AREA_PADDING: AUTO_MOVE_AREA_PADDING,
691
- get AnimationLength () { return AnimationLength; },
692
626
  get Scale () { return Scale; },
693
627
  get TimeInMinutes () { return TimeInMinutes; },
694
- calculateMouseTimeAndCheckBounds: calculateMouseTimeAndCheckBounds,
695
628
  defaultAnimationDelayAtStart: defaultAnimationDelayAtStart,
696
629
  defaultDelay: defaultDelay,
697
630
  defaultSecondsPerPx: defaultSecondsPerPx,
@@ -704,18 +637,15 @@ var timeSliderUtils = /*#__PURE__*/Object.freeze({
704
637
  getScaleFromSpan: getScaleFromSpan,
705
638
  getSpeedDelay: getSpeedDelay,
706
639
  minutesToDescribedDuration: minutesToDescribedDuration,
707
- moveRelativeToTimePoint: moveRelativeToTimePoint,
708
640
  pixelToTimestamp: pixelToTimestamp,
709
641
  roundWithTimeStep: roundWithTimeStep,
710
642
  scaleToSecondsPerPx: scaleToSecondsPerPx,
711
643
  scalingCoefficient: scalingCoefficient,
712
644
  secondsPerPxFromCanvasWidth: secondsPerPxFromCanvasWidth,
713
645
  secondsPerPxToScale: secondsPerPxToScale,
714
- secondsPerPxValues: secondsPerPxValues,
715
646
  spanToScale: spanToScale,
716
647
  speedFactors: speedFactors,
717
648
  timeBoxGeom: timeBoxGeom,
718
- timeSliderLegendDefaultProps: timeSliderLegendDefaultProps,
719
649
  timestampToPixel: timestampToPixel,
720
650
  timestampToPixelEdges: timestampToPixelEdges,
721
651
  useCanvasTarget: useCanvasTarget
@@ -905,78 +835,500 @@ var HideSliderButtonComponent = function HideSliderButtonComponent(_ref) {
905
835
  };
906
836
  var HideSliderButton = /*#__PURE__*/React.memo(HideSliderButtonComponent);
907
837
 
908
- /* *
909
- * Licensed under the Apache License, Version 2.0 (the "License");
910
- * you may not use this file except in compliance with the License.
911
- * You may obtain a copy of the License at
912
- *
913
- * http://www.apache.org/licenses/LICENSE-2.0
914
- *
915
- * Unless required by applicable law or agreed to in writing, software
916
- * distributed under the License is distributed on an "AS IS" BASIS,
917
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
918
- * See the License for the specific language governing permissions and
919
- * limitations under the License.
920
- *
921
- * Copyright 2022 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
922
- * Copyright 2022 - Finnish Meteorological Institute (FMI)
923
- * Copyright 2024 - The Norwegian Meteorological Institute (MET Norway)
924
- * */
838
+ var _modeTranslationKey;
839
+ var AnimationLengthMode;
840
+ (function (AnimationLengthMode) {
841
+ AnimationLengthMode["Auto"] = "auto";
842
+ AnimationLengthMode["Now"] = "now";
843
+ AnimationLengthMode["RecurringPeriod"] = "recurringPeriod";
844
+ AnimationLengthMode["ReferenceTime"] = "referenceTime";
845
+ AnimationLengthMode["Exact"] = "exact";
846
+ })(AnimationLengthMode || (AnimationLengthMode = {}));
847
+ var timeUnits = ['years', 'months', 'weeks', 'days', 'hours', 'minutes'];
848
+ var maxOffsetByUnit = {
849
+ minutes: 999,
850
+ hours: 999,
851
+ days: 365,
852
+ weeks: 99,
853
+ months: 99,
854
+ years: 99
855
+ };
856
+ /** Shared by the menu and the slider handles, so neither can reach an offset the other rejects. */
857
+ var clampOffset = function clampOffset(_ref) {
858
+ var value = _ref.value,
859
+ unit = _ref.unit;
860
+ var maxOffset = maxOffsetByUnit[unit];
861
+ return {
862
+ value: Math.max(-maxOffset, Math.min(maxOffset, value)),
863
+ unit: unit
864
+ };
865
+ };
866
+ var defaultStartOffset = {
867
+ value: 0,
868
+ unit: 'hours'
869
+ };
870
+ var defaultEndOffset = {
871
+ value: 2,
872
+ unit: 'hours'
873
+ };
874
+ var defaultOffsetSelections = {
875
+ startOffset: defaultStartOffset,
876
+ endOffset: defaultEndOffset
877
+ };
878
+ var unitTranslationKey = {
879
+ years: 'timeslider-unit-years',
880
+ months: 'timeslider-unit-months',
881
+ weeks: 'timeslider-unit-weeks',
882
+ days: 'timeslider-unit-days',
883
+ hours: 'timeslider-unit-hours',
884
+ minutes: 'timeslider-unit-minutes'
885
+ };
886
+ var shortUnitTranslationKey = {
887
+ years: 'timeslider-unit-short-years',
888
+ months: 'timeslider-unit-short-months',
889
+ weeks: 'timeslider-unit-short-weeks',
890
+ days: 'timeslider-unit-short-days',
891
+ hours: 'timeslider-unit-short-hours',
892
+ minutes: 'timeslider-unit-short-minutes'
893
+ };
894
+ var modeTranslationKey = (_modeTranslationKey = {}, _modeTranslationKey[AnimationLengthMode.Auto] = 'timeslider-animation-length-auto', _modeTranslationKey[AnimationLengthMode.Now] = 'timeslider-animation-length-now', _modeTranslationKey[AnimationLengthMode.RecurringPeriod] = 'timeslider-animation-length-recurring-period', _modeTranslationKey[AnimationLengthMode.ReferenceTime] = 'timeslider-animation-length-reference-time', _modeTranslationKey[AnimationLengthMode.Exact] = 'timeslider-animation-length-exact', _modeTranslationKey);
895
+ var offsetToDuration = function offsetToDuration(_ref2) {
896
+ var _ref3;
897
+ var value = _ref2.value,
898
+ unit = _ref2.unit;
899
+ return _ref3 = {}, _ref3[unit] = value, _ref3;
900
+ };
901
+ var toAnimationTime = function toAnimationTime(date) {
902
+ return dateUtils.dateToString(date, dateUtils.DATE_FORMAT_UTC);
903
+ };
904
+ /* The clock in the menu offers minutes in these increments, so an anchor must land on one. */
905
+ var ANCHOR_MINUTE_STEP = 5;
906
+ var padded = function padded(value) {
907
+ return String(value).padStart(2, '0');
908
+ };
925
909
  /**
926
- * @param x The pin location
927
- * @param selectedTimePx The selected px
928
- * @param width The default width of a hoverable icon (usually 24px)
929
- * @returns
910
+ * Reads the UTC fields directly: `dateUtils.dateToString` formats a local twin of the
911
+ * date, which a DST gap shifts by an hour.
930
912
  */
931
- var isSelectedTimeIconArea = function isSelectedTimeIconArea(x, selectedTimePx, width) {
932
- return x >= selectedTimePx && x <= selectedTimePx + width / 2;
913
+ var toAnchorTime = function toAnchorTime(date) {
914
+ var minutes = date.getUTCMinutes();
915
+ return padded(date.getUTCHours()) + ":" + padded(minutes - minutes % ANCHOR_MINUTE_STEP);
916
+ };
917
+ /* Modes that count their offsets from an anchor instant rather than a stored range. */
918
+ var anchoredModes = [AnimationLengthMode.Now, AnimationLengthMode.RecurringPeriod, AnimationLengthMode.ReferenceTime];
919
+ var isAnchoredMode = function isAnchoredMode(mode) {
920
+ return !!mode && anchoredModes.includes(mode);
921
+ };
922
+ /** Narrows the mode so the compiler can require an anchor time alongside it. */
923
+ var isRecurringPeriodMode = function isRecurringPeriodMode(mode) {
924
+ return mode === AnimationLengthMode.RecurringPeriod;
925
+ };
926
+ var ANCHOR_TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
927
+ var isValidAnchorTime = function isValidAnchorTime(anchorTime) {
928
+ return !!anchorTime && ANCHOR_TIME_PATTERN.test(anchorTime);
929
+ };
930
+ var selectionsForMode = function selectionsForMode(setting) {
931
+ if (setting.mode === AnimationLengthMode.Now) {
932
+ return setting.now;
933
+ }
934
+ if (setting.mode === AnimationLengthMode.RecurringPeriod) {
935
+ return setting.recurringPeriod;
936
+ }
937
+ if (setting.mode === AnimationLengthMode.ReferenceTime) {
938
+ return setting.referenceTime;
939
+ }
940
+ return defaultOffsetSelections;
941
+ };
942
+ /** The offsets of whichever mode is selected; the modes without offsets use the defaults. */
943
+ var activeOffsets = function activeOffsets(setting) {
944
+ var _selectionsForMode = selectionsForMode(setting),
945
+ startOffset = _selectionsForMode.startOffset,
946
+ endOffset = _selectionsForMode.endOffset;
947
+ return {
948
+ startOffset: startOffset,
949
+ endOffset: endOffset
950
+ };
951
+ };
952
+ /** The unit the offsets are edited and dragged in. */
953
+ var activeEditUnit = function activeEditUnit(setting) {
954
+ return selectionsForMode(setting).startOffset.unit;
955
+ };
956
+ var defaultRecurringPeriodSelections = function defaultRecurringPeriodSelections(now) {
957
+ if (now === void 0) {
958
+ now = dateUtils.utc();
959
+ }
960
+ return Object.assign({}, defaultOffsetSelections, {
961
+ anchorTime: toAnchorTime(now)
962
+ });
963
+ };
964
+ /** Presets carry settings the compiler cannot vouch for, so the selected mode is given the slice it needs. */
965
+ var withModeSelections = function withModeSelections(setting, now) {
966
+ if (now === void 0) {
967
+ now = dateUtils.utc();
968
+ }
969
+ if (setting.mode === AnimationLengthMode.Now) {
970
+ return Object.assign({}, setting, {
971
+ now: Object.assign({}, defaultOffsetSelections, setting.now)
972
+ });
973
+ }
974
+ if (setting.mode === AnimationLengthMode.RecurringPeriod) {
975
+ var merged = Object.assign({}, defaultRecurringPeriodSelections(now), setting.recurringPeriod);
976
+ return Object.assign({}, setting, {
977
+ recurringPeriod: isValidAnchorTime(merged.anchorTime) ? merged : Object.assign({}, merged, {
978
+ anchorTime: toAnchorTime(now)
979
+ })
980
+ });
981
+ }
982
+ if (setting.mode === AnimationLengthMode.Exact) {
983
+ return Object.assign({}, setting, {
984
+ exact: Object.assign({}, defaultExactSelections(now), setting.exact)
985
+ });
986
+ }
987
+ return setting;
933
988
  };
934
989
  /**
935
- * @param x The pin location
936
- * @param selectedTimePx The selected px
937
- * @param width The default width of a hoverable icon (usually 24px)
938
- * @returns
990
+ * A recurring period anchors on its time of day within the current UTC date, so the
991
+ * window it resolves rolls onto the next day once the clock passes midnight.
939
992
  */
940
- var isRightAnimationIconArea = function isRightAnimationIconArea(x, rightMarkerPx, width) {
941
- return x >= rightMarkerPx - width && x <= rightMarkerPx;
993
+ var resolveAnchorInstant = function resolveAnchorInstant(setting, now) {
994
+ if (now === void 0) {
995
+ now = dateUtils.utc();
996
+ }
997
+ if (setting.mode !== AnimationLengthMode.RecurringPeriod) {
998
+ return now;
999
+ }
1000
+ var _setting$recurringPer = setting.recurringPeriod.anchorTime.split(':').map(Number),
1001
+ hours = _setting$recurringPer[0],
1002
+ minutes = _setting$recurringPer[1];
1003
+ return dateUtils.set(now, {
1004
+ hours: hours,
1005
+ minutes: minutes,
1006
+ seconds: 0,
1007
+ milliseconds: 0
1008
+ });
942
1009
  };
943
- var isInsideAnimationArea = function isInsideAnimationArea(x, leftMarkerPx, rightMarkerPx) {
944
- var isInsideOnLeftSide = x >= leftMarkerPx;
945
- var isInsideOnRightSide = x <= rightMarkerPx;
946
- return isInsideOnLeftSide && isInsideOnRightSide;
1010
+ /**
1011
+ * The instant a mode counts its offsets from. Only ReferenceTime takes one from
1012
+ * outside, so handing a layer's reference time to any other mode cannot move it.
1013
+ */
1014
+ var anchorInstantFor = function anchorInstantFor(setting, referenceTime, now) {
1015
+ if (now === void 0) {
1016
+ now = dateUtils.utc();
1017
+ }
1018
+ return setting.mode === AnimationLengthMode.ReferenceTime && referenceTime ? dateUtils.utc(referenceTime) : resolveAnchorInstant(setting, now);
947
1019
  };
948
1020
  /**
949
- * @param x The pin location
950
- * @param selectedTimePx The selected px
951
- * @param width The default width of a hoverable icon (usually 24px)
952
- * @returns
1021
+ * Offsets are signed: negative lands before the anchor, positive after. They are
1022
+ * applied as calendar durations, so "+ 1 month" lands on the same day of the next
1023
+ * month rather than a fixed number of days later.
953
1024
  */
954
- var isLeftAnimationIconArea = function isLeftAnimationIconArea(x, leftMarkerPx, width) {
955
- return x >= leftMarkerPx && x <= leftMarkerPx + width;
1025
+ var resolveAnimationRange = function resolveAnimationRange(setting, now) {
1026
+ if (now === void 0) {
1027
+ now = dateUtils.utc();
1028
+ }
1029
+ var _activeOffsets = activeOffsets(setting),
1030
+ startOffset = _activeOffsets.startOffset,
1031
+ endOffset = _activeOffsets.endOffset;
1032
+ var anchor = resolveAnchorInstant(setting, now);
1033
+ return {
1034
+ animationStartTime: toAnimationTime(dateUtils.add(anchor, offsetToDuration(startOffset))),
1035
+ animationEndTime: toAnimationTime(dateUtils.add(anchor, offsetToDuration(endOffset)))
1036
+ };
956
1037
  };
957
- function convertStringTimeToUnix(time) {
958
- return time ? dateUtils.unix(new Date(time)) : undefined;
959
- }
960
- var timeValues = [3600, 10800, 21600, 43200, 86400, 172800, 604800, 1209600, 2592000, 7776000, 31536000];
961
- var TIME_SLIDER_LEGEND_HEIGHT = 24;
962
- var DRAG_AREA_WIDTH = 24;
963
- var isDraggingStartWithinBounds = function isDraggingStartWithinBounds(endPos, mousePosition) {
964
- var startAnimationMaxPosition = endPos - DRAG_AREA_WIDTH * 2;
965
- return mousePosition >= startAnimationMaxPosition || mousePosition <= 0;
1038
+ /* Calendar units have no fixed length, so this only seeds the search for the nearest offset. */
1039
+ var approximateMinutesPerUnit = {
1040
+ minutes: 1,
1041
+ hours: 60,
1042
+ days: 60 * 24,
1043
+ weeks: 60 * 24 * 7,
1044
+ months: 60 * 24 * 30,
1045
+ years: 60 * 24 * 365
1046
+ };
1047
+ /** The whole-unit offset landing nearest the instant, so a handle reaches only what the menu can express. */
1048
+ var offsetForInstant = function offsetForInstant(anchor, instant, unit) {
1049
+ var estimate = Math.round(dateUtils.differenceInMinutes(instant, anchor) / approximateMinutesPerUnit[unit]);
1050
+ var distanceFromInstant = function distanceFromInstant(value) {
1051
+ return Math.abs(dateUtils.add(anchor, offsetToDuration({
1052
+ value: value,
1053
+ unit: unit
1054
+ })).getTime() - instant.getTime());
1055
+ };
1056
+ var nearest = [estimate - 1, estimate, estimate + 1].reduce(function (closest, value) {
1057
+ return distanceFromInstant(value) < distanceFromInstant(closest) ? value : closest;
1058
+ });
1059
+ return {
1060
+ value: nearest,
1061
+ unit: unit
1062
+ };
966
1063
  };
967
- var isDraggingEndWithinBounds = function isDraggingEndWithinBounds(startPos, mousePosition, canvasWidth) {
968
- var endAnimationMinimumPosition = startPos + DRAG_AREA_WIDTH * 2;
969
- return endAnimationMinimumPosition >= mousePosition || mousePosition >= canvasWidth;
1064
+ /** Snaps a dragged handle onto the grid the mode's offsets can describe. */
1065
+ var snapInstantToOffsetGrid = function snapInstantToOffsetGrid(anchor, instant, unit) {
1066
+ return dateUtils.add(anchor, offsetToDuration(clampOffset(offsetForInstant(anchor, instant, unit))));
970
1067
  };
971
- var getTimeSliderLegendId = function getTimeSliderLegendId(mapId) {
972
- return "timeSliderLegend_" + mapId;
1068
+ var isWithinOffsetLimit = function isWithinOffsetLimit(_ref4) {
1069
+ var value = _ref4.value,
1070
+ unit = _ref4.unit;
1071
+ return Math.abs(value) <= maxOffsetByUnit[unit];
973
1072
  };
974
- var getTimeSliderLegendElement = function getTimeSliderLegendElement(mapId) {
975
- return document.querySelector(".timeSliderLegend_" + mapId);
1073
+ /**
1074
+ * Recounts only the edges the range names, on the grid of the unit in use. An edge left
1075
+ * out keeps the offset it holds: reading its instant back would subtract whatever time
1076
+ * passed since that instant was resolved.
1077
+ */
1078
+ var offsetsForRange = function offsetsForRange(setting, _ref5, now) {
1079
+ var animationStartTime = _ref5.animationStartTime,
1080
+ animationEndTime = _ref5.animationEndTime;
1081
+ var anchor = resolveAnchorInstant(setting, now);
1082
+ var unit = activeEditUnit(setting);
1083
+ var heldOffsets = activeOffsets(setting);
1084
+ var offsetOf = function offsetOf(time) {
1085
+ return offsetForInstant(anchor, dateUtils.utc(time), unit);
1086
+ };
1087
+ var startOffset = animationStartTime ? offsetOf(animationStartTime) : heldOffsets.startOffset;
1088
+ var endOffset = animationEndTime ? offsetOf(animationEndTime) : heldOffsets.endOffset;
1089
+ var movedBeyondReach = !!animationStartTime && !isWithinOffsetLimit(startOffset) || !!animationEndTime && !isWithinOffsetLimit(endOffset);
1090
+ return movedBeyondReach ? undefined : {
1091
+ startOffset: startOffset,
1092
+ endOffset: endOffset
1093
+ };
976
1094
  };
977
- var getPixelsBetweenViewportAndTimesliderOnLeft = function getPixelsBetweenViewportAndTimesliderOnLeft(mapId) {
978
- var timeSliderLegendElement = document.querySelector(".timeSliderLegend_" + mapId);
979
- return timeSliderLegendElement == null ? void 0 : timeSliderLegendElement.getBoundingClientRect()['left'];
1095
+ /**
1096
+ * A range set on the slider belongs to whichever mode is selected, so only that mode's
1097
+ * own selections change, and only on the edges the range names; the modes that read their
1098
+ * range from elsewhere keep it, and so does an edge the mode's offsets cannot reach.
1099
+ */
1100
+ var withDraggedRange = function withDraggedRange(setting, range, now) {
1101
+ if (now === void 0) {
1102
+ now = dateUtils.utc();
1103
+ }
1104
+ if (setting.mode === AnimationLengthMode.Exact) {
1105
+ var _range$animationStart, _range$animationEndTi;
1106
+ return Object.assign({}, setting, {
1107
+ exact: {
1108
+ animationStartTime: (_range$animationStart = range.animationStartTime) != null ? _range$animationStart : setting.exact.animationStartTime,
1109
+ animationEndTime: (_range$animationEndTi = range.animationEndTime) != null ? _range$animationEndTi : setting.exact.animationEndTime
1110
+ }
1111
+ });
1112
+ }
1113
+ var offsets = isAnchoredMode(setting.mode) ? offsetsForRange(setting, range, now) : undefined;
1114
+ if (!offsets) {
1115
+ return setting;
1116
+ }
1117
+ if (setting.mode === AnimationLengthMode.RecurringPeriod) {
1118
+ return Object.assign({}, setting, {
1119
+ recurringPeriod: Object.assign({}, setting.recurringPeriod, offsets)
1120
+ });
1121
+ }
1122
+ if (setting.mode === AnimationLengthMode.ReferenceTime) {
1123
+ return Object.assign({}, setting, {
1124
+ referenceTime: Object.assign({}, setting.referenceTime, offsets)
1125
+ });
1126
+ }
1127
+ return Object.assign({}, setting, {
1128
+ now: Object.assign({}, setting.now, offsets)
1129
+ });
1130
+ };
1131
+ /** Offsets can use different units, so validity is decided on the resolved instants. */
1132
+ /** Exact names its range outright; every other mode resolves one from its offsets. */
1133
+ var settingRange = function settingRange(setting, now) {
1134
+ if (now === void 0) {
1135
+ now = dateUtils.utc();
1136
+ }
1137
+ return setting.mode === AnimationLengthMode.Exact ? setting.exact : resolveAnimationRange(setting, now);
1138
+ };
1139
+ /* Exact times are picked to the minute, so seconds must not decide validity. */
1140
+ var isValidAnimationLength = function isValidAnimationLength(setting, now) {
1141
+ if (now === void 0) {
1142
+ now = dateUtils.utc();
1143
+ }
1144
+ var _settingRange = settingRange(setting, now),
1145
+ animationStartTime = _settingRange.animationStartTime,
1146
+ animationEndTime = _settingRange.animationEndTime;
1147
+ return dateUtils.isBefore(dateUtils.utc(animationStartTime).setSeconds(0, 0), dateUtils.utc(animationEndTime).setSeconds(0, 0));
1148
+ };
1149
+ /** Frames the animation may hold; the range is expanded one entry per time step. */
1150
+ var MAX_ANIMATION_STEPS = 10000;
1151
+ /**
1152
+ * The cost of an animation is its span divided by the time step, so a wide range
1153
+ * is affordable with a coarse step and unaffordable with a fine one.
1154
+ */
1155
+ var isWithinAnimationStepLimit = function isWithinAnimationStepLimit(setting, timeStepInMinutes, now) {
1156
+ if (now === void 0) {
1157
+ now = dateUtils.utc();
1158
+ }
1159
+ if (!timeStepInMinutes) {
1160
+ return true;
1161
+ }
1162
+ var _settingRange2 = settingRange(setting, now),
1163
+ animationStartTime = _settingRange2.animationStartTime,
1164
+ animationEndTime = _settingRange2.animationEndTime;
1165
+ var spanInMinutes = dateUtils.differenceInMinutes(dateUtils.utc(animationEndTime), dateUtils.utc(animationStartTime));
1166
+ return Math.abs(spanInMinutes) / timeStepInMinutes <= MAX_ANIMATION_STEPS;
1167
+ };
1168
+ /* Shares the animation-time format, so a typed and a dragged range serialise alike. */
1169
+ var toExactAnimationTime = toAnimationTime;
1170
+ /** A day either side of now, so an exact window opens on something usable. */
1171
+ var defaultExactSelections = function defaultExactSelections(now) {
1172
+ if (now === void 0) {
1173
+ now = dateUtils.utc();
1174
+ }
1175
+ var wholeMinute = dateUtils.set(now, {
1176
+ seconds: 0,
1177
+ milliseconds: 0
1178
+ });
1179
+ return {
1180
+ animationStartTime: toExactAnimationTime(dateUtils.sub(wholeMinute, {
1181
+ days: 1
1182
+ })),
1183
+ animationEndTime: toExactAnimationTime(dateUtils.add(wholeMinute, {
1184
+ days: 1
1185
+ }))
1186
+ };
1187
+ };
1188
+ /** Starts Exact from whatever range is already on screen, falling back to the defaults. */
1189
+ var exactAnimationLengthFrom = function exactAnimationLengthFrom(_ref6, now) {
1190
+ var animationStartTime = _ref6.animationStartTime,
1191
+ animationEndTime = _ref6.animationEndTime;
1192
+ if (now === void 0) {
1193
+ now = dateUtils.utc();
1194
+ }
1195
+ return {
1196
+ mode: AnimationLengthMode.Exact,
1197
+ exact: animationStartTime && animationEndTime ? {
1198
+ animationStartTime: animationStartTime,
1199
+ animationEndTime: animationEndTime
1200
+ } : defaultExactSelections(now)
1201
+ };
1202
+ };
1203
+ var getOffsetFromTime = function getOffsetFromTime(time, anchorTime) {
1204
+ var relativeTime = dateUtils.utc(time);
1205
+ var value = dateUtils.differenceInMinutes(relativeTime, anchorTime);
1206
+ return {
1207
+ unit: 'minutes',
1208
+ value: value
1209
+ };
1210
+ };
1211
+ var getAnimationLengthFromRange = function getAnimationLengthFromRange(mode, _ref7, now) {
1212
+ var animationStartTime = _ref7.animationStartTime,
1213
+ animationEndTime = _ref7.animationEndTime;
1214
+ if (now === void 0) {
1215
+ now = dateUtils.utc();
1216
+ }
1217
+ switch (mode) {
1218
+ case AnimationLengthMode.Now:
1219
+ {
1220
+ return {
1221
+ mode: mode,
1222
+ now: {
1223
+ startOffset: getOffsetFromTime(animationStartTime, now),
1224
+ endOffset: getOffsetFromTime(animationEndTime, now)
1225
+ }
1226
+ };
1227
+ }
1228
+ case AnimationLengthMode.RecurringPeriod:
1229
+ {
1230
+ var today = dateUtils.startOfDayUTC(now);
1231
+ return {
1232
+ mode: mode,
1233
+ recurringPeriod: {
1234
+ anchorTime: '00:00',
1235
+ startOffset: getOffsetFromTime(animationStartTime, today),
1236
+ endOffset: getOffsetFromTime(animationEndTime, today)
1237
+ }
1238
+ };
1239
+ }
1240
+ case AnimationLengthMode.Exact:
1241
+ default:
1242
+ return exactAnimationLengthFrom({
1243
+ animationStartTime: animationStartTime,
1244
+ animationEndTime: animationEndTime
1245
+ });
1246
+ }
1247
+ };
1248
+
1249
+ /* *
1250
+ * Licensed under the Apache License, Version 2.0 (the "License");
1251
+ * you may not use this file except in compliance with the License.
1252
+ * You may obtain a copy of the License at
1253
+ *
1254
+ * http://www.apache.org/licenses/LICENSE-2.0
1255
+ *
1256
+ * Unless required by applicable law or agreed to in writing, software
1257
+ * distributed under the License is distributed on an "AS IS" BASIS,
1258
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1259
+ * See the License for the specific language governing permissions and
1260
+ * limitations under the License.
1261
+ *
1262
+ * Copyright 2022 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
1263
+ * Copyright 2022 - Finnish Meteorological Institute (FMI)
1264
+ * Copyright 2024 - The Norwegian Meteorological Institute (MET Norway)
1265
+ * */
1266
+ /**
1267
+ * @param x The pin location
1268
+ * @param selectedTimePx The selected px
1269
+ * @param width The default width of a hoverable icon (usually 24px)
1270
+ * @returns
1271
+ */
1272
+ var isSelectedTimeIconArea = function isSelectedTimeIconArea(x, selectedTimePx, width) {
1273
+ return x >= selectedTimePx && x <= selectedTimePx + width / 2;
1274
+ };
1275
+ /**
1276
+ * @param x The pin location
1277
+ * @param selectedTimePx The selected px
1278
+ * @param width The default width of a hoverable icon (usually 24px)
1279
+ * @returns
1280
+ */
1281
+ var isRightAnimationIconArea = function isRightAnimationIconArea(x, rightMarkerPx, width) {
1282
+ return x >= rightMarkerPx - width && x <= rightMarkerPx;
1283
+ };
1284
+ var isInsideAnimationArea = function isInsideAnimationArea(x, leftMarkerPx, rightMarkerPx) {
1285
+ var isInsideOnLeftSide = x >= leftMarkerPx;
1286
+ var isInsideOnRightSide = x <= rightMarkerPx;
1287
+ return isInsideOnLeftSide && isInsideOnRightSide;
1288
+ };
1289
+ /**
1290
+ * @param x The pin location
1291
+ * @param selectedTimePx The selected px
1292
+ * @param width The default width of a hoverable icon (usually 24px)
1293
+ * @returns
1294
+ */
1295
+ var isLeftAnimationIconArea = function isLeftAnimationIconArea(x, leftMarkerPx, width) {
1296
+ return x >= leftMarkerPx && x <= leftMarkerPx + width;
1297
+ };
1298
+ function convertStringTimeToUnix(time) {
1299
+ return time ? dateUtils.unix(new Date(time)) : undefined;
1300
+ }
1301
+ var timeValues = [3600, 10800, 21600, 43200, 86400, 172800, 604800, 1209600, 2592000, 7776000, 31536000];
1302
+ var TIME_SLIDER_LEGEND_HEIGHT = 24;
1303
+ var DRAG_AREA_WIDTH = 24;
1304
+ /* A window narrower than the handles would otherwise trap them, so only closing the gap is refused. */
1305
+ var isDraggingStartWithinBounds = function isDraggingStartWithinBounds(endPos, mousePosition, startPos) {
1306
+ var startAnimationMaxPosition = endPos - DRAG_AREA_WIDTH * 2;
1307
+ var crowdsTheEndHandle = mousePosition >= startAnimationMaxPosition && mousePosition >= startPos;
1308
+ return crowdsTheEndHandle || mousePosition <= 0;
1309
+ };
1310
+ var isDraggingEndWithinBounds = function isDraggingEndWithinBounds(startPos, mousePosition, canvasWidth, endPos) {
1311
+ var endAnimationMinimumPosition = startPos + DRAG_AREA_WIDTH * 2;
1312
+ var crowdsTheStartHandle = endAnimationMinimumPosition >= mousePosition && mousePosition <= endPos;
1313
+ return crowdsTheStartHandle || mousePosition >= canvasWidth;
1314
+ };
1315
+ /** The animation holds one frame per time step, so a wide enough range would outgrow the limit. */
1316
+ var exceedsAnimationStepLimit = function exceedsAnimationStepLimit(startTimeUnix, endTimeUnix, timeStepInMinutes) {
1317
+ if (!timeStepInMinutes) {
1318
+ return false;
1319
+ }
1320
+ var spanInMinutes = Math.abs(endTimeUnix - startTimeUnix) / 60;
1321
+ return spanInMinutes / timeStepInMinutes > MAX_ANIMATION_STEPS;
1322
+ };
1323
+ var getTimeSliderLegendId = function getTimeSliderLegendId(mapId) {
1324
+ return "timeSliderLegend_" + mapId;
1325
+ };
1326
+ var getTimeSliderLegendElement = function getTimeSliderLegendElement(mapId) {
1327
+ return document.querySelector(".timeSliderLegend_" + mapId);
1328
+ };
1329
+ var getPixelsBetweenViewportAndTimesliderOnLeft = function getPixelsBetweenViewportAndTimesliderOnLeft(mapId) {
1330
+ var timeSliderLegendElement = document.querySelector(".timeSliderLegend_" + mapId);
1331
+ return timeSliderLegendElement == null ? void 0 : timeSliderLegendElement.getBoundingClientRect()['left'];
980
1332
  };
981
1333
  function getPositions(width, unfilteredSelectedTime, centerTime, secondsPerPx, start, end) {
982
1334
  var selectedTimePosition = timestampToPixel(unfilteredSelectedTime, centerTime, width, secondsPerPx);
@@ -1747,6 +2099,9 @@ var TimeSliderLegend = function TimeSliderLegend(_ref) {
1747
2099
  onStartTimeChange = _ref.onStartTimeChange,
1748
2100
  onEndTimeChange = _ref.onEndTimeChange,
1749
2101
  onSetTimeSliderSpan = _ref.onSetTimeSliderSpan,
2102
+ snapAnimationTime = _ref.snapAnimationTime,
2103
+ _ref$isAnimationRange = _ref.isAnimationRangeDraggable,
2104
+ isAnimationRangeDraggable = _ref$isAnimationRange === void 0 ? true : _ref$isAnimationRange,
1750
2105
  _ref$adjustSelectedTi = _ref.adjustSelectedTimeOnWheel,
1751
2106
  adjustSelectedTimeOnWheel = _ref$adjustSelectedTi === void 0 ? function () {} : _ref$adjustSelectedTi;
1752
2107
  var _useCanvasTarget = useCanvasTarget('mousedown'),
@@ -1817,18 +2172,17 @@ var TimeSliderLegend = function TimeSliderLegend(_ref) {
1817
2172
  return undefined;
1818
2173
  }
1819
2174
  };
2175
+ /** The handles already sit on the grid their mode allows, so their time is reported as it is. */
1820
2176
  var getTooltipText = function getTooltipText() {
1821
2177
  var time = getTooltipTime();
1822
2178
  if (!time) {
1823
2179
  return undefined;
1824
2180
  }
1825
- var roundedTooltipTime = showTooltip === 'mousePointer' ? time : roundWithTimeStep$1(time);
1826
- var tooltipText = dateUtils.dateToString(dateUtils.fromUnix(roundedTooltipTime), dateUtils.DATE_FORMAT_HOURS);
1827
- return tooltipText;
1828
- };
1829
- var roundWithTimeStep$1 = function roundWithTimeStep$1(time) {
1830
- return roundWithTimeStep(time, timeStep);
2181
+ return dateUtils.dateToString(dateUtils.fromUnix(time), dateUtils.DATE_FORMAT_HOURS);
1831
2182
  };
2183
+ var snapAnimationTimeToGrid = React.useCallback(function (time) {
2184
+ return snapAnimationTime ? snapAnimationTime(time) : roundWithTimeStep(time, timeStep);
2185
+ }, [snapAnimationTime, timeStep]);
1832
2186
  var currentIndex = timeValues.indexOf(timeSliderSpan);
1833
2187
  var updateTimeSpan = function updateTimeSpan(deltaY) {
1834
2188
  if (onSetTimeSliderSpan) {
@@ -1850,8 +2204,7 @@ var TimeSliderLegend = function TimeSliderLegend(_ref) {
1850
2204
  var startPos = timestampToPixel(localAnimationStartTime, centerTime, canvasWidth, secondsPerPx);
1851
2205
  var endPos = timestampToPixel(localAnimationEndTime, centerTime, canvasWidth, secondsPerPx);
1852
2206
  var mouseTimeUnix = pixelToTimestamp(mousePosition, centerTime, canvasWidth, secondsPerPx);
1853
- var startTime = roundWithTimeStep(mouseTimeUnix, timeStep);
1854
- var endTime = roundWithTimeStep(mouseTimeUnix, timeStep);
2207
+ var snappedMouseTime = snapAnimationTimeToGrid(mouseTimeUnix);
1855
2208
  var pixelsBetweenOnLeft = getPixelsBetweenViewportAndTimesliderOnLeft(mapId);
1856
2209
  if (isDraggingStartAnimation || isDraggingEndAnimation || isDraggingAnimationArea) {
1857
2210
  if (!isAnimationTimeModifiedSetRef.current) {
@@ -1859,24 +2212,24 @@ var TimeSliderLegend = function TimeSliderLegend(_ref) {
1859
2212
  }
1860
2213
  }
1861
2214
  if (isDraggingStartAnimation) {
1862
- if (isDraggingStartWithinBounds(endPos, mousePosition)) {
2215
+ if (isDraggingStartWithinBounds(endPos, mousePosition, startPos) || exceedsAnimationStepLimit(snappedMouseTime, end, timeStep)) {
1863
2216
  return;
1864
2217
  }
1865
- setLocalAnimationStartTime(roundWithTimeStep(mouseTimeUnix, timeStep));
1866
- if (lastHandledRoundedTime !== startTime) {
1867
- setLastHandledRoundedTime(startTime);
2218
+ setLocalAnimationStartTime(snappedMouseTime);
2219
+ if (lastHandledRoundedTime !== snappedMouseTime) {
2220
+ setLastHandledRoundedTime(snappedMouseTime);
1868
2221
  setShowTooltip('animationStart');
1869
- onStartTimeChange == null || onStartTimeChange(startTime);
2222
+ onStartTimeChange == null || onStartTimeChange(snappedMouseTime);
1870
2223
  }
1871
2224
  } else if (isDraggingEndAnimation && pixelsBetweenOnLeft) {
1872
- if (isDraggingEndWithinBounds(startPos, mousePosition, canvasWidth)) {
2225
+ if (isDraggingEndWithinBounds(startPos, mousePosition, canvasWidth, endPos) || exceedsAnimationStepLimit(start, snappedMouseTime, timeStep)) {
1873
2226
  return;
1874
2227
  }
1875
- setLocalAnimationEndTime(roundWithTimeStep(mouseTimeUnix, timeStep));
1876
- if (lastHandledRoundedTime !== endTime) {
1877
- setLastHandledRoundedTime(endTime);
2228
+ setLocalAnimationEndTime(snappedMouseTime);
2229
+ if (lastHandledRoundedTime !== snappedMouseTime) {
2230
+ setLastHandledRoundedTime(snappedMouseTime);
1878
2231
  setShowTooltip('animationEnd');
1879
- onEndTimeChange == null || onEndTimeChange(endTime);
2232
+ onEndTimeChange == null || onEndTimeChange(snappedMouseTime);
1880
2233
  }
1881
2234
  }
1882
2235
  if (isDraggingAnimationArea) {
@@ -1897,12 +2250,10 @@ var TimeSliderLegend = function TimeSliderLegend(_ref) {
1897
2250
  timeFromMouseToStart = _timeBetweenMouseAndS.timeFromMouseToStart;
1898
2251
  var newStartTime = mouseTimeUnix - timeFromMouseToStart;
1899
2252
  var newEndTime = mouseTimeUnix + timeFromMouseToEnd;
1900
- var roundedNewStartTime = roundWithTimeStep(newStartTime, timeStep);
1901
- var roundedNewEndTime = roundWithTimeStep(newEndTime, timeStep);
1902
- setLocalAnimationStartTime(roundedNewStartTime);
1903
- setLocalAnimationEndTime(roundedNewEndTime);
2253
+ setLocalAnimationStartTime(snapAnimationTimeToGrid(newStartTime));
2254
+ setLocalAnimationEndTime(snapAnimationTimeToGrid(newEndTime));
1904
2255
  }
1905
- }, [canvasWidth, centerTime, end, isDraggingAnimationArea, isDraggingEndAnimation, isDraggingStartAnimation, lastHandledRoundedTime, localAnimationEndTime, localAnimationStartTime, mapId, onEndTimeChange, onStartTimeChange, secondsPerPx, start, timeStep, xOffset]);
2256
+ }, [canvasWidth, centerTime, end, isDraggingAnimationArea, isDraggingEndAnimation, isDraggingStartAnimation, lastHandledRoundedTime, localAnimationEndTime, localAnimationStartTime, mapId, onEndTimeChange, onStartTimeChange, secondsPerPx, snapAnimationTimeToGrid, start, timeStep, xOffset]);
1906
2257
  useEffect(function () {
1907
2258
  var handleMouseUp = function handleMouseUp() {
1908
2259
  pixelsMovedSinceStartDragging.current = 0;
@@ -2009,7 +2360,7 @@ var TimeSliderLegend = function TimeSliderLegend(_ref) {
2009
2360
  setIsDraggingSelectedTime(true);
2010
2361
  return;
2011
2362
  }
2012
- if (reduxAnimationStartTime && reduxAnimationEndTime) {
2363
+ if (isAnimationRangeDraggable && reduxAnimationStartTime && reduxAnimationEndTime) {
2013
2364
  var pixelsBetweenOnLeft = getPixelsBetweenViewportAndTimesliderOnLeft(mapId);
2014
2365
  tooltipPosition.current = pixelsBetweenOnLeft ? x + pixelsBetweenOnLeft : x;
2015
2366
  // start dragging either marker
@@ -2145,603 +2496,21 @@ var TimeSliderLegend = function TimeSliderLegend(_ref) {
2145
2496
  "data-testid": "customCanvasMouseOverContainer",
2146
2497
  onMouseLeave: function onMouseLeave() {
2147
2498
  return setIsMouseOverCanvas(false);
2148
- },
2149
- onMouseOver: function onMouseOver() {
2150
- return setIsMouseOverCanvas(true);
2151
- },
2152
- sx: {
2153
- position: 'absolute',
2154
- top: '-40px',
2155
- left: 0,
2156
- right: 0,
2157
- bottom: 0,
2158
- height: TIME_SLIDER_LEGEND_HEIGHT + " + 40px",
2159
- zIndex: isMouseOverCanvas ? 100 : 0
2160
- }
2161
- })]
2162
- });
2163
- };
2164
-
2165
- var THROTTLE_WAIT_TIME = 300; // [ms]
2166
- var THROTTLE_OPTIONS = {
2167
- trailing: false // Invoke function when called, but not more than once every <THROTTLE_WAIT_TIME>.
2168
- };
2169
- var TimeSliderMenu = function TimeSliderMenu(_ref) {
2170
- var marks = _ref.marks,
2171
- value = _ref.value,
2172
- allOptionsDisabled = _ref.allOptionsDisabled,
2173
- isAutoDisabled = _ref.isAutoDisabled,
2174
- handleAutoClick = _ref.handleAutoClick,
2175
- isAutoSelected = _ref.isAutoSelected,
2176
- title = _ref.title,
2177
- icon = _ref.icon,
2178
- _ref$onChangeSliderVa = _ref.onChangeSliderValue,
2179
- onChangeSliderValue = _ref$onChangeSliderVa === void 0 ? function () {} : _ref$onChangeSliderVa,
2180
- onSelectSliderValue = _ref.onSelectSliderValue,
2181
- displayCurrentLength = _ref.displayCurrentLength,
2182
- _ref$disbleUserSetDur = _ref.disbleUserSetDuration,
2183
- disbleUserSetDuration = _ref$disbleUserSetDur === void 0 ? true : _ref$disbleUserSetDur,
2184
- customLengthSelector = _ref.customLengthSelector,
2185
- _ref$setOpen = _ref.setOpen,
2186
- setOpen = _ref$setOpen === void 0 ? function () {} : _ref$setOpen,
2187
- _ref$customFieldHeade = _ref.customFieldHeader,
2188
- customFieldHeader = _ref$customFieldHeade === void 0 ? 'timeslider-length' : _ref$customFieldHeade,
2189
- open = _ref.open;
2190
- var currentMarkIndex = marks.findIndex(function (mark) {
2191
- return mark.value === value;
2192
- });
2193
- var currentMark = marks[currentMarkIndex];
2194
- var _React$useState = React.useState(null),
2195
- anchorRef = _React$useState[0],
2196
- setAnchorRef = _React$useState[1];
2197
- var _useTimesliderTransla = useTimesliderTranslation(),
2198
- t = _useTimesliderTransla.t;
2199
- var _useState = useState(false),
2200
- displayLengthInputField = _useState[0],
2201
- setDisplayLengthInputField = _useState[1];
2202
- React.useEffect(function () {
2203
- if (!open && displayLengthInputField) {
2204
- setDisplayLengthInputField(false);
2205
- }
2206
- }, [displayLengthInputField, open]);
2207
- var getDisplayTextFromMark = function getDisplayTextFromMark(mark) {
2208
- if (!(mark != null && mark.label)) {
2209
- return '';
2210
- }
2211
- var parts = t(mark.label).split(' ');
2212
- if (parts.length > 1) {
2213
- return parts[0] + parts.slice(1).join(' ');
2214
- }
2215
- return parts.join('');
2216
- };
2217
- var buttonText = function buttonText(value, displayCurrentLength, currentMark) {
2218
- if (displayCurrentLength) {
2219
- if (!Number.isInteger(value)) {
2220
- return value + "m";
2221
- }
2222
- return minutesToDescribedDuration(value);
2223
- }
2224
- if (currentMark != null && currentMark.label) {
2225
- return getDisplayTextFromMark(currentMark);
2226
- }
2227
- return '';
2228
- };
2229
- // eslint-disable-next-line react-hooks/exhaustive-deps
2230
- var throttledScroll = React.useCallback(throttle(function (newValue) {
2231
- onChangeSliderValue(newValue);
2232
- }, THROTTLE_WAIT_TIME, THROTTLE_OPTIONS), []);
2233
- var onWheel = React.useCallback(function (event) {
2234
- var direction = event.deltaY < 0 ? 1 : -1;
2235
- var newValue = marks[Math.min(Math.max(currentMarkIndex - direction, 0), marks.length - 1)];
2236
- throttledScroll(newValue.value);
2237
- }, [currentMarkIndex, marks, throttledScroll]);
2238
- var buttonDisplayText = buttonText(value, displayCurrentLength, currentMark);
2239
- var menuDisplayValue = !Number.isInteger(value) ? value + "m" : minutesToDescribedDuration(value);
2240
- var displayValue = isAutoSelected ? "A: " + buttonDisplayText : buttonDisplayText;
2241
- return jsxs(Box, {
2242
- onKeyDown: function onKeyDown(event) {
2243
- event.stopPropagation();
2244
- },
2245
- children: [jsxs(CustomIconButton, {
2246
- isSelected: true,
2247
- onClick: function onClick() {
2248
- setOpen(true);
2249
- },
2250
- onWheel: onWheel,
2251
- ref: setAnchorRef,
2252
- sx: {
2253
- padding: '0px',
2254
- paddingRight: '12px',
2255
- width: '100%!important',
2256
- minWidth: '64px',
2257
- justifyContent: 'space-between'
2258
- },
2259
- tooltipTitle: title,
2260
- variant: "tool",
2261
- children: [icon, jsx(Typography, {
2262
- sx: {
2263
- fontSize: 12,
2264
- fontWeight: 400,
2265
- textTransform: 'none'
2266
- },
2267
- children: displayValue
2268
- })]
2269
- }), !!anchorRef && jsxs(Menu, {
2270
- anchorEl: anchorRef,
2271
- anchorOrigin: {
2272
- vertical: -15,
2273
- horizontal: 'center'
2274
- },
2275
- onClose: function onClose() {
2276
- return setOpen(false);
2277
- },
2278
- open: open,
2279
- slotProps: {
2280
- backdrop: {
2281
- sx: {
2282
- backgroundColor: 'transparent'
2283
- }
2284
- }
2285
- },
2286
- transformOrigin: {
2287
- vertical: 'bottom',
2288
- horizontal: 'center'
2289
- },
2290
- children: [jsx(MenuItem, {
2291
- disabled: true,
2292
- sx: {
2293
- fontSize: '12px',
2294
- opacity: 0.67,
2295
- padding: '0px 12px',
2296
- '&.MuiMenuItem-root': {
2297
- minHeight: '30px!important',
2298
- minWidth: 150
2299
- }
2300
- },
2301
- children: title
2302
- }), marks.map(function (mark) {
2303
- var isSelected = (currentMark == null ? void 0 : currentMark.value) === mark.value;
2304
- return jsx(MenuItem, {
2305
- disabled: allOptionsDisabled,
2306
- onClick: function onClick() {
2307
- if (!isSelected) {
2308
- onChangeSliderValue(mark.value);
2309
- }
2310
- onSelectSliderValue == null || onSelectSliderValue(mark.value);
2311
- setOpen(false);
2312
- },
2313
- selected: !!isSelected && !displayLengthInputField,
2314
- children: mark.label || mark.text
2315
- }, mark.value);
2316
- }), !!handleAutoClick && jsx(MenuItem, {
2317
- disabled: allOptionsDisabled || isAutoDisabled,
2318
- onClick: function onClick() {
2319
- handleAutoClick();
2320
- setOpen(false);
2321
- },
2322
- selected: isAutoSelected,
2323
- children: t('timeslider-auto')
2324
- }), !!displayCurrentLength && jsxs(MenuItem, {
2325
- disabled: disbleUserSetDuration,
2326
- onClick: function onClick() {
2327
- return setDisplayLengthInputField(true);
2328
- },
2329
- sx: {
2330
- maxWidth: '175px'
2331
- },
2332
- children: [!!displayLengthInputField && customLengthSelector, !displayLengthInputField && jsxs(Box, {
2333
- sx: {
2334
- flexDirection: 'column',
2335
- display: 'flex',
2336
- flexGrow: 1
2337
- },
2338
- children: [jsx(CardHeader, {
2339
- slotProps: {
2340
- title: {
2341
- sx: {
2342
- fontSize: 12
2343
- }
2344
- }
2345
- },
2346
- sx: {
2347
- height: 2,
2348
- padding: 0,
2349
- color: 'geowebColors.greys.accessible'
2350
- },
2351
- title: t(customFieldHeader)
2352
- }), jsxs(Box, {
2353
- sx: {
2354
- flexDirection: 'row',
2355
- display: 'flex',
2356
- justifyContent: 'space-between',
2357
- flexGrow: 1,
2358
- paddingTop: 0.5
2359
- },
2360
- children: [jsx(Typography, {
2361
- sx: {
2362
- fontSize: 14,
2363
- textTransform: 'none',
2364
- lineHeight: 2
2365
- },
2366
- children: menuDisplayValue
2367
- }), jsx(CustomTooltip, {
2368
- title: t('timeslider-edit'),
2369
- children: jsx("span", {
2370
- children: jsx(CustomIconButton, {
2371
- "aria-label": "edit animation option",
2372
- "data-testid": "editAnimationOption",
2373
- children: jsx(Edit, {})
2374
- })
2375
- })
2376
- })]
2377
- })]
2378
- })]
2379
- })]
2380
- })]
2381
- });
2382
- };
2383
-
2384
- var marks$2 = [{
2385
- value: AnimationLength.Minutes15,
2386
- text: 'timeslider-min',
2387
- count: 15
2388
- }, {
2389
- value: AnimationLength.Minutes30,
2390
- text: 'timeslider-min',
2391
- count: 30
2392
- }, {
2393
- value: AnimationLength.Hours1,
2394
- text: 'timeslider-hour',
2395
- count: 1
2396
- }, {
2397
- value: AnimationLength.Hours2,
2398
- text: 'timeslider-hour',
2399
- count: 2
2400
- }, {
2401
- value: AnimationLength.Hours3,
2402
- text: 'timeslider-hour',
2403
- count: 3
2404
- }, {
2405
- value: AnimationLength.Hours6,
2406
- text: 'timeslider-hour',
2407
- count: 6
2408
- }, {
2409
- value: AnimationLength.Hours12,
2410
- text: 'timeslider-hour',
2411
- count: 12
2412
- }, {
2413
- value: AnimationLength.Hours24,
2414
- text: 'timeslider-hour',
2415
- count: 24
2416
- }].sort(function (ta, tb) {
2417
- return tb.value - ta.value;
2418
- });
2419
- var AnimationLengthButton = function AnimationLengthButton(_ref) {
2420
- var _ref$disabled = _ref.disabled,
2421
- disabled = _ref$disabled === void 0 ? false : _ref$disabled,
2422
- _ref$animationLength = _ref.animationLength,
2423
- animationLength = _ref$animationLength === void 0 ? AnimationLength.Hours1 : _ref$animationLength,
2424
- _ref$isAuto = _ref.isAuto,
2425
- isAuto = _ref$isAuto === void 0 ? false : _ref$isAuto,
2426
- layerHasTimeStep = _ref.layerHasTimeStep,
2427
- onChangeAnimationLength = _ref.onChangeAnimationLength,
2428
- toggleAnimationLengthAuto = _ref.toggleAnimationLengthAuto;
2429
- var _useTimesliderTransla = useTimesliderTranslation(),
2430
- t = _useTimesliderTransla.t;
2431
- var translatedMarks = marks$2.map(function (mark) {
2432
- return Object.assign({}, mark, {
2433
- label: mark.count + " " + t(mark.text)
2434
- });
2435
- });
2436
- var onChangeSliderValue = function onChangeSliderValue(newSliderValue) {
2437
- var newLength = newSliderValue ? Math.round(newSliderValue) : 0;
2438
- onChangeAnimationLength == null || onChangeAnimationLength(newLength);
2439
- };
2440
- var _useState = useState(false),
2441
- open = _useState[0],
2442
- setOpen = _useState[1];
2443
- return jsx(TimeSliderMenu, {
2444
- allOptionsDisabled: disabled,
2445
- displayCurrentLength: true,
2446
- handleAutoClick: toggleAnimationLengthAuto,
2447
- icon: jsx(Animation, {
2448
- "data-testid": "animationLengthButton"
2449
- }),
2450
- isAutoDisabled: !layerHasTimeStep,
2451
- isAutoSelected: isAuto,
2452
- marks: translatedMarks,
2453
- onChangeSliderValue: onChangeSliderValue,
2454
- open: open,
2455
- setOpen: setOpen,
2456
- title: t('timeslider-animation'),
2457
- value: animationLength
2458
- });
2459
- };
2460
-
2461
- var _modeTranslationKey;
2462
- var AnimationLengthMode;
2463
- (function (AnimationLengthMode) {
2464
- AnimationLengthMode["Auto"] = "auto";
2465
- AnimationLengthMode["Now"] = "now";
2466
- AnimationLengthMode["RecurringPeriod"] = "recurringPeriod";
2467
- AnimationLengthMode["ReferenceTime"] = "referenceTime";
2468
- AnimationLengthMode["Exact"] = "exact";
2469
- })(AnimationLengthMode || (AnimationLengthMode = {}));
2470
- var timeUnits = ['years', 'months', 'weeks', 'days', 'hours', 'minutes'];
2471
- var defaultStartOffset = {
2472
- value: 0,
2473
- unit: 'hours'
2474
- };
2475
- var defaultEndOffset = {
2476
- value: 2,
2477
- unit: 'hours'
2478
- };
2479
- var defaultOffsetSelections = {
2480
- startOffset: defaultStartOffset,
2481
- endOffset: defaultEndOffset
2482
- };
2483
- var unitTranslationKey = {
2484
- years: 'timeslider-unit-years',
2485
- months: 'timeslider-unit-months',
2486
- weeks: 'timeslider-unit-weeks',
2487
- days: 'timeslider-unit-days',
2488
- hours: 'timeslider-unit-hours',
2489
- minutes: 'timeslider-unit-minutes'
2490
- };
2491
- var shortUnitTranslationKey = {
2492
- years: 'timeslider-unit-short-years',
2493
- months: 'timeslider-unit-short-months',
2494
- weeks: 'timeslider-unit-short-weeks',
2495
- days: 'timeslider-unit-short-days',
2496
- hours: 'timeslider-unit-short-hours',
2497
- minutes: 'timeslider-unit-short-minutes'
2498
- };
2499
- var modeTranslationKey = (_modeTranslationKey = {}, _modeTranslationKey[AnimationLengthMode.Auto] = 'timeslider-animation-length-auto', _modeTranslationKey[AnimationLengthMode.Now] = 'timeslider-animation-length-now', _modeTranslationKey[AnimationLengthMode.RecurringPeriod] = 'timeslider-animation-length-recurring-period', _modeTranslationKey[AnimationLengthMode.ReferenceTime] = 'timeslider-animation-length-reference-time', _modeTranslationKey[AnimationLengthMode.Exact] = 'timeslider-animation-length-exact', _modeTranslationKey);
2500
- var offsetToDuration = function offsetToDuration(_ref) {
2501
- var _ref2;
2502
- var value = _ref.value,
2503
- unit = _ref.unit;
2504
- return _ref2 = {}, _ref2[unit] = value, _ref2;
2505
- };
2506
- var toAnimationTime = function toAnimationTime(date) {
2507
- return dateUtils.dateToString(date, dateUtils.DATE_FORMAT_UTC);
2508
- };
2509
- /* The clock in the menu offers minutes in these increments, so an anchor must land on one. */
2510
- var ANCHOR_MINUTE_STEP = 5;
2511
- var padded = function padded(value) {
2512
- return String(value).padStart(2, '0');
2513
- };
2514
- /**
2515
- * Reads the UTC fields directly: `dateUtils.dateToString` formats a local twin of the
2516
- * date, which a DST gap shifts by an hour.
2517
- */
2518
- var toAnchorTime = function toAnchorTime(date) {
2519
- var minutes = date.getUTCMinutes();
2520
- return padded(date.getUTCHours()) + ":" + padded(minutes - minutes % ANCHOR_MINUTE_STEP);
2521
- };
2522
- /* Modes that count their offsets from an anchor instant rather than a stored range. */
2523
- var anchoredModes = [AnimationLengthMode.Now, AnimationLengthMode.RecurringPeriod, AnimationLengthMode.ReferenceTime];
2524
- var isAnchoredMode = function isAnchoredMode(mode) {
2525
- return !!mode && anchoredModes.includes(mode);
2526
- };
2527
- /** Narrows the mode so the compiler can require an anchor time alongside it. */
2528
- var isRecurringPeriodMode = function isRecurringPeriodMode(mode) {
2529
- return mode === AnimationLengthMode.RecurringPeriod;
2530
- };
2531
- var ANCHOR_TIME_PATTERN = /^([01]\d|2[0-3]):[0-5]\d$/;
2532
- var isValidAnchorTime = function isValidAnchorTime(anchorTime) {
2533
- return !!anchorTime && ANCHOR_TIME_PATTERN.test(anchorTime);
2534
- };
2535
- var selectionsForMode = function selectionsForMode(setting) {
2536
- if (setting.mode === AnimationLengthMode.Now) {
2537
- return setting.now;
2538
- }
2539
- if (setting.mode === AnimationLengthMode.RecurringPeriod) {
2540
- return setting.recurringPeriod;
2541
- }
2542
- if (setting.mode === AnimationLengthMode.ReferenceTime) {
2543
- return setting.referenceTime;
2544
- }
2545
- return defaultOffsetSelections;
2546
- };
2547
- /** The offsets of whichever mode is selected; the modes without offsets use the defaults. */
2548
- var activeOffsets = function activeOffsets(setting) {
2549
- var _selectionsForMode = selectionsForMode(setting),
2550
- startOffset = _selectionsForMode.startOffset,
2551
- endOffset = _selectionsForMode.endOffset;
2552
- return {
2553
- startOffset: startOffset,
2554
- endOffset: endOffset
2555
- };
2556
- };
2557
- var defaultRecurringPeriodSelections = function defaultRecurringPeriodSelections(now) {
2558
- if (now === void 0) {
2559
- now = dateUtils.utc();
2560
- }
2561
- return Object.assign({}, defaultOffsetSelections, {
2562
- anchorTime: toAnchorTime(now)
2563
- });
2564
- };
2565
- /** Presets carry settings the compiler cannot vouch for, so the selected mode is given the slice it needs. */
2566
- var withModeSelections = function withModeSelections(setting, now) {
2567
- if (now === void 0) {
2568
- now = dateUtils.utc();
2569
- }
2570
- if (setting.mode === AnimationLengthMode.Now) {
2571
- return Object.assign({}, setting, {
2572
- now: Object.assign({}, defaultOffsetSelections, setting.now)
2573
- });
2574
- }
2575
- if (setting.mode === AnimationLengthMode.RecurringPeriod) {
2576
- var merged = Object.assign({}, defaultRecurringPeriodSelections(now), setting.recurringPeriod);
2577
- return Object.assign({}, setting, {
2578
- recurringPeriod: isValidAnchorTime(merged.anchorTime) ? merged : Object.assign({}, merged, {
2579
- anchorTime: toAnchorTime(now)
2580
- })
2581
- });
2582
- }
2583
- if (setting.mode === AnimationLengthMode.Exact) {
2584
- return Object.assign({}, setting, {
2585
- exact: Object.assign({}, defaultExactSelections(now), setting.exact)
2586
- });
2587
- }
2588
- return setting;
2589
- };
2590
- /**
2591
- * A recurring period anchors on its time of day within the current UTC date, so the
2592
- * window it resolves rolls onto the next day once the clock passes midnight.
2593
- */
2594
- var resolveAnchorInstant = function resolveAnchorInstant(setting, now) {
2595
- if (now === void 0) {
2596
- now = dateUtils.utc();
2597
- }
2598
- if (setting.mode !== AnimationLengthMode.RecurringPeriod) {
2599
- return now;
2600
- }
2601
- var _setting$recurringPer = setting.recurringPeriod.anchorTime.split(':').map(Number),
2602
- hours = _setting$recurringPer[0],
2603
- minutes = _setting$recurringPer[1];
2604
- return dateUtils.set(now, {
2605
- hours: hours,
2606
- minutes: minutes,
2607
- seconds: 0,
2608
- milliseconds: 0
2609
- });
2610
- };
2611
- /**
2612
- * Offsets are signed: negative lands before the anchor, positive after. They are
2613
- * applied as calendar durations, so "+ 1 month" lands on the same day of the next
2614
- * month rather than a fixed number of days later.
2615
- */
2616
- var resolveAnimationRange = function resolveAnimationRange(setting, now) {
2617
- if (now === void 0) {
2618
- now = dateUtils.utc();
2619
- }
2620
- var _activeOffsets = activeOffsets(setting),
2621
- startOffset = _activeOffsets.startOffset,
2622
- endOffset = _activeOffsets.endOffset;
2623
- var anchor = resolveAnchorInstant(setting, now);
2624
- return {
2625
- animationStartTime: toAnimationTime(dateUtils.add(anchor, offsetToDuration(startOffset))),
2626
- animationEndTime: toAnimationTime(dateUtils.add(anchor, offsetToDuration(endOffset)))
2627
- };
2628
- };
2629
- /** Offsets can use different units, so validity is decided on the resolved instants. */
2630
- /** Exact names its range outright; every other mode resolves one from its offsets. */
2631
- var settingRange = function settingRange(setting, now) {
2632
- if (now === void 0) {
2633
- now = dateUtils.utc();
2634
- }
2635
- return setting.mode === AnimationLengthMode.Exact ? setting.exact : resolveAnimationRange(setting, now);
2636
- };
2637
- /* Exact times are picked to the minute, so seconds must not decide validity. */
2638
- var isValidAnimationLength = function isValidAnimationLength(setting, now) {
2639
- if (now === void 0) {
2640
- now = dateUtils.utc();
2641
- }
2642
- var _settingRange = settingRange(setting, now),
2643
- animationStartTime = _settingRange.animationStartTime,
2644
- animationEndTime = _settingRange.animationEndTime;
2645
- return dateUtils.isBefore(dateUtils.utc(animationStartTime).setSeconds(0, 0), dateUtils.utc(animationEndTime).setSeconds(0, 0));
2646
- };
2647
- /** Frames the animation may hold; the range is expanded one entry per time step. */
2648
- var MAX_ANIMATION_STEPS = 10000;
2649
- /**
2650
- * The cost of an animation is its span divided by the time step, so a wide range
2651
- * is affordable with a coarse step and unaffordable with a fine one.
2652
- */
2653
- var isWithinAnimationStepLimit = function isWithinAnimationStepLimit(setting, timeStepInMinutes, now) {
2654
- if (now === void 0) {
2655
- now = dateUtils.utc();
2656
- }
2657
- if (!timeStepInMinutes) {
2658
- return true;
2659
- }
2660
- var _settingRange2 = settingRange(setting, now),
2661
- animationStartTime = _settingRange2.animationStartTime,
2662
- animationEndTime = _settingRange2.animationEndTime;
2663
- var spanInMinutes = dateUtils.differenceInMinutes(dateUtils.utc(animationEndTime), dateUtils.utc(animationStartTime));
2664
- return Math.abs(spanInMinutes) / timeStepInMinutes <= MAX_ANIMATION_STEPS;
2665
- };
2666
- /* Shares the animation-time format, so a typed and a dragged range serialise alike. */
2667
- var toExactAnimationTime = toAnimationTime;
2668
- /** A day either side of now, so an exact window opens on something usable. */
2669
- var defaultExactSelections = function defaultExactSelections(now) {
2670
- if (now === void 0) {
2671
- now = dateUtils.utc();
2672
- }
2673
- var wholeMinute = dateUtils.set(now, {
2674
- seconds: 0,
2675
- milliseconds: 0
2676
- });
2677
- return {
2678
- animationStartTime: toExactAnimationTime(dateUtils.sub(wholeMinute, {
2679
- days: 1
2680
- })),
2681
- animationEndTime: toExactAnimationTime(dateUtils.add(wholeMinute, {
2682
- days: 1
2683
- }))
2684
- };
2685
- };
2686
- /** Starts Exact from whatever range is already on screen, falling back to the defaults. */
2687
- var exactAnimationLengthFrom = function exactAnimationLengthFrom(_ref3, now) {
2688
- var animationStartTime = _ref3.animationStartTime,
2689
- animationEndTime = _ref3.animationEndTime;
2690
- if (now === void 0) {
2691
- now = dateUtils.utc();
2692
- }
2693
- return {
2694
- mode: AnimationLengthMode.Exact,
2695
- exact: animationStartTime && animationEndTime ? {
2696
- animationStartTime: animationStartTime,
2697
- animationEndTime: animationEndTime
2698
- } : defaultExactSelections(now)
2699
- };
2700
- };
2701
- var getOffsetFromTime = function getOffsetFromTime(time, anchorTime) {
2702
- var relativeTime = dateUtils.utc(time);
2703
- var value = dateUtils.differenceInMinutes(relativeTime, anchorTime);
2704
- return {
2705
- unit: 'minutes',
2706
- value: value
2707
- };
2708
- };
2709
- var getAnimationLengthFromRange = function getAnimationLengthFromRange(mode, _ref4, now) {
2710
- var animationStartTime = _ref4.animationStartTime,
2711
- animationEndTime = _ref4.animationEndTime;
2712
- if (now === void 0) {
2713
- now = dateUtils.utc();
2714
- }
2715
- switch (mode) {
2716
- case AnimationLengthMode.Now:
2717
- {
2718
- return {
2719
- mode: mode,
2720
- now: {
2721
- startOffset: getOffsetFromTime(animationStartTime, now),
2722
- endOffset: getOffsetFromTime(animationEndTime, now)
2723
- }
2724
- };
2725
- }
2726
- case AnimationLengthMode.RecurringPeriod:
2727
- {
2728
- var today = dateUtils.startOfDayUTC(now);
2729
- return {
2730
- mode: mode,
2731
- recurringPeriod: {
2732
- anchorTime: '00:00',
2733
- startOffset: getOffsetFromTime(animationStartTime, today),
2734
- endOffset: getOffsetFromTime(animationEndTime, today)
2735
- }
2736
- };
2499
+ },
2500
+ onMouseOver: function onMouseOver() {
2501
+ return setIsMouseOverCanvas(true);
2502
+ },
2503
+ sx: {
2504
+ position: 'absolute',
2505
+ top: '-40px',
2506
+ left: 0,
2507
+ right: 0,
2508
+ bottom: 0,
2509
+ height: TIME_SLIDER_LEGEND_HEIGHT + " + 40px",
2510
+ zIndex: isMouseOverCanvas ? 100 : 0
2737
2511
  }
2738
- case AnimationLengthMode.Exact:
2739
- default:
2740
- return exactAnimationLengthFrom({
2741
- animationStartTime: animationStartTime,
2742
- animationEndTime: animationEndTime
2743
- });
2744
- }
2512
+ })]
2513
+ });
2745
2514
  };
2746
2515
 
2747
2516
  var styles$2 = {
@@ -2834,14 +2603,6 @@ var AnchorTimeField = function AnchorTimeField(_ref) {
2834
2603
 
2835
2604
  var selectableModes = [AnimationLengthMode.Auto, AnimationLengthMode.Now, AnimationLengthMode.RecurringPeriod, AnimationLengthMode.ReferenceTime, AnimationLengthMode.Exact];
2836
2605
  var OFFSET_MAX_DIGITS = 3;
2837
- var maxOffsetByUnit = {
2838
- minutes: 999,
2839
- hours: 999,
2840
- days: 365,
2841
- weeks: 99,
2842
- months: 99,
2843
- years: 99
2844
- };
2845
2606
  /* One line of MuiFormHelperText: 20px line box plus its 3px top margin. */
2846
2607
  var RESERVED_ERROR_HEIGHT = '23px';
2847
2608
  /* Every row splits the panel the same way, so the fields line up in two columns. */
@@ -2924,6 +2685,7 @@ var AnimationLengthMenu = function AnimationLengthMenu(_ref) {
2924
2685
  referenceTimeFromLayer = _ref.referenceTimeFromLayer;
2925
2686
  var _useTimesliderTransla = useTimesliderTranslation(),
2926
2687
  t = _useTimesliderTransla.t;
2688
+ var modeGroupLabelId = React.useId();
2927
2689
  /* Spreading the setting keeps every other mode's selections while this one changes. */
2928
2690
  var changeMode = function changeMode(mode) {
2929
2691
  if (isRecurringPeriodMode(mode)) {
@@ -3002,11 +2764,10 @@ var AnimationLengthMenu = function AnimationLengthMenu(_ref) {
3002
2764
  var asTimeOffset = function asTimeOffset(_ref2) {
3003
2765
  var value = _ref2.value,
3004
2766
  unit = _ref2.unit;
3005
- var maxOffset = maxOffsetByUnit[unit];
3006
- return {
3007
- value: Math.max(-maxOffset, Math.min(maxOffset, value)),
2767
+ return clampOffset({
2768
+ value: value,
3008
2769
  unit: unit
3009
- };
2770
+ });
3010
2771
  };
3011
2772
  /* One unit spans the whole window, so picking it on either field moves both. */
3012
2773
  var changeStartOffset = function changeStartOffset(offset, isStep) {
@@ -3047,12 +2808,6 @@ var AnimationLengthMenu = function AnimationLengthMenu(_ref) {
3047
2808
  var asDisplayTime = function asDisplayTime(time) {
3048
2809
  return time ? "" + dateUtils.dateToString(dateUtils.utc(time), dateUtils.DATE_FORMAT_DATEPICKER, true) : '';
3049
2810
  };
3050
- /* The surrounding Menu dismisses itself on Tab; keep it open so focus can reach the next field. */
3051
- var keepMenuOpenOnTab = function keepMenuOpenOnTab(event) {
3052
- if (event.key === 'Tab') {
3053
- event.stopPropagation();
3054
- }
3055
- };
3056
2811
  var isSteppingExactTimeRef = React.useRef(false);
3057
2812
  var handleExactTimeKeyDown = function handleExactTimeKeyDown(event) {
3058
2813
  isSteppingExactTimeRef.current = event.key === 'ArrowUp' || event.key === 'ArrowDown';
@@ -3083,12 +2838,13 @@ var AnimationLengthMenu = function AnimationLengthMenu(_ref) {
3083
2838
  }), isStep);
3084
2839
  };
3085
2840
  return jsxs(Box, {
3086
- onKeyDown: keepMenuOpenOnTab,
3087
2841
  sx: styles$1.menu,
3088
2842
  children: [jsx(Typography, {
2843
+ id: modeGroupLabelId,
3089
2844
  sx: styles$1.sectionTitle,
3090
2845
  children: t('timeslider-animation-length')
3091
2846
  }), jsx(RadioGroup, {
2847
+ "aria-labelledby": modeGroupLabelId,
3092
2848
  onChange: function onChange(event) {
3093
2849
  return changeMode(event.target.value);
3094
2850
  },
@@ -3330,6 +3086,8 @@ var AnimationLengthPanel = function AnimationLengthPanel(_ref) {
3330
3086
  event.stopPropagation();
3331
3087
  },
3332
3088
  children: [jsxs(CustomIconButton, {
3089
+ "aria-expanded": open,
3090
+ "aria-haspopup": "dialog",
3333
3091
  "aria-label": t('timeslider-animation-length'),
3334
3092
  "data-testid": "animationLengthPanelButton",
3335
3093
  isSelected: true,
@@ -3347,7 +3105,7 @@ var AnimationLengthPanel = function AnimationLengthPanel(_ref) {
3347
3105
  sx: styles.buttonLabel,
3348
3106
  children: buttonLabel
3349
3107
  })]
3350
- }), !!anchorRef && jsx(Menu, {
3108
+ }), !!anchorRef && jsx(Popover, {
3351
3109
  anchorEl: anchorRef,
3352
3110
  anchorOrigin: {
3353
3111
  vertical: -15,
@@ -3362,6 +3120,10 @@ var AnimationLengthPanel = function AnimationLengthPanel(_ref) {
3362
3120
  sx: {
3363
3121
  backgroundColor: 'transparent'
3364
3122
  }
3123
+ },
3124
+ paper: {
3125
+ role: 'dialog',
3126
+ 'aria-label': t('timeslider-animation-length')
3365
3127
  }
3366
3128
  },
3367
3129
  transformOrigin: {
@@ -3506,6 +3268,225 @@ var ControlButtons = function ControlButtons(_ref) {
3506
3268
  });
3507
3269
  };
3508
3270
 
3271
+ var THROTTLE_WAIT_TIME = 300; // [ms]
3272
+ var THROTTLE_OPTIONS = {
3273
+ trailing: false // Invoke function when called, but not more than once every <THROTTLE_WAIT_TIME>.
3274
+ };
3275
+ var TimeSliderMenu = function TimeSliderMenu(_ref) {
3276
+ var marks = _ref.marks,
3277
+ value = _ref.value,
3278
+ allOptionsDisabled = _ref.allOptionsDisabled,
3279
+ isAutoDisabled = _ref.isAutoDisabled,
3280
+ handleAutoClick = _ref.handleAutoClick,
3281
+ isAutoSelected = _ref.isAutoSelected,
3282
+ title = _ref.title,
3283
+ icon = _ref.icon,
3284
+ _ref$onChangeSliderVa = _ref.onChangeSliderValue,
3285
+ onChangeSliderValue = _ref$onChangeSliderVa === void 0 ? function () {} : _ref$onChangeSliderVa,
3286
+ onSelectSliderValue = _ref.onSelectSliderValue,
3287
+ displayCurrentLength = _ref.displayCurrentLength,
3288
+ _ref$disbleUserSetDur = _ref.disbleUserSetDuration,
3289
+ disbleUserSetDuration = _ref$disbleUserSetDur === void 0 ? true : _ref$disbleUserSetDur,
3290
+ customLengthSelector = _ref.customLengthSelector,
3291
+ _ref$setOpen = _ref.setOpen,
3292
+ setOpen = _ref$setOpen === void 0 ? function () {} : _ref$setOpen,
3293
+ _ref$customFieldHeade = _ref.customFieldHeader,
3294
+ customFieldHeader = _ref$customFieldHeade === void 0 ? 'timeslider-length' : _ref$customFieldHeade,
3295
+ open = _ref.open;
3296
+ var currentMarkIndex = marks.findIndex(function (mark) {
3297
+ return mark.value === value;
3298
+ });
3299
+ var currentMark = marks[currentMarkIndex];
3300
+ var _React$useState = React.useState(null),
3301
+ anchorRef = _React$useState[0],
3302
+ setAnchorRef = _React$useState[1];
3303
+ var _useTimesliderTransla = useTimesliderTranslation(),
3304
+ t = _useTimesliderTransla.t;
3305
+ var _useState = useState(false),
3306
+ displayLengthInputField = _useState[0],
3307
+ setDisplayLengthInputField = _useState[1];
3308
+ React.useEffect(function () {
3309
+ if (!open && displayLengthInputField) {
3310
+ setDisplayLengthInputField(false);
3311
+ }
3312
+ }, [displayLengthInputField, open]);
3313
+ var getDisplayTextFromMark = function getDisplayTextFromMark(mark) {
3314
+ if (!(mark != null && mark.label)) {
3315
+ return '';
3316
+ }
3317
+ var parts = t(mark.label).split(' ');
3318
+ if (parts.length > 1) {
3319
+ return parts[0] + parts.slice(1).join(' ');
3320
+ }
3321
+ return parts.join('');
3322
+ };
3323
+ var buttonText = function buttonText(value, displayCurrentLength, currentMark) {
3324
+ if (displayCurrentLength) {
3325
+ if (!Number.isInteger(value)) {
3326
+ return value + "m";
3327
+ }
3328
+ return minutesToDescribedDuration(value);
3329
+ }
3330
+ if (currentMark != null && currentMark.label) {
3331
+ return getDisplayTextFromMark(currentMark);
3332
+ }
3333
+ return '';
3334
+ };
3335
+ // eslint-disable-next-line react-hooks/exhaustive-deps
3336
+ var throttledScroll = React.useCallback(throttle(function (newValue) {
3337
+ onChangeSliderValue(newValue);
3338
+ }, THROTTLE_WAIT_TIME, THROTTLE_OPTIONS), []);
3339
+ var onWheel = React.useCallback(function (event) {
3340
+ var direction = event.deltaY < 0 ? 1 : -1;
3341
+ var newValue = marks[Math.min(Math.max(currentMarkIndex - direction, 0), marks.length - 1)];
3342
+ throttledScroll(newValue.value);
3343
+ }, [currentMarkIndex, marks, throttledScroll]);
3344
+ var buttonDisplayText = buttonText(value, displayCurrentLength, currentMark);
3345
+ var menuDisplayValue = !Number.isInteger(value) ? value + "m" : minutesToDescribedDuration(value);
3346
+ var displayValue = isAutoSelected ? "A: " + buttonDisplayText : buttonDisplayText;
3347
+ return jsxs(Box, {
3348
+ onKeyDown: function onKeyDown(event) {
3349
+ event.stopPropagation();
3350
+ },
3351
+ children: [jsxs(CustomIconButton, {
3352
+ isSelected: true,
3353
+ onClick: function onClick() {
3354
+ setOpen(true);
3355
+ },
3356
+ onWheel: onWheel,
3357
+ ref: setAnchorRef,
3358
+ sx: {
3359
+ padding: '0px',
3360
+ paddingRight: '12px',
3361
+ width: '100%!important',
3362
+ minWidth: '64px',
3363
+ justifyContent: 'space-between'
3364
+ },
3365
+ tooltipTitle: title,
3366
+ variant: "tool",
3367
+ children: [icon, jsx(Typography, {
3368
+ sx: {
3369
+ fontSize: 12,
3370
+ fontWeight: 400,
3371
+ textTransform: 'none'
3372
+ },
3373
+ children: displayValue
3374
+ })]
3375
+ }), !!anchorRef && jsxs(Menu, {
3376
+ anchorEl: anchorRef,
3377
+ anchorOrigin: {
3378
+ vertical: -15,
3379
+ horizontal: 'center'
3380
+ },
3381
+ onClose: function onClose() {
3382
+ return setOpen(false);
3383
+ },
3384
+ open: open,
3385
+ slotProps: {
3386
+ backdrop: {
3387
+ sx: {
3388
+ backgroundColor: 'transparent'
3389
+ }
3390
+ }
3391
+ },
3392
+ transformOrigin: {
3393
+ vertical: 'bottom',
3394
+ horizontal: 'center'
3395
+ },
3396
+ children: [jsx(MenuItem, {
3397
+ disabled: true,
3398
+ sx: {
3399
+ fontSize: '12px',
3400
+ opacity: 0.67,
3401
+ padding: '0px 12px',
3402
+ '&.MuiMenuItem-root': {
3403
+ minHeight: '30px!important',
3404
+ minWidth: 150
3405
+ }
3406
+ },
3407
+ children: title
3408
+ }), marks.map(function (mark) {
3409
+ var isSelected = (currentMark == null ? void 0 : currentMark.value) === mark.value;
3410
+ return jsx(MenuItem, {
3411
+ disabled: allOptionsDisabled,
3412
+ onClick: function onClick() {
3413
+ if (!isSelected) {
3414
+ onChangeSliderValue(mark.value);
3415
+ }
3416
+ onSelectSliderValue == null || onSelectSliderValue(mark.value);
3417
+ setOpen(false);
3418
+ },
3419
+ selected: !!isSelected && !displayLengthInputField,
3420
+ children: mark.label || mark.text
3421
+ }, mark.value);
3422
+ }), !!handleAutoClick && jsx(MenuItem, {
3423
+ disabled: allOptionsDisabled || isAutoDisabled,
3424
+ onClick: function onClick() {
3425
+ handleAutoClick();
3426
+ setOpen(false);
3427
+ },
3428
+ selected: isAutoSelected,
3429
+ children: t('timeslider-auto')
3430
+ }), !!displayCurrentLength && jsxs(MenuItem, {
3431
+ disabled: disbleUserSetDuration,
3432
+ onClick: function onClick() {
3433
+ return setDisplayLengthInputField(true);
3434
+ },
3435
+ sx: {
3436
+ maxWidth: '175px'
3437
+ },
3438
+ children: [!!displayLengthInputField && customLengthSelector, !displayLengthInputField && jsxs(Box, {
3439
+ sx: {
3440
+ flexDirection: 'column',
3441
+ display: 'flex',
3442
+ flexGrow: 1
3443
+ },
3444
+ children: [jsx(CardHeader, {
3445
+ slotProps: {
3446
+ title: {
3447
+ sx: {
3448
+ fontSize: 12
3449
+ }
3450
+ }
3451
+ },
3452
+ sx: {
3453
+ height: 2,
3454
+ padding: 0,
3455
+ color: 'geowebColors.greys.accessible'
3456
+ },
3457
+ title: t(customFieldHeader)
3458
+ }), jsxs(Box, {
3459
+ sx: {
3460
+ flexDirection: 'row',
3461
+ display: 'flex',
3462
+ justifyContent: 'space-between',
3463
+ flexGrow: 1,
3464
+ paddingTop: 0.5
3465
+ },
3466
+ children: [jsx(Typography, {
3467
+ sx: {
3468
+ fontSize: 14,
3469
+ textTransform: 'none',
3470
+ lineHeight: 2
3471
+ },
3472
+ children: menuDisplayValue
3473
+ }), jsx(CustomTooltip, {
3474
+ title: t('timeslider-edit'),
3475
+ children: jsx("span", {
3476
+ children: jsx(CustomIconButton, {
3477
+ "aria-label": "edit animation option",
3478
+ "data-testid": "editAnimationOption",
3479
+ children: jsx(Edit, {})
3480
+ })
3481
+ })
3482
+ })]
3483
+ })]
3484
+ })]
3485
+ })]
3486
+ })]
3487
+ });
3488
+ };
3489
+
3509
3490
  var SpeedButton = function SpeedButton(_ref) {
3510
3491
  var animationDelay = _ref.animationDelay,
3511
3492
  setMapAnimationDelay = _ref.setMapAnimationDelay;
@@ -3975,9 +3956,11 @@ var OptionsMenu = function OptionsMenu(_ref) {
3975
3956
  size: {
3976
3957
  xs: 'auto'
3977
3958
  },
3978
- children: animationLengthBtn || jsx(AnimationLengthButton, {
3979
- onChangeAnimationLength: function onChangeAnimationLength() {},
3980
- toggleAnimationLengthAuto: function toggleAnimationLengthAuto() {}
3959
+ children: animationLengthBtn || jsx(AnimationLengthPanel, {
3960
+ onChange: function onChange() {},
3961
+ setting: {
3962
+ mode: AnimationLengthMode.Auto
3963
+ }
3981
3964
  })
3982
3965
  }), jsx(Grid, {
3983
3966
  size: {
@@ -4696,4 +4679,4 @@ var DemoWrapper = function DemoWrapper(_ref2) {
4696
4679
  });
4697
4680
  };
4698
4681
 
4699
- export { ANCHOR_MINUTE_STEP, AUTO_MOVE_AREA_PADDING, AnchorTimeField, AnimationLength, AnimationLengthButton, AnimationLengthMenu, AnimationLengthMode, AnimationLengthPanel, AutoUpdateButton, _BackwardForwardStepButton as BackwardForwardStepButton, ControlButtons, DRAG_AREA_WIDTH, DemoWrapper, HideSliderButton, MAX_ANIMATION_STEPS, NowButton, OptionsMenu, OptionsMenuButton, PlayButton, Scale, SpeedButton, TIME_SLIDER_LEGEND_HEIGHT, TIME_SLIDER_NAMESPACE, TimeInMinutes, TimeSlider, TimeSliderButtons, TimeSliderCurrentTimeBox, TimeSliderLegend, TimeSliderMenu, TimeSpanButton, _TimeStepButton as TimeStepButton, activeOffsets, anchorTimeToPickerDate, asDisplayTime, calculateMouseTimeAndCheckBounds, convertStringTimeToUnix, defaultAnimationDelayAtStart, defaultDelay, defaultEndOffset, defaultExactSelections, defaultOffsetSelections, defaultRecurringPeriodSelections, defaultSecondsPerPx, defaultStartOffset, defaultTimeSpan, defaultTimeStep, exactAnimationLengthFrom, getAnimationLengthFromRange, getAutoMoveAreaWidth, getFilteredTime, getFundamentalScale, getLargestTimeUnit, getNewCenterOfFixedPointZoom, getPixelsBetweenViewportAndTimesliderOnLeft, getPositions, getScaleFromSpan, getSpeedDelay, getTimeBoxWidth, getTimeSliderLegendElement, getTimeSliderLegendId, handleSetNowEvent, isAnchoredMode, isDraggingEndWithinBounds, isDraggingStartWithinBounds, isInsideAnimationArea, isLeftAnimationIconArea, isRecurringPeriodMode, isRightAnimationIconArea, isSelectedTimeIconArea, isValidAnchorTime, isValidAnimationLength, isWithinAnimationStepLimit, marks$1 as marks, minutesToDescribedDuration, modeTranslationKey, moveRelativeToTimePoint, moveSelectedTimePx, onsetNewDateDebounced, pickerDateToAnchorTime, pixelToTimestamp, resolveAnchorInstant, resolveAnimationRange, roundWithTimeStep, scaleToSecondsPerPx, scalingCoefficient, secondsPerPxFromCanvasWidth, secondsPerPxToScale, secondsPerPxValues, setNextTimeStep, setPreviousTimeStep, settingRange, shortUnitTranslationKey, spanToScale, speedFactors, timeBoxGeom, timeSliderLegendDefaultProps, timeSliderUtils, timeUnits, timeValues, timesliderTranslations, timestampToPixel, timestampToPixelEdges, toAnchorTime, toExactAnimationTime, unitTranslationKey, useCanvasTarget, withModeSelections };
4682
+ export { ANCHOR_MINUTE_STEP, AUTO_MOVE_AREA_PADDING, AnchorTimeField, AnimationLengthMenu, AnimationLengthMode, AnimationLengthPanel, AutoUpdateButton, _BackwardForwardStepButton as BackwardForwardStepButton, ControlButtons, DRAG_AREA_WIDTH, DemoWrapper, HideSliderButton, MAX_ANIMATION_STEPS, NowButton, OptionsMenu, OptionsMenuButton, PlayButton, Scale, SpeedButton, TIME_SLIDER_LEGEND_HEIGHT, TIME_SLIDER_NAMESPACE, TimeInMinutes, TimeSlider, TimeSliderButtons, TimeSliderCurrentTimeBox, TimeSliderLegend, TimeSliderMenu, TimeSpanButton, _TimeStepButton as TimeStepButton, activeEditUnit, activeOffsets, anchorInstantFor, anchorTimeToPickerDate, asDisplayTime, clampOffset, convertStringTimeToUnix, defaultAnimationDelayAtStart, defaultDelay, defaultEndOffset, defaultExactSelections, defaultOffsetSelections, defaultRecurringPeriodSelections, defaultSecondsPerPx, defaultStartOffset, defaultTimeSpan, defaultTimeStep, exactAnimationLengthFrom, exceedsAnimationStepLimit, getAnimationLengthFromRange, getAutoMoveAreaWidth, getFilteredTime, getFundamentalScale, getLargestTimeUnit, getNewCenterOfFixedPointZoom, getPixelsBetweenViewportAndTimesliderOnLeft, getPositions, getScaleFromSpan, getSpeedDelay, getTimeBoxWidth, getTimeSliderLegendElement, getTimeSliderLegendId, handleSetNowEvent, isAnchoredMode, isDraggingEndWithinBounds, isDraggingStartWithinBounds, isInsideAnimationArea, isLeftAnimationIconArea, isRecurringPeriodMode, isRightAnimationIconArea, isSelectedTimeIconArea, isValidAnchorTime, isValidAnimationLength, isWithinAnimationStepLimit, marks$1 as marks, maxOffsetByUnit, minutesToDescribedDuration, modeTranslationKey, moveSelectedTimePx, offsetForInstant, onsetNewDateDebounced, pickerDateToAnchorTime, pixelToTimestamp, resolveAnchorInstant, resolveAnimationRange, roundWithTimeStep, scaleToSecondsPerPx, scalingCoefficient, secondsPerPxFromCanvasWidth, secondsPerPxToScale, setNextTimeStep, setPreviousTimeStep, settingRange, shortUnitTranslationKey, snapInstantToOffsetGrid, spanToScale, speedFactors, timeBoxGeom, timeSliderUtils, timeUnits, timeValues, timesliderTranslations, timestampToPixel, timestampToPixelEdges, toAnchorTime, toExactAnimationTime, unitTranslationKey, useCanvasTarget, withDraggedRange, withModeSelections };