Skip to content

Commit

Permalink
[feat] Improve timeline sync filer UI (#2722)
Browse files Browse the repository at this point in the history
- Improve time filter and sync timeline UI

Signed-off-by: Ihor Dykhta <dikhta.igor@gmail.com>
Co-authored-by: Giuseppe Macrì <macri.giuseppe@gmail.com>
  • Loading branch information
igorDykhta and macrigiuseppe authored Oct 31, 2024
1 parent d6f6837 commit 934f8e8
Show file tree
Hide file tree
Showing 27 changed files with 435 additions and 137 deletions.
1 change: 1 addition & 0 deletions src/actions/src/action-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ export const ActionTypes = {
SET_LOCALE: `${ACTION_PREFIX}SET_LOCALE`,
LAYER_FILTERED_ITEMS_CHANGE: `${ACTION_PREFIX}LAYER_FILTERED_ITEMS_CHANGE`,
SYNC_TIME_FILTER_WITH_LAYER_TIMELINE: `${ACTION_PREFIX}SYNC_TIME_FILTER_WITH_LAYER_TIMELINE`,
SYNC_TIME_FILTER_TIMELINE_MODE: `${ACTION_PREFIX}SYNC_TIME_FILTER_TIMELINE_MODE`,
TOGGLE_PANEL_LIST_VIEW: `${ACTION_PREFIX}TOGGLE_PANEL_LIST_VIEW`,

// uiState > export image
Expand Down
22 changes: 21 additions & 1 deletion src/actions/src/vis-state-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
Filter,
ParsedConfig,
ParsedLayer,
EffectPropsPartial
EffectPropsPartial,
SyncTimelineMode
} from '@kepler.gl/types';
// TODO - import LoaderObject type from @loaders.gl/core when supported
// TODO - import LoadOptions type from @loaders.gl/core when supported
Expand Down Expand Up @@ -1573,6 +1574,25 @@ export function syncTimeFilterWithLayerTimeline(
};
}

export type setTimeFilterSyncTimelineModeAction = {
id: string;
mode: SyncTimelineMode;
};

export function setTimeFilterSyncTimelineMode({
id,
mode
}: setTimeFilterSyncTimelineModeAction): Merge<
setTimeFilterSyncTimelineModeAction,
{type: typeof ActionTypes.SYNC_TIME_FILTER_TIMELINE_MODE}
> {
return {
type: ActionTypes.SYNC_TIME_FILTER_TIMELINE_MODE,
id,
mode
};
}

