Skip to content

Commit

Permalink
[Feat] Add MapsLayoutFactory for custom split map layouts (#1588)
Browse files Browse the repository at this point in the history
- Add MapsLayoutFactory for custom split map layouts
- Avoid manual calculation of the sizes of the map-containers and positioning them with CSS instead
- Add observeDimensions and useDimensions to allow components to listen to their size changes
- Fixed the issue with the HistogramPlot not being updated by adding a resize observer to RangeSlider
- Crash on Double-click in dual map
- Fix: Pinning a tooltip moves it to the top left of the screen
- Changed width > 0 || height > 0 to width > 0 && height > 0
- Fix: Crash on Double-click in dual map
- Fix formatting
- Fix: Map layers can't be moved on top (#210)
- Prevent padding around bottom widget, sidebar and map control from blocking input
- ResizeObserver debouncing

Signed-off-by: Ilya Boyandin <iboyandin@foursquare.com>
Co-authored-by: Ilya Boyandin <ilyabo@gmail.com>
  • Loading branch information
heshan0131 and ilyabo committed Aug 30, 2021
1 parent d8db8f6 commit 878750c
Show file tree
Hide file tree
Showing 35 changed files with 1,367 additions and 1,320 deletions.
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -156,6 +156,7 @@
"redux": "^4.0.5",
"redux-actions": "^2.2.1",
"reselect": "^4.0.0",
"resize-observer-polyfill": "^1.5.1",
"s2-geometry": "^1.2.10",
"supercluster": "^7.1.0",
"type-analyzer": "0.3.0",
Expand Down
24 changes: 7 additions & 17 deletions src/components/bottom-widget.js
Expand Up @@ -20,38 +20,30 @@

import React, {useCallback, useMemo} from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';
import TimeWidgetFactory from './filters/time-widget';
import AnimationControlFactory from './common/animation-control/animation-control';
import AnimationControllerFactory from './common/animation-control/animation-controller';
import {ANIMATION_WINDOW, FILTER_TYPES} from 'constants/default-settings';
import {getIntervalBins} from 'utils/filter-utils';

const propTypes = {
filters: PropTypes.arrayOf(PropTypes.object),
datasets: PropTypes.object,
uiState: PropTypes.object,
layers: PropTypes.arrayOf(PropTypes.object),
animationConfig: PropTypes.object,
visStateActions: PropTypes.object,
sidePanelWidth: PropTypes.number,
containerW: PropTypes.number
};
import {media} from 'styles/media-breakpoints';

const maxWidth = 1080;

const BottomWidgetContainer = styled.div`
position: absolute;
display: flex;
flex-direction: column;
padding-top: ${props => (props.hasPadding ? props.theme.bottomWidgetPaddingTop : 0)}px;
padding-right: ${props => (props.hasPadding ? props.theme.bottomWidgetPaddingRight : 0)}px;
padding-bottom: ${props => (props.hasPadding ? props.theme.bottomWidgetPaddingBottom : 0)}px;
padding-left: ${props => (props.hasPadding ? props.theme.bottomWidgetPaddingLeft : 0)}px;
pointer-events: none !important; /* prevent padding from blocking input */
& > * {
/* all children should allow input */
pointer-events: all;
}
width: ${props => props.width}px;
bottom: 0;
right: 0;
z-index: 1;
${media.portable`padding: 0;`}
`;

FilterAnimationControllerFactory.deps = [AnimationControllerFactory];
Expand Down Expand Up @@ -235,8 +227,6 @@ export default function BottomWidgetFactory(
);
};

BottomWidget.propTypes = propTypes;

