Skip to content

Commit

Permalink
Add MapsLayoutFactory for custom split map layouts (UN-1091) (#188)
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

[Bug] Crash on Double-click in dual map (UN-1336) (#205)

* 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)
  • Loading branch information
ilyabo authored and heshan0131 committed Aug 29, 2021
1 parent d8db8f6 commit ba5b7ae
Show file tree
Hide file tree
Showing 34 changed files with 1,387 additions and 1,320 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
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
19 changes: 2 additions & 17 deletions src/components/bottom-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,25 @@

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;
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 +222,6 @@ export default function BottomWidgetFactory(
);
};

BottomWidget.propTypes = propTypes;

return BottomWidget;
}
/* eslint-enable complexity */
12 changes: 12 additions & 0 deletions src/components/common/position.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import {FC} from 'react';

type Props = {
absolute?: boolean;
top?: number | string;
right?: number | string;
bottom?: number | string;
left?: number | string;
};

const Position: FC<Props>;
export default Position;
34 changes: 34 additions & 0 deletions src/components/common/position.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) 2021 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import styled from 'styled-components';

const withPx = v => (typeof v === 'number' ? `${v}px` : v);

const Position = styled.div(
({absolute = true, top, left, right, bottom}) => `
${absolute ? `position: absolute;` : ''};
${top !== undefined ? `top: ${withPx(top)};` : ''};
${bottom !== undefined ? `bottom: ${withPx(bottom)};` : ''};
${left !== undefined ? `left: ${withPx(left)};` : ''};
${right !== undefined ? `right: ${withPx(right)};` : ''};
`
);
export default Position;
130 changes: 72 additions & 58 deletions src/components/common/range-slider.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 ba5b7ae

Please sign in to comment.