Skip to content

Commit 1c2db0f

Browse files
authored
feat: add layer visibility toggle to map legend (#3303) (#3324)
Signed-off-by: pierreeurope <pierre.europe@pm.me>
1 parent 385ed90 commit 1c2db0f

4 files changed

Lines changed: 69 additions & 6 deletions

File tree

src/components/src/map-container.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import EditorFactory from './editor/editor';
2828
import {
2929
generateMapboxLayers,
3030
updateMapboxLayers,
31+
Layer,
3132
LayerBaseConfig,
3233
VisualChannelDomain,
3334
EditorLayerUtils,
@@ -1065,6 +1066,11 @@ export default function MapContainerFactory(
10651066
this.props.visStateActions.setLoadingIndicator({change: 0});
10661067
}, DEBOUNCE_LOADING_STATE_PROPAGATE);
10671068

1069+
_handleToggleLayerVisibility = (layer: Layer) => {
1070+
const {visStateActions} = this.props;
1071+
visStateActions.layerConfigChange(layer, {isVisible: !layer.config.isVisible});
1072+
};
1073+
10681074
_toggleMapControl = panelId => {
10691075
const {index, uiStateActions} = this.props;
10701076

@@ -1175,6 +1181,7 @@ export default function MapContainerFactory(
11751181
onSetLocale={uiStateActions.setLocale}
11761182
onToggleEditorVisibility={visStateActions.toggleEditorVisibility}
11771183
onLayerVisConfigChange={visStateActions.layerVisConfigChange}
1184+
onToggleLayerVisibility={this._handleToggleLayerVisibility}
11781185
mapHeight={mapState.height}
11791186
setMapControlSettings={uiStateActions.setMapControlSettings}
11801187
activeSidePanel={activeSidePanel}

src/components/src/map/map-control.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export type MapControlProps = {
6262
onSetEditorMode: (mode: string) => void;
6363
onToggleEditorVisibility: () => void;
6464
onLayerVisConfigChange: (oldLayer: Layer, newVisConfig: Partial<LayerVisConfig>) => void;
65+
onToggleLayerVisibility?: (layer: Layer) => void;
6566
top: number;
6667
onSetLocale: typeof UIStateActions.setLocale;
6768
availableLocales: string[];

src/components/src/map/map-legend-panel.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ export type MapLegendPanelProps = {
320320
mapControls: MapControls;
321321
mapState?: MapState;
322322
onLayerVisConfigChange?: (oldLayer: Layer, newVisConfig: Partial<LayerVisConfig>) => void;
323+
onToggleLayerVisibility?: (layer: Layer) => void;
323324
onToggleSplitMapViewport?: ActionHandler<typeof toggleSplitMapViewport>;
324325
isViewportUnsyncAllowed?: boolean;
325326
onClickControlBtn?: (e?: MouseEvent) => void;
@@ -352,6 +353,7 @@ const MapLegendPanelComponent = ({
352353
actionIcons = defaultActionIcons,
353354
mapState,
354355
onLayerVisConfigChange,
356+
onToggleLayerVisibility,
355357
onToggleSplitMapViewport,
356358
onClickControlBtn,
357359
activeSidePanel,
@@ -406,6 +408,7 @@ const MapLegendPanelComponent = ({
406408
disableEdit={disableEdit}
407409
isExport={isExport}
408410
onLayerVisConfigChange={onLayerVisConfigChange}
411+
onToggleLayerVisibility={onToggleLayerVisibility}
409412
/>
410413
</MapControlPanel>
411414
) : null;

src/components/src/map/map-legend.tsx

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {FormattedMessage} from '@kepler.gl/localization';
1313
import {Layer, LayerBaseConfig, VisualChannel, VisualChannelDescription} from '@kepler.gl/layers';
1414
import {LayerVisConfig, MapState, RGBColor} from '@kepler.gl/types';
1515
import {getDistanceScales} from 'viewport-mercator-project';
16-
import {ArrowDown, ArrowRight} from '../common/icons';
16+
import {ArrowDown, ArrowRight, EyeSeen, EyeUnseen} from '../common/icons';
1717
import PanelHeaderActionFactory from '../side-panel/panel-header-action';
1818

1919
interface StyledMapControlLegendProps {
@@ -73,6 +73,26 @@ export const StyledMapControlLegend = styled.div<StyledMapControlLegendProps>`
7373
}
7474
`;
7575

76+
const StyledLegendHeaderRow = styled.div`
77+
display: flex;
78+
align-items: center;
79+
justify-content: space-between;
80+
`;
81+
82+
const StyledVisibilityToggle = styled.div<{isVisible: boolean}>`
83+
cursor: pointer;
84+
color: ${props => (props.isVisible ? props.theme.textColor : props.theme.subtextColor)};
85+
display: flex;
86+
align-items: center;
87+
margin-left: 8px;
88+
opacity: ${props => (props.isVisible ? 1 : 0.5)};
89+
90+
&:hover {
91+
color: ${props => props.theme.textColorHl};
92+
opacity: 1;
93+
}
94+
`;
95+
7696
export const VisualChannelMetric = ({name}) => {
7797
return (
7898
<div className="legend--layer__title">
@@ -300,16 +320,41 @@ export type LayerLegendHeaderProps = {
300320
showLayerName?: boolean;
301321
};
302322
isExport?: boolean;
323+
onToggleLayerVisibility?: (layer: Layer) => void;
303324
};
304325

305326
const isRadiusChannel = visualChannel =>
306327
[CHANNEL_SCALES.radius].includes(visualChannel.channelScaleType);
307328

308329
export function LayerLegendHeaderFactory() {
309-
const LayerLegendHeader: React.FC<LayerLegendHeaderProps> = ({options, layer}) => {
310-
return options?.showLayerName !== false ? (
311-
<div className="legend--layer_name">{layer.config.label}</div>
312-
) : null;
330+
const LayerLegendHeader: React.FC<LayerLegendHeaderProps> = ({
331+
options,
332+
layer,
333+
onToggleLayerVisibility
334+
}) => {
335+
const isVisible = layer.config.isVisible;
336+
const onToggle = useCallback(() => {
337+
if (onToggleLayerVisibility) {
338+
onToggleLayerVisibility(layer);
339+
}
340+
}, [layer, onToggleLayerVisibility]);
341+
342+
if (options?.showLayerName === false) {
343+
return null;
344+
}
345+
346+
return (
347+
<StyledLegendHeaderRow>
348+
<div className="legend--layer_name" style={{opacity: isVisible ? 1 : 0.5}}>
349+
{layer.config.label}
350+
</div>
351+
{onToggleLayerVisibility ? (
352+
<StyledVisibilityToggle isVisible={isVisible} onClick={onToggle}>
353+
{isVisible ? <EyeSeen height="12px" /> : <EyeUnseen height="12px" />}
354+
</StyledVisibilityToggle>
355+
) : null}
356+
</StyledLegendHeaderRow>
357+
);
313358
};
314359
return LayerLegendHeader;
315360
}
@@ -421,6 +466,7 @@ export type MapLegendProps = {
421466
disableEdit?: boolean;
422467
isExport?: boolean;
423468
onLayerVisConfigChange?: (oldLayer: Layer, newVisConfig: Partial<LayerVisConfig>) => void;
469+
onToggleLayerVisibility?: (layer: Layer) => void;
424470
actionIcons?: MapLegendIcons;
425471
};
426472

@@ -438,6 +484,7 @@ function MapLegendFactory(
438484
disableEdit,
439485
isExport,
440486
onLayerVisConfigChange,
487+
onToggleLayerVisibility,
441488
actionIcons = defaultActionIcons
442489
}) => (
443490
<div className="map-legend">
@@ -454,7 +501,12 @@ function MapLegendFactory(
454501
key={index}
455502
width={containerW}
456503
>
457-
<LayerLegendHeader isExport={isExport} options={options} layer={layer} />
504+
<LayerLegendHeader
505+
isExport={isExport}
506+
options={options}
507+
layer={layer}
508+
onToggleLayerVisibility={onToggleLayerVisibility}
509+
/>
458510
<LayerLegendContent
459511
containerW={containerW}
460512
layer={layer}

0 commit comments

Comments
 (0)