import type { ColorToken } from './map.types'; /** * A named, toggleable layer of map content. * * @description * Deliberately **not** called `layers` — that prop already exists on `` and * means Google's native traffic / transit / bicycling overlays. Renaming it * would silently change the meaning of working code, so the new concept gets a * new name: `dataLayers`. * * Markers and geometries point at a layer through `layerId`; toggling the layer * shows or hides everything that belongs to it. */ export interface MapDataLayer { id: string; label: string; /** Optional heading used to group layers in the panel. */ group?: string; /** Swatch shown beside the label. */ colorToken?: ColorToken; /** @default true */ visible?: boolean; /** Stacking order for everything in the layer. Higher draws on top. */ zIndex?: number; /** Multiplies each feature's own opacity. @default 1 */ opacity?: number; } /** Resolved visibility per layer id. Features with no `layerId` are always visible. */ export type MapLayerVisibility = Record; export function resolveLayerVisibility(layers: MapDataLayer[] | undefined): MapLayerVisibility { const visibility: MapLayerVisibility = {}; layers?.forEach(layer => { visibility[layer.id] = layer.visible !== false; }); return visibility; } export function isLayerVisible( layerId: string | undefined, visibility: MapLayerVisibility ): boolean { if (!layerId) return true; // An unknown layer id is shown rather than hidden: silently swallowing // features because of a typo is far worse than showing one too many. return visibility[layerId] !== false; } export function layerById( layerId: string | undefined, layers: MapDataLayer[] | undefined ): MapDataLayer | undefined { if (!layerId || !layers) return undefined; return layers.find(layer => layer.id === layerId); } /** * Combines a feature's own opacity with its layer's. * * With four overlays stacked at once the effective opacity has to be a single * number, so layer and feature multiply rather than one overriding the other. */ export function effectiveOpacity( featureOpacity: number | undefined, layer: MapDataLayer | undefined ): number { const own = featureOpacity ?? 1; const fromLayer = layer?.opacity ?? 1; return Math.max(0, Math.min(1, own * fromLayer)); } /** A feature's z-index falls back to its layer's. */ export function effectiveZIndex( featureZIndex: number | undefined, layer: MapDataLayer | undefined ): number | undefined { return featureZIndex ?? layer?.zIndex; }