kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
601 lines (536 loc) • 16.1 kB
JavaScript
// 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 styled from 'styled-components';
import ReactTooltip from 'react-tooltip';
import {media} from 'styles/media-breakpoints';
import classnames from 'classnames';
export const SelectText = styled.span`
color: ${props => props.theme.labelColor};
font-size: ${props => props.theme.selectFontSize};
font-weight: 400;
i {
font-size: 13px;
margin-right: 6px;
}
`;
export const SelectTextBold = styled(SelectText)`
color: ${props => props.theme.textColor};
font-weight: 500;
`;
export const IconRoundSmall = styled.div`
display: flex;
width: 18px;
height: 18px;
border-radius: 9px;
background-color: ${props =>
props.theme.secondaryBtnBgdHover}; // updated after checking sketch file
color: ${props => props.theme.secondaryBtnColor};
align-items: center;
justify-content: center;
:hover {
cursor: pointer;
background-color: ${props => props.theme.secondaryBtnBgdHover};
}
`;
export const CenterFlexbox = styled.div`
display: flex;
align-items: center;
`;
export const CenterVerticalFlexbox = styled.div`
display: flex;
flex-direction: column;
align-items: center;
`;
export const SpaceBetweenFlexbox = styled.div`
display: flex;
justify-content: space-between;
margin-left: -16px;
`;
export const SBFlexboxItem = styled.div`
flex-grow: 1;
margin-left: 16px;
`;
export const PanelLabel = styled.label.attrs({
className: 'side-panel-panel__label'
})`
color: ${props => props.theme.labelColor};
display: inline-block;
font-size: 11px;
font-weight: 400;
margin-bottom: 4px;
text-transform: capitalize;
`;
export const PanelLabelWrapper = styled.div.attrs({
className: 'side-panel-panel__label-wrapper'
})`
display: flex;
align-items: self-start;
`;
export const PanelLabelBold = styled(PanelLabel)`
font-weight: 500;
`;
export const PanelHeaderTitle = styled.span.attrs({
className: 'side-panel-panel__header__title'
})`
color: ${props => props.theme.textColor};
font-size: 13px;
letter-spacing: 0.43px;
text-transform: capitalize;
`;
export const PanelHeaderContent = styled.div`
display: flex;
align-items: center;
color: ${props => props.theme.textColor};
padding-left: 12px;
.icon {
color: ${props => props.theme.labelColor};
display: flex;
align-items: center;
margin-right: 12px;
}
`;
export const PanelContent = styled.div.attrs({
className: 'side-panel-panel__content'
})`
background-color: ${props => props.theme.panelBackground};
padding: 12px;
`;
export const SidePanelSection = styled.div.attrs({
className: 'side-panel-section'
})`
margin-bottom: 12px;
opacity: ${props => (props.disabled ? 0.4 : 1)};
pointer-events: ${props => (props.disabled ? 'none' : 'all')};
`;
export const SidePanelDivider = styled.div.attrs({
className: 'side-panel-divider'
})`
border-bottom: 1px solid ${props => props.theme.panelBorderColor};
height: 12px;
margin-bottom: 12px;
`;
export const Tooltip = styled(ReactTooltip)`
&.__react_component_tooltip {
font-size: 9.5px;
font-weight: 500;
padding: 7px 18px;
&.type-dark {
background-color: ${props => props.theme.tooltipBg};
color: ${props => props.theme.tooltipColor};
&.place-bottom {
:after {
border-bottom-color: ${props => props.theme.tooltipBg};
}
}
&.place-top {
:after {
border-top-color: ${props => props.theme.tooltipBg};
}
}
&.place-right {
:after {
border-right-color: ${props => props.theme.tooltipBg};
}
}
&.place-left {
:after {
border-left-color: ${props => props.theme.tooltipBg};
}
}
}
}
`;
export const Button = styled.div.attrs(props => ({
className: classnames('button', props.className)
}))`
align-items: center;
background-color: ${props =>
props.negative
? props.theme.negativeBtnBgd
: props.secondary
? props.theme.secondaryBtnBgd
: props.link
? props.theme.linkBtnBgd
: props.floating
? props.theme.floatingBtnBgd
: props.theme.primaryBtnBgd};
border-radius: ${props => props.theme.primaryBtnRadius};
color: ${props =>
props.negative
? props.theme.negativeBtnColor
: props.secondary
? props.theme.secondaryBtnColor
: props.link
? props.theme.linkBtnColor
: props.floating
? props.theme.floatingBtnColor
: props.theme.primaryBtnColor};
cursor: pointer;
display: inline-flex;
font-size: ${props => (props.large ? '14px' : props.small ? '10px' : '11px')};
font-weight: 500;
justify-content: center;
letter-spacing: 0.3px;
line-height: 14px;
outline: 0;
padding: ${props => (props.large ? '14px 32px' : props.small ? '6px 9px' : '9px 12px')};
text-align: center;
transition: ${props => props.theme.transition};
vertical-align: middle;
width: ${props => props.width || 'auto'};
opacity: ${props => (props.disabled ? 0.4 : 1)};
pointer-events: ${props => (props.disabled ? 'none' : 'all')};
:hover,
:focus,
:active,
&.active {
background-color: ${props =>
props.negative
? props.theme.negativeBtnBgdHover
: props.secondary
? props.theme.secondaryBtnBgdHover
: props.link
? props.theme.linkBtnActBgdHover
: props.floating
? props.theme.floatingBtnBgdHover
: props.theme.primaryBtnBgdHover};
color: ${props =>
props.negative
? props.theme.negativeBtnActColor
: props.secondary
? props.theme.secondaryBtnActColor
: props.link
? props.theme.linkBtnActColor
: props.floating
? props.theme.floatingBtnActColor
: props.theme.primaryBtnActColor};
}
svg {
margin-right: ${props => (props.large ? '10px' : props.small ? '6px' : '8px')};
}
`;
export const Input = styled.input`
${props => (props.secondary ? props.theme.secondaryInput : props.theme.input)};
`;
export const InputLight = styled.input`
${props => props.theme.inputLT}
`;
export const TextArea = styled.textarea`
${props => (props.secondary ? props.theme.secondaryInput : props.theme.input)};
`;
export const TextAreaLight = styled.textarea`
${props => props.theme.inputLT}
height: auto;
white-space: pre-wrap;
`;
export const InlineInput = styled(Input)`
${props => props.theme.inlineInput};
`;
export const StyledPanelHeader = styled.div`
background-color: ${props =>
props.active ? props.theme.panelBackgroundHover : props.theme.panelBackground};
border-left: 3px solid
rgb(
${props => (props.labelRCGColorValues ? props.labelRCGColorValues.join(',') : 'transparent')}
);
padding: 0 10px 0 0;
height: ${props => props.theme.panelHeaderHeight}px;
display: flex;
justify-content: space-between;
align-items: center;
transition: ${props => props.theme.transition};
`;
export const StyledPanelDropdown = styled.div`
${props => props.theme.panelDropdownScrollBar}
background-color: ${props =>
props.type === 'light' ? props.theme.modalDropdownBackground : props.theme.panelBackground};
overflow-y: auto;
box-shadow: ${props => props.theme.panelBoxShadow};
border-radius: ${props => props.theme.panelBorderRadius};
margin-top: 2px;
max-height: 500px;
position: relative;
z-index: 999;
`;
export const ButtonGroup = styled.div`
display: flex;
.button {
border-radius: 0;
margin-left: 2px;
}
.button:first-child {
border-bottom-left-radius: ${props => props.theme.primaryBtnRadius};
border-top-left-radius: ${props => props.theme.primaryBtnRadius};
margin-left: 0;
}
.button:last-child {
border-bottom-right-radius: ${props => props.theme.primaryBtnRadius};
border-top-right-radius: ${props => props.theme.primaryBtnRadius};
}
`;
export const DatasetSquare = styled.div`
display: inline-block;
width: 8px;
height: 8px;
background-color: rgb(${props => props.color.join(',')});
margin-right: 12px;
`;
export const SelectionButton = styled.div`
border-radius: 2px;
border: 1px solid
${props => (props.selected ? props.theme.primaryBtnBgd : props.theme.selectBorderColorLT)};
color: ${props => (props.selected ? props.theme.primaryBtnBgd : props.theme.selectBorderColorLT)};
cursor: pointer;
font-weight: 500;
margin-right: 6px;
padding: 6px 10px;
:hover {
color: ${props => props.available && props.theme.primaryBtnBgd};
border: 1px solid ${props => props.available && props.theme.primaryBtnBgd};
}
`;
export const Table = styled.table`
width: 100%;
border-spacing: 0;
thead {
tr th {
background: ${props => props.theme.panelBackgroundLT};
color: ${props => props.theme.titleColorLT};
padding: 18px 12px;
text-align: start;
}
}
tbody {
tr td {
border-bottom: ${props => props.theme.panelBorderLT};
padding: 12px;
}
}
`;
export const StyledModalContent = styled.div`
background: ${props => props.theme.panelBackgroundLT};
color: ${props => props.theme.textColorLT};
display: flex;
flex-direction: row;
font-size: 10px;
padding: 24px ${props => props.theme.modalLateralPadding};
margin: 0 -${props => props.theme.modalLateralPadding};
justify-content: space-between;
${media.portable`
flex-direction: column;
padding: 16px ${props => props.theme.modalPortableLateralPadding};
margin: 0 -${props => props.theme.modalPortableLateralPadding};
`};
`;
export const StyledModalVerticalPanel = styled.div.attrs({
className: 'modal-vertical-panel'
})`
display: flex;
flex-direction: column;
justify-content: space-around;
font-size: 12px;
.modal-section:first-child {
margin-top: 24px;
${media.palm`
margin-top: 0;
`};
}
input {
margin-right: 8px;
}
`;
export const StyledModalSection = styled.div.attrs({
className: 'modal-section'
})`
margin-bottom: 32px;
.modal-section-title {
font-weight: 500;
}
.modal-section-subtitle {
color: ${props => props.theme.subtextColorLT};
}
input {
margin-top: 8px;
}
${media.portable`
margin-bottom: 24px;
`};
${media.palm`
margin-bottom: 16px;
`};
`;
export const StyledModalInputFootnote = styled.div.attrs({
className: 'modal-input__footnote'
})`
display: flex;
justify-content: flex-end;
color: ${props => (props.error ? props.theme.errorColor : props.theme.subtextColorLT)};
font-size: 10px;
`;
/**
* Newer versions of mapbox.gl display an error message banner on top of the map by default
* which will cause the map to display points in the wrong locations
* This workaround will hide the error banner.
*/
export const StyledMapContainer = styled.div`
.mapboxgl-map .mapboxgl-missing-css {
display: none;
}
`;
export const StyledExportSection = styled.div`
display: flex;
flex-direction: row;
margin: 35px 0;
width: 100%;
color: ${props => props.theme.textColorLT};
font-size: 12px;
opacity: ${props => (props.disabled ? 0.3 : 1)};
pointer-events: ${props => (props.disabled ? 'none' : 'all')};
.description {
width: 185px;
.title {
font-weight: 500;
}
.subtitle {
color: ${props => props.theme.subtextColorLT};
font-size: 11px;
}
}
.warning {
color: ${props => props.theme.errorColor};
font-weight: 500;
}
.description.full {
width: 100%;
}
.selection {
display: flex;
flex-wrap: wrap;
flex: 1;
padding-left: 50px;
select {
background-color: white;
border-radius: 1px;
display: inline-block;
font: inherit;
line-height: 1.5em;
padding: 0.5em 3.5em 0.5em 1em;
margin: 0;
box-sizing: border-box;
appearance: none;
width: 250px;
height: 36px;
background-image: linear-gradient(45deg, transparent 50%, gray 50%),
linear-gradient(135deg, gray 50%, transparent 50%), linear-gradient(to right, #ccc, #ccc);
background-position: calc(100% - 20px) calc(1em + 2px), calc(100% - 15px) calc(1em + 2px),
calc(100% - 2.5em) 4.5em;
background-size: 5px 5px, 5px 5px, 1px 1.5em;
background-repeat: no-repeat;
}
select:focus {
background-image: linear-gradient(45deg, green 50%, transparent 50%),
linear-gradient(135deg, transparent 50%, green 50%), linear-gradient(to right, #ccc, #ccc);
background-position: calc(100% - 15px) 1em, calc(100% - 20px) 1em, calc(100% - 2.5em) 4.5em;
background-size: 5px 5px, 5px 5px, 1px 1.5em;
background-repeat: no-repeat;
border-color: green;
outline: 0;
}
}
`;
export const StyledFilteredOption = styled.div`
align-items: center;
border-radius: 2px;
border: 1px solid
${props => (props.selected ? props.theme.primaryBtnBgd : props.theme.selectBorderColorLT)};
cursor: pointer;
display: flex;
flex-direction: column;
height: 60px;
justify-content: center;
margin: 4px;
padding: 8px 12px;
width: 140px;
:hover {
border: 1px solid ${props => props.theme.primaryBtnBgd};
}
.filter-option-title {
color: ${props => props.theme.textColorLT};
font-size: 12px;
font-weight: 500;
}
.filter-option-subtitle {
color: ${props => props.theme.textColor};
font-size: 11px;
}
`;
export const StyledType = styled.div`
border-radius: 2px;
border: 1px solid
${props => (props.selected ? props.theme.primaryBtnBgdHover : props.theme.selectBorderColorLT)};
color: ${props =>
props.selected ? props.theme.primaryBtnBgdHover : props.theme.selectBorderColorLT};
cursor: pointer;
font-weight: 500;
height: 100px;
margin: 4px;
padding: 6px 10px;
width: 100px;
:hover {
color: ${props => props.available && props.theme.primaryBtnBgd};
border: 1px solid ${props => props.available && props.theme.primaryBtnBgd};
}
`;
export const WidgetContainer = styled.div`
z-index: 1;
`;
export const BottomWidgetInner = styled.div`
background-color: ${props => props.theme.panelBackground};
padding: ${props => `${props.theme.bottomInnerPdVert}px ${props.theme.bottomInnerPdSide}px`};
position: relative;
margin-top: ${props => props.theme.bottomPanelGap}px;
`;
export const MapControlButton = styled(Button).attrs({
className: 'map-control-button'
})`
box-shadow: 0 6px 12px 0 rgba(0, 0, 0, 0.16);
height: 32px;
width: 32px;
padding: 0;
border-radius: 0;
background-color: ${props =>
props.active ? props.theme.floatingBtnBgdHover : props.theme.floatingBtnBgd};
color: ${props =>
props.active ? props.theme.floatingBtnActColor : props.theme.floatingBtnColor};
:hover,
:focus,
:active,
&.active {
background-color: ${props => props.theme.floatingBtnBgdHover};
color: ${props => props.theme.floatingBtnActColor};
}
svg {
margin-right: 0;
}
`;
export const StyledFilterContent = styled.div`
background-color: ${props => props.theme.panelBackground};
padding: 12px;
`;