maplibre-gl-layer-control 0.6.0 → 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
@@ -1736,7 +1736,7 @@ class LayerControl {
1736
1736
  if (!itemEl) return;
1737
1737
  const layerState = this.state.layerStates[layerId];
1738
1738
  if (layerState == null ? void 0 : layerState.isCustomLayer) {
1739
- const editor2 = this.createCustomLayerInfoPanel(layerId, layerState.customLayerType);
1739
+ const editor2 = this.createCustomLayerInfoPanel(layerId);
1740
1740
  itemEl.appendChild(editor2);
1741
1741
  this.styleEditors.set(layerId, editor2);
1742
1742
  this.state.activeStyleEditor = layerId;
@@ -1763,7 +1763,7 @@ class LayerControl {
1763
1763
  /**
1764
1764
  * Create info panel for custom layers (style editing not supported)
1765
1765
  */
1766
- createCustomLayerInfoPanel(layerId, layerType) {
1766
+ createCustomLayerInfoPanel(layerId) {
1767
1767
  const editor = document.createElement("div");
1768
1768
  editor.className = "layer-control-style-editor layer-control-custom-info";
1769
1769
  const header = document.createElement("div");
@@ -1785,8 +1785,7 @@ class LayerControl {
1785
1785
  content.className = "style-editor-controls";
1786
1786
  const infoText = document.createElement("p");
1787
1787
  infoText.className = "layer-control-custom-info-text";
1788
- const typeLabel = layerType ? layerType.toUpperCase() : "Custom";
1789
- infoText.textContent = `This is a ${typeLabel} layer. Style editing is not available for this layer type. Use the visibility toggle and opacity slider to control the layer.`;
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.`;
1790
1789
  content.appendChild(infoText);
1791
1790
  const actions = document.createElement("div");
1792
1791
  actions.className = "style-editor-actions";