/**
* This declaration is needed to group actions in docs
*/
Expand Down
1 change: 1 addition & 0 deletions src/components/src/bottom-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export default function BottomWidgetFactory(
setFilterPlot={visStateActions.setFilterPlot}
setFilterAnimationTime={setTimelineValue}
setFilterAnimationWindow={visStateActions.setFilterAnimationWindow}
setFilterSyncTimelineMode={visStateActions.setTimeFilterSyncTimelineMode}
toggleAnimation={visStateActions.toggleFilterAnimation}
updateAnimationSpeed={visStateActions.updateFilterAnimationSpeed}
resetAnimation={resetAnimation}
Expand Down
29 changes: 29 additions & 0 deletions src/components/src/common/icons/arrow-down-full.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import Base from './base';

export default class ArrowDown extends Component {
static propTypes = {
/** Set the height of the icon, ex. '16px' */
height: PropTypes.string,
style: PropTypes.any
};

static defaultProps = {
height: '8px',
width: '8px',
predefinedClassName: 'data-ex-icons-arrowdown-full',
viewBox: '0 0 8 4'
};

render() {
return (
<Base {...this.props}>
<path d="M4 4L0 0H8L4 4Z" fill="currentColor" />
</Base>
);
}
}
1 change: 1 addition & 0 deletions src/components/src/common/icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export {default as AnchorWindow} from './anchor_window';
export {default as ArrowDown} from './arrow-down';
export {default as ArrowDownAlt} from './arrow-down-alt';
export {default as ArrowDownSolid} from './arrow-down-alt';
export {default as ArrowDownFull} from './arrow-down-full';
export {default as ArrowLeft} from './arrow-left';
export {default as ArrowRight} from './arrow-right';
export {default as ArrowUpSolid} from './arrow-up-solid';
Expand Down
15 changes: 4 additions & 11 deletions src/components/src/common/icons/timeline-marker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,17 @@ export default class TimelineMarker extends Component {
};

static defaultProps = {
height: '16px',
height: '12px',
width: '5px',
predefinedClassName: 'data-ex-icons-timeline-marker',
viewBox: '0 0 5 12'
};

render() {
return (
<Base {...this.props}>
<svg
width="5"
height="12"
viewBox="0 0 5 12"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="5" height="9" fill="#C4C4C4" />
<path d="M2.5 11.5L0 9H5L2.5 11.5Z" fill="#C4C4C4" />
</svg>
<rect width="5" height="9" fill="currentColor" />
<path d="M2.5 11.5L0 9H5L2.5 11.5Z" fill="currentColor" />
</Base>
);
}
Expand Down
11 changes: 4 additions & 7 deletions src/components/src/common/icons/trash.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,17 @@ import Base, {BaseProps} from './base';
export default class Trash extends Component<Partial<BaseProps>> {
static defaultProps = {
height: '16px',
viewBox: '0 0 16 16',
predefinedClassName: 'data-ex-icons-trash'
};

render() {
return (
<Base {...this.props}>
<path
d="M51.4,13.9v1.6c0,0.9-0.7,1.6-1.6,1.6H13.6c-0.9,0-1.6-0.7-1.6-1.6v-1.6c0-0.9,0.7-1.6,1.6-1.6h9
c0.9,0,1.6-0.7,1.6-1.6C24.3,9.7,25.1,9,26,9h11.5c0.9,0,1.6,0.7,1.6,1.6c0,0.9,0.7,1.6,1.6,1.6h9C50.7,12.3,51.4,13,51.4,13.9z"
/>
<path
d="M40.8,50.1l0.8-25.4h-3.3l-0.8,25.4H40.8z M30.1,50.1h3.3V24.7h-3.3V50.1z M26,50.1l-0.8-25.4h-3.3l0.8,25.4H26
z M44.9,55H18.5c-0.9,0-1.6-0.7-1.6-1.6l-1.5-31.2c0-0.9,0.7-1.7,1.6-1.7h29.4c0.9,0,1.7,0.8,1.6,1.7l-1.5,31.2
C46.5,54.3,45.8,55,44.9,55z"
fillRule="evenodd"
clipRule="evenodd"
d="M6.09647 1.76979C6.21891 1.64735 6.38496 1.57857 6.55811 1.57857H9.4424C9.61554 1.57857 9.7816 1.64735 9.90404 1.76979C10.0265 1.89222 10.0953 2.05828 10.0953 2.23143V2.88429H5.90525V2.23143C5.90525 2.05828 5.97404 1.89222 6.09647 1.76979ZM4.32668 2.88429V2.23143C4.32668 1.63962 4.56178 1.07204 4.98025 0.65357C5.39873 0.235096 5.9663 0 6.55811 0H9.4424C10.0342 0 10.6018 0.235096 11.0203 0.65357C11.4387 1.07204 11.6738 1.63962 11.6738 2.23143V2.88429L14.4896 2.88429C14.9256 2.88429 15.2789 3.23766 15.2789 3.67357C15.2789 4.10948 14.9256 4.46286 14.4896 4.46286H13.837V13.7686C13.837 14.3604 13.6019 14.928 13.1835 15.3464C12.765 15.7649 12.1974 16 11.6056 16H4.3949C3.80308 16 3.23551 15.7649 2.81704 15.3464C2.39856 14.928 2.16347 14.3604 2.16347 13.7686V4.46286H1.51035C1.07444 4.46286 0.721069 4.10948 0.721069 3.67357C0.721069 3.23766 1.07444 2.88429 1.51035 2.88429L4.32668 2.88429ZM3.74204 13.7686V4.46286H12.2585V13.7686C12.2585 13.9417 12.1897 14.1078 12.0673 14.2302C11.9448 14.3526 11.7788 14.4214 11.6056 14.4214H4.3949C4.22175 14.4214 4.05569 14.3526 3.93326 14.2302C3.81082 14.1078 3.74204 13.9417 3.74204 13.7686Z"
/>
</Base>
);
Expand Down
28 changes: 15 additions & 13 deletions src/components/src/common/range-plot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ export default function RangePlotFactory(
}
});
} else if (isLineChart(plotType) && !lineChart && !isLoading) {
// load linechart
// load line chart
setIsLoading(true);
setFilterPlot({
plotType: {
Expand All @@ -203,19 +203,21 @@ export default function RangePlotFactory(
}
}, [plotType, bins, lineChart, setFilterPlot, isLoading, setIsLoading]);

const rangePlotStyle = useMemo(
() => ({
height: `${
isEnlarged
? hasMobileWidth(breakPointValues)
? theme.rangePlotContainerHLargePalm
: theme.rangePlotContainerHLarge
: theme.rangePlotContainerH
}px`
}),
[isEnlarged, theme]
);

return (
<StyledRangePlot
style={{
height: `${
isEnlarged
? hasMobileWidth(breakPointValues)
? theme.rangePlotContainerHLargePalm
: theme.rangePlotContainerHLarge
: theme.rangePlotContainerH
}px`
}}
className="kg-range-slider__plot"
>
<StyledRangePlot style={rangePlotStyle} className="kg-range-slider__plot">
{isLoading ? (
<div
style={{
Expand Down
80 changes: 62 additions & 18 deletions src/components/src/common/range-slider-subline.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,76 @@
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project

import React from 'react';
import {TimelineMarker} from '../common/icons';
import React, {useMemo} from 'react';
// eslint-disable-next-line no-unused-vars
import {CSSProperties} from 'react';
import {ArrowDownFull, TimelineMarker} from '../common/icons';

const LINE_STYLE = {
const BACKGROUND_LINE_STYLE: CSSProperties = {
height: '4px',
backgroundColor: '#1C2233',
position: 'relative',
width: '100%',
backgroundColor: '#5558DB'
marginTop: '20px'
};

const TIMELINE_MARKER_LEFT_STYLE = {position: 'absolute', top: '0', left: '-2px'};
const TIMELINE_MARKER_RIGHT_STYLE = {position: 'absolute', top: '0', right: '-2px'};
const TIMELINE_MARKER_STYLE: CSSProperties = {
position: 'absolute',
top: '-8px',
fill: '#3D4866',
color: '#3D4866'
};

const TIMELINE_INDICATOR_STYLE: CSSProperties = {
position: 'absolute',
top: '-14px',
fill: '#C4C4C4',
color: '#C4C4C4'
};

function RangeSliderSublineFactory() {
const RangeSliderSubline = ({line}) => {
const RangeSliderSubline = ({line, scaledValue}) => {
const style: CSSProperties = {
left: `${line[0]}%`,
top: '0',
width: `${line[1] - line[0]}%`,
height: '100%',
position: 'absolute',
backgroundColor: '#5558DB'
};

const value = scaledValue[line[2]];

const leftMarketStyle = useMemo(
() => ({
left: `calc(${line[0]}% - 4px)`,
...TIMELINE_MARKER_STYLE
}),
[line]
);

const rightMarketStyle = useMemo(
() => ({
left: `calc(${line[1]}% - 4px)`,
...TIMELINE_MARKER_STYLE
}),
[line]
);

const indicatorStyle = useMemo(
() => ({
...TIMELINE_INDICATOR_STYLE,
left: `calc(${value}% - 2px)`
}),
[value]
);

return (
<div
style={{
marginLeft: `${line[0]}%`,
paddingTop: '14px',
width: `${line[1] - line[0]}%`,
position: 'relative'
}}
>
<TimelineMarker height="12px" width="5px" style={TIMELINE_MARKER_LEFT_STYLE} />
<div style={LINE_STYLE} />
<TimelineMarker height="12px" width="5px" style={TIMELINE_MARKER_RIGHT_STYLE} />
<div style={BACKGROUND_LINE_STYLE}>
<div style={style} />
<ArrowDownFull style={leftMarketStyle} />
<ArrowDownFull style={rightMarketStyle} />
<TimelineMarker style={indicatorStyle} />
</div>
);
};
Expand Down
27 changes: 21 additions & 6 deletions src/components/src/common/range-slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ import RangePlotFactory from './range-plot';
import Slider from './slider/slider';
import {Input} from './styled-components';
import RangeSliderSublineFactory from '../common/range-slider-subline';
import {observeDimensions, unobserveDimensions, roundValToStep, clamp} from '@kepler.gl/utils';
import {
observeDimensions,
unobserveDimensions,
roundValToStep,
clamp,
scaleSourceDomainToDestination
} from '@kepler.gl/utils';
import {LineChart, Filter, Bins} from '@kepler.gl/types';
import {Datasets} from '@kepler.gl/table';
import {ActionHandler, setFilterPlot} from '@kepler.gl/actions';
Expand Down Expand Up @@ -60,7 +66,7 @@ interface RangeSliderProps {
step?: number;
sliderHandleWidth?: number;
xAxis?: ElementType;
sublines?: [number, number][];
timelines?: number[];

timezone?: string | null;
timeFormat?: string;
Expand Down Expand Up @@ -230,7 +236,7 @@ export default function RangeSliderFactory(
timeFormat,
playbackControlWidth,
setFilterPlot,
sublines,
timelines,
animationWindow,
filter,
datasets
Expand All @@ -239,6 +245,13 @@ export default function RangeSliderFactory(
const {width} = this.state;
const plotWidth = Math.max(width - Number(sliderHandleWidth), 0);
const hasPlot = plotType?.type;

const value = this.props.plotValue || this.filterValueSelector(this.props);
const scaledValue = range
? // TODO figure out correct types for value and range
scaleSourceDomainToDestination(value as [number, number], range as [number, number])
: [0, 0];

return (
<div
className="kg-range-slider"
Expand All @@ -260,7 +273,7 @@ export default function RangeSliderFactory(
filter={filter}
datasets={datasets}
range={range}
value={this.props.plotValue || this.filterValueSelector(this.props)}
value={value}
width={plotWidth}
isRanged={isRanged}
step={step}
Expand All @@ -270,8 +283,10 @@ export default function RangeSliderFactory(
setFilterPlot={setFilterPlot}
/>
) : null}
{sublines?.length
? sublines.map((line, index) => <RangeSliderSubline key={index} line={line} />)
{timelines?.length
? timelines.map((line, index) => (
<RangeSliderSubline key={index} line={line} scaledValue={scaledValue} />
))
: null}
<SliderWrapper
className="kg-range-slider__slider"
Expand Down
Loading

0 comments on commit 934f8e8

Please sign in to comment.