diff --git a/website/src/lib/components/map/layer-control/CustomLayers.svelte b/website/src/lib/components/map/layer-control/CustomLayers.svelte index d9bb06329..8d9825382 100644 --- a/website/src/lib/components/map/layer-control/CustomLayers.svelte +++ b/website/src/lib/components/map/layer-control/CustomLayers.svelte @@ -18,7 +18,7 @@ Layers2, } from '@lucide/svelte'; import { i18n } from '$lib/i18n/i18n.svelte'; - import { defaultBasemap, type CustomLayer } from '$lib/data/layers'; + import { defaultBasemap, layerGroup, type CustomLayer } from '$lib/data/layers'; import { onMount } from 'svelte'; import { remove } from './utils'; import { settings } from '$lib/state/settings'; @@ -128,8 +128,9 @@ ], }; } - addLayer(layerId); + $customLayers[layerId] = layer; + addLayer(layerId); selectedLayerId = undefined; setDataFromSelectedLayer(); } @@ -255,13 +256,12 @@ onfinalize={(e) => { customBasemapItems = e.detail.items; $customBasemapOrder = customBasemapItems.map((item) => item.id); - $selectedBasemapTree.basemaps['custom'] = customBasemapItems.reduce( - (acc, item) => { - acc[item.id] = true; - return acc; - }, - {} - ); + selectedBasemapTree.update(($tree) => { + layerGroup($tree.basemaps)['custom'] = Object.fromEntries( + customBasemapItems.map((item) => [item.id, true]) + ); + return $tree; + }); }} > {#each customBasemapItems as item (item.id)} @@ -315,13 +315,12 @@ onfinalize={(e) => { customOverlayItems = e.detail.items; $customOverlayOrder = customOverlayItems.map((item) => item.id); - $selectedOverlayTree.overlays['custom'] = customOverlayItems.reduce( - (acc, item) => { - acc[item.id] = true; - return acc; - }, - {} - ); + selectedOverlayTree.update(($tree) => { + layerGroup($tree.overlays)['custom'] = Object.fromEntries( + customOverlayItems.map((item) => [item.id, true]) + ); + return $tree; + }); }} > {#each customOverlayItems as item (item.id)} diff --git a/website/src/lib/components/map/layer-control/LayerTreeNode.svelte b/website/src/lib/components/map/layer-control/LayerTreeNode.svelte index 815e7956e..76908c314 100644 --- a/website/src/lib/components/map/layer-control/LayerTreeNode.svelte +++ b/website/src/lib/components/map/layer-control/LayerTreeNode.svelte @@ -22,7 +22,7 @@ selected?: string; onselect?: (value: string) => void; multiple: boolean; - checked: LayerTreeType; + checked?: LayerTreeType; } = $props(); const { customLayers } = settings; @@ -53,7 +53,9 @@ id="{name}-{id}" {name} value={id} - bind:checked={checked[id]} + bind:checked={ + () => checked[id] === true, (value) => (checked[id] = value) + } class="scale-90" aria-label={i18n._(`layers.label.${id}`)} /> @@ -90,12 +92,18 @@ {#snippet content()}
{ + const group = checked[id]; + return typeof group === 'object' ? group : undefined; + }, + (value) => (checked[id] = value ?? {}) + } />
{/snippet} diff --git a/website/src/lib/components/map/layer-control/extension-api.ts b/website/src/lib/components/map/layer-control/extension-api.ts index 50441b593..b20285099 100644 --- a/website/src/lib/components/map/layer-control/extension-api.ts +++ b/website/src/lib/components/map/layer-control/extension-api.ts @@ -1,7 +1,7 @@ import { settings } from '$lib/state/settings'; import { derived, get, writable, type Writable } from 'svelte/store'; import { isSelected, remove, removeAll } from './utils'; -import { overlays, overlayTree, type LayerTreeType } from '$lib/data/layers'; +import { layerGroup, overlays, overlayTree, type LayerTreeType } from '$lib/data/layers'; import { browser } from '$app/environment'; import { map } from '$lib/components/map/map'; @@ -84,11 +84,12 @@ export class ExtensionAPI { ], }; - if (!Object.hasOwn(overlayTree.overlays as LayerTreeType, overlay.extensionName)) { - overlayTree.overlays[overlay.extensionName] = {}; + const extensions = layerGroup(overlayTree.overlays); + if (!Object.hasOwn(extensions, overlay.extensionName)) { + extensions[overlay.extensionName] = {}; } - overlayTree.overlays[overlay.extensionName][overlay.id] = true; + layerGroup(extensions[overlay.extensionName])[overlay.id] = true; selectedOverlayTree.update((selected) => { if (!Object.hasOwn(selected.overlays, overlay.extensionName)) { diff --git a/website/src/lib/data/layers.ts b/website/src/lib/data/layers.ts index 18eea4ed5..83d1aeded 100644 --- a/website/src/lib/data/layers.ts +++ b/website/src/lib/data/layers.ts @@ -794,6 +794,14 @@ export const defaultOpacities: { [key: string]: number } = { export type LayerTreeType = { [key: string]: LayerTreeType | boolean }; +/** A node of a layer tree that is a group of layers, not a layer (which is selected or not). */ +export function layerGroup(node: LayerTreeType | boolean | undefined): LayerTreeType { + if (typeof node !== 'object') { + throw new Error('Not a group of layers'); + } + return node; +} + // Hierarchy containing all basemaps export const basemapTree: LayerTreeType = { basemaps: {