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