diff --git a/package-lock.json b/package-lock.json index 72aa3bec1..20d002fb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8994,37 +8994,6 @@ "tslib": "^1.10.0" } }, - "portfinder": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.25.tgz", - "integrity": "sha512-6ElJnHBbxVA1XSLgBp7G1FiCkQdlqGzuF7DswL5tcea+E8UpuvPU7beVAjjRwCioTS9ZluNbu+ZyRvgTsmqEBg==", - "dev": true, - "requires": { - "async": "^2.6.2", - "debug": "^3.1.1", - "mkdirp": "^0.5.1" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", diff --git a/src/scripts/components/icons/pause-icon.tsx b/src/scripts/components/icons/pause-icon.tsx new file mode 100644 index 000000000..5bdb955d5 --- /dev/null +++ b/src/scripts/components/icons/pause-icon.tsx @@ -0,0 +1,11 @@ +import React, {FunctionComponent} from 'react'; + +export const PauseIcon: FunctionComponent = () => ( + + + +); diff --git a/src/scripts/components/time-playback/time-playback.ts b/src/scripts/components/time-playback/time-playback.ts new file mode 100644 index 000000000..43e995fb2 --- /dev/null +++ b/src/scripts/components/time-playback/time-playback.ts @@ -0,0 +1,40 @@ +import {FunctionComponent} from 'react'; +import {useSelector, useDispatch} from 'react-redux'; + +import {timeSelector} from '../../selectors/globe/time'; +import setGlobeTime from '../../actions/set-globe-time'; +import {useInterval} from '../../hooks/use-interval'; + +const PLAYBACK_STEP = 1000 * 60 * 60 * 24 * 30; // one month +const PLAYBACK_SPEED = 1000; // increase one step per x milliseconds + +interface Props { + minTime: number; + maxTime: number; + speed?: number; + step?: number; +} + +const TimePlayback: FunctionComponent = ({ + minTime, + maxTime, + speed = PLAYBACK_SPEED, + step = PLAYBACK_STEP +}) => { + const dispatch = useDispatch(); + const time = useSelector(timeSelector); + + useInterval(() => { + let newTime = time + step; + + if (newTime > maxTime) { + newTime = minTime; + } + + dispatch(setGlobeTime(newTime)); + }, speed); + + return null; +}; + +export default TimePlayback; diff --git a/src/scripts/components/time-slider/time-slider.styl b/src/scripts/components/time-slider/time-slider.styl index d9c087273..91e71fc69 100644 --- a/src/scripts/components/time-slider/time-slider.styl +++ b/src/scripts/components/time-slider/time-slider.styl @@ -30,6 +30,7 @@ compareThumb() font-size: emCalc(16px) .container + position: relative min-width: 400px width: 30% @@ -85,3 +86,8 @@ compareThumb() bottom: 15px color: $textWhite transform: translateX(-50%) + +.playButton + position: absolute + top: -4px + right: calc(100% + 1em) diff --git a/src/scripts/components/time-slider/time-slider.tsx b/src/scripts/components/time-slider/time-slider.tsx index bceeeabf1..83cfaf1c8 100644 --- a/src/scripts/components/time-slider/time-slider.tsx +++ b/src/scripts/components/time-slider/time-slider.tsx @@ -18,6 +18,10 @@ import {State} from '../../reducers'; import {selectedLayerIdsSelector} from '../../selectors/layers/selected-ids'; import {getLayerTimeIndex} from '../../libs/get-layer-tile-url'; import TimeSliderRange from '../time-slider-range/time-slider-range'; +import TimePlayback from '../time-playback/time-playback'; +import Button from '../button/button'; +import {PlayIcon} from '../icons/play-icon'; +import {PauseIcon} from '../icons/pause-icon'; import styles from './time-slider.styl'; @@ -32,6 +36,7 @@ const TimeSlider: FunctionComponent = () => { const language = useSelector(languageSelector); const globeTime = useSelector(timeSelector); const [time, setTime] = useState(globeTime); + const [isPlaying, setIsPlaying] = useState(false); const stepSize = 1000 * 60 * 60 * 24; // one day const mainLayerDetails = useSelector((state: State) => layerDetailsSelector(state, mainId) @@ -81,16 +86,12 @@ const TimeSlider: FunctionComponent = () => { [] ); - // clamp time according to min/max + // sync local time useEffect(() => { - if (time < combined.min) { - setTime(combined.min); + if (time !== globeTime) { + setTime(globeTime); } - - if (time > combined.max) { - setTime(combined.max); - } - }, [time, combined.min, combined.max]); + }, [time, globeTime]); // return nothing when no timesteps available if (combined.timestamps.length === 0) { @@ -108,7 +109,16 @@ const TimeSlider: FunctionComponent = () => { return ( + {isPlaying && ( + + )} + setIsPlaying(!isPlaying)}> + {isPlaying ? 'playing' : 'pausing'} + { const newTime = parseInt(target.value, 10); setTime(newTime); debouncedSetGlobeTime(newTime); + setIsPlaying(false); }} min={combined.min} max={combined.max} diff --git a/src/scripts/hooks/use-interval.ts b/src/scripts/hooks/use-interval.ts index e7b5cffa6..b0efabe8e 100644 --- a/src/scripts/hooks/use-interval.ts +++ b/src/scripts/hooks/use-interval.ts @@ -1,6 +1,6 @@ import {useEffect, useRef} from 'react'; -export const useInterval = (callback: () => void, delay: number) => { +export const useInterval = (callback: () => void, delay: number | null) => { const savedCallback = useRef<() => void | undefined>(); // Remember the latest callback.