Skip to content

Commit

Permalink
fix: value update in time slider panel
Browse files Browse the repository at this point in the history
ahennr committed Jan 22, 2025
1 parent 91a1ab5 commit 4eefb99
Showing 2 changed files with 420 additions and 505 deletions.
151 changes: 97 additions & 54 deletions src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.example.md
Original file line number Diff line number Diff line change
@@ -2,88 +2,131 @@ This example demonstrates the usage of the TimeLayerSliderPanel
(Data: IEM generated CONUS composite of NWS NEXRAD WSR-88D level III base reflectivity, Iowa State University)

```jsx
import MapComponent from '@terrestris/react-geo/dist/Map/MapComponent/MapComponent';
import TimeLayerSliderPanel from '@terrestris/react-geo/dist/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel';
import MapContext from '@terrestris/react-util/dist/Context/MapContext/MapContext';
import moment from 'moment';
import { getCenter } from 'ol/extent';
import OlLayerTile from 'ol/layer/Tile';
import OlMap from 'ol/Map';
import { transformExtent } from 'ol/proj';
import { transformExtent } from 'ol/proj'
import OlSourceOSM from 'ol/source/OSM';
import OlSourceTileWMS from 'ol/source/TileWMS';
import OlView from 'ol/View';
import * as React from 'react';

class TimeLayerSliderPanelExample extends React.Component {

constructor(props) {

super(props);

this.mapDivId = `map-${Math.random()}`;
var extent = transformExtent([-126, 24, -66, 50], 'EPSG:4326', 'EPSG:3857');
this.layers = [
new OlLayerTile({
extent: extent,
type: 'WMSTime',
timeFormat: 'YYYY-MM-DDTHH:mm',
roundToFullHours: true,
source: new OlSourceTileWMS({
attributions: ['Iowa State University'],
url: '//mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi',
params: {LAYERS: 'nexrad-n0r-wmst'}
})
})
];
import {
useEffect,
useMemo,
useState
} from 'react';

const TimeLayerSliderPanelExample = () => {

var extent = useMemo(() => transformExtent([-126, 24, -66, 50], 'EPSG:4326', 'EPSG:3857'), []);

const [value, setValue] = useState();
const [map, setMap] = useState();

const timeLayer = useMemo(() => new OlLayerTile({
extent: extent,
type: 'WMSTime',
timeFormat: 'YYYY-MM-DDTHH:mm',
roundToFullHours: true,
source: new OlSourceTileWMS({
attributions: ['Iowa State University'],
url: '//mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi',
params: {LAYERS: 'nexrad-n0r-wmst'}
})
}), [extent]);


const timeLayer2 = useMemo(() => new OlLayerTile({
extent: extent,
type: 'WMSTime',
timeFormat: 'YYYY-MM-DDTHH:mm',
roundToFullHours: true,
source: new OlSourceTileWMS({
attributions: ['Iowa State University'],
url: '//mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi',
params: {LAYERS: 'nexrad-n0r-wmst'}
})
}), [extent]);

this.map = new OlMap({
const timeLayer3 = useMemo(() => new OlLayerTile({
extent: extent,
type: 'WMSTime',
timeFormat: 'YYYY-MM-DDTHH:mm',
roundToFullHours: true,
source: new OlSourceTileWMS({
attributions: ['Iowa State University'],
url: '//mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-t.cgi',
params: {LAYERS: 'nexrad-n0r-wmst'}
})
}), [extent]);

useEffect(() => {
const newMap = new OlMap({
layers: [
new OlLayerTile({
name: 'OSM',
properties: {
name: 'OSM'
},
source: new OlSourceOSM()
}),
...this.layers
timeLayer,
timeLayer2,
timeLayer3
],
view: new OlView({
center: getCenter(extent),
zoom: 4
})
});
}

componentDidMount() {
this.map.setTarget(this.mapDivId);
}

render() {
const tooltips = {
setToNow: 'Set to now',
hours: 'Hours',
days: 'Days',
weeks: 'Weeks',
months: 'Months',
years: 'Years',
dataRange: 'Set data range'
};

return (
<div>
<div
id={this.mapDivId}

setMap(newMap);
}, [extent, timeLayer, timeLayer2, timeLayer3]);

const tooltips = {
setToNow: 'Set to now',
hours: 'Hours',
days: 'Days',
weeks: 'Weeks',
months: 'Months',
years: 'Years',
dataRange: 'Set data range'
};

const initStartDate = moment().subtract(3, 'hours');
const initEndDate = moment();

const onTimeChanged = (newTimeValue) => setValue(newTimeValue);

useEffect(() => {
setValue(moment().subtract(1, 'hours'))
}, []);

return (
<div>
<MapContext.Provider value={map}>
<MapComponent
map={map}
style={{
position: 'relative',
height: '400px'
}}
/>
<TimeLayerSliderPanel
initStartDate={moment().subtract(3, 'hours')}
initEndDate={moment()}
timeAwareLayers={this.layers}
initStartDate={initStartDate}
initEndDate={initEndDate}
timeAwareLayers={[timeLayer, timeLayer2, timeLayer3]}
tooltips={tooltips}
autoPlaySpeedOptions={[0.5, 1, 2, 3]}
dateFormat='YYYY-MM-DD HH:mm'
value={value}
onChange={onTimeChanged}
/>
</div>
);
}
</MapContext.Provider>
</div>
);
}

<TimeLayerSliderPanelExample />
774 changes: 323 additions & 451 deletions src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.tsx
Original file line number Diff line number Diff line change
@@ -8,17 +8,14 @@ import {
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { WmsLayer } from '@terrestris/ol-util/dist/typeUtils/typeUtils';
import { TimeLayerAwareConfig } from '@terrestris/react-util/dist/Hooks/useTimeLayerAware/useTimeLayerAware';
import { DatePicker, Popover, Select, Spin } from 'antd';
import dayjs from 'dayjs';
import { debounce } from 'lodash';
import _isEqual from 'lodash/isEqual';
import _isFinite from 'lodash/isFinite';
import _isFunction from 'lodash/isFunction';
import _isNil from 'lodash/isNil';
import moment, { Moment } from 'moment';
import { getUid } from 'ol';
import { TileWMS } from 'ol/source';
import ImageWMS from 'ol/source/ImageWMS';
import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
import {ImageWMS, TileWMS} from 'ol/source';
import React, {useCallback, useEffect, useState} from 'react';

import SimpleButton from '../../Button/SimpleButton/SimpleButton';
import ToggleButton from '../../Button/ToggleButton/ToggleButton';
@@ -27,509 +24,384 @@ import TimeSlider from '../../Slider/TimeSlider/TimeSlider';
const RangePicker = DatePicker.RangePicker;
const Option = Select.Option;

export type Tooltips = {
hours: string;
export type TimeLayerSliderPanelTooltips = {
dataRange: string;
days: string;
weeks: string;
hours: string;
months: string;
years: string;
setToMostRecent: string;
dataRange: string;
weeks: string;
years: string;
};

export type PlaybackSpeedType = 'hours' | 'days' | 'weeks' | 'months' | 'years';

export type TimeLayerSliderPanelProps = {
autoPlaySpeedOptions?: number[];
className?: string;
dateFormat?: string;
initEndDate?: moment.Moment;
initStartDate?: moment.Moment;
onChange?: (arg: moment.Moment) => void;
timeAwareLayers: WmsLayer[];
tooltips?: TimeLayerSliderPanelTooltips;
value?: moment.Moment;
dateFormat?: string;
tooltips?: Tooltips;
autoPlaySpeedOptions?: number[];
initStartDate?: moment.Moment;
initEndDate?: moment.Moment;
};

export type TimeLayerSliderPanelState = {
value: moment.Moment;
playbackSpeed: number | PlaybackSpeedType;
autoPlayActive: boolean;
startDate: moment.Moment;
endDate: moment.Moment;
};

/**
* The panel combining all time slider related parts.
*/
export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = memo(
({
className = '',
onChange = () => {},
timeAwareLayers = [],
value = moment(moment.now()),
dateFormat = 'YYYY-MM-DD HH:mm',
tooltips = {
setToMostRecent: 'Set to most recent date',
hours: 'Hours',
days: 'Days',
weeks: 'Weeks',
months: 'Months',
years: 'Years',
dataRange: 'Set data range'
},
autoPlaySpeedOptions = [0.5, 1, 2, 5, 10, 100, 300],
initStartDate = moment(moment.now()),
initEndDate = moment(moment.now()).add(1, 'days')
}) => {
const [currentValue, setCurrentValue] = useState<Moment>(value);
const [playbackSpeed, setPlaybackSpeed] = useState<
number | PlaybackSpeedType
>(1);
const [autoPlayActive, setAutoPlayActive] = useState(false);
const [startDate, setStartDate] = useState<Moment>(initStartDate);
const [endDate, setEndDate] = useState<Moment>(initEndDate);
const [loadingCount, setLoadingCount] = useState(0);

const wmsTimeLayersRef = useRef<TimeLayerAwareConfig[]>([]);
const intervalRef = useRef<number | undefined>(1000);
const prevPropsRef = useRef<TimeLayerSliderPanelProps>();

const isLoading = loadingCount > 0;

const wrapTimeSlider = useCallback(() => {
const wmsTimeLayers: TimeLayerAwareConfig[] = [];
timeAwareLayers.forEach(l => {
if (l.get('type') === 'WMSTime') {
wmsTimeLayers.push({ layer: l });
}
});
wmsTimeLayersRef.current = wmsTimeLayers;
}, [timeAwareLayers]);

const autoPlay = useCallback(() => {
setAutoPlayActive(prevState => !prevState);
}, []);

const onTimeChanged = useCallback(
(val: string | [string, string]) => {
const newTime = moment(val);
setCurrentValue(newTime);
if (onChange) {
onChange(newTime);
}
debouncedWmsTimeHandlerRef.current(val);
},
[onChange]
);
export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = ({
autoPlaySpeedOptions = [0.5, 1, 2, 5, 10, 100, 300],
className = '',
dateFormat = 'YYYY-MM-DD HH:mm',
initEndDate = moment(moment.now()).add(1, 'days'),
initStartDate = moment(moment.now()),
onChange = () => {},
timeAwareLayers = [],
tooltips = {
setToMostRecent: 'Set to most recent date',
hours: 'Hours',
days: 'Days',
weeks: 'Weeks',
months: 'Months',
years: 'Years',
dataRange: 'Set data range'
},
value = moment(moment.now())
}) => {

const updateDataRange = useCallback(([start, end]: [Moment, Moment]) => {
setStartDate(start);
setEndDate(end);
}, []);

const setSliderToMostRecent = useCallback(() => {
setCurrentValue(initEndDate);
setEndDate(initEndDate);
wmsTimeHandler(initEndDate);
}, [initEndDate]);

const onPlaybackSpeedChange = useCallback(
(val: number | PlaybackSpeedType) => {
setPlaybackSpeed(val);
},
[]
);
const [playbackSpeed, setPlaybackSpeed] = useState<number | PlaybackSpeedType>(1);
const [autoPlayActive, setAutoPlayActive] = useState(false);
const [startDate, setStartDate] = useState<Moment>(initStartDate);
const [endDate, setEndDate] = useState<Moment>(initEndDate);
const [loadingCount, setLoadingCount] = useState(0);

const wmsTimeHandler = (val: moment.Moment | string | [string, string]) => {
wmsTimeLayersRef.current.forEach(config => {
if (config.layer && config.layer.get('type') === 'WMSTime') {
const source = config.layer.getSource();
const params = source?.getParams();
let time;
if (Array.isArray(val)) {
time = val[0];
} else {
time = val;
}
if (!moment.isMoment(time)) {
time = moment(time);
}
const timeFormat = config.layer.get('timeFormat');
let newTimeParam: string;
if (
timeFormat.toLowerCase().includes('hh') &&
config.layer.get('roundToFullHours')
) {
time.set('minute', 0);
time.set('second', 0);
newTimeParam = time.toISOString();
} else {
newTimeParam = time.format(timeFormat);
}
const isLoading = loadingCount > 0;

if (params.TIME !== newTimeParam) {
params.TIME = newTimeParam;
source?.updateParams(params);
source?.refresh();
}
}
});
};
const autoPlay = () => setAutoPlayActive(prevState => !prevState);

const debouncedWmsTimeHandlerRef = useRef(
debounce(val => wmsTimeHandler(val), 300)
);

useEffect(() => {
const handler = debouncedWmsTimeHandlerRef.current;

return () => {
handler.cancel();
};
}, []);

const timeSliderCustomHandler = useCallback(
(val: any) => {
const currentMoment = moment(val).milliseconds(0);
if (!currentMoment.isSame(currentValue)) {
const newValue = currentMoment.clone();
if (onChange) {
onChange(newValue);
}
const onTimeChanged = (val: string | [string, string]) => {
const newTime = moment(val);
if (_isFunction(onChange)) {
onChange(newTime);
}
};

const updateDataRange = ([start, end]: [Moment, Moment]) => {
setStartDate(start);
setEndDate(end);
};

const setSliderToMostRecent = () => {
setEndDate(initEndDate);
wmsTimeHandler(initEndDate);
onChange(initEndDate);
};

const onPlaybackSpeedChange= (val: number | PlaybackSpeedType) => setPlaybackSpeed(val);

const wmsTimeHandler = useCallback((val: moment.Moment | string | [string, string]) => {
timeAwareLayers.forEach(layer => {
if (!_isNil(layer) && layer.get('type') === 'WMSTime') {
const source = layer.getSource();
const params = source?.getParams();
let time;
if (Array.isArray(val)) {
time = val[0];
} else {
time = val;
}
if (!moment.isMoment(time)) {
time = moment(time);
}
const timeFormat = layer.get('timeFormat');
let newTimeParam: string;
if (
timeFormat.toLowerCase().includes('hh') &&
layer.get('roundToFullHours')
) {
time.set('minute', 0);
time.set('second', 0);
newTimeParam = time.toISOString();
} else {
newTimeParam = time.format(timeFormat);
}
},
[currentValue, onChange]
);

const findRangeForLayers = useCallback(() => {
if (timeAwareLayers.length === 0) {
return;
if (params.TIME !== newTimeParam) {
params.TIME = newTimeParam;
source?.updateParams(params);
source?.refresh();
}
}
});
}, [timeAwareLayers]);

const startDatesFromLayers: moment.Moment[] = [];
const endDatesFromLayers: moment.Moment[] = [];

timeAwareLayers.forEach(l => {
const layerType = l.get('type');
if (layerType === 'WMSTime') {
const layerStartDate = l.get('startDate');
const layerEndDate = l.get('endDate');
let sdm;
let edm;
if (layerStartDate) {
sdm = moment(layerStartDate);
}
if (layerEndDate) {
edm = moment(layerEndDate);
}
if (sdm) {
startDatesFromLayers.push(sdm);
}
if (edm) {
endDatesFromLayers.push(edm);
}
const findRangeForLayers = useCallback(() => {
if (timeAwareLayers.length === 0) {
return;
}

const startDatesFromLayers: moment.Moment[] = [];
const endDatesFromLayers: moment.Moment[] = [];

timeAwareLayers.forEach(l => {
const layerType = l.get('type');
if (layerType === 'WMSTime') {
const layerStartDate = l.get('startDate');
const layerEndDate = l.get('endDate');
let sdm;
let edm;
if (layerStartDate) {
sdm = moment(layerStartDate);
}
});
if (layerEndDate) {
edm = moment(layerEndDate);
}
if (sdm) {
startDatesFromLayers.push(sdm);
}
if (edm) {
endDatesFromLayers.push(edm);
}
}
});

const newStartDate =
const newStartDate =
startDatesFromLayers.length > 0
? moment.min(startDatesFromLayers)
: startDate;
const newEndDate =
const newEndDate =
endDatesFromLayers.length > 0
? moment.max(endDatesFromLayers)
: endDate;

updateDataRange([newStartDate, newEndDate]);
}, [timeAwareLayers, startDate, endDate, updateDataRange]);
updateDataRange([newStartDate, newEndDate]);
}, [endDate, startDate, timeAwareLayers]);

useEffect(() => {
if (timeAwareLayers.length === 0) {
return;
}
useEffect(() => {
if (timeAwareLayers.length === 0) {
return;
}

const handleTileLoadStart = () => {
setLoadingCount(prevCount => prevCount + 1);
};
const handleTileLoadEnd = () => {
setLoadingCount(prevCount => Math.max(prevCount - 1, 0));
};
const handleImageLoadStart = () => {
setLoadingCount(prevCount => prevCount + 1);
};
const handleImageLoadEnd = () => {
setLoadingCount(prevCount => Math.max(prevCount - 1, 0));
};

const handleTileLoadStart = () => {
setLoadingCount(prevCount => prevCount + 1);
};
const handleTileLoadEnd = () => {
setLoadingCount(prevCount => Math.max(prevCount - 1, 0));
};
const handleImageLoadStart = () => {
setLoadingCount(prevCount => prevCount + 1);
};
const handleImageLoadEnd = () => {
setLoadingCount(prevCount => Math.max(prevCount - 1, 0));
};
timeAwareLayers.forEach(layer => {
if (layer.get('type') === 'WMSTime') {
const source = layer.getSource();

if (source instanceof TileWMS) {
source.on('tileloadstart', handleTileLoadStart);
source.on('tileloadend', handleTileLoadEnd);
source.on('tileloaderror', handleTileLoadEnd);
} else if (source instanceof ImageWMS) {
source.on('imageloadstart', handleImageLoadStart);
source.on('imageloadend', handleImageLoadEnd);
source.on('imageloaderror', handleImageLoadEnd);
}
}
});

return () => {
timeAwareLayers.forEach(layer => {
if (layer.get('type') === 'WMSTime') {
const source = layer.getSource();

if (source instanceof TileWMS) {
source.on('tileloadstart', handleTileLoadStart);
source.on('tileloadend', handleTileLoadEnd);
source.on('tileloaderror', handleTileLoadEnd);
source.un('tileloadstart', handleTileLoadStart);
source.un('tileloadend', handleTileLoadEnd);
source.un('tileloaderror', handleTileLoadEnd);
} else if (source instanceof ImageWMS) {
source.on('imageloadstart', handleImageLoadStart);
source.on('imageloadend', handleImageLoadEnd);
source.on('imageloaderror', handleImageLoadEnd);
source.un('imageloadstart', handleImageLoadStart);
source.un('imageloadend', handleImageLoadEnd);
source.un('imageloaderror', handleImageLoadEnd);
}
}
});
};
}, [timeAwareLayers]);

return () => {
timeAwareLayers.forEach(layer => {
if (layer.get('type') === 'WMSTime') {
const source = layer.getSource();

if (source instanceof TileWMS) {
source.un('tileloadstart', handleTileLoadStart);
source.un('tileloadend', handleTileLoadEnd);
source.un('tileloaderror', handleTileLoadEnd);
} else if (source instanceof ImageWMS) {
source.un('imageloadstart', handleImageLoadStart);
source.un('imageloadend', handleImageLoadEnd);
source.un('imageloaderror', handleImageLoadEnd);
}
}
});
};
}, [timeAwareLayers]);
useEffect(() => {
if (!autoPlayActive) {
return;
}

useEffect(() => {
window.clearInterval(intervalRef.current);
if (!autoPlayActive) {
const interval = window.setInterval(() => {
if (value >= endDate) {
clearInterval(interval);
setAutoPlayActive(false);
return;
}

intervalRef.current = window.setInterval(() => {
if (currentValue >= endDate) {
clearInterval(intervalRef.current);
setAutoPlayActive(false);
return;
}
const newValue: Moment = currentValue.clone();

if (_isFinite(playbackSpeed)) {
wmsTimeHandler(
moment(newValue.clone().add(playbackSpeed, 'hours').format())
);
setCurrentValue(
moment(newValue.clone().add(playbackSpeed, 'hours').format())
);
} else {
const time = moment(
newValue
.clone()
.add(1, playbackSpeed as moment.unitOfTime.DurationConstructor)
.format()
);
wmsTimeHandler(time);
setCurrentValue(time);
}
}, 1000);

return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
}
};
}, [autoPlayActive, currentValue, endDate, playbackSpeed]);

useEffect(() => {
wrapTimeSlider();
return () => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current);
}
};
}, [wrapTimeSlider]);

useEffect(() => {
if (autoPlayActive) {
autoPlay();
}
return () => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current);
}
};
}, [autoPlayActive, autoPlay]);

useEffect(() => {
setStartDate(initStartDate);
setEndDate(initEndDate);
}, [initStartDate, initEndDate]);

useEffect(() => {
const prevProps = prevPropsRef.current;
if (prevProps && prevProps.timeAwareLayers) {
prevProps.timeAwareLayers.forEach((pl, i) => {
if (timeAwareLayers) {
const tpl = timeAwareLayers[i];
if (!_isEqual(getUid(pl), getUid(tpl))) {
wrapTimeSlider();
findRangeForLayers();
}
}
});
const newValue: Moment = value.clone();

if (_isFinite(playbackSpeed)) {
wmsTimeHandler(
moment(newValue.clone().add(playbackSpeed, 'hours').format())
);
onChange(
moment(newValue.clone().add(playbackSpeed, 'hours').format())
);
} else {
const time = moment(
newValue
.clone()
.add(1, playbackSpeed as moment.unitOfTime.DurationConstructor)
.format()
);
wmsTimeHandler(time);
onChange(time);
}
}, 1000);

prevPropsRef.current = { timeAwareLayers };
}, [timeAwareLayers, findRangeForLayers, wrapTimeSlider]);
return () => clearInterval(interval);
}, [autoPlayActive, value, endDate, playbackSpeed, wmsTimeHandler, onChange]);

useEffect(() => {
timeSliderCustomHandler(value);
}, [timeSliderCustomHandler, value]);

useEffect(() => {
setSliderToMostRecent();
}, [setSliderToMostRecent]);

useEffect(() => {
if (autoPlayActive) {
autoPlay();
}
}, [playbackSpeed, autoPlayActive, autoPlay]);
useEffect(() => {
setStartDate(initStartDate);
setEndDate(initEndDate);
}, [initStartDate, initEndDate]);

const resetVisible = true;

const startDateString = startDate.toISOString();
const endDateString = endDate.toISOString();
const valueString = currentValue.toISOString();
const mid = startDate!.clone().add(endDate!.diff(startDate) / 2);
const marks: { [k: string]: any } = {};
const futureClass = moment().isBefore(value) ? ' timeslider-in-future' : '';
const extraCls = className ? className : '';
const disabledCls = timeAwareLayers.length < 1 ? 'no-layers-available' : '';

marks[startDateString] = {
label: startDate!.format(dateFormat)
};
marks[endDateString] = {
label: endDate!.format(dateFormat),
style: {
left: 'unset',
right: 0,
transform: 'translate(50%)'
}
};
marks[mid.toISOString()] = {
label: mid.format(dateFormat)
};

const speedOptions = autoPlaySpeedOptions.map(function (val: number) {
return (
<Option
key={val}
value={val}
>
{val}
</Option>
);
});
useEffect(() => {
if (!_isNil(timeAwareLayers)) {
findRangeForLayers();
}
}, [timeAwareLayers, findRangeForLayers]);

const resetVisible = true;

const startDateString = startDate.toISOString();
const endDateString = endDate.toISOString();
const valueString = value?.toISOString();
const mid = startDate!.clone().add(endDate!.diff(startDate) / 2);
const marks: { [k: string]: any } = {};
const futureClass = moment().isBefore(value) ? ' timeslider-in-future' : '';
const extraCls = className ? className : '';
const disabledCls = timeAwareLayers.length < 1 ? 'no-layers-available' : '';

marks[startDateString] = {
label: startDate!.format(dateFormat)
};
marks[endDateString] = {
label: endDate!.format(dateFormat),
style: {
left: 'unset',
right: 0,
transform: 'translate(50%)'
}
};
marks[mid.toISOString()] = {
label: mid.format(dateFormat)
};

const speedOptions = autoPlaySpeedOptions.map(function (val: number) {
return (
<div className={`time-layer-slider ${disabledCls}`.trim()}>
<Popover
placement="topRight"
title={tooltips.dataRange}
trigger="click"
content={
<RangePicker
showTime={{ format: 'HH:mm' }}
defaultValue={[
dayjs(startDate.toISOString()),
dayjs(endDate.toISOString())
]}
onOk={range => {
if (!range) {
return;
}
const [start, end] = range;
if (!start || !end) {
return;
}

updateDataRange([
moment(start.toISOString()),
moment(end.toISOString())
]);
}}
/>
}
>
<SimpleButton
className="change-datarange-button"
icon={<FontAwesomeIcon icon={faCalendar} />}
/>
</Popover>
{resetVisible ? (
<SimpleButton
type="primary"
icon={<FontAwesomeIcon icon={faSync} />}
onClick={setSliderToMostRecent}
tooltip={tooltips.setToMostRecent}
/>
) : null}
<div className="time-slider-container">
<TimeSlider
className={`${extraCls} timeslider ${futureClass}`.trim()}
formatString={dateFormat}
defaultValue={startDateString}
min={startDateString}
max={endDateString}
value={valueString}
marks={marks}
onChange={onTimeChanged}
<Option
key={val}
value={val}
>
{val}
</Option>
);
});

return (
<div className={`time-layer-slider ${disabledCls}`.trim()}>
<Popover
placement="topRight"
title={tooltips.dataRange}
trigger="click"
content={
<RangePicker
showTime={{ format: 'HH:mm' }}
defaultValue={[
dayjs(startDate.toISOString()),
dayjs(endDate.toISOString())
]}
onOk={range => {
if (!range) {
return;
}
const [start, end] = range;
if (!start || !end) {
return;
}

updateDataRange([
moment(start.toISOString()),
moment(end.toISOString())
]);
}}
/>
<div className="spin-indicator">
<Spin spinning={isLoading} size="small" />
</div>
</div>
<div className="time-value">
{currentValue.format(dateFormat || 'DD.MM.YYYY HH:mm:ss')}
</div>
<ToggleButton
}
>
<SimpleButton
className="change-datarange-button"
icon={<FontAwesomeIcon icon={faCalendar} />}
/>
</Popover>
{resetVisible ? (
<SimpleButton
type="primary"
icon={<FontAwesomeIcon icon={faPlayCircle} />}
className={extraCls + ' playback'}
pressed={autoPlayActive}
onChange={autoPlay}
tooltip={autoPlayActive ? 'Pause' : 'Autoplay'}
aria-label={autoPlayActive ? 'Pause' : 'Autoplay'}
pressedIcon={<FontAwesomeIcon icon={faPauseCircle} />}
icon={<FontAwesomeIcon icon={faSync} />}
onClick={setSliderToMostRecent}
tooltip={tooltips.setToMostRecent}
/>
<Select
defaultValue={'hours'}
className={extraCls + ' speed-picker'}
onChange={onPlaybackSpeedChange}
popupMatchSelectWidth={false}
dropdownStyle={{ minWidth: '100px' }}
>
{speedOptions}
<Option value="hours">{tooltips.hours}</Option>
<Option value="days">{tooltips.days}</Option>
<Option value="weeks">{tooltips.weeks}</Option>
<Option value="months">{tooltips.months}</Option>
<Option value="years">{tooltips.years}</Option>
</Select>
) : null}
<div className="time-slider-container">
<TimeSlider
className={`${extraCls} timeslider ${futureClass}`.trim()}
formatString={dateFormat}
defaultValue={startDateString}
min={startDateString}
max={endDateString}
value={valueString}
marks={marks}
onChange={onTimeChanged}
/>
<div className="spin-indicator">
<Spin spinning={isLoading} size="small" />
</div>
</div>
);
},
(prevProps, nextProps) => {
if (!_isEqual(prevProps.value, nextProps.value)) {
return false;
}
if (!_isEqual(prevProps.timeAwareLayers, nextProps.timeAwareLayers)) {
return false;
}
if (!_isEqual(prevProps.tooltips, nextProps.tooltips)) {
return false;
}
return true;
}
);
<div className="time-value">
{value.format(dateFormat || 'DD.MM.YYYY HH:mm:ss')}
</div>
<ToggleButton
type="primary"
icon={<FontAwesomeIcon icon={faPlayCircle} />}
className={extraCls + ' playback'}
pressed={autoPlayActive}
onChange={autoPlay}
tooltip={autoPlayActive ? 'Pause' : 'Autoplay'}
aria-label={autoPlayActive ? 'Pause' : 'Autoplay'}
pressedIcon={<FontAwesomeIcon icon={faPauseCircle} />}
/>
<Select
defaultValue={'hours'}
className={extraCls + ' speed-picker'}
onChange={onPlaybackSpeedChange}
popupMatchSelectWidth={false}
dropdownStyle={{ minWidth: '100px' }}
>
{speedOptions}
<Option value="hours">{tooltips.hours}</Option>
<Option value="days">{tooltips.days}</Option>
<Option value="weeks">{tooltips.weeks}</Option>
<Option value="months">{tooltips.months}</Option>
<Option value="years">{tooltips.years}</Option>
</Select>
</div>
);
};

export default TimeLayerSliderPanel;

0 comments on commit 4eefb99

Please sign in to comment.