Skip to content

Commit

Permalink
[Maps] timeslider play button (#103147) (#103313)
Browse files Browse the repository at this point in the history
* [Maps] timeslider play button

* cancel subscription on unmount

* change playback speed to 1750

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>

Co-authored-by: Nathan Reese <reese.nathan@gmail.com>
  • Loading branch information
kibanamachine and nreese committed Jun 24, 2021
1 parent 492f5bd commit 7a16272
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { i18n } from '@kbn/i18n';
import uuid from 'uuid/v4';
import { Filter } from 'src/plugins/data/public';
import { ActionExecutionContext, Action } from 'src/plugins/ui_actions/public';
import { Observable } from 'rxjs';
import { MBMap } from '../mb_map';
import { RightSideControls } from '../right_side_controls';
import { Timeslider } from '../timeslider';
Expand Down Expand Up @@ -47,6 +48,7 @@ export interface Props {
description?: string;
settings: MapSettings;
layerList: ILayer[];
waitUntilTimeLayersLoad$: Observable<void>;
}

interface State {
Expand Down Expand Up @@ -223,7 +225,7 @@ export class MapContainer extends Component<Props, State> {
<RightSideControls />
</EuiFlexItem>

<Timeslider />
<Timeslider waitForTimesliceToLoad$={this.props.waitUntilTimeLayersLoad$} />

<EuiFlexItem
className={classNames('mapMapLayerPanel', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import React, { Component } from 'react';
import { EuiButtonIcon, EuiDualRange, EuiText } from '@elastic/eui';
import { EuiRangeTick } from '@elastic/eui/src/components/form/range/range_ticks';
import { i18n } from '@kbn/i18n';
import { Observable, Subscription } from 'rxjs';
import { first } from 'rxjs/operators';
import { epochToKbnDateFormat, getInterval, getTicks } from './time_utils';
import { TimeRange } from '../../../../../../src/plugins/data/common';
import { getTimeFilter } from '../../kibana_services';
Expand All @@ -20,9 +22,11 @@ export interface Props {
setTimeslice: (timeslice: Timeslice) => void;
isTimesliderOpen: boolean;
timeRange: TimeRange;
waitForTimesliceToLoad$: Observable<void>;
}

interface State {
isPaused: boolean;
max: number;
min: number;
range: number;
Expand All @@ -44,6 +48,8 @@ export function Timeslider(props: Props) {

class KeyedTimeslider extends Component<Props, State> {
private _isMounted: boolean = false;
private _timeoutId: number | undefined;
private _subscription: Subscription | undefined;

constructor(props: Props) {
super(props);
Expand All @@ -59,6 +65,7 @@ class KeyedTimeslider extends Component<Props, State> {
const timeslice: [number, number] = [min, max];

this.state = {
isPaused: true,
max,
min,
range: interval,
Expand All @@ -68,6 +75,7 @@ class KeyedTimeslider extends Component<Props, State> {
}

componentWillUnmount() {
this._onPause();
this._isMounted = false;
}

Expand Down Expand Up @@ -118,6 +126,44 @@ class KeyedTimeslider extends Component<Props, State> {
}
}, 300);

_onPlay = () => {
this.setState({ isPaused: false });
this._playNextFrame();
};

_onPause = () => {
this.setState({ isPaused: true });
if (this._subscription) {
this._subscription.unsubscribe();
this._subscription = undefined;
}
if (this._timeoutId) {
clearTimeout(this._timeoutId);
this._timeoutId = undefined;
}
};

_playNextFrame() {
// advance to next frame
this._onNext();

// use waitForTimesliceToLoad$ observable to wait until next frame loaded
// .pipe(first()) waits until the first value is emitted from an observable and then automatically unsubscribes
this._subscription = this.props.waitForTimesliceToLoad$.pipe(first()).subscribe(() => {
if (this.state.isPaused) {
return;
}

// use timeout to display frame for small time period before moving to next frame
this._timeoutId = window.setTimeout(() => {
if (this.state.isPaused) {
return;
}
this._playNextFrame();
}, 1750);
});
}

render() {
return (
<div className="mapTimeslider">
Expand Down Expand Up @@ -154,6 +200,20 @@ class KeyedTimeslider extends Component<Props, State> {
defaultMessage: 'Next time window',
})}
/>
<EuiButtonIcon
onClick={this.state.isPaused ? this._onPlay : this._onPause}
iconType={this.state.isPaused ? 'play' : 'pause'}
color="text"
aria-label={
this.state.isPaused
? i18n.translate('xpack.maps.timeslider.playLabel', {
defaultMessage: 'Play',
})
: i18n.translate('xpack.maps.timeslider.pauseLabel', {
defaultMessage: 'Pause',
})
}
/>
</div>
</div>
</div>
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/maps/public/embeddable/map_embeddable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import { SavedMap } from '../routes/map_page';
import { getIndexPatternsFromIds } from '../index_pattern_util';
import { getMapAttributeService } from '../map_attribute_service';
import { isUrlDrilldown, toValueClickDataFormat } from '../trigger_actions/trigger_utils';
import { waitUntilTimeLayersLoad$ } from '../routes/map_page/map_app/wait_until_time_layers_load';

import {
MapByValueInput,
Expand Down Expand Up @@ -345,6 +346,7 @@ export class MapEmbeddable
renderTooltipContent={this._renderTooltipContent}
title={this.getTitle()}
description={this.getDescription()}
waitUntilTimeLayersLoad$={waitUntilTimeLayersLoad$(this._savedMap.getStore())}
/>
</I18nContext>
</Provider>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ export class MapApp extends React.Component<Props, State> {
addFilters={this._addFilter}
title={this.props.savedMap.getAttributes().title}
description={this.props.savedMap.getAttributes().description}
waitUntilTimeLayersLoad$={waitUntilTimeLayersLoad$(this.props.savedMap.getStore())}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import { from } from 'rxjs';
import { debounceTime, first, switchMap } from 'rxjs/operators';
import { debounceTime, first, map, switchMap } from 'rxjs/operators';
import { getLayerList } from '../../../selectors/map_selectors';
import { MapStore } from '../../../reducers/store';

Expand All @@ -31,6 +31,11 @@ export function waitUntilTimeLayersLoad$(store: MapStore) {
.filter(({ isFilteredByGlobalTime }) => isFilteredByGlobalTime)
.some(({ layer }) => layer.isLayerLoading());
return !areTimeLayersStillLoading;
}),
map(() => {
// Observable notifies subscriber when loading is finished
// Return void to not expose internal implemenation details of observabale
return;
})
);
}

0 comments on commit 7a16272

Please sign in to comment.