maplibre-gl-basemap-control 0.2.2 → 0.3.0

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.
@@ -3,20 +3,23 @@ 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
  const DEFAULT_BASEMAP_PROVIDERS = [
6
- { id: "openstreetmap", name: "OpenStreetMap", category: "Community" },
7
- { id: "google", name: "Google", category: "General" },
6
+ { id: "amazon", name: "Amazon Location", category: "General" },
8
7
  { id: "carto", name: "Carto", category: "General" },
9
8
  { id: "cyclosm", name: "CyclOSM", category: "Cycling" },
10
9
  { id: "esri", name: "ESRI", category: "Imagery" },
10
+ { id: "google", name: "Google", category: "General" },
11
+ { id: "mapbox", name: "Mapbox", category: "General" },
12
+ { id: "maptiler", name: "MapTiler", category: "General" },
11
13
  { id: "nasa-gibs", name: "NASA GIBS", category: "Imagery" },
14
+ { id: "nlmaps", name: "nlmaps", category: "Regional" },
15
+ { id: "openfreemap", name: "OpenFreeMap", category: "Vector Styles" },
12
16
  { id: "openrailwaymap", name: "OpenRailwayMap", category: "Transport" },
17
+ { id: "openstreetmap", name: "OpenStreetMap", category: "Community" },
13
18
  { id: "opentopomap", name: "OpenTopoMap", category: "Terrain" },
14
- { id: "openfreemap", name: "OpenFreeMap", category: "Vector Styles" },
15
19
  { id: "swisstopo", name: "Swiss Federal Geoportal", category: "Regional" },
16
20
  { id: "topplusopen", name: "TopPlusOpen", category: "Regional" },
17
21
  { id: "usgs", name: "USGS", category: "United States" },
18
- { id: "waymarkedtrails", name: "Waymarked Trails", category: "Outdoor" },
19
- { id: "nlmaps", name: "nlmaps", category: "Regional" }
22
+ { id: "waymarkedtrails", name: "Waymarked Trails", category: "Outdoor" }
20
23
  ];