return BottomWidget;
}
/* eslint-enable complexity */
Expand Up @@ -34,6 +34,11 @@ const StyledTimeDisplayWrapper = styled.div.attrs({
width: 100%;
margin-left: -${props => props.theme.bottomInnerPdSide}px;
justify-content: center;
pointer-events: none; /* prevent padding from blocking input */
& > * {
/* all children should allow input */
pointer-events: all;
}
`;

const StyledTimeDisplay = styled.div.attrs({
Expand Down
130 changes: 72 additions & 58 deletions src/components/common/range-slider.js
Expand Up @@ -28,6 +28,7 @@ import Slider from 'components/common/slider/slider';
import {Input} from 'components/common/styled-components';

import {roundValToStep, clamp} from 'utils/data-utils';
import {observeDimensions, unobserveDimensions} from '../../utils/observe-dimensions';

const SliderInput = styled(Input)`
width: ${props => props.theme.sliderInputWidth}px;
Expand Down Expand Up @@ -98,10 +99,22 @@ export default function RangeSliderFactory(RangePlot) {
width: 288
};

componentDidMount() {
if (this.sliderContainer instanceof Element) {
observeDimensions(this.sliderContainer, this._resize);
}
}

componentDidUpdate() {
this._resize();
}

componentWillUnmount() {
if (this.sliderContainer instanceof Element) {
unobserveDimensions(this.sliderContainer);
}
}

sliderContainer = null;
setSliderContainer = element => {
this.sliderContainer = element;
Expand Down Expand Up @@ -203,73 +216,74 @@ export default function RangeSliderFactory(RangePlot) {
const {width} = this.state;
const plotWidth = Math.max(width - sliderHandleWidth, 0);
const renderPlot = (histogram && histogram.length) || lineChart;
if (!Array.isArray(range) || !range.every(Number.isFinite)) {
return null;
}
return (
<div
className="kg-range-slider"
style={{width: '100%', padding: `0 ${sliderHandleWidth / 2}px`}}
ref={this.setSliderContainer}
>
{renderPlot ? (
<RangePlot
histogram={histogram}
lineChart={this.props.lineChart}
plotType={this.props.plotType}
isEnlarged={this.props.isEnlarged}
onBrush={(val0, val1) => onChange([val0, val1])}
marks={this.props.marks}
range={range}
value={this.props.plotValue || this.filterValueSelector(this.props)}
width={plotWidth}
isRanged={isRanged}
step={step}
timezone={timezone}
timeFormat={timeFormat}
playbackControlWidth={playbackControlWidth}
/>
) : null}
<SliderWrapper
className="kg-range-slider__slider"
isRanged={isRanged}
showInput={showInput}
>
{this.props.xAxis ? (
<div style={{height: '30px'}}>
<this.props.xAxis
{Array.isArray(range) && range.every(Number.isFinite) && (
<>
{renderPlot ? (
<RangePlot
histogram={histogram}
lineChart={this.props.lineChart}
plotType={this.props.plotType}
isEnlarged={this.props.isEnlarged}
onBrush={(val0, val1) => onChange([val0, val1])}
marks={this.props.marks}
range={range}
value={this.props.plotValue || this.filterValueSelector(this.props)}
width={plotWidth}
isRanged={isRanged}
step={step}
timezone={timezone}
domain={range}
isEnlarged={this.props.isEnlarged}
timeFormat={timeFormat}
playbackControlWidth={playbackControlWidth}
/>
) : null}
<SliderWrapper
className="kg-range-slider__slider"
isRanged={isRanged}
showInput={showInput}
>
{this.props.xAxis ? (
<div style={{height: '30px'}}>
<this.props.xAxis
width={plotWidth}
timezone={timezone}
domain={range}
isEnlarged={this.props.isEnlarged}
/>
</div>
) : null}
<Slider
marks={this.props.marks}
showValues={false}
isRanged={isRanged}
minValue={range[0]}
maxValue={range[1]}
value0={this.props.value0}
value1={this.props.value1}
step={step}
handleWidth={sliderHandleWidth}
onSlider0Change={this._setRangeVal0}
onSlider1Change={this._setRangeVal1}
onSliderBarChange={(val0, val1) => {
onChange([val0, val1]);
}}
enableBarDrag
/>
</div>
) : null}
<Slider
marks={this.props.marks}
showValues={false}
isRanged={isRanged}
minValue={range[0]}
maxValue={range[1]}
value0={this.props.value0}
value1={this.props.value1}
step={step}
handleWidth={sliderHandleWidth}
onSlider0Change={this._setRangeVal0}
onSlider1Change={this._setRangeVal1}
onSliderBarChange={(val0, val1) => {
onChange([val0, val1]);
}}
enableBarDrag
/>
{!isRanged && showInput ? this._renderInput('value1') : null}
</SliderWrapper>
{isRanged && showInput ? (
<RangeInputWrapper className="range-slider__input-group">
{this._renderInput('value0')}
{this._renderInput('value1')}
</RangeInputWrapper>
) : null}
{!isRanged && showInput ? this._renderInput('value1') : null}
</SliderWrapper>
{isRanged && showInput ? (
<RangeInputWrapper className="range-slider__input-group">
{this._renderInput('value0')}
{this._renderInput('value1')}
</RangeInputWrapper>
) : null}
</>
)}
</div>
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/components/common/styled-components.js
Expand Up @@ -476,6 +476,8 @@ export const StyledModalInputFootnote = styled.div.attrs({
* This workaround will hide the error banner.
*/
export const StyledMapContainer = styled.div`
width: 100%;
height: 100%;
.mapboxgl-map {
.mapboxgl-missing-css {
display: none;
Expand Down
6 changes: 5 additions & 1 deletion src/components/index.js
Expand Up @@ -37,7 +37,8 @@ export {default as KeplerGl, default, injectComponents, ContainerFactory} from '
export {default as KeplerGlFactory, DEFAULT_KEPLER_GL_PROPS} from './kepler-gl';
export {default as SidePanelFactory} from './side-panel';
export {default as PanelTitleFactory} from './side-panel/panel-title';
export {default as MapContainerFactory} from './map-container';
export {default as MapContainerFactory, Attribution, prepareLayersToRender} from './map-container';
export {default as MapsLayoutFactory} from './maps-layout';
export {
default as BottomWidgetFactory,
LayerAnimationControllerFactory,
Expand Down Expand Up @@ -165,6 +166,9 @@ export {DatasetSquare} from './common/styled-components';
export {default as ActionPanel, ActionPanelItem} from './common/action-panel';
export {default as DataTableFactory} from './common/data-table';
export {default as CanvasHack} from './common/data-table/canvas';
export {default as MapLayerSelector} from './common/map-layer-selector';
export {default as VerticalToolbar} from './common/vertical-toolbar';
export {default as ToolbarItem} from './common/toolbar-item';

// side pane components
export {default as LayerTypeSelectorFactory} from './side-panel/layer-panel/layer-type-selector';
Expand Down

0 comments on commit 878750c

Please sign in to comment.