UNPKG

kepler.gl

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

183 lines (156 loc) 5.15 kB
// Copyright (c) 2020 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 React, {PureComponent, createRef} from 'react'; import styled from 'styled-components'; import PropTypes from 'prop-types'; import LayerHoverInfoFactory from './layer-hover-info'; import CoordinateInfoFactory from './coordinate-info'; import {Pin} from 'components/common/icons'; import ErrorBoundary from 'components/common/error-boundary'; const MAX_WIDTH = 500; const MAX_HEIGHT = 600; const StyledMapPopover = styled.div` ${props => props.theme.scrollBar}; font-size: 11px; font-weight: 500; background-color: ${props => props.theme.panelBackground}; color: ${props => props.theme.textColor}; z-index: 1001; position: absolute; overflow-x: auto; .gutter { height: 6px; } table { margin: 2px 12px 12px 12px; width: auto; tbody { border-top: transparent; border-bottom: transparent; } td { border-color: transparent; padding: 4px; color: ${props => props.theme.textColor}; } td.row__value { text-align: right; font-weight: 500; color: ${props => props.theme.textColorHl}; } } `; const StyledPin = styled.div` position: absolute; left: 50%; transform: rotate(30deg); top: 10px; color: ${props => props.theme.primaryBtnBgd}; :hover { cursor: pointer; color: ${props => props.theme.linkBtnColor}; } `; MapPopoverFactory.deps = [LayerHoverInfoFactory, CoordinateInfoFactory]; export default function MapPopoverFactory(LayerHoverInfo, CoordinateInfo) { class MapPopover extends PureComponent { static propTypes = { layerHoverProp: PropTypes.object, coordinate: PropTypes.oneOfType([PropTypes.array, PropTypes.bool]), freezed: PropTypes.bool, x: PropTypes.number, y: PropTypes.number, mapW: PropTypes.number.isRequired, mapH: PropTypes.number.isRequired, onClose: PropTypes.func.isRequired }; constructor(props) { super(props); this.state = { width: 380, height: 160 }; } componentDidMount() { this._setContainerSize(); } componentDidUpdate() { this._setContainerSize(); } popover = createRef(); _setContainerSize() { const node = this.popover.current; if (!node) { return; } const width = Math.min(Math.round(node.scrollWidth), MAX_WIDTH); const height = Math.min(Math.round(node.scrollHeight), MAX_HEIGHT); if (width !== this.state.width || height !== this.state.height) { this.setState({width, height}); } } _getPosition(x, y) { const topOffset = 20; const leftOffset = 20; const {mapW, mapH} = this.props; const {width, height} = this.state; const pos = {}; if (x + leftOffset + width > mapW) { pos.right = mapW - x + leftOffset; } else { pos.left = x + leftOffset; } if (y + topOffset + height > mapH) { pos.bottom = 10; } else { pos.top = y + topOffset; } return pos; } render() { const {x, y, freezed, coordinate, layerHoverProp} = this.props; const style = Number.isFinite(x) && Number.isFinite(y) ? this._getPosition(x, y) : {}; return ( <ErrorBoundary> <StyledMapPopover ref={this.popover} className="map-popover" style={{ ...style, maxWidth: MAX_WIDTH }} > {freezed ? ( <div className="map-popover__top"> <div className="gutter" /> <StyledPin className="popover-pin" onClick={this.props.onClose}> <Pin height="16px" /> </StyledPin> </div> ) : null} {Array.isArray(coordinate) && <CoordinateInfo coordinate={coordinate} />} {layerHoverProp && <LayerHoverInfo {...layerHoverProp} />} </StyledMapPopover> </ErrorBoundary> ); } } return MapPopover; }