// Copyright (c) 2022 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 from 'react'; import styled from 'styled-components'; import {rgb} from 'd3-color'; import ColorLegend from 'components/common/color-legend'; import {CHANNEL_SCALES, DIMENSIONS} from 'constants/default-settings'; import {FormattedMessage} from 'localization'; import Layer, {LayerBaseConfig, VisualChannel, VisualChannelDescription} from 'layers/base-layer'; interface StyledMapControlLegendProps { width?: number; last?: boolean; } export const StyledMapControlLegend = styled.div` padding: 10px ${props => props.theme.mapControl.padding}px 10px ${props => props.theme.mapControl.padding}px; font-size: 11px; font-family: ${props => props.theme.fontFamily}; border-bottom-color: ${props => props.theme.panelBorderColor}; border-bottom-style: solid; border-bottom-width: ${props => (props.last ? 0 : '1px')}; width: ${props => props.width}px; box-sizing: border-box; .legend--layer_name { font-size: 12px; padding-right: ${props => props.theme.mapControl.padding}px; color: ${props => props.theme.textColor}; font-weight: 500; } .legend--layer_type { color: ${props => props.theme.subtextColor}; font-weight: 500; font-size: 11px; padding-right: ${props => props.theme.mapControl.padding}px; } .legend--layer__title { padding-right: ${props => props.theme.mapControl.padding}px; } .legend--layer_by { color: ${props => props.theme.subtextColor}; } .legend--layer_color_field { color: ${props => props.theme.textColorHl}; font-weight: 500; } .legend--layer_color-legend { margin-top: 6px; } `; export const VisualChannelMetric = ({name}) => { return (
); }; export type LayerSizeLegendProps = { label: string; name: string; }; /** @type {typeof import('./map-legend').LayerSizeLegend} */ export const LayerSizeLegend: React.FC = ({label, name}) => label ? (

{label ? : null} by

) : null; const SINGLE_COLOR_DOMAIN = ['']; export type SingleColorLegendProps = { width: number; color: string; }; /** @type {typeof import('./map-legend').SingleColorLegend} */ export const SingleColorLegend: React.FC = React.memo(({width, color}) => ( )); SingleColorLegend.displayName = 'SingleColorLegend'; export type LayerColorLegendProps = { description: VisualChannelDescription; config: LayerBaseConfig; width: number; colorChannel: VisualChannel; }; /** @type {typeof import('./map-legend').LayerColorLegend} */ export const LayerColorLegend: React.FC = React.memo( ({description, config, width, colorChannel}) => { const enableColorBy = description.measure; const {scale, field, domain, range, property} = colorChannel; const [colorScale, colorField, colorDomain] = [scale, field, domain].map(k => config[k]); const colorRange = config.visConfig[range]; return (
{enableColorBy ? : null}
{enableColorBy ? ( ) : ( )}
); } ); LayerColorLegend.displayName = 'LayerColorLegend'; const isColorChannel = visualChannel => [CHANNEL_SCALES.color, CHANNEL_SCALES.colorAggr].includes(visualChannel.channelScaleType); export type LayerLegendHeaderProps = { layer: Layer; options?: { showLayerName?: boolean; }; }; export function LayerLegendHeaderFactory() { /** @type {typeof import('./map-legend').LayerLegendHeader }> */ const LayerLegendHeader: React.FC = ({options, layer}) => { return options?.showLayerName !== false ? (
{layer.config.label}
) : null; }; return LayerLegendHeader; } export type LayerLegendContentProps = { layer: Layer; containerW: number; }; export function LayerLegendContentFactory() { /** @type {typeof import('./map-legend').LayerLegendContent }> */ const LayerLegendContent: React.FC = ({layer, containerW}) => { const colorChannels = Object.values(layer.visualChannels).filter(isColorChannel); const nonColorChannels = Object.values(layer.visualChannels).filter(vc => !isColorChannel(vc)); return ( <> {colorChannels.map(colorChannel => !colorChannel.condition || colorChannel.condition(layer.config) ? ( ) : null )} {nonColorChannels.map(visualChannel => { const matchCondition = !visualChannel.condition || visualChannel.condition(layer.config); const enabled = layer.config[visualChannel.field] || visualChannel.defaultMeasure; const description = layer.getVisualChannelDescription(visualChannel.key); return matchCondition && enabled ? ( ) : null; })} ); }; return LayerLegendContent; } export type MapLegendProps = { layers?: ReadonlyArray; width?: number; mapHeight?: number; options?: { showLayerName?: boolean; }; }; MapLegendFactory.deps = [LayerLegendHeaderFactory, LayerLegendContentFactory]; function MapLegendFactory(LayerLegendHeader, LayerLegendContent) { /** @type {typeof import('./map-legend').MapLegend }> */ const MapLegend: React.FC = ({layers = [], width, mapHeight, options}) => (
{layers.map((layer, index) => { if (!layer.isValidToSave() || layer.config.hidden) { return null; } const containerW = width || DIMENSIONS.mapControl.width; return ( ); })}
); MapLegend.displayName = 'MapLegend'; return MapLegend; } export default MapLegendFactory;