21
24
  function rasterBasemap({
22
25
  id,
@@ -46,8 +49,99 @@ function rasterBasemap({
46
49
  tags
47
50
  };
48
51
  }
52
+ function styleBasemap({
53
+ id,
54
+ name,
55
+ provider,
56
+ category,
57
+ description,
58
+ attribution,
59
+ url,
60
+ view,
61
+ tags = []
62
+ }) {
63
+ return {
64
+ id,
65
+ name,
66
+ provider,
67
+ type: "style",
68
+ category,
69
+ description,
70
+ attribution,
71
+ source: {
72
+ type: "style",
73
+ url
74
+ },
75
+ view,
76
+ tags
77
+ };
78
+ }
79
+ function mapTilerStyleBasemap({
80
+ id,
81
+ name,
82
+ mapId,
83
+ category,
84
+ description,
85
+ url,
86
+ tags = []
87
+ }) {
88
+ return styleBasemap({
89
+ id,
90
+ name,
91
+ provider: "maptiler",
92
+ category,
93
+ description,
94
+ attribution: MAPTILER_ATTRIBUTION,
95
+ url: url ?? `https://api.maptiler.com/maps/${mapId}/style.json?key={api-key}`,
96
+ tags: ["maptiler", "vector", "style", ...tags]
97
+ });
98
+ }
99
+ function amazonStyleBasemap({
100
+ id,
101
+ name,
102
+ mapStyle,
103
+ category,
104
+ description,
105
+ tags = []
106
+ }) {
107
+ return styleBasemap({
108
+ id,
109
+ name,
110
+ provider: "amazon",
111
+ category,
112
+ description,
113
+ attribution: AMAZON_ATTRIBUTION,
114
+ url: `https://maps.geo.{aws-region}.amazonaws.com/v2/styles/${mapStyle}/descriptor?key={api-key}`,
115
+ tags: ["amazon", "aws", "location", "style", ...tags]
116
+ });
117
+ }
118
+ function mapboxStyleBasemap({
119
+ id,
120
+ name,
121
+ styleId,
122
+ category,
123
+ description,
124
+ tags = []
125
+ }) {
126
+ return styleBasemap({
127
+ id,
128
+ name,
129
+ provider: "mapbox",
130
+ category,
131
+ description,
132
+ attribution: MAPBOX_ATTRIBUTION,
133
+ url: `https://api.mapbox.com/styles/v1/mapbox/${styleId}?access_token={api-key}`,
134
+ tags: ["mapbox", "vector", "style", ...tags]
135
+ });
136
+ }
137
+ function sortProviders(providers) {
138
+ return [...providers].sort((a, b) => a.name.localeCompare(b.name));
139
+ }
140
+ const AMAZON_ATTRIBUTION = "© Amazon Location Service";
49
141
  const CARTO_ATTRIBUTION = "© OpenStreetMap contributors © CARTO";
50
142
  const ESRI_ATTRIBUTION = "Tiles © Esri and the GIS User Community";
143
+ const MAPBOX_ATTRIBUTION = "© Mapbox © OpenStreetMap contributors";
144
+ const MAPTILER_ATTRIBUTION = "© MapTiler © OpenStreetMap contributors";
51
145
  const OSM_ATTRIBUTION = "© OpenStreetMap contributors";
52
146
  const OPENFREEMAP_ATTRIBUTION = "OpenFreeMap © OpenMapTiles Data from OpenStreetMap";
53
147
  const DEFAULT_BASEMAPS = [
@@ -139,6 +233,216 @@ const DEFAULT_BASEMAPS = [
139
233
  maxzoom: 20,
140
234
  tags: ["google", "hybrid", "imagery"]
141
235
  }),
236
+ amazonStyleBasemap({
237
+ id: "amazon-standard",
238
+ name: "Amazon Standard",
239
+ mapStyle: "Standard",
240
+ category: "Street",
241
+ description: "Amazon Location general-purpose vector map style.",
242
+ tags: ["standard", "street"]
243
+ }),
244
+ amazonStyleBasemap({
245
+ id: "amazon-monochrome",
246
+ name: "Amazon Monochrome",
247
+ mapStyle: "Monochrome",
248
+ category: "Light",
249
+ description: "Amazon Location grey scale map style for overlays.",
250
+ tags: ["monochrome", "light", "dataviz"]
251
+ }),
252
+ amazonStyleBasemap({
253
+ id: "amazon-hybrid",
254
+ name: "Amazon Hybrid",
255
+ mapStyle: "Hybrid",
256
+ category: "Imagery",
257
+ description: "Amazon Location satellite imagery with road and label overlay.",
258
+ tags: ["hybrid", "satellite", "imagery", "labels"]
259
+ }),
260
+ amazonStyleBasemap({
261
+ id: "amazon-satellite",
262
+ name: "Amazon Satellite",
263
+ mapStyle: "Satellite",
264
+ category: "Imagery",
265
+ description: "Amazon Location satellite imagery map style.",
266
+ tags: ["satellite", "imagery"]
267
+ }),
268
+ mapboxStyleBasemap({
269
+ id: "mapbox-streets",
270
+ name: "Mapbox Streets",
271
+ styleId: "streets-v12",
272
+ category: "Street",
273
+ description: "Mapbox general-purpose street map style.",
274
+ tags: ["streets", "street"]
275
+ }),
276
+ mapboxStyleBasemap({
277
+ id: "mapbox-outdoors",
278
+ name: "Mapbox Outdoors",
279
+ styleId: "outdoors-v12",
280
+ category: "Outdoor",
281
+ description: "Mapbox outdoor recreation and terrain style.",
282
+ tags: ["outdoors", "terrain", "recreation"]
283
+ }),
284
+ mapboxStyleBasemap({
285
+ id: "mapbox-light",
286
+ name: "Mapbox Light",
287
+ styleId: "light-v11",
288
+ category: "Light",
289
+ description: "Light Mapbox style for data overlays.",
290
+ tags: ["light", "dataviz"]
291
+ }),
292
+ mapboxStyleBasemap({
293
+ id: "mapbox-dark",
294
+ name: "Mapbox Dark",
295
+ styleId: "dark-v11",
296
+ category: "Dark",
297
+ description: "Dark Mapbox style for high-contrast overlays.",
298
+ tags: ["dark"]
299
+ }),
300
+ mapboxStyleBasemap({
301
+ id: "mapbox-satellite",
302
+ name: "Mapbox Satellite",
303
+ styleId: "satellite-v9",
304
+ category: "Imagery",
305
+ description: "Mapbox satellite imagery style.",
306
+ tags: ["satellite", "imagery"]
307
+ }),
308
+ mapboxStyleBasemap({
309
+ id: "mapbox-satellite-streets",
310
+ name: "Mapbox Satellite Streets",
311
+ styleId: "satellite-streets-v12",
312
+ category: "Imagery",
313
+ description: "Mapbox satellite imagery with streets and labels.",
314
+ tags: ["satellite", "streets", "imagery", "labels"]
315
+ }),
316
+ mapboxStyleBasemap({
317
+ id: "mapbox-navigation-day",
318
+ name: "Mapbox Navigation Day",
319
+ styleId: "navigation-day-v1",
320
+ category: "Navigation",
321
+ description: "Mapbox daytime navigation style.",
322
+ tags: ["navigation", "day"]
323
+ }),
324
+ mapboxStyleBasemap({
325
+ id: "mapbox-navigation-night",
326
+ name: "Mapbox Navigation Night",
327
+ styleId: "navigation-night-v1",
328
+ category: "Navigation",
329
+ description: "Mapbox nighttime navigation style.",
330
+ tags: ["navigation", "night", "dark"]
331
+ }),
332
+ mapTilerStyleBasemap({
333
+ id: "maptiler-aquarelle",
334
+ name: "MapTiler Aquarelle",
335
+ mapId: "aquarelle-v4",
336
+ category: "Artistic",
337
+ description: "Watercolor-style vector basemap.",
338
+ tags: ["aquarelle", "watercolor", "artistic"]
339
+ }),
340
+ mapTilerStyleBasemap({
341
+ id: "maptiler-backdrop",
342
+ name: "MapTiler Backdrop",
343
+ mapId: "backdrop-v4",
344
+ category: "Light",
345
+ description: "Subtle monochrome context map.",
346
+ tags: ["backdrop", "monochrome", "context"]
347
+ }),
348
+ mapTilerStyleBasemap({
349
+ id: "maptiler-base",
350
+ name: "MapTiler Base",
351
+ mapId: "base-v4",
352
+ category: "Street",
353
+ description: "General-purpose vector basemap.",
354
+ tags: ["base", "street"]
355
+ }),
356
+ mapTilerStyleBasemap({
357
+ id: "maptiler-dataviz",
358
+ name: "MapTiler Dataviz",
359
+ mapId: "dataviz-v4",
360
+ category: "Light",
361
+ description: "Minimal basemap for data visualization and overlays.",
362
+ tags: ["dataviz", "data", "light"]
363
+ }),
364
+ mapTilerStyleBasemap({
365
+ id: "maptiler-landscape",
366
+ name: "MapTiler Landscape",
367
+ mapId: "landscape-v4",
368
+ category: "Terrain",
369
+ description: "Light terrain and hillshade map.",
370
+ tags: ["landscape", "terrain", "hillshade"]
371
+ }),
372
+ mapTilerStyleBasemap({
373
+ id: "maptiler-ocean",
374
+ name: "MapTiler Ocean",
375
+ mapId: "ocean-v4",
376
+ category: "Marine",
377
+ description: "Ocean bathymetry and marine-focused basemap.",
378
+ tags: ["ocean", "marine", "bathymetry"]
379
+ }),
380
+ mapTilerStyleBasemap({
381
+ id: "maptiler-openstreetmap",
382
+ name: "MapTiler OpenStreetMap",
383
+ mapId: "openstreetmap",
384
+ category: "Street",
385
+ description: "OpenStreetMap style hosted by MapTiler.",
386
+ url: "https://api.maptiler.com/maps/openstreetmap/style.json?key",
387
+ tags: ["openstreetmap", "osm", "street"]
388
+ }),
389
+ mapTilerStyleBasemap({
390
+ id: "maptiler-outdoor",
391
+ name: "MapTiler Outdoor",
392
+ mapId: "outdoor-v4",
393
+ category: "Outdoor",
394
+ description: "Outdoor recreation basemap with terrain detail.",
395
+ tags: ["outdoor", "terrain", "recreation"]
396
+ }),
397
+ mapTilerStyleBasemap({
398
+ id: "maptiler-satellite-hybrid",
399
+ name: "MapTiler Satellite Hybrid",
400
+ mapId: "hybrid-v4",
401
+ category: "Imagery",
402
+ description: "Satellite imagery with labels, roads, and borders.",
403
+ tags: ["satellite", "hybrid", "imagery", "labels"]
404
+ }),
405
+ mapTilerStyleBasemap({
406
+ id: "maptiler-satellite-plain",
407
+ name: "MapTiler Satellite Plain",
408
+ mapId: "satellite-v4",
409
+ category: "Imagery",
410
+ description: "Satellite imagery without the hybrid reference overlay.",
411
+ tags: ["satellite", "imagery"]
412
+ }),
413
+ mapTilerStyleBasemap({
414
+ id: "maptiler-streets",
415
+ name: "MapTiler Streets",
416
+ mapId: "streets-v4",
417
+ category: "Street",
418
+ description: "Detailed street map for general navigation.",
419
+ tags: ["streets", "street", "navigation"]
420
+ }),
421
+ mapTilerStyleBasemap({
422
+ id: "maptiler-toner",
423
+ name: "MapTiler Toner",
424
+ mapId: "toner-v2",
425
+ category: "Dark",
426
+ description: "High-contrast monochrome basemap.",
427
+ url: "https://api.maptiler.com/maps/toner-v2/style.json?key=",
428
+ tags: ["toner", "monochrome", "contrast"]
429
+ }),
430
+ mapTilerStyleBasemap({
431
+ id: "maptiler-topo",
432
+ name: "MapTiler Topo",
433
+ mapId: "topo-v4",
434
+ category: "Terrain",
435
+ description: "Topographic basemap with contours and terrain context.",
436
+ tags: ["topo", "topographic", "terrain"]
437
+ }),
438
+ mapTilerStyleBasemap({
439
+ id: "maptiler-winter",
440
+ name: "MapTiler Winter",
441
+ mapId: "winter-v4",
442
+ category: "Outdoor",
443
+ description: "Winter sports and mountain activity basemap.",
444
+ tags: ["winter", "skiing", "outdoor"]
445
+ }),
142
446
  rasterBasemap({
143
447
  id: "carto-positron",
144
448
  name: "Carto Positron",
@@ -751,7 +1055,7 @@ function combineProviders(defaults, custom = []) {
751
1055
  const byId = /* @__PURE__ */ new Map();
752
1056
  defaults.forEach((provider) => byId.set(provider.id, provider));
753
1057
  custom.forEach((provider) => byId.set(provider.id, provider));
754
- return [...byId.values()];
1058
+ return sortProviders([...byId.values()]);
755
1059
  }
756
1060
  function resolveBasemapProviders(basemaps, customProviders = [], includeDefaultProviders = true) {
757
1061
  const byId = /* @__PURE__ */ new Map();
@@ -764,7 +1068,7 @@ function resolveBasemapProviders(basemaps, customProviders = [], includeDefaultP
764
1068
  byId.set(basemap.provider, { id: basemap.provider, name: basemap.provider });
765
1069
  }
766
1070
  });
767
- return [...byId.values()];
1071
+ return sortProviders([...byId.values()]);
768
1072
  }
769
1073
  function createBasemapCatalog(customBasemaps = [], includeDefaultBasemaps = true) {
770
1074
  const basemaps = includeDefaultBasemaps ? [...DEFAULT_BASEMAPS, ...customBasemaps] : [...customBasemaps];
@@ -802,6 +1106,11 @@ const DEFAULT_OPTIONS = {
802
1106
  };
803
1107
  const CONTROL_SOURCE_PREFIX = "maplibre-basemap-control-source";
804
1108
  const CONTROL_LAYER_PREFIX = "";
1109
+ const API_KEY_PLACEHOLDER = "{api-key}";
1110
+ const MAPTILER_API_KEY_EXAMPLE = "YOUR_MAPTILER_API_KEY";
1111
+ const MAPTILER_API_KEY_QUERY = "?key";
1112
+ const MAPTILER_API_KEY_EMPTY_QUERY = "?key=";
1113
+ const AWS_REGION_PLACEHOLDER = "{aws-region}";
805
1114
  class BasemapControl {
806
1115
  constructor(options) {
807
1116
  __publicField(this, "_map");
@@ -816,10 +1125,18 @@ class BasemapControl {
816
1125
  __publicField(this, "_eventHandlers", new globalThis.Map());
817
1126
  __publicField(this, "_managedSourceIds", []);
818
1127
  __publicField(this, "_managedLayerIds", []);
1128
+ __publicField(this, "_mapTilerApiKey", "");
1129
+ __publicField(this, "_amazonApiKey", "");
1130
+ __publicField(this, "_awsRegion", "");
1131
+ __publicField(this, "_mapboxAccessToken", "");
1132
+ __publicField(this, "_providerSettingsCollapsed", true);
819
1133
  __publicField(this, "_resizeHandler", null);
820
1134
  __publicField(this, "_mapResizeHandler", null);
821
- __publicField(this, "_clickOutsideHandler", null);
822
1135
  this._options = { ...DEFAULT_OPTIONS, ...options };
1136
+ this._mapTilerApiKey = (options == null ? void 0 : options.mapTilerApiKey) ?? "";
1137
+ this._amazonApiKey = (options == null ? void 0 : options.amazonApiKey) ?? "";
1138
+ this._awsRegion = (options == null ? void 0 : options.awsRegion) ?? "us-east-1";
1139
+ this._mapboxAccessToken = (options == null ? void 0 : options.mapboxAccessToken) ?? "";
823
1140
  this._basemaps = createBasemapCatalog(
824
1141
  options == null ? void 0 : options.basemaps,
825
1142
  this._options.includeDefaultBasemaps
@@ -867,10 +1184,6 @@ class BasemapControl {
867
1184
  this._map.off("resize", this._mapResizeHandler);
868
1185
  this._mapResizeHandler = null;
869
1186
  }
870
- if (this._clickOutsideHandler) {
871
- document.removeEventListener("click", this._clickOutsideHandler);
872
- this._clickOutsideHandler = null;
873
- }
874
1187
  (_b = (_a = this._panel) == null ? void 0 : _a.parentNode) == null ? void 0 : _b.removeChild(this._panel);
875
1188
  (_d = (_c = this._container) == null ? void 0 : _c.parentNode) == null ? void 0 : _d.removeChild(this._container);
876
1189
  this._map = void 0;
@@ -904,6 +1217,25 @@ class BasemapControl {
904
1217
  this._renderContent();
905
1218
  this._emit({ type: "statechange", state: this.getState() });
906
1219
  }
1220
+ setMapTilerApiKey(apiKey) {
1221
+ this._mapTilerApiKey = apiKey;
1222
+ this._state = { ...this._state, error: void 0 };
1223
+ this._renderContent();
1224
+ this._emit({ type: "statechange", state: this.getState() });
1225
+ }
1226
+ setAmazonCredentials(apiKey, awsRegion = this._awsRegion) {
1227
+ this._amazonApiKey = apiKey;
1228
+ this._awsRegion = awsRegion;
1229
+ this._state = { ...this._state, error: void 0 };
1230
+ this._renderContent();
1231
+ this._emit({ type: "statechange", state: this.getState() });
1232
+ }
1233
+ setMapboxAccessToken(accessToken) {
1234
+ this._mapboxAccessToken = accessToken;
1235
+ this._state = { ...this._state, error: void 0 };
1236
+ this._renderContent();
1237
+ this._emit({ type: "statechange", state: this.getState() });
1238
+ }
907
1239
  getActiveBasemap() {
908
1240
  return this._basemaps.find((basemap) => basemap.id === this._state.activeBasemapId);
909
1241
  }
@@ -920,7 +1252,7 @@ class BasemapControl {
920
1252
  throw error;
921
1253
  }
922
1254
  this._state = { ...this._state, loading: true, error: void 0 };
923
- this._renderContent();
1255
+ this._renderContent(true);
924
1256
  this._emit({ type: "statechange", state: this.getState() });
925
1257
  try {
926
1258
  let managedRaster;
@@ -929,8 +1261,14 @@ class BasemapControl {
929
1261
  this._removeManagedBasemap();
930
1262
  managedRaster = this._addRasterBasemap(basemap);
931
1263
  } else {
1264
+ const styleUrl = this._resolveStyleUrl(basemap);
1265
+ const styleOptions = this._getStyleOptions(basemap);
932
1266
  this._removeManagedBasemap();
933
- this._map.setStyle(basemap.source.url);
1267
+ if (styleOptions) {
1268
+ this._map.setStyle(styleUrl, styleOptions);
1269
+ } else {
1270
+ this._map.setStyle(styleUrl);
1271
+ }
934
1272
  }
935
1273
  this._applyBasemapView(basemap);
936
1274
  this._state = {
@@ -939,7 +1277,7 @@ class BasemapControl {
939
1277
  loading: false,
940
1278
  error: void 0
941
1279
  };
942
- this._renderContent();
1280
+ this._renderContent(true);
943
1281
  this._emit({
944
1282
  type: "basemapchange",
945
1283
  state: this.getState(),
@@ -950,7 +1288,7 @@ class BasemapControl {
950
1288
  } catch (cause) {
951
1289
  const error = cause instanceof Error ? cause : new Error(String(cause));
952
1290
  this._state = { ...this._state, loading: false, error: error.message };
953
- this._renderContent();
1291
+ this._renderContent(true);
954
1292
  this._handleError(error, basemap);
955
1293
  throw error;
956
1294
  }
@@ -1050,16 +1388,23 @@ class BasemapControl {
1050
1388
  panel.appendChild(this._content);
1051
1389
  return panel;
1052
1390
  }
1053
- _renderContent() {
1391
+ _renderContent(preserveResultsScroll = false) {
1392
+ var _a;
1054
1393
  if (!this._content) return;
1055
1394
  const categories = getBasemapCategories(this._basemaps);
1056
1395
  const results = this._getFilteredBasemaps();
1396
+ const previousResultsScrollTop = preserveResultsScroll ? (_a = this._content.querySelector(".basemap-control-results")) == null ? void 0 : _a.scrollTop : void 0;
1057
1397
  this._content.replaceChildren(
1058
1398
  this._createSearchRow(),
1399
+ ...this._hasProviderSettings() ? [this._createProviderSettingsSection()] : [],
1059
1400
  this._createFilterRow(this._providers, categories),
1060
1401
  this._createStatus(results.length),
1061
1402
  this._createResults(results)
1062
1403
  );
1404
+ if (previousResultsScrollTop !== void 0) {
1405
+ const resultsElement = this._content.querySelector(".basemap-control-results");
1406
+ if (resultsElement) resultsElement.scrollTop = previousResultsScrollTop;
1407
+ }
1063
1408
  }
1064
1409
  _renderFilteredResults() {
1065
1410
  var _a, _b;
@@ -1119,6 +1464,106 @@ class BasemapControl {
1119
1464
  wrapper.appendChild(input);
1120
1465
  return wrapper;
1121
1466
  }
1467
+ _createProviderSettingsSection() {
1468
+ const details = document.createElement("details");
1469
+ details.className = "basemap-control-provider-settings";
1470
+ details.open = !this._providerSettingsCollapsed;
1471
+ details.addEventListener("toggle", () => {
1472
+ this._providerSettingsCollapsed = !details.open;
1473
+ });
1474
+ const summary = document.createElement("summary");
1475
+ summary.className = "basemap-control-provider-settings-summary";
1476
+ summary.textContent = "Provider settings";
1477
+ details.appendChild(summary);
1478
+ const fields = document.createElement("div");
1479
+ fields.className = "basemap-control-provider-settings-fields";
1480
+ if (this._hasMapTilerBasemaps()) {
1481
+ fields.appendChild(
1482
+ this._createProviderSettingsInput({
1483
+ className: "basemap-control-maptiler-key",
1484
+ type: "password",
1485
+ placeholder: "MapTiler API key",
1486
+ ariaLabel: "MapTiler API key",
1487
+ value: this._mapTilerApiKey,
1488
+ onInput: (value) => {
1489
+ this._mapTilerApiKey = value;
1490
+ }
1491
+ })
1492
+ );
1493
+ }
1494
+ if (this._hasMapboxBasemaps()) {
1495
+ fields.appendChild(
1496
+ this._createProviderSettingsInput({
1497
+ className: "basemap-control-mapbox-token",
1498
+ type: "password",
1499
+ placeholder: "Mapbox access token",
1500
+ ariaLabel: "Mapbox access token",
1501
+ value: this._mapboxAccessToken,
1502
+ onInput: (value) => {
1503
+ this._mapboxAccessToken = value;
1504
+ }
1505
+ })
1506
+ );
1507
+ }
1508
+ if (this._hasAmazonBasemaps()) {
1509
+ fields.appendChild(
1510
+ this._createProviderSettingsInput({
1511
+ className: "basemap-control-amazon-key",
1512
+ type: "password",
1513
+ placeholder: "Amazon API key",
1514
+ ariaLabel: "Amazon API key",
1515
+ value: this._amazonApiKey,
1516
+ onInput: (value) => {
1517
+ this._amazonApiKey = value;
1518
+ }
1519
+ })
1520
+ );
1521
+ fields.appendChild(
1522
+ this._createProviderSettingsInput({
1523
+ className: "basemap-control-aws-region",
1524
+ type: "text",
1525
+ placeholder: "AWS region",
1526
+ ariaLabel: "AWS region",
1527
+ value: this._awsRegion,
1528
+ onInput: (value) => {
1529
+ this._awsRegion = value;
1530
+ }
1531
+ })
1532
+ );
1533
+ }
1534
+ details.appendChild(fields);
1535
+ return details;
1536
+ }
1537
+ _createProviderSettingsInput({
1538
+ className,
1539
+ type,
1540
+ placeholder,
1541
+ ariaLabel,
1542
+ value,
1543
+ onInput
1544
+ }) {
1545
+ const wrapper = document.createElement("div");
1546
+ wrapper.className = className;
1547
+ const input = document.createElement("input");
1548
+ input.className = "basemap-control-input";
1549
+ input.type = type;
1550
+ input.placeholder = placeholder;
1551
+ input.value = value;
1552
+ input.name = ariaLabel.toLowerCase().replace(/\s+/g, "-");
1553
+ input.autocomplete = "new-password";
1554
+ input.autocapitalize = "none";
1555
+ input.spellcheck = false;
1556
+ input.setAttribute("autocorrect", "off");
1557
+ input.setAttribute("aria-label", ariaLabel);
1558
+ input.addEventListener("input", () => {
1559
+ onInput(input.value);
1560
+ this._state = { ...this._state, error: void 0 };
1561
+ this._renderFilteredResults();
1562
+ this._emit({ type: "statechange", state: this.getState() });
1563
+ });
1564
+ wrapper.appendChild(input);
1565
+ return wrapper;
1566
+ }
1122
1567
  _createFilterRow(providers, categories) {
1123
1568
  const row = document.createElement("div");
1124
1569
  row.className = "basemap-control-filters";
@@ -1236,6 +1681,132 @@ class BasemapControl {
1236
1681
  template.innerHTML = value;
1237
1682
  return template.content.textContent ?? value;
1238
1683
  }
1684
+ _hasMapTilerBasemaps() {
1685
+ return this._basemaps.some((basemap) => basemap.provider === "maptiler");
1686
+ }
1687
+ _hasAmazonBasemaps() {
1688
+ return this._basemaps.some((basemap) => basemap.provider === "amazon");
1689
+ }
1690
+ _hasMapboxBasemaps() {
1691
+ return this._basemaps.some((basemap) => basemap.provider === "mapbox");
1692
+ }
1693
+ _hasProviderSettings() {
1694
+ return this._hasMapTilerBasemaps() || this._hasAmazonBasemaps() || this._hasMapboxBasemaps();
1695
+ }
1696
+ _resolveStyleUrl(basemap) {
1697
+ if (basemap.source.type === "raster") {
1698
+ throw new Error(`Basemap "${basemap.id}" is not a style basemap.`);
1699
+ }
1700
+ const url = basemap.source.url;
1701
+ if (basemap.provider === "amazon") {
1702
+ return this._resolveAmazonStyleUrl(url);
1703
+ }
1704
+ if (basemap.provider === "maptiler") {
1705
+ return this._resolveMapTilerStyleUrl(url);
1706
+ }
1707
+ if (basemap.provider === "mapbox") {
1708
+ return this._resolveMapboxStyleUrl(url);
1709
+ }
1710
+ return url;
1711
+ }
1712
+ _getStyleOptions(basemap) {
1713
+ if (basemap.provider === "mapbox") {
1714
+ return {
1715
+ validate: false,
1716
+ transformStyle: (_previousStyle, nextStyle) => this._transformMapboxStyle(nextStyle, this._mapboxAccessToken.trim())
1717
+ };
1718
+ }
1719
+ return void 0;
1720
+ }
1721
+ _transformMapboxStyle(style, accessToken) {
1722
+ const encodedAccessToken = encodeURIComponent(accessToken);
1723
+ const sources = Object.fromEntries(
1724
+ Object.entries(style.sources).map(([id, source]) => {
1725
+ if ("url" in source && typeof source.url === "string") {
1726
+ return [id, { ...source, url: this._resolveMapboxInternalUrl(source.url, encodedAccessToken) }];
1727
+ }
1728
+ return [id, source];
1729
+ })
1730
+ );
1731
+ return {
1732
+ ...style,
1733
+ glyphs: typeof style.glyphs === "string" ? this._resolveMapboxInternalUrl(style.glyphs, encodedAccessToken) : style.glyphs,
1734
+ sprite: this._resolveMapboxSprite(style.sprite, encodedAccessToken),
1735
+ projection: { type: "mercator" },
1736
+ sources
1737
+ };
1738
+ }
1739
+ _resolveMapboxSprite(sprite, encodedAccessToken) {
1740
+ if (typeof sprite === "string") {
1741
+ return this._resolveMapboxInternalUrl(sprite, encodedAccessToken);
1742
+ }
1743
+ if (Array.isArray(sprite)) {
1744
+ return sprite.map((item) => ({
1745
+ ...item,
1746
+ url: this._resolveMapboxInternalUrl(item.url, encodedAccessToken)
1747
+ }));
1748
+ }
1749
+ return sprite;
1750
+ }
1751
+ _resolveMapboxInternalUrl(url, encodedAccessToken) {
1752
+ if (url.startsWith("mapbox://sprites/")) {
1753
+ const [, owner, styleId] = /^mapbox:\/\/sprites\/([^/]+)\/(.+)$/.exec(url) ?? [];
1754
+ if (owner && styleId) {
1755
+ return `https://api.mapbox.com/styles/v1/${owner}/${styleId}/sprite?access_token=${encodedAccessToken}`;
1756
+ }
1757
+ }
1758
+ if (url.startsWith("mapbox://fonts/")) {
1759
+ const [, owner, fontPath] = /^mapbox:\/\/fonts\/([^/]+)\/(.+)$/.exec(url) ?? [];
1760
+ if (owner && fontPath) {
1761
+ return `https://api.mapbox.com/fonts/v1/${owner}/${fontPath}?access_token=${encodedAccessToken}`;
1762
+ }
1763
+ }
1764
+ if (url.startsWith("mapbox://")) {
1765
+ const tileset = url.replace(/^mapbox:\/\//, "");
1766
+ return `https://api.mapbox.com/v4/${tileset}.json?secure&access_token=${encodedAccessToken}`;
1767
+ }
1768
+ return url;
1769
+ }
1770
+ _resolveMapTilerStyleUrl(url) {
1771
+ const needsMapTilerKey = url.includes(API_KEY_PLACEHOLDER) || url.includes(MAPTILER_API_KEY_EXAMPLE) || url.endsWith(MAPTILER_API_KEY_QUERY) || url.endsWith(MAPTILER_API_KEY_EMPTY_QUERY);
1772
+ if (!needsMapTilerKey) return url;
1773
+ const apiKey = this._mapTilerApiKey.trim();
1774
+ if (!apiKey) {
1775
+ throw new Error("Enter a MapTiler API key before applying this basemap.");
1776
+ }
1777
+ const encodedApiKey = encodeURIComponent(apiKey);
1778
+ if (url.endsWith(MAPTILER_API_KEY_QUERY)) {
1779
+ return `${url}=${encodedApiKey}`;
1780
+ }
1781
+ if (url.endsWith(MAPTILER_API_KEY_EMPTY_QUERY)) {
1782
+ return `${url}${encodedApiKey}`;
1783
+ }
1784
+ return url.split(API_KEY_PLACEHOLDER).join(encodedApiKey).split(MAPTILER_API_KEY_EXAMPLE).join(encodedApiKey);
1785
+ }
1786
+ _resolveAmazonStyleUrl(url) {
1787
+ const apiKey = this._amazonApiKey.trim();
1788
+ const awsRegion = this._awsRegion.trim();
1789
+ if (url.includes(API_KEY_PLACEHOLDER) && !apiKey) {
1790
+ throw new Error("Enter an Amazon API key before applying this basemap.");
1791
+ }
1792
+ if (url.includes(AWS_REGION_PLACEHOLDER) && !awsRegion) {
1793
+ throw new Error("Enter an AWS region before applying this basemap.");
1794
+ }
1795
+ return url.split(AWS_REGION_PLACEHOLDER).join(awsRegion).split(API_KEY_PLACEHOLDER).join(encodeURIComponent(apiKey));
1796
+ }
1797
+ _resolveMapboxStyleUrl(url) {
1798
+ const accessToken = this._mapboxAccessToken.trim();
1799
+ if (url.includes(API_KEY_PLACEHOLDER) && !accessToken) {
1800
+ throw new Error("Enter a Mapbox access token before applying this basemap.");
1801
+ }
1802
+ if (this._isUrlLikeCredential(accessToken)) {
1803
+ throw new Error("Enter a valid Mapbox access token, not a URL.");
1804
+ }
1805
+ return url.split(API_KEY_PLACEHOLDER).join(encodeURIComponent(accessToken));
1806
+ }
1807
+ _isUrlLikeCredential(value) {
1808
+ return /^https?:\/\//i.test(value);
1809
+ }
1239
1810
  _addRasterBasemap(basemap) {
1240
1811
  if (!this._map || basemap.source.type !== "raster") return void 0;
1241
1812
  const sourceId = `${CONTROL_SOURCE_PREFIX}-${basemap.id}`;
@@ -1315,13 +1886,6 @@ class BasemapControl {
1315
1886
  }
1316
1887
  _setupEventListeners() {
1317
1888
  var _a;
1318
- this._clickOutsideHandler = (e) => {
1319
- const target = e.target;
1320
- if (this._container && this._panel && !this._container.contains(target) && !this._panel.contains(target)) {
1321
- this.collapse();
1322
- }
1323
- };
1324
- document.addEventListener("click", this._clickOutsideHandler);
1325
1889
  this._resizeHandler = () => {
1326
1890
  if (!this._state.collapsed) this._updatePanelPosition();
1327
1891
  };
@@ -1385,4 +1949,4 @@ exports.createBasemapCatalog = createBasemapCatalog;
1385
1949
  exports.filterBasemaps = filterBasemaps;
1386
1950
  exports.getBasemapCategories = getBasemapCategories;
1387
1951
  exports.resolveBasemapProviders = resolveBasemapProviders;
1388
- //# sourceMappingURL=BasemapControl-CMZxjxEY.cjs.map
1952
+ //# sourceMappingURL=BasemapControl-DrZ_VXsL.cjs.map