maplibre-gl-layer-control 0.5.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,6 +25,7 @@ A comprehensive layer control for MapLibre GL with advanced styling capabilities
25
25
  - ✅ **Accessibility** - Full ARIA support and keyboard navigation
26
26
  - ✅ **TypeScript** - Full type safety and IntelliSense support
27
27
  - ✅ **React integration** - Optional React components and hooks
28
+ - ✅ **Custom layer adapters** - Integrate non-MapLibre layers (deck.gl, Zarr, etc.)
28
29
 
29
30
  ## Installation
30
31
 
@@ -159,6 +160,7 @@ function MapComponent() {
159
160
  | `showOpacitySlider` | `boolean` | `true` | Show opacity slider for layers |
160
161
  | `showLayerSymbol` | `boolean` | `true` | Show layer type symbols (colored icons) next to layer names |
161
162
  | `excludeDrawnLayers` | `boolean` | `true` | Exclude layers from drawing libraries (Geoman, Mapbox GL Draw, etc.) |
163
+ | `customLayerAdapters` | `CustomLayerAdapter[]` | `undefined` | Adapters for non-MapLibre layers (deck.gl, Zarr, etc.) |
162
164
 
163
165
  ### LayerState
164
166
 
@@ -218,6 +220,156 @@ When using the `layers` option to specify specific layers, all other layers are
218
220
 
219
221
  This allows fine-grained control over which basemap layers are visible while maintaining a simplified layer control interface.
220
222
 
223
+ ### Custom Layer Adapters
224
+
225
+ The layer control supports non-MapLibre layers (such as deck.gl or Zarr layers) through the Custom Layer Adapter interface. This allows you to integrate any custom layer type with the layer control's visibility toggle, opacity slider, and layer list.
226
+
227
+ #### CustomLayerAdapter Interface
228
+
229
+ ```typescript
230
+ interface CustomLayerAdapter {
231
+ /** Unique type identifier for this adapter (e.g., 'cog', 'zarr', 'deck') */
232
+ type: string;
233
+
234
+ /** Get all layer IDs managed by this adapter */
235
+ getLayerIds(): string[];
236
+
237
+ /** Get the current state of a layer */
238
+ getLayerState(layerId: string): LayerState | null;
239
+
240
+ /** Set layer visibility */
241
+ setVisibility(layerId: string, visible: boolean): void;
242
+
243
+ /** Set layer opacity (0-1) */
244
+ setOpacity(layerId: string, opacity: number): void;
245
+
246
+ /** Get display name for a layer */
247
+ getName(layerId: string): string;
248
+
249
+ /** Get layer symbol type for UI display (optional) */
250
+ getSymbolType?(layerId: string): string;
251
+
252
+ /**
253
+ * Subscribe to layer changes (add/remove).
254
+ * Returns an unsubscribe function.
255
+ */
256
+ onLayerChange?(callback: (event: 'add' | 'remove', layerId: string) => void): () => void;
257
+ }
258
+ ```
259
+
260
+ #### Implementing a Custom Adapter
261
+
262
+ Here's an example of implementing an adapter for deck.gl layers:
263
+
264
+ ```typescript
265
+ import type { CustomLayerAdapter, LayerState } from 'maplibre-gl-layer-control';
266
+ import type { MapboxOverlay } from '@deck.gl/mapbox';
267
+
268
+ class DeckLayerAdapter implements CustomLayerAdapter {
269
+ readonly type = 'deck';
270
+
271
+ private deckOverlay: MapboxOverlay;
272
+ private deckLayers: Map<string, any>;
273
+ private changeCallbacks: Array<(event: 'add' | 'remove', layerId: string) => void> = [];
274
+
275
+ constructor(deckOverlay: MapboxOverlay, deckLayers: Map<string, any>) {
276
+ this.deckOverlay = deckOverlay;
277
+ this.deckLayers = deckLayers;
278
+ }
279
+
280
+ getLayerIds(): string[] {
281
+ return Array.from(this.deckLayers.keys());
282
+ }
283
+
284
+ getLayerState(layerId: string): LayerState | null {
285
+ const layer = this.deckLayers.get(layerId);
286
+ if (!layer?.props) return null;
287
+
288
+ return {
289
+ visible: layer.props.visible !== false,
290
+ opacity: layer.props.opacity ?? 1,
291
+ name: this.getName(layerId),
292
+ };
293
+ }
294
+
295
+ setVisibility(layerId: string, visible: boolean): void {
296
+ const layer = this.deckLayers.get(layerId);
297
+ if (!layer?.clone) return;
298
+
299
+ // deck.gl layers are immutable; clone with new props
300
+ const updatedLayer = layer.clone({ visible });
301
+ this.deckLayers.set(layerId, updatedLayer);
302
+ this.updateOverlay();
303
+ }
304
+
305
+ setOpacity(layerId: string, opacity: number): void {
306
+ const layer = this.deckLayers.get(layerId);
307
+ if (!layer?.clone) return;
308
+
309
+ const updatedLayer = layer.clone({ opacity });
310
+ this.deckLayers.set(layerId, updatedLayer);
311
+ this.updateOverlay();
312
+ }
313
+
314
+ getName(layerId: string): string {
315
+ return layerId.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
316
+ }
317
+
318
+ getSymbolType(): string {
319
+ return 'raster'; // Use raster symbol for deck.gl layers
320
+ }
321
+
322
+ onLayerChange(callback: (event: 'add' | 'remove', layerId: string) => void): () => void {
323
+ this.changeCallbacks.push(callback);
324
+ return () => {
325
+ const idx = this.changeCallbacks.indexOf(callback);
326
+ if (idx >= 0) this.changeCallbacks.splice(idx, 1);
327
+ };
328
+ }
329
+
330
+ // Call this when layers are added/removed
331
+ notifyLayerAdded(layerId: string): void {
332
+ this.changeCallbacks.forEach(cb => cb('add', layerId));
333
+ }
334
+
335
+ notifyLayerRemoved(layerId: string): void {
336
+ this.changeCallbacks.forEach(cb => cb('remove', layerId));
337
+ }
338
+
339
+ private updateOverlay(): void {
340
+ this.deckOverlay.setProps({ layers: Array.from(this.deckLayers.values()) });
341
+ }
342
+ }
343
+ ```
344
+
345
+ #### Using Custom Adapters
346
+
347
+ Pass your custom adapters to the `customLayerAdapters` option:
348
+
349
+ ```typescript
350
+ import { LayerControl } from 'maplibre-gl-layer-control';
351
+
352
+ // Create your custom adapter
353
+ const deckAdapter = new DeckLayerAdapter(deckOverlay, deckLayers);
354
+
355
+ // Create the layer control with the adapter
356
+ const layerControl = new LayerControl({
357
+ collapsed: false,
358
+ customLayerAdapters: [deckAdapter]
359
+ });
360
+
361
+ map.addControl(layerControl, 'top-right');
362
+
363
+ // When you add a new deck.gl layer, notify the adapter
364
+ deckLayers.set('my-deck-layer', myDeckLayer);
365
+ deckAdapter.notifyLayerAdded('my-deck-layer');
366
+ ```
367
+
368
+ #### Limitations
369
+
370
+ - **Style Editor**: The style editor (gear icon) is not available for custom layers since they don't use MapLibre's paint properties. Clicking the gear icon will show an info panel explaining this.
371
+ - **Opacity Support**: Some layer types (like deck.gl's COGLayer) may not support dynamic opacity changes due to underlying library limitations. In these cases, the opacity slider will have no effect.
372
+
221
373
  ## Development
222
374
 
223
375
  ```bash
package/dist/index.cjs CHANGED
@@ -3,6 +3,145 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ class CustomLayerRegistry {
7
+ constructor() {
8
+ __publicField(this, "adapters", /* @__PURE__ */ new Map());
9
+ __publicField(this, "changeListeners", []);
10
+ __publicField(this, "unsubscribers", []);
11
+ }
12
+ /**
13
+ * Register a custom layer adapter.
14
+ * @param adapter The adapter to register
15
+ */
16
+ register(adapter) {
17
+ this.adapters.set(adapter.type, adapter);
18
+ if (adapter.onLayerChange) {
19
+ const unsubscribe = adapter.onLayerChange((event, layerId) => {
20
+ this.notifyChange(event, layerId);
21
+ });
22
+ this.unsubscribers.push(unsubscribe);
23
+ }
24
+ }
25
+ /**
26
+ * Unregister an adapter by type.
27
+ * @param type The adapter type to unregister
28
+ */
29
+ unregister(type) {
30
+ this.adapters.delete(type);
31
+ }
32
+ /**
33
+ * Get all custom layer IDs across all adapters.
34
+ * @returns Array of layer IDs
35
+ */
36
+ getAllLayerIds() {
37
+ const ids = [];
38
+ this.adapters.forEach((adapter) => {
39
+ ids.push(...adapter.getLayerIds());
40
+ });
41
+ return ids;
42
+ }
43
+ /**
44
+ * Check if a layer ID is managed by any adapter.
45
+ * @param layerId The layer ID to check
46
+ * @returns true if the layer is managed by an adapter
47
+ */
48
+ hasLayer(layerId) {
49
+ for (const adapter of this.adapters.values()) {
50
+ if (adapter.getLayerIds().includes(layerId)) {
51
+ return true;
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+ /**
57
+ * Get the adapter responsible for a specific layer.
58
+ * @param layerId The layer ID
59
+ * @returns The adapter or null if not found
60
+ */
61
+ getAdapterForLayer(layerId) {
62
+ for (const adapter of this.adapters.values()) {
63
+ if (adapter.getLayerIds().includes(layerId)) {
64
+ return adapter;
65
+ }
66
+ }
67
+ return null;
68
+ }
69
+ /**
70
+ * Get the state of a custom layer.
71
+ * @param layerId The layer ID
72
+ * @returns The layer state or null if not found
73
+ */
74
+ getLayerState(layerId) {
75
+ const adapter = this.getAdapterForLayer(layerId);
76
+ return adapter ? adapter.getLayerState(layerId) : null;
77
+ }
78
+ /**
79
+ * Set visibility of a custom layer.
80
+ * @param layerId The layer ID
81
+ * @param visible Whether the layer should be visible
82
+ * @returns true if the operation was handled by an adapter
83
+ */
84
+ setVisibility(layerId, visible) {
85
+ const adapter = this.getAdapterForLayer(layerId);
86
+ if (adapter) {
87
+ adapter.setVisibility(layerId, visible);
88
+ return true;
89
+ }
90
+ return false;
91
+ }
92
+ /**
93
+ * Set opacity of a custom layer.
94
+ * @param layerId The layer ID
95
+ * @param opacity The opacity value (0-1)
96
+ * @returns true if the operation was handled by an adapter
97
+ */
98
+ setOpacity(layerId, opacity) {
99
+ const adapter = this.getAdapterForLayer(layerId);
100
+ if (adapter) {
101
+ adapter.setOpacity(layerId, opacity);
102
+ return true;
103
+ }
104
+ return false;
105
+ }
106
+ /**
107
+ * Get the symbol type for a custom layer (for UI display).
108
+ * @param layerId The layer ID
109
+ * @returns The symbol type or null if not available
110
+ */
111
+ getSymbolType(layerId) {
112
+ const adapter = this.getAdapterForLayer(layerId);
113
+ if (adapter && adapter.getSymbolType) {
114
+ return adapter.getSymbolType(layerId);
115
+ }
116
+ return null;
117
+ }
118
+ /**
119
+ * Subscribe to layer changes across all adapters.
120
+ * @param callback Function called when layers are added or removed
121
+ * @returns Unsubscribe function
122
+ */
123
+ onChange(callback) {
124
+ this.changeListeners.push(callback);
125
+ return () => {
126
+ const idx = this.changeListeners.indexOf(callback);
127
+ if (idx >= 0) {
128
+ this.changeListeners.splice(idx, 1);
129
+ }
130
+ };
131
+ }
132
+ notifyChange(event, layerId) {
133
+ this.changeListeners.forEach((cb) => cb(event, layerId));
134
+ }
135
+ /**
136
+ * Clean up all subscriptions and adapters.
137
+ */
138
+ destroy() {
139
+ this.unsubscribers.forEach((unsub) => unsub());
140
+ this.unsubscribers = [];
141
+ this.adapters.clear();
142
+ this.changeListeners = [];
143
+ }
144
+ }
6
145
  function getOpacityProperty(layerType) {
7
146
  switch (layerType) {
8
147
  case "fill":
@@ -379,6 +518,46 @@ function createDefaultSymbol(size, color) {
379
518
  fill="${color}" stroke="${borderColor}" stroke-width="1"/>
380
519
  </svg>`;
381
520
  }
521
+ function createCOGSymbol(size, color) {
522
+ const padding = 2;
523
+ const borderColor = darkenColor(color, 0.3);
524
+ const cellSize = (size - padding * 2) / 2;
525
+ return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
526
+ <rect x="${padding}" y="${padding}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5"/>
527
+ <rect x="${padding + cellSize}" y="${padding}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" opacity="0.8"/>
528
+ <rect x="${padding}" y="${padding + cellSize}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" opacity="0.6"/>
529
+ <rect x="${padding + cellSize}" y="${padding + cellSize}" width="${cellSize}" height="${cellSize}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" opacity="0.4"/>
530
+ </svg>`;
531
+ }
532
+ function createZarrSymbol(size, color) {
533
+ const padding = 2;
534
+ const borderColor = darkenColor(color, 0.3);
535
+ const innerSize = size - padding * 2;
536
+ return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
537
+ <rect x="${padding + 2}" y="${padding}" width="${innerSize - 2}" height="${innerSize - 2}" fill="${darkenColor(color, 0.2)}" stroke="${borderColor}" stroke-width="0.5" rx="1"/>
538
+ <rect x="${padding + 1}" y="${padding + 1}" width="${innerSize - 2}" height="${innerSize - 2}" fill="${darkenColor(color, 0.1)}" stroke="${borderColor}" stroke-width="0.5" rx="1"/>
539
+ <rect x="${padding}" y="${padding + 2}" width="${innerSize - 2}" height="${innerSize - 2}" fill="${color}" stroke="${borderColor}" stroke-width="0.5" rx="1"/>
540
+ <line x1="${padding + 3}" y1="${padding + 5}" x2="${padding + innerSize - 5}" y2="${padding + 5}" stroke="${borderColor}" stroke-width="0.5" opacity="0.5"/>
541
+ <line x1="${padding + 3}" y1="${padding + 8}" x2="${padding + innerSize - 5}" y2="${padding + 8}" stroke="${borderColor}" stroke-width="0.5" opacity="0.5"/>
542
+ </svg>`;
543
+ }
544
+ function createCustomRasterSymbol(size, color) {
545
+ const padding = 2;
546
+ const borderColor = darkenColor(color, 0.3);
547
+ const id = `customRasterGrad_${Math.random().toString(36).slice(2, 9)}`;
548
+ return `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg">
549
+ <defs>
550
+ <linearGradient id="${id}" x1="0%" y1="0%" x2="100%" y2="100%">
551
+ <stop offset="0%" stop-color="${color}"/>
552
+ <stop offset="100%" stop-color="${darkenColor(color, 0.4)}"/>
553
+ </linearGradient>
554
+ </defs>
555
+ <rect x="${padding}" y="${padding}" width="${size - padding * 2}" height="${size - padding * 2}"
556
+ fill="url(#${id})" stroke="${borderColor}" stroke-width="1" rx="1"/>
557
+ <line x1="${size / 2}" y1="${padding}" x2="${size / 2}" y2="${size - padding}" stroke="${borderColor}" stroke-width="0.5" opacity="0.3"/>
558
+ <line x1="${padding}" y1="${size / 2}" x2="${size - padding}" y2="${size / 2}" stroke="${borderColor}" stroke-width="0.5" opacity="0.3"/>
559
+ </svg>`;
560
+ }
382
561
  function createStackedLayersSymbol(size) {
383
562
  const colors = ["#a8d4a8", "#8ec4e8", "#d4c4a8"];
384
563
  const borderColor = "#666666";
@@ -413,6 +592,12 @@ function createLayerSymbolSVG(layerType, color, options = {}) {
413
592
  return createFillExtrusionSymbol(size, fillColor);
414
593
  case "background-group":
415
594
  return createStackedLayersSymbol(size);
595
+ case "cog":
596
+ return createCOGSymbol(size, fillColor);
597
+ case "zarr":
598
+ return createZarrSymbol(size, fillColor);
599
+ case "custom-raster":
600
+ return createCustomRasterSymbol(size, fillColor);
416
601
  default:
417
602
  return createDefaultSymbol(size, fillColor);
418
603
  }
@@ -438,6 +623,8 @@ class LayerControl {
438
623
  __publicField(this, "showOpacitySlider");
439
624
  __publicField(this, "showLayerSymbol");
440
625
  __publicField(this, "excludeDrawnLayers");
626
+ __publicField(this, "customLayerRegistry", null);
627
+ __publicField(this, "customLayerUnsubscribe", null);
441
628
  __publicField(this, "widthSliderEl", null);
442
629
  __publicField(this, "widthThumbEl", null);
443
630
  __publicField(this, "widthValueEl", null);
@@ -455,7 +642,7 @@ class LayerControl {
455
642
  this.excludeDrawnLayers = options.excludeDrawnLayers !== false;
456
643
  this.state = {
457
644
  collapsed: options.collapsed !== false,
458
- panelWidth: options.panelWidth || 320,
645
+ panelWidth: options.panelWidth || 348,
459
646
  activeStyleEditor: null,
460
647
  layerStates: options.layerStates || {},
461
648
  originalStyles: /* @__PURE__ */ new Map(),
@@ -466,6 +653,12 @@ class LayerControl {
466
653
  };
467
654
  this.targetLayers = options.layers || Object.keys(this.state.layerStates);
468
655
  this.styleEditors = /* @__PURE__ */ new Map();
656
+ if (options.customLayerAdapters && options.customLayerAdapters.length > 0) {
657
+ this.customLayerRegistry = new CustomLayerRegistry();
658
+ options.customLayerAdapters.forEach((adapter) => {
659
+ this.customLayerRegistry.register(adapter);
660
+ });
661
+ }
469
662
  }
470
663
  /**
471
664
  * Called when the control is added to the map
@@ -490,6 +683,14 @@ class LayerControl {
490
683
  */
491
684
  onRemove() {
492
685
  var _a;
686
+ if (this.customLayerUnsubscribe) {
687
+ this.customLayerUnsubscribe();
688
+ this.customLayerUnsubscribe = null;
689
+ }
690
+ if (this.customLayerRegistry) {
691
+ this.customLayerRegistry.destroy();
692
+ this.customLayerRegistry = null;
693
+ }
493
694
  (_a = this.container.parentNode) == null ? void 0 : _a.removeChild(this.container);
494
695
  }
495
696
  /**
@@ -572,6 +773,22 @@ class LayerControl {
572
773
  };
573
774
  });
574
775
  }
776
+ if (this.customLayerRegistry) {
777
+ const customLayerIds = this.customLayerRegistry.getAllLayerIds();
778
+ customLayerIds.forEach((layerId) => {
779
+ if (this.state.layerStates[layerId]) return;
780
+ const customState = this.customLayerRegistry.getLayerState(layerId);
781
+ if (customState) {
782
+ this.state.layerStates[layerId] = {
783
+ visible: customState.visible,
784
+ opacity: customState.opacity,
785
+ name: customState.name,
786
+ isCustomLayer: true,
787
+ customLayerType: this.customLayerRegistry.getSymbolType(layerId) || void 0
788
+ };
789
+ }
790
+ });
791
+ }
575
792
  this.targetLayers = Object.keys(this.state.layerStates);
576
793
  }
577
794
  /**
@@ -725,12 +942,7 @@ class LayerControl {
725
942
  */
726
943
  setAllLayersVisibility(visible) {
727
944
  Object.keys(this.state.layerStates).forEach((layerId) => {
728
- if (layerId === "Background") {
729
- this.toggleBackgroundVisibility(visible);
730
- } else {
731
- this.state.layerStates[layerId].visible = visible;
732
- this.map.setLayoutProperty(layerId, "visibility", visible ? "visible" : "none");
733
- }
945
+ this.toggleLayerVisibility(layerId, visible);
734
946
  const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
735
947
  if (itemEl) {
736
948
  const checkbox = itemEl.querySelector(".layer-control-checkbox");
@@ -965,6 +1177,11 @@ class LayerControl {
965
1177
  }, 150);
966
1178
  }
967
1179
  });
1180
+ if (this.customLayerRegistry) {
1181
+ this.customLayerUnsubscribe = this.customLayerRegistry.onChange(() => {
1182
+ setTimeout(() => this.checkForNewLayers(), 100);
1183
+ });
1184
+ }
968
1185
  }
969
1186
  /**
970
1187
  * Toggle panel expanded/collapsed state
@@ -1077,6 +1294,17 @@ class LayerControl {
1077
1294
  * @returns The symbol HTML element, or null if layer not found
1078
1295
  */
1079
1296
  createLayerSymbol(layerId) {
1297
+ const layerState = this.state.layerStates[layerId];
1298
+ if (layerState == null ? void 0 : layerState.isCustomLayer) {
1299
+ const symbolType = layerState.customLayerType || "custom-raster";
1300
+ const color2 = "#4a90d9";
1301
+ const svgMarkup2 = createLayerSymbolSVG(symbolType, color2);
1302
+ const symbolContainer2 = document.createElement("span");
1303
+ symbolContainer2.className = "layer-control-symbol";
1304
+ symbolContainer2.innerHTML = svgMarkup2;
1305
+ symbolContainer2.title = `Layer type: ${symbolType}`;
1306
+ return symbolContainer2;
1307
+ }
1080
1308
  const layer = this.map.getLayer(layerId);
1081
1309
  if (!layer) return null;
1082
1310
  const layerType = layer.type;
@@ -1119,6 +1347,7 @@ class LayerControl {
1119
1347
  * Toggle layer visibility
1120
1348
  */
1121
1349
  toggleLayerVisibility(layerId, visible) {
1350
+ var _a;
1122
1351
  if (layerId === "Background") {
1123
1352
  this.toggleBackgroundVisibility(visible);
1124
1353
  return;
@@ -1126,12 +1355,16 @@ class LayerControl {
1126
1355
  if (this.state.layerStates[layerId]) {
1127
1356
  this.state.layerStates[layerId].visible = visible;
1128
1357
  }
1358
+ if ((_a = this.customLayerRegistry) == null ? void 0 : _a.setVisibility(layerId, visible)) {
1359
+ return;
1360
+ }
1129
1361
  this.map.setLayoutProperty(layerId, "visibility", visible ? "visible" : "none");
1130
1362
  }
1131
1363
  /**
1132
1364
  * Change layer opacity
1133
1365
  */
1134
1366
  changeLayerOpacity(layerId, opacity) {
1367
+ var _a;
1135
1368
  if (layerId === "Background") {
1136
1369
  this.changeBackgroundOpacity(opacity);
1137
1370
  return;
@@ -1139,6 +1372,9 @@ class LayerControl {
1139
1372
  if (this.state.layerStates[layerId]) {
1140
1373
  this.state.layerStates[layerId].opacity = opacity;
1141
1374
  }
1375
+ if ((_a = this.customLayerRegistry) == null ? void 0 : _a.setOpacity(layerId, opacity)) {
1376
+ return;
1377
+ }
1142
1378
  const layerType = getLayerType(this.map, layerId);
1143
1379
  if (layerType) {
1144
1380
  setLayerOpacity(this.map, layerId, layerType, opacity);
@@ -1461,11 +1697,18 @@ class LayerControl {
1461
1697
  if (layerId === "Background") {
1462
1698
  return null;
1463
1699
  }
1700
+ const layerState = this.state.layerStates[layerId];
1701
+ const isCustomLayer = (layerState == null ? void 0 : layerState.isCustomLayer) === true;
1464
1702
  const button = document.createElement("button");
1465
1703
  button.className = "layer-control-style-button";
1466
1704
  button.innerHTML = "&#9881;";
1467
- button.title = "Edit layer style";
1468
- button.setAttribute("aria-label", `Edit style for ${layerId}`);
1705
+ if (isCustomLayer) {
1706
+ button.title = "Layer info (style editing not available)";
1707
+ button.setAttribute("aria-label", `Layer info for ${layerId}`);
1708
+ } else {
1709
+ button.title = "Edit layer style";
1710
+ button.setAttribute("aria-label", `Edit style for ${layerId}`);
1711
+ }
1469
1712
  button.addEventListener("click", (e) => {
1470
1713
  e.stopPropagation();
1471
1714
  this.toggleStyleEditor(layerId);
@@ -1491,6 +1734,17 @@ class LayerControl {
1491
1734
  openStyleEditor(layerId) {
1492
1735
  const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
1493
1736
  if (!itemEl) return;
1737
+ const layerState = this.state.layerStates[layerId];
1738
+ if (layerState == null ? void 0 : layerState.isCustomLayer) {
1739
+ const editor2 = this.createCustomLayerInfoPanel(layerId);
1740
+ itemEl.appendChild(editor2);
1741
+ this.styleEditors.set(layerId, editor2);
1742
+ this.state.activeStyleEditor = layerId;
1743
+ setTimeout(() => {
1744
+ editor2.scrollIntoView({ behavior: "smooth", block: "nearest" });
1745
+ }, 50);
1746
+ return;
1747
+ }
1494
1748
  if (!this.state.originalStyles.has(layerId)) {
1495
1749
  const layer = this.map.getLayer(layerId);
1496
1750
  if (layer) {
@@ -1506,6 +1760,48 @@ class LayerControl {
1506
1760
  editor.scrollIntoView({ behavior: "smooth", block: "nearest" });
1507
1761
  }, 50);
1508
1762
  }
1763
+ /**
1764
+ * Create info panel for custom layers (style editing not supported)
1765
+ */
1766
+ createCustomLayerInfoPanel(layerId) {
1767
+ const editor = document.createElement("div");
1768
+ editor.className = "layer-control-style-editor layer-control-custom-info";
1769
+ const header = document.createElement("div");
1770
+ header.className = "style-editor-header";
1771
+ const title = document.createElement("span");
1772
+ title.className = "style-editor-title";
1773
+ title.textContent = "Layer Info";
1774
+ const closeBtn = document.createElement("button");
1775
+ closeBtn.className = "style-editor-close";
1776
+ closeBtn.innerHTML = "&times;";
1777
+ closeBtn.title = "Close";
1778
+ closeBtn.addEventListener("click", (e) => {
1779
+ e.stopPropagation();
1780
+ this.closeStyleEditor(layerId);
1781
+ });
1782
+ header.appendChild(title);
1783
+ header.appendChild(closeBtn);
1784
+ const content = document.createElement("div");
1785
+ content.className = "style-editor-controls";
1786
+ const infoText = document.createElement("p");
1787
+ infoText.className = "layer-control-custom-info-text";
1788
+ infoText.textContent = `This is a custom layer. Style editing is not available for this layer type. Use the visibility toggle and opacity slider to control the layer.`;
1789
+ content.appendChild(infoText);
1790
+ const actions = document.createElement("div");
1791
+ actions.className = "style-editor-actions";
1792
+ const closeActionBtn = document.createElement("button");
1793
+ closeActionBtn.className = "style-editor-button style-editor-button-close";
1794
+ closeActionBtn.textContent = "Close";
1795
+ closeActionBtn.addEventListener("click", (e) => {
1796
+ e.stopPropagation();
1797
+ this.closeStyleEditor(layerId);
1798
+ });
1799
+ actions.appendChild(closeActionBtn);
1800
+ editor.appendChild(header);
1801
+ editor.appendChild(content);
1802
+ editor.appendChild(actions);
1803
+ return editor;
1804
+ }
1509
1805
  /**
1510
1806
  * Close style editor for a layer
1511
1807
  */
@@ -1823,7 +2119,14 @@ class LayerControl {
1823
2119
  return;
1824
2120
  }
1825
2121
  Object.keys(this.state.layerStates).forEach((layerId) => {
2122
+ var _a;
1826
2123
  try {
2124
+ if ((_a = this.state.layerStates[layerId]) == null ? void 0 : _a.isCustomLayer) {
2125
+ return;
2126
+ }
2127
+ if (layerId === "Background") {
2128
+ return;
2129
+ }
1827
2130
  const layer = this.map.getLayer(layerId);
1828
2131
  if (!layer) return;
1829
2132
  const visibility = this.map.getLayoutProperty(layerId, "visibility");
@@ -1891,7 +2194,8 @@ class LayerControl {
1891
2194
  });
1892
2195
  const removedLayers = [];
1893
2196
  Object.keys(this.state.layerStates).forEach((layerId) => {
1894
- if (layerId !== "Background" && !currentMapLayerIds.has(layerId)) {
2197
+ const state = this.state.layerStates[layerId];
2198
+ if (layerId !== "Background" && !state.isCustomLayer && !currentMapLayerIds.has(layerId)) {
1895
2199
  removedLayers.push(layerId);
1896
2200
  }
1897
2201
  });
@@ -1924,11 +2228,44 @@ class LayerControl {
1924
2228
  this.addLayerItem(layerId, this.state.layerStates[layerId]);
1925
2229
  });
1926
2230
  }
2231
+ if (this.customLayerRegistry) {
2232
+ const customLayerIds = this.customLayerRegistry.getAllLayerIds();
2233
+ customLayerIds.forEach((layerId) => {
2234
+ if (!this.state.layerStates[layerId]) {
2235
+ const customState = this.customLayerRegistry.getLayerState(layerId);
2236
+ if (customState) {
2237
+ this.state.layerStates[layerId] = {
2238
+ visible: customState.visible,
2239
+ opacity: customState.opacity,
2240
+ name: customState.name,
2241
+ isCustomLayer: true,
2242
+ customLayerType: this.customLayerRegistry.getSymbolType(layerId) || void 0
2243
+ };
2244
+ this.addLayerItem(layerId, this.state.layerStates[layerId]);
2245
+ }
2246
+ }
2247
+ });
2248
+ Object.keys(this.state.layerStates).forEach((layerId) => {
2249
+ const state = this.state.layerStates[layerId];
2250
+ if (state.isCustomLayer && !customLayerIds.includes(layerId)) {
2251
+ delete this.state.layerStates[layerId];
2252
+ const itemEl = this.panel.querySelector(`[data-layer-id="${layerId}"]`);
2253
+ if (itemEl) {
2254
+ itemEl.remove();
2255
+ }
2256
+ if (this.state.activeStyleEditor === layerId) {
2257
+ this.state.activeStyleEditor = null;
2258
+ }
2259
+ this.styleEditors.delete(layerId);
2260
+ }
2261
+ });
2262
+ }
1927
2263
  } catch (error) {
1928
2264
  console.warn("Failed to check for new layers:", error);
1929
2265
  }
1930
2266
  }
1931
2267
  }
2268
+ exports.CustomLayerRegistry = CustomLayerRegistry;
1932
2269
  exports.LayerControl = LayerControl;
1933
2270
  exports.clamp = clamp;
1934
2271
  exports.createBackgroundGroupSymbolSVG = createBackgroundGroupSymbolSVG;