map-gl-style-switcher 0.5.0 → 0.7.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
@@ -2,24 +2,30 @@
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/map-gl-style-switcher.svg)](https://badge.fury.io/js/map-gl-style-switcher)
4
4
  [![CI](https://github.com/muimsd/map-gl-style-switcher/actions/workflows/ci.yml/badge.svg)](https://github.com/muimsd/map-gl-style-switcher/actions/workflows/ci.yml)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![Netlify Status](https://api.netlify.com/api/v1/badges/ea3ee6af-161d-46be-9006-9d31ad52da3c/deploy-status)](https://app.netlify.com/projects/map-gl-style-switcher/deploys)
5
7
 
6
8
  <!-- [![Coverage Status](https://codecov.io/gh/muimsd/map-gl-style-switcher/branch/main/graph/badge.svg)](https://codecov.io/gh/muimsd/map-gl-style-switcher) -->
7
9
 
8
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
-
10
10
  A TypeScript control for switching Mapbox GL / MapLibre GL map styles. Easily add a floating style switcher to your map app, with support for multiple styles, images, dark/light themes, and before/after change callbacks.
11
11
 
12
- **[🌐 Live Demo](https://map-gl-style-switcher.netlify.app/)**
12
+ **Available as:**
13
+
14
+ - `StyleSwitcherControl` - Direct IControl implementation for Mapbox/MapLibre GL
15
+ - `MapGLStyleSwitcher` - React component for react-map-gl integration
16
+
17
+ **<a href="https://map-gl-style-switcher.netlify.app/" target="_blank">🌐 Live Demo</a>**
13
18
 
14
19
  ## Demo
15
20
 
16
21
  ![Demo GIF](./images/demo.gif)
17
22
 
18
- *Live demo of the style switcher control in action*
23
+ _Live demo of the style switcher control in action_
19
24
 
20
25
  ## Features
21
26
 
22
27
  - IControl implementation for Mapbox GL / MapLibre GL
28
+ - **React component for react-map-gl integration**
23
29
  - Floating style switcher in any corner (via map.addControl position)
24
30
  - Support for multiple map styles with thumbnails
25
31
  - Expand/collapse on hover with smooth animations
@@ -30,6 +36,7 @@ A TypeScript control for switching Mapbox GL / MapLibre GL map styles. Easily ad
30
36
  - Fully customizable CSS classes
31
37
  - TypeScript support
32
38
  - Accessibility features (ARIA labels, keyboard navigation)
39
+ - Comprehensive test coverage
33
40
 
34
41
  ## Install
35
42
 
@@ -56,18 +63,42 @@ import 'map-gl-style-switcher/dist/map-gl-style-switcher.css';
56
63
  // Define styles
57
64
  const styles = [
58
65
  {
59
- id: 'basic',
60
- name: 'Basic',
61
- image: 'path/to/thumbnail.png',
62
- styleUrl: 'https://demotiles.maplibre.org/style.json',
66
+ id: 'voyager',
67
+ name: 'Voyager',
68
+ image: './voyager.png',
69
+ styleUrl: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
70
+ description: 'Voyager style from Carto',
71
+ },
72
+ {
73
+ id: 'positron',
74
+ name: 'Positron',
75
+ image: './positron.png',
76
+ styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
77
+ description: 'Positron style from Carto',
63
78
  },
64
79
  {
65
- id: 'satellite',
66
- name: 'Satellite',
67
- image: 'path/to/satellite-thumb.png',
68
- styleUrl: 'https://your-satellite-style.json',
80
+ id: 'dark-matter',
81
+ name: 'Dark Matter',
82
+ image: './dark.png',
83
+ styleUrl: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
84
+ description: 'Dark style from Carto',
85
+ },
86
+ {
87
+ id: 'arcgis-hybrid',
88
+ name: 'ArcGIS Hybrid',
89
+ image: './arcgis-hybrid.png',
90
+ styleUrl:
91
+ 'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/arcgis_hybrid.json',
92
+ description: 'Hybrid Satellite style from ESRI',
93
+ },
94
+ {
95
+ id: 'osm',
96
+ name: 'OSM',
97
+ image: './osm.png',
98
+ styleUrl:
99
+ 'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/openStreetMap.json',
100
+ description: 'OSM style',
69
101
  },
70
- // ... more styles
71
102
  ];
72
103
  const defaultStyle = styles[0];
73
104
  // Create map
@@ -95,10 +126,270 @@ const styleSwitcher = new StyleSwitcherControl({
95
126
 
96
127
  map.addControl(styleSwitcher, 'bottom-left');
97
128
  ```
129
+
130
+ ### React Integration with react-map-gl
131
+
132
+ For React applications using `react-map-gl`, This package provides a ready-to-use `MapGLStyleSwitcher` component:
133
+
134
+ ```tsx
135
+ import React, { useState } from 'react';
136
+ import { Map } from 'react-map-gl/maplibre';
137
+ import { MapGLStyleSwitcher } from 'map-gl-style-switcher';
138
+ import 'map-gl-style-switcher/dist/map-gl-style-switcher.css';
139
+
140
+ const styles = [
141
+ {
142
+ id: 'voyager',
143
+ name: 'Voyager',
144
+ image: './voyager.png',
145
+ styleUrl: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
146
+ description: 'Voyager style from Carto',
147
+ },
148
+ {
149
+ id: 'positron',
150
+ name: 'Positron',
151
+ image: './positron.png',
152
+ styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
153
+ description: 'Positron style from Carto',
154
+ },
155
+ {
156
+ id: 'dark-matter',
157
+ name: 'Dark Matter',
158
+ image: './dark.png',
159
+ styleUrl: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
160
+ description: 'Dark style from Carto',
161
+ },
162
+ {
163
+ id: 'arcgis-hybrid',
164
+ name: 'ArcGIS Hybrid',
165
+ image: './arcgis-hybrid.png',
166
+ styleUrl:
167
+ 'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/arcgis_hybrid.json',
168
+ description: 'Hybrid Satellite style from ESRI',
169
+ },
170
+ {
171
+ id: 'osm',
172
+ name: 'OSM',
173
+ image: './osm.png',
174
+ styleUrl:
175
+ 'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/openStreetMap.json',
176
+ description: 'OSM style',
177
+ },
178
+ ];
179
+
180
+ export const MapComponent = () => {
181
+ const [mapStyle, setMapStyle] = useState(styles[0].styleUrl);
182
+
183
+ return (
184
+ <Map
185
+ mapLib={import('maplibre-gl')}
186
+ initialViewState={{
187
+ longitude: 0,
188
+ latitude: 0,
189
+ zoom: 2,
190
+ }}
191
+ style={{ width: '100%', height: '100vh' }}
192
+ mapStyle={mapStyle}
193
+ >
194
+ <MapGLStyleSwitcher
195
+ styles={styles}
196
+ activeStyleId={styles[0].id}
197
+ theme="auto"
198
+ showLabels={true}
199
+ showImages={true}
200
+ onStyleChange={setMapStyle}
201
+ position="bottom-left"
202
+ />
203
+ </Map>
204
+ );
205
+ };
206
+ ```
207
+
208
+ **Installation for React:**
209
+
210
+ ```sh
211
+ npm install react-map-gl maplibre-gl map-gl-style-switcher
212
+ ```
213
+
214
+ #### MapGLStyleSwitcher Props
215
+
216
+ ```tsx
217
+ interface MapGLStyleSwitcherProps {
218
+ styles: StyleItem[]; // Array of map styles (required)
219
+ activeStyleId?: string; // Currently active style ID
220
+ theme?: 'light' | 'dark' | 'auto'; // UI theme (default: 'light')
221
+ showLabels?: boolean; // Show style names (default: true)
222
+ showImages?: boolean; // Show style thumbnails (default: true)
223
+ position?:
224
+ | 'top-left'
225
+ | 'top-right' // Control position (default: 'bottom-left')
226
+ | 'bottom-left'
227
+ | 'bottom-right';
228
+ animationDuration?: number; // Animation duration in ms (default: 200)
229
+ maxHeight?: number; // Max height of expanded list (default: 300)
230
+ rtl?: boolean; // Enable RTL layout (default: false)
231
+ classNames?: Partial<{
232
+ // Custom CSS classes
233
+ container: string;
234
+ list: string;
235
+ item: string;
236
+ itemSelected: string;
237
+ itemHideLabel: string;
238
+ dark: string;
239
+ light: string;
240
+ }>;
241
+ onBeforeStyleChange?: (from: StyleItem, to: StyleItem) => void; // Callback before style change
242
+ onAfterStyleChange?: (from: StyleItem, to: StyleItem) => void; // Callback after style change
243
+ onStyleChange?: (styleUrl: string) => void; // Simplified callback for style URL
244
+ }
245
+ ```
246
+
247
+ #### Advanced React Usage
248
+
249
+ For more complex scenarios, you can use both callbacks:
250
+
251
+ ```tsx
252
+ const MapComponent = () => {
253
+ const [mapStyle, setMapStyle] = useState(styles[0].styleUrl);
254
+ const [loading, setLoading] = useState(false);
255
+
256
+ const handleBeforeStyleChange = (from: StyleItem, to: StyleItem) => {
257
+ console.log(`Switching from ${from.name} to ${to.name}`);
258
+ setLoading(true);
259
+ };
260
+
261
+ const handleAfterStyleChange = (from: StyleItem, to: StyleItem) => {
262
+ console.log(`Style changed to ${to.name}`);
263
+ setLoading(false);
264
+ };
265
+
266
+ const handleStyleChange = (styleUrl: string) => {
267
+ setMapStyle(styleUrl);
268
+ };
269
+
270
+ return (
271
+ <Map mapStyle={mapStyle} /* ...other props */>
272
+ <MapGLStyleSwitcher
273
+ styles={styles}
274
+ activeStyleId="voyager"
275
+ theme="auto"
276
+ onBeforeStyleChange={handleBeforeStyleChange}
277
+ onAfterStyleChange={handleAfterStyleChange}
278
+ onStyleChange={handleStyleChange}
279
+ position="bottom-left"
280
+ />
281
+ {loading && <div className="loading-indicator">Switching style...</div>}
282
+ </Map>
283
+ );
284
+ };
285
+ ```
286
+
287
+ ## Examples
288
+
289
+ ### React + Vite + TypeScript Example
290
+
291
+ A complete working example is available in the `examples/react-map-gl` directory:
292
+
293
+ ```bash
294
+ cd examples/react-map-gl
295
+ npm install
296
+ npm run dev
297
+ ```
298
+
299
+ This example demonstrates:
300
+
301
+ - React 19 with TypeScript and Vite 7
302
+ - MapLibre GL integration with react-map-gl
303
+ - MapGLStyleSwitcher component usage
304
+ - Multiple basemap styles with thumbnails
305
+ - Responsive design with auto theme detection
306
+ - Style switching with smooth transitions
307
+
308
+ [View the complete example →](examples/react-map-gl/)
309
+
310
+ ### Vanilla JavaScript Example
311
+
312
+ The main demo at the root level demonstrates vanilla JavaScript usage:
313
+
314
+ ```bash
315
+ npm install
316
+ npm run dev
317
+ ```
318
+
319
+ This example shows:
320
+
321
+ - Pure TypeScript/JavaScript implementation
322
+ - MapLibre GL integration
323
+ - StyleSwitcherControl direct usage
324
+ - Multiple themes and configuration options
325
+
98
326
  ## Available Styles
99
327
 
100
328
  ![Available Styles](./images/styles.png)
101
329
 
330
+ The style switcher supports various map styles. Here are some popular options you can use:
331
+
332
+ ### Carto Basemaps
333
+
334
+ - **Voyager** - A balanced, colorful style perfect for data visualization and general mapping applications
335
+ - **Positron** - A clean, minimal light basemap ideal for overlaying data with maximum contrast
336
+ - **Dark Matter** - A dark theme basemap excellent for creating striking data visualizations and night mode interfaces
337
+
338
+ ### Satellite & Hybrid
339
+
340
+ - **ArcGIS Hybrid** - Combines satellite imagery with street labels, perfect for geographic context and navigation
341
+ - **Satellite** - High-resolution satellite imagery for detailed geographic analysis
342
+
343
+ ### OpenStreetMap
344
+
345
+ - **OSM (OpenStreetMap)** - Community-driven open-source mapping with detailed street-level information
346
+
347
+ ### Example Styles Configuration
348
+
349
+ ```ts
350
+ const styles = [
351
+ {
352
+ id: 'voyager',
353
+ name: 'Voyager',
354
+ image: './voyager.png',
355
+ styleUrl: 'https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json',
356
+ description: 'Voyager style from Carto',
357
+ },
358
+ {
359
+ id: 'positron',
360
+ name: 'Positron',
361
+ image: './positron.png',
362
+ styleUrl: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
363
+ description: 'Positron style from Carto',
364
+ },
365
+ {
366
+ id: 'dark-matter',
367
+ name: 'Dark Matter',
368
+ image: './dark.png',
369
+ styleUrl: 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json',
370
+ description: 'Dark style from Carto',
371
+ },
372
+ {
373
+ id: 'arcgis-hybrid',
374
+ name: 'ArcGIS Hybrid',
375
+ image: './arcgis-hybrid.png',
376
+ styleUrl:
377
+ 'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/arcgis_hybrid.json',
378
+ description: 'Hybrid Satellite style from ESRI',
379
+ },
380
+ {
381
+ id: 'osm',
382
+ name: 'OSM',
383
+ image: './osm.png',
384
+ styleUrl:
385
+ 'https://raw.githubusercontent.com/go2garret/maps/main/src/assets/json/openStreetMap.json',
386
+ description: 'OSM style',
387
+ },
388
+ ];
389
+ ```
390
+
391
+ > **Note**: The `description` property is optional and will be shown as a tooltip when users hover over a style option.
392
+
102
393
  ## Configuration Options
103
394
 
104
395
  ```ts
@@ -135,7 +426,7 @@ interface StyleSwitcherClassNames {
135
426
  }
136
427
  ```
137
428
 
138
- *Example of different map styles that can be used with the style switcher*
429
+ _Example of different map styles that can be used with the style switcher_
139
430
 
140
431
  ### Option Details
141
432
 
@@ -178,20 +469,29 @@ See the default class names in the `StyleSwitcherControl` source for all availab
178
469
  map-gl-style-switcher/
179
470
  ├── src/
180
471
  │ ├── components/
181
- │ │ └── StyleSwitcherControl.ts # Main IControl implementation
472
+ │ │ ├── StyleSwitcherControl.ts # Main IControl implementation
473
+ │ │ └── MapGLStyleSwitcher.tsx # React component for react-map-gl
182
474
  │ ├── styles/
183
475
  │ │ └── style-switcher.css # Control styles (themes, RTL support)
184
476
  │ ├── types/
185
477
  │ │ ├── index.ts # TypeScript type definitions
186
478
  │ │ └── css.d.ts # CSS module declarations
187
479
  │ ├── tests/
188
- │ │ ├── StyleSwitcherControl.test.ts # Test suite
480
+ │ │ ├── StyleSwitcherControl.test.ts # Core control test suite
481
+ │ │ ├── MapGLStyleSwitcher.test.tsx # React component test suite
189
482
  │ │ └── test-setup.ts # Jest test setup
190
483
  │ ├── demo/
191
484
  │ │ ├── main.tsx # Demo entry point
192
485
  │ │ ├── App.tsx # Demo component
193
486
  │ │ └── index.css # Demo-specific styles
194
487
  │ └── index.ts # Package entry point
488
+ ├── examples/
489
+ │ └── react-map-gl/ # Complete React example
490
+ │ ├── src/
491
+ │ │ ├── App.tsx # Example React application
492
+ │ │ └── main.tsx # React entry point
493
+ │ ├── package.json # Example dependencies
494
+ │ └── vite.config.ts # Vite configuration
195
495
  ├── dist/ # Built package output
196
496
  │ ├── index.js # ES Module
197
497
  │ ├── index.umd.js # UMD Module
@@ -283,7 +583,6 @@ We welcome contributions! Please feel free to submit a Pull Request. For major c
283
583
  ```
284
584
 
285
585
  4. **Make your changes**
286
-
287
586
  - Follow TypeScript best practices
288
587
  - Maintain backward compatibility when possible
289
588
  - Add tests for new features
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import React from 'react';
2
+
1
3
  type MapInstance = any;
2
4
  interface IControl {
3
5
  onAdd(map: MapInstance): HTMLElement;
@@ -48,5 +50,68 @@ declare class StyleSwitcherControl implements IControl {
48
50
  private _createStyleItem;
49
51
  }
50
52
 
51
- export { StyleSwitcherControl };
53
+ interface MapGLStyleSwitcherProps {
54
+ /** Array of map styles to choose from */
55
+ styles: StyleItem[];
56
+ /** Currently active style ID */
57
+ activeStyleId?: string;
58
+ /** Theme of the control */
59
+ theme?: 'light' | 'dark' | 'auto';
60
+ /** Whether to show style labels */
61
+ showLabels?: boolean;
62
+ /** Whether to show style images */
63
+ showImages?: boolean;
64
+ /** Position of the control on the map */
65
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
66
+ /** Animation duration in milliseconds */
67
+ animationDuration?: number;
68
+ /** Maximum height of the control */
69
+ maxHeight?: number;
70
+ /** Right-to-left layout */
71
+ rtl?: boolean;
72
+ /** Custom CSS class names */
73
+ classNames?: Partial<{
74
+ container: string;
75
+ list: string;
76
+ item: string;
77
+ itemSelected: string;
78
+ itemHideLabel: string;
79
+ dark: string;
80
+ light: string;
81
+ }>;
82
+ /** Callback when style is about to change */
83
+ onBeforeStyleChange?: (from: StyleItem, to: StyleItem) => void;
84
+ /** Callback when style has changed */
85
+ onAfterStyleChange?: (from: StyleItem, to: StyleItem) => void;
86
+ /** Simplified callback that just returns the new style URL */
87
+ onStyleChange?: (styleUrl: string) => void;
88
+ }
89
+ /**
90
+ * React component for adding a style switcher control to react-map-gl Map
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * import { MapGLStyleSwitcher } from 'map-gl-style-switcher';
95
+ * import { Map } from 'react-map-gl/maplibre';
96
+ * import 'map-gl-style-switcher/dist/map-gl-style-switcher.css';
97
+ *
98
+ * function MyMap() {
99
+ * const [mapStyle, setMapStyle] = useState('style-url');
100
+ *
101
+ * return (
102
+ * <Map mapStyle={mapStyle} ...otherProps>
103
+ * <MapGLStyleSwitcher
104
+ * styles={styles}
105
+ * activeStyleId="voyager"
106
+ * onStyleChange={setMapStyle}
107
+ * position="bottom-left"
108
+ * />
109
+ * </Map>
110
+ * );
111
+ * }
112
+ * ```
113
+ */
114
+ declare const MapGLStyleSwitcher: React.FC<MapGLStyleSwitcherProps>;
115
+
116
+ export { MapGLStyleSwitcher, StyleSwitcherControl };
52
117
  export type { StyleItem, StyleSwitcherClassNames, StyleSwitcherControlOptions };