maplibre-gl-basemap-control 0.6.0 → 0.8.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.
- package/README.md +4 -2
- package/dist/{BasemapControl-CnjmO-_2.js → BasemapControl-C9T87uJx.js} +326 -124
- package/dist/BasemapControl-C9T87uJx.js.map +1 -0
- package/dist/{BasemapControl-DBRMf6n3.cjs → BasemapControl-Dl0aWr1N.cjs} +326 -124
- package/dist/BasemapControl-Dl0aWr1N.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/maplibre-gl-basemap-control.css +107 -8
- package/dist/react.cjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/types/lib/core/BasemapControl.d.ts +12 -2
- package/dist/types/lib/core/BasemapControl.d.ts.map +1 -1
- package/dist/types/lib/core/catalog.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/BasemapControl-CnjmO-_2.js.map +0 -1
- package/dist/BasemapControl-DBRMf6n3.cjs.map +0 -1
package/README.md
CHANGED
|
@@ -141,8 +141,10 @@ Mapbox styles include Streets, Outdoors, Light, Dark, Satellite, Satellite Stree
|
|
|
141
141
|
and Navigation Night.
|
|
142
142
|
|
|
143
143
|
MapTiler and Amazon Location styles require API keys. Mapbox styles require an access token. Users
|
|
144
|
-
can enter keys and tokens in the
|
|
145
|
-
can provide them when creating the control.
|
|
144
|
+
can enter keys and tokens in the dedicated API keys view, opened from the key button in the panel
|
|
145
|
+
header, or you can provide them when creating the control. When a basemap is selected before its key
|
|
146
|
+
is set, the control surfaces the error and the matching credential field inline, before any
|
|
147
|
+
destructive style change, so the missing key can be entered and the basemap retried with Enter.
|
|
146
148
|
|
|
147
149
|
```typescript
|
|
148
150
|
const control = new BasemapControl({
|
|
@@ -12,6 +12,7 @@ const DEFAULT_BASEMAP_PROVIDERS = [
|
|
|
12
12
|
{ id: "maptiler", name: "MapTiler", category: "General" },
|
|
13
13
|
{ id: "nasa-gibs", name: "NASA GIBS", category: "Imagery" },
|
|
14
14
|
{ id: "nlmaps", name: "nlmaps", category: "Regional" },
|
|
15
|
+
{ id: "openbasiskaart", name: "Openbasiskaart", category: "Regional" },
|
|
15
16
|
{ id: "openfreemap", name: "OpenFreeMap", category: "Vector Styles" },
|
|
16
17
|
{ id: "openrailwaymap", name: "OpenRailwayMap", category: "Transport" },
|
|
17
18
|
{ id: "openstreetmap", name: "OpenStreetMap", category: "Community" },
|
|
@@ -999,6 +1000,21 @@ const DEFAULT_BASEMAPS = [
|
|
|
999
1000
|
],
|
|
1000
1001
|
tags: ["netherlands", "imagery", "aerial"]
|
|
1001
1002
|
}),
|
|
1003
|
+
rasterBasemap({
|
|
1004
|
+
id: "openbasiskaart",
|
|
1005
|
+
name: "Openbasiskaart",
|
|
1006
|
+
provider: "openbasiskaart",
|
|
1007
|
+
category: "Regional",
|
|
1008
|
+
description: "Netherlands topographic background map based on OpenStreetMap data.",
|
|
1009
|
+
attribution: "Map data © OpenStreetMap contributors | Openbasiskaart",
|
|
1010
|
+
// The Google Maps Compatible (osm-g) layer uses the standard Web Mercator
|
|
1011
|
+
// grid, so its WMTS {TileMatrix}/{TileRow}/{TileCol} maps directly to xyz.
|
|
1012
|
+
tiles: [
|
|
1013
|
+
"https://www.openbasiskaart.nl/mapcache/wmts/1.0.0/osm-g/default/g/{z}/{y}/{x}.png"
|
|
1014
|
+
],
|
|
1015
|
+
maxzoom: 18,
|
|
1016
|
+
tags: ["netherlands", "regional", "topographic", "osm", "openbasiskaart"]
|
|
1017
|
+
}),
|
|
1002
1018
|
{
|
|
1003
1019
|
id: "openfreemap-positron",
|
|
1004
1020
|
name: "OpenFreeMap Positron",
|
|
@@ -1300,11 +1316,18 @@ class BasemapControl {
|
|
|
1300
1316
|
// a traffic overlay reuses a token until it expires instead of creating a new
|
|
1301
1317
|
// session on every add. Keyed by the serialized config + API key.
|
|
1302
1318
|
__publicField(this, "_googleSessions", new globalThis.Map());
|
|
1303
|
-
|
|
1319
|
+
// Which sub-view the panel content shows: the basemap list, or the dedicated
|
|
1320
|
+
// API-keys settings view opened from the header key button (#837).
|
|
1321
|
+
__publicField(this, "_activeView", "basemaps");
|
|
1322
|
+
__publicField(this, "_settingsButton");
|
|
1304
1323
|
// Provider of the most recent missing-credential error, used to pick the
|
|
1305
|
-
// matching help link
|
|
1306
|
-
// in `
|
|
1324
|
+
// matching help link and the inline credential field beside the error. Kept
|
|
1325
|
+
// in sync with `_state.error` (both are cleared together) so it is never
|
|
1326
|
+
// stale when consulted.
|
|
1307
1327
|
__publicField(this, "_missingCredentialProvider");
|
|
1328
|
+
// The basemap whose application last failed for a missing credential, so the
|
|
1329
|
+
// inline credential field's Enter key can re-attempt it (#837).
|
|
1330
|
+
__publicField(this, "_lastFailedBasemapId");
|
|
1308
1331
|
__publicField(this, "_resizeHandler", null);
|
|
1309
1332
|
__publicField(this, "_mapResizeHandler", null);
|
|
1310
1333
|
__publicField(this, "_resizeAnchor", null);
|
|
@@ -1452,39 +1475,39 @@ class BasemapControl {
|
|
|
1452
1475
|
}
|
|
1453
1476
|
setMapTilerApiKey(apiKey) {
|
|
1454
1477
|
this._mapTilerApiKey = apiKey;
|
|
1455
|
-
this.
|
|
1478
|
+
this._clearError();
|
|
1456
1479
|
this._renderContent();
|
|
1457
1480
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1458
1481
|
}
|
|
1459
1482
|
setAmazonCredentials(apiKey, awsRegion = this._awsRegion) {
|
|
1460
1483
|
this._amazonApiKey = apiKey;
|
|
1461
1484
|
this._awsRegion = awsRegion;
|
|
1462
|
-
this.
|
|
1485
|
+
this._clearError();
|
|
1463
1486
|
this._renderContent();
|
|
1464
1487
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1465
1488
|
}
|
|
1466
1489
|
setMapboxAccessToken(accessToken) {
|
|
1467
1490
|
this._mapboxAccessToken = accessToken;
|
|
1468
|
-
this.
|
|
1491
|
+
this._clearError();
|
|
1469
1492
|
this._renderContent();
|
|
1470
1493
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1471
1494
|
}
|
|
1472
1495
|
setTomTomApiKey(apiKey) {
|
|
1473
1496
|
this._tomtomApiKey = apiKey;
|
|
1474
|
-
this.
|
|
1497
|
+
this._clearError();
|
|
1475
1498
|
this._renderContent();
|
|
1476
1499
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1477
1500
|
}
|
|
1478
1501
|
setHereApiKey(apiKey) {
|
|
1479
1502
|
this._hereApiKey = apiKey;
|
|
1480
|
-
this.
|
|
1503
|
+
this._clearError();
|
|
1481
1504
|
this._renderContent();
|
|
1482
1505
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1483
1506
|
}
|
|
1484
1507
|
setGoogleMapsApiKey(apiKey) {
|
|
1485
1508
|
this._googleMapsApiKey = apiKey;
|
|
1486
1509
|
this._googleSessions.clear();
|
|
1487
|
-
this.
|
|
1510
|
+
this._clearError();
|
|
1488
1511
|
this._renderContent();
|
|
1489
1512
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1490
1513
|
}
|
|
@@ -1552,6 +1575,16 @@ class BasemapControl {
|
|
|
1552
1575
|
}
|
|
1553
1576
|
const isOverlay = basemap.source.type === "raster" || basemap.source.type === "vector-overlay";
|
|
1554
1577
|
const effectiveMode = isOverlay ? mode : "replace";
|
|
1578
|
+
if (!isOverlay) {
|
|
1579
|
+
try {
|
|
1580
|
+
this._resolveStyleUrl(basemap);
|
|
1581
|
+
} catch (cause) {
|
|
1582
|
+
if (cause instanceof MissingCredentialError) {
|
|
1583
|
+
this._enterCredentialError(cause, basemap);
|
|
1584
|
+
throw cause;
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1555
1588
|
if (!isOverlay && this._state.allowMultiple && this._managedRasters.size > 0 && this._options.confirmStyleReplace) {
|
|
1556
1589
|
const replacedBasemapIds = [...this._managedRasters.keys()];
|
|
1557
1590
|
let confirmed = false;
|
|
@@ -1605,13 +1638,14 @@ class BasemapControl {
|
|
|
1605
1638
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1606
1639
|
} catch (cause) {
|
|
1607
1640
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
this.
|
|
1641
|
+
if (error instanceof MissingCredentialError) {
|
|
1642
|
+
this._enterCredentialError(error, basemap);
|
|
1643
|
+
} else {
|
|
1644
|
+
this._state = { ...this._state, loading: false, error: error.message };
|
|
1645
|
+
this._missingCredentialProvider = void 0;
|
|
1646
|
+
this._renderContent(true);
|
|
1647
|
+
this._handleError(error, basemap);
|
|
1612
1648
|
}
|
|
1613
|
-
this._renderContent(true);
|
|
1614
|
-
this._handleError(error, basemap);
|
|
1615
1649
|
throw error;
|
|
1616
1650
|
}
|
|
1617
1651
|
}
|
|
@@ -1674,6 +1708,28 @@ class BasemapControl {
|
|
|
1674
1708
|
this._emit({ type: "error", state: this.getState(), error, basemap });
|
|
1675
1709
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1676
1710
|
}
|
|
1711
|
+
// Clears the current error and its associated missing-credential provider so
|
|
1712
|
+
// the inline credential field and help link disappear together.
|
|
1713
|
+
_clearError() {
|
|
1714
|
+
if (this._state.error === void 0 && this._missingCredentialProvider === void 0 && this._lastFailedBasemapId === void 0) {
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1717
|
+
this._state = { ...this._state, error: void 0 };
|
|
1718
|
+
this._missingCredentialProvider = void 0;
|
|
1719
|
+
this._lastFailedBasemapId = void 0;
|
|
1720
|
+
}
|
|
1721
|
+
// Records a missing-credential failure: stores the error, remembers the
|
|
1722
|
+
// provider (for the help link and inline field) and the basemap to retry, and
|
|
1723
|
+
// surfaces the basemaps view so the inline credential field beside the error
|
|
1724
|
+
// is visible (#837).
|
|
1725
|
+
_enterCredentialError(error, basemap) {
|
|
1726
|
+
this._state = { ...this._state, loading: false, error: error.message };
|
|
1727
|
+
this._missingCredentialProvider = error.provider;
|
|
1728
|
+
this._lastFailedBasemapId = basemap.id;
|
|
1729
|
+
this._activeView = "basemaps";
|
|
1730
|
+
this._renderContent(true);
|
|
1731
|
+
this._handleError(error, basemap);
|
|
1732
|
+
}
|
|
1677
1733
|
_createContainer() {
|
|
1678
1734
|
const container = document.createElement("div");
|
|
1679
1735
|
container.className = `maplibregl-ctrl maplibregl-ctrl-group basemap-control${this._options.className ? ` ${this._options.className}` : ""}`;
|
|
@@ -1708,6 +1764,18 @@ class BasemapControl {
|
|
|
1708
1764
|
const title = document.createElement("span");
|
|
1709
1765
|
title.className = "basemap-control-title";
|
|
1710
1766
|
title.textContent = this._options.title;
|
|
1767
|
+
const settingsBtn = document.createElement("button");
|
|
1768
|
+
settingsBtn.className = "basemap-control-settings-toggle";
|
|
1769
|
+
settingsBtn.type = "button";
|
|
1770
|
+
settingsBtn.setAttribute("aria-label", "API keys");
|
|
1771
|
+
settingsBtn.title = "API keys";
|
|
1772
|
+
settingsBtn.innerHTML = `
|
|
1773
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
1774
|
+
<path d="m21 2-2 2m-7.61 7.61a5.5 5.5 0 1 1-7.778 7.778 5.5 5.5 0 0 1 7.777-7.777zm0 0L15.5 7.5m0 0 3 3L22 7l-3-3m-3.5 3.5L19 4"/>
|
|
1775
|
+
</svg>
|
|
1776
|
+
`;
|
|
1777
|
+
settingsBtn.addEventListener("click", () => this._toggleSettingsView());
|
|
1778
|
+
this._settingsButton = settingsBtn;
|
|
1711
1779
|
const closeBtn = document.createElement("button");
|
|
1712
1780
|
closeBtn.className = "basemap-control-close";
|
|
1713
1781
|
closeBtn.type = "button";
|
|
@@ -1715,6 +1783,7 @@ class BasemapControl {
|
|
|
1715
1783
|
closeBtn.innerHTML = "×";
|
|
1716
1784
|
closeBtn.addEventListener("click", () => this.collapse());
|
|
1717
1785
|
header.appendChild(title);
|
|
1786
|
+
header.appendChild(settingsBtn);
|
|
1718
1787
|
header.appendChild(closeBtn);
|
|
1719
1788
|
this._content = document.createElement("div");
|
|
1720
1789
|
this._content.className = "basemap-control-content";
|
|
@@ -1736,13 +1805,17 @@ class BasemapControl {
|
|
|
1736
1805
|
_renderContent(preserveResultsScroll = false) {
|
|
1737
1806
|
var _a;
|
|
1738
1807
|
if (!this._content) return;
|
|
1808
|
+
this._reconcileSettingsButton();
|
|
1809
|
+
if (this._activeView === "settings" && this._hasProviderSettings()) {
|
|
1810
|
+
this._content.replaceChildren(this._createSettingsView());
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1739
1813
|
const categories = getBasemapCategories(this._basemaps);
|
|
1740
1814
|
const results = this._getFilteredBasemaps();
|
|
1741
1815
|
const previousResultsScrollTop = preserveResultsScroll ? (_a = this._content.querySelector(".basemap-control-results")) == null ? void 0 : _a.scrollTop : void 0;
|
|
1742
1816
|
this._content.replaceChildren(
|
|
1743
1817
|
this._createSearchRow(),
|
|
1744
1818
|
...this._options.showMultipleToggle ? [this._createMultipleToggleRow()] : [],
|
|
1745
|
-
...this._hasProviderSettings() ? [this._createProviderSettingsSection()] : [],
|
|
1746
1819
|
this._createFilterRow(this._providers, categories),
|
|
1747
1820
|
this._createStatus(results.length),
|
|
1748
1821
|
this._createResults(results)
|
|
@@ -1752,6 +1825,23 @@ class BasemapControl {
|
|
|
1752
1825
|
if (resultsElement) resultsElement.scrollTop = previousResultsScrollTop;
|
|
1753
1826
|
}
|
|
1754
1827
|
}
|
|
1828
|
+
// Show the key button only when the catalog actually needs a provider
|
|
1829
|
+
// credential, and reflect whether the settings view is currently open.
|
|
1830
|
+
_reconcileSettingsButton() {
|
|
1831
|
+
if (!this._settingsButton) return;
|
|
1832
|
+
const hasSettings = this._hasProviderSettings();
|
|
1833
|
+
this._settingsButton.hidden = !hasSettings;
|
|
1834
|
+
this._settingsButton.classList.toggle(
|
|
1835
|
+
"is-active",
|
|
1836
|
+
hasSettings && this._activeView === "settings"
|
|
1837
|
+
);
|
|
1838
|
+
}
|
|
1839
|
+
_toggleSettingsView() {
|
|
1840
|
+
if (!this._hasProviderSettings()) return;
|
|
1841
|
+
this._activeView = this._activeView === "settings" ? "basemaps" : "settings";
|
|
1842
|
+
this._renderContent();
|
|
1843
|
+
this._emit({ type: "statechange", state: this.getState() });
|
|
1844
|
+
}
|
|
1755
1845
|
_renderFilteredResults() {
|
|
1756
1846
|
var _a, _b;
|
|
1757
1847
|
if (!this._content) return;
|
|
@@ -1806,8 +1896,18 @@ class BasemapControl {
|
|
|
1806
1896
|
input.value = this._state.query;
|
|
1807
1897
|
input.setAttribute("aria-label", "Search basemaps");
|
|
1808
1898
|
input.addEventListener("input", () => {
|
|
1899
|
+
var _a;
|
|
1900
|
+
const hadError = this._state.error !== void 0;
|
|
1809
1901
|
this._state = { ...this._state, query: input.value };
|
|
1810
|
-
this.
|
|
1902
|
+
this._clearError();
|
|
1903
|
+
if (hadError) {
|
|
1904
|
+
this._renderContent();
|
|
1905
|
+
input.focus();
|
|
1906
|
+
const end = input.value.length;
|
|
1907
|
+
(_a = input.setSelectionRange) == null ? void 0 : _a.call(input, end, end);
|
|
1908
|
+
} else {
|
|
1909
|
+
this._renderFilteredResults();
|
|
1910
|
+
}
|
|
1811
1911
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1812
1912
|
});
|
|
1813
1913
|
wrapper.appendChild(input);
|
|
@@ -1829,118 +1929,205 @@ class BasemapControl {
|
|
|
1829
1929
|
wrapper.appendChild(input);
|
|
1830
1930
|
return wrapper;
|
|
1831
1931
|
}
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
value: this._mapTilerApiKey,
|
|
1853
|
-
onInput: (value) => {
|
|
1854
|
-
this._mapTilerApiKey = value;
|
|
1932
|
+
// The ordered set of provider credential fields, gated by whether the catalog
|
|
1933
|
+
// actually contains basemaps for each provider. A single source of truth for
|
|
1934
|
+
// both the centralized settings view and the inline field shown beside a
|
|
1935
|
+
// missing-credential error, so the two never drift (#837).
|
|
1936
|
+
_providerFieldDefs() {
|
|
1937
|
+
return [
|
|
1938
|
+
{
|
|
1939
|
+
provider: "maptiler",
|
|
1940
|
+
label: "MapTiler",
|
|
1941
|
+
has: this._hasMapTilerBasemaps(),
|
|
1942
|
+
fields: [
|
|
1943
|
+
{
|
|
1944
|
+
className: "basemap-control-maptiler-key",
|
|
1945
|
+
type: "password",
|
|
1946
|
+
placeholder: "MapTiler API key",
|
|
1947
|
+
ariaLabel: "MapTiler API key",
|
|
1948
|
+
value: this._mapTilerApiKey,
|
|
1949
|
+
onInput: (value) => {
|
|
1950
|
+
this._mapTilerApiKey = value;
|
|
1951
|
+
}
|
|
1855
1952
|
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
this.
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
this._mapboxAccessToken
|
|
1953
|
+
]
|
|
1954
|
+
},
|
|
1955
|
+
{
|
|
1956
|
+
provider: "mapbox",
|
|
1957
|
+
label: "Mapbox",
|
|
1958
|
+
has: this._hasMapboxBasemaps(),
|
|
1959
|
+
fields: [
|
|
1960
|
+
{
|
|
1961
|
+
className: "basemap-control-mapbox-token",
|
|
1962
|
+
type: "password",
|
|
1963
|
+
placeholder: "Mapbox access token",
|
|
1964
|
+
ariaLabel: "Mapbox access token",
|
|
1965
|
+
value: this._mapboxAccessToken,
|
|
1966
|
+
onInput: (value) => {
|
|
1967
|
+
this._mapboxAccessToken = value;
|
|
1968
|
+
}
|
|
1869
1969
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
this.
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
this._tomtomApiKey
|
|
1970
|
+
]
|
|
1971
|
+
},
|
|
1972
|
+
{
|
|
1973
|
+
provider: "tomtom",
|
|
1974
|
+
label: "TomTom",
|
|
1975
|
+
has: this._hasTomTomBasemaps(),
|
|
1976
|
+
fields: [
|
|
1977
|
+
{
|
|
1978
|
+
className: "basemap-control-tomtom-key",
|
|
1979
|
+
type: "password",
|
|
1980
|
+
placeholder: "TomTom API key",
|
|
1981
|
+
ariaLabel: "TomTom API key",
|
|
1982
|
+
value: this._tomtomApiKey,
|
|
1983
|
+
onInput: (value) => {
|
|
1984
|
+
this._tomtomApiKey = value;
|
|
1985
|
+
}
|
|
1883
1986
|
}
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
this.
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
this._hereApiKey
|
|
1987
|
+
]
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
provider: "here",
|
|
1991
|
+
label: "HERE",
|
|
1992
|
+
has: this._hasHereBasemaps(),
|
|
1993
|
+
fields: [
|
|
1994
|
+
{
|
|
1995
|
+
className: "basemap-control-here-key",
|
|
1996
|
+
type: "password",
|
|
1997
|
+
placeholder: "HERE API key",
|
|
1998
|
+
ariaLabel: "HERE API key",
|
|
1999
|
+
value: this._hereApiKey,
|
|
2000
|
+
onInput: (value) => {
|
|
2001
|
+
this._hereApiKey = value;
|
|
2002
|
+
}
|
|
1897
2003
|
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
this.
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
this._googleMapsApiKey
|
|
1911
|
-
|
|
2004
|
+
]
|
|
2005
|
+
},
|
|
2006
|
+
{
|
|
2007
|
+
provider: "google",
|
|
2008
|
+
label: "Google",
|
|
2009
|
+
has: this._hasGoogleApiKeyBasemaps(),
|
|
2010
|
+
fields: [
|
|
2011
|
+
{
|
|
2012
|
+
className: "basemap-control-google-key",
|
|
2013
|
+
type: "password",
|
|
2014
|
+
placeholder: "Google Maps API key",
|
|
2015
|
+
ariaLabel: "Google Maps API key",
|
|
2016
|
+
value: this._googleMapsApiKey,
|
|
2017
|
+
onInput: (value) => {
|
|
2018
|
+
this._googleMapsApiKey = value;
|
|
2019
|
+
this._googleSessions.clear();
|
|
2020
|
+
}
|
|
1912
2021
|
}
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
this.
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
this._amazonApiKey
|
|
2022
|
+
]
|
|
2023
|
+
},
|
|
2024
|
+
{
|
|
2025
|
+
provider: "amazon",
|
|
2026
|
+
label: "Amazon",
|
|
2027
|
+
has: this._hasAmazonBasemaps(),
|
|
2028
|
+
fields: [
|
|
2029
|
+
{
|
|
2030
|
+
className: "basemap-control-amazon-key",
|
|
2031
|
+
type: "password",
|
|
2032
|
+
placeholder: "Amazon API key",
|
|
2033
|
+
ariaLabel: "Amazon API key",
|
|
2034
|
+
value: this._amazonApiKey,
|
|
2035
|
+
onInput: (value) => {
|
|
2036
|
+
this._amazonApiKey = value;
|
|
2037
|
+
}
|
|
2038
|
+
},
|
|
2039
|
+
{
|
|
2040
|
+
className: "basemap-control-aws-region",
|
|
2041
|
+
type: "text",
|
|
2042
|
+
placeholder: "AWS region",
|
|
2043
|
+
ariaLabel: "AWS region",
|
|
2044
|
+
value: this._awsRegion,
|
|
2045
|
+
onInput: (value) => {
|
|
2046
|
+
this._awsRegion = value;
|
|
2047
|
+
}
|
|
1926
2048
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
2049
|
+
]
|
|
2050
|
+
}
|
|
2051
|
+
];
|
|
2052
|
+
}
|
|
2053
|
+
// The dedicated API-keys view, opened from the header key button. It collects
|
|
2054
|
+
// every provider credential in one place, separate from the basemap list, so
|
|
2055
|
+
// the list itself stays free of input clutter (#837, Bug 5).
|
|
2056
|
+
_createSettingsView() {
|
|
2057
|
+
const view = document.createElement("div");
|
|
2058
|
+
view.className = "basemap-control-settings-view";
|
|
2059
|
+
const head = document.createElement("div");
|
|
2060
|
+
head.className = "basemap-control-settings-head";
|
|
2061
|
+
const back = document.createElement("button");
|
|
2062
|
+
back.type = "button";
|
|
2063
|
+
back.className = "basemap-control-settings-back";
|
|
2064
|
+
back.setAttribute("aria-label", "Back to basemaps");
|
|
2065
|
+
back.innerHTML = `
|
|
2066
|
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
2067
|
+
<path d="m15 18-6-6 6-6"/>
|
|
2068
|
+
</svg>
|
|
2069
|
+
<span>Basemaps</span>
|
|
2070
|
+
`;
|
|
2071
|
+
back.addEventListener("click", () => this._toggleSettingsView());
|
|
2072
|
+
const heading = document.createElement("span");
|
|
2073
|
+
heading.className = "basemap-control-settings-heading";
|
|
2074
|
+
heading.textContent = "API keys";
|
|
2075
|
+
head.appendChild(back);
|
|
2076
|
+
head.appendChild(heading);
|
|
2077
|
+
view.appendChild(head);
|
|
2078
|
+
const intro = document.createElement("p");
|
|
2079
|
+
intro.className = "basemap-control-settings-intro";
|
|
2080
|
+
intro.textContent = "Add the credentials each provider requires to use its basemaps.";
|
|
2081
|
+
view.appendChild(intro);
|
|
2082
|
+
const fields = document.createElement("div");
|
|
2083
|
+
fields.className = "basemap-control-provider-settings-fields";
|
|
2084
|
+
for (const def of this._providerFieldDefs()) {
|
|
2085
|
+
if (def.has) fields.appendChild(this._createProviderFieldGroup(def));
|
|
2086
|
+
}
|
|
2087
|
+
view.appendChild(fields);
|
|
2088
|
+
return view;
|
|
2089
|
+
}
|
|
2090
|
+
_createProviderFieldGroup(def) {
|
|
2091
|
+
const group = document.createElement("div");
|
|
2092
|
+
group.className = `basemap-control-provider-group basemap-control-provider-group-${def.provider}`;
|
|
2093
|
+
const label = document.createElement("span");
|
|
2094
|
+
label.className = "basemap-control-provider-group-label";
|
|
2095
|
+
label.textContent = def.label;
|
|
2096
|
+
group.appendChild(label);
|
|
2097
|
+
for (const field of def.fields) {
|
|
2098
|
+
group.appendChild(this._createProviderSettingsInput(field));
|
|
2099
|
+
}
|
|
2100
|
+
return group;
|
|
2101
|
+
}
|
|
2102
|
+
// Renders the credential field(s) for the provider of the active
|
|
2103
|
+
// missing-credential error, inline beside the error message. Only that one
|
|
2104
|
+
// provider is shown, so the user is not asked to wade through every other
|
|
2105
|
+
// provider's inputs (#837, Bug 3). Returns null when no such error is active.
|
|
2106
|
+
_createInlineCredentialFields() {
|
|
2107
|
+
if (!this._state.error || !this._missingCredentialProvider) return null;
|
|
2108
|
+
const def = this._providerFieldDefs().find(
|
|
2109
|
+
(candidate) => candidate.provider === this._missingCredentialProvider && candidate.has
|
|
2110
|
+
);
|
|
2111
|
+
if (!def) return null;
|
|
2112
|
+
const wrapper = document.createElement("div");
|
|
2113
|
+
wrapper.className = "basemap-control-status-fields";
|
|
2114
|
+
for (const field of def.fields) {
|
|
2115
|
+
wrapper.appendChild(
|
|
1930
2116
|
this._createProviderSettingsInput({
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
}
|
|
2117
|
+
...field,
|
|
2118
|
+
// Keep the error (and this field) on screen while the user types, and
|
|
2119
|
+
// let Enter re-attempt the basemap that failed, so the field is not
|
|
2120
|
+
// yanked out from under the cursor on the first keystroke.
|
|
2121
|
+
rerenderOnInput: false,
|
|
2122
|
+
clearErrorOnInput: false,
|
|
2123
|
+
onEnter: () => this._retryLastFailedBasemap()
|
|
1939
2124
|
})
|
|
1940
2125
|
);
|
|
1941
2126
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
2127
|
+
return wrapper;
|
|
2128
|
+
}
|
|
2129
|
+
_retryLastFailedBasemap() {
|
|
2130
|
+
if (this._lastFailedBasemapId) this._selectBasemap(this._lastFailedBasemapId);
|
|
1944
2131
|
}
|
|
1945
2132
|
_createProviderSettingsInput({
|
|
1946
2133
|
className,
|
|
@@ -1948,7 +2135,10 @@ class BasemapControl {
|
|
|
1948
2135
|
placeholder,
|
|
1949
2136
|
ariaLabel,
|
|
1950
2137
|
value,
|
|
1951
|
-
onInput
|
|
2138
|
+
onInput,
|
|
2139
|
+
rerenderOnInput = true,
|
|
2140
|
+
clearErrorOnInput = true,
|
|
2141
|
+
onEnter
|
|
1952
2142
|
}) {
|
|
1953
2143
|
const wrapper = document.createElement("div");
|
|
1954
2144
|
wrapper.className = className;
|
|
@@ -1965,10 +2155,18 @@ class BasemapControl {
|
|
|
1965
2155
|
input.setAttribute("aria-label", ariaLabel);
|
|
1966
2156
|
input.addEventListener("input", () => {
|
|
1967
2157
|
onInput(input.value);
|
|
1968
|
-
|
|
1969
|
-
this._renderFilteredResults();
|
|
2158
|
+
if (clearErrorOnInput) this._clearError();
|
|
2159
|
+
if (rerenderOnInput) this._renderFilteredResults();
|
|
1970
2160
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1971
2161
|
});
|
|
2162
|
+
if (onEnter) {
|
|
2163
|
+
input.addEventListener("keydown", (event) => {
|
|
2164
|
+
if (event.key === "Enter") {
|
|
2165
|
+
event.preventDefault();
|
|
2166
|
+
onEnter();
|
|
2167
|
+
}
|
|
2168
|
+
});
|
|
2169
|
+
}
|
|
1972
2170
|
wrapper.appendChild(input);
|
|
1973
2171
|
return wrapper;
|
|
1974
2172
|
}
|
|
@@ -1982,6 +2180,7 @@ class BasemapControl {
|
|
|
1982
2180
|
providers.map((provider) => ({ value: provider.id, label: provider.name })),
|
|
1983
2181
|
(value) => {
|
|
1984
2182
|
this._state = { ...this._state, providerFilter: value };
|
|
2183
|
+
this._clearError();
|
|
1985
2184
|
this._renderContent();
|
|
1986
2185
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1987
2186
|
}
|
|
@@ -1994,6 +2193,7 @@ class BasemapControl {
|
|
|
1994
2193
|
categories.map((category) => ({ value: category, label: category })),
|
|
1995
2194
|
(value) => {
|
|
1996
2195
|
this._state = { ...this._state, categoryFilter: value };
|
|
2196
|
+
this._clearError();
|
|
1997
2197
|
this._renderContent();
|
|
1998
2198
|
this._emit({ type: "statechange", state: this.getState() });
|
|
1999
2199
|
}
|
|
@@ -2043,10 +2243,12 @@ class BasemapControl {
|
|
|
2043
2243
|
actions.appendChild(link);
|
|
2044
2244
|
const hint = document.createElement("span");
|
|
2045
2245
|
hint.className = "basemap-control-status-hint";
|
|
2046
|
-
hint.textContent = ", then
|
|
2246
|
+
hint.textContent = ", then enter it below and press Enter.";
|
|
2047
2247
|
actions.appendChild(hint);
|
|
2048
2248
|
status.appendChild(actions);
|
|
2049
2249
|
}
|
|
2250
|
+
const inlineFields = this._createInlineCredentialFields();
|
|
2251
|
+
if (inlineFields) status.appendChild(inlineFields);
|
|
2050
2252
|
} else {
|
|
2051
2253
|
status.textContent = `${resultCount} basemap${resultCount === 1 ? "" : "s"}`;
|
|
2052
2254
|
}
|
|
@@ -2619,4 +2821,4 @@ export {
|
|
|
2619
2821
|
getBasemapCategories as g,
|
|
2620
2822
|
resolveBasemapProviders as r
|
|
2621
2823
|
};
|
|
2622
|
-
//# sourceMappingURL=BasemapControl-
|
|
2824
|
+
//# sourceMappingURL=BasemapControl-C9T87uJx.js.map
|