ogm-viewer 0.3.0 → 0.5.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.
Files changed (215) hide show
  1. package/dist/cjs/{index-CVTUnaNg.js → index-mu9UK_Fb.js} +66 -3
  2. package/dist/cjs/{lerc-TQEssgMv.js → lerc-Df0hDWkS.js} +7 -7
  3. package/dist/cjs/loader.cjs.js +2 -2
  4. package/dist/cjs/{ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-settings.ogm-sidebar.ogm-viewer-ifPxRNEo.js → ogm-alerts.ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-preview.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-DMEfXquS.js} +13843 -6976
  5. package/dist/cjs/ogm-alerts_11.cjs.entry.js +18 -0
  6. package/dist/cjs/ogm-viewer.cjs.js +2 -2
  7. package/dist/collection/assets/icons/exclamation-triangle-fill.svg +3 -0
  8. package/dist/collection/assets/webawesome/styles/color/palettes/base.css +44 -0
  9. package/dist/collection/assets/webawesome/styles/color/palettes/bright.css +145 -0
  10. package/dist/collection/assets/webawesome/styles/color/palettes/default.css +146 -0
  11. package/dist/collection/assets/webawesome/styles/color/palettes/shoelace.css +146 -0
  12. package/dist/collection/assets/webawesome/styles/color/variants/brand.css +162 -0
  13. package/dist/collection/assets/webawesome/styles/color/variants/danger.css +162 -0
  14. package/dist/collection/assets/webawesome/styles/color/variants/neutral.css +162 -0
  15. package/dist/collection/assets/webawesome/styles/color/variants/success.css +162 -0
  16. package/dist/collection/assets/webawesome/styles/color/variants/warning.css +162 -0
  17. package/dist/collection/assets/webawesome/styles/color/variants.css +5 -0
  18. package/dist/collection/assets/webawesome/styles/component/form-control.styles.d.ts +2 -0
  19. package/dist/collection/assets/webawesome/styles/component/form-control.styles.ts +43 -0
  20. package/dist/collection/assets/webawesome/styles/component/host.styles.d.ts +2 -0
  21. package/dist/collection/assets/webawesome/styles/component/host.styles.ts +18 -0
  22. package/dist/collection/assets/webawesome/styles/component/segmented-field.styles.d.ts +19 -0
  23. package/dist/collection/assets/webawesome/styles/component/segmented-field.styles.ts +92 -0
  24. package/dist/collection/assets/webawesome/styles/component/size.styles.d.ts +2 -0
  25. package/dist/collection/assets/webawesome/styles/component/size.styles.ts +26 -0
  26. package/dist/collection/assets/webawesome/styles/component/variants.styles.d.ts +2 -0
  27. package/dist/collection/assets/webawesome/styles/component/variants.styles.ts +69 -0
  28. package/dist/collection/assets/webawesome/styles/component/visually-hidden.styles.d.ts +2 -0
  29. package/dist/collection/assets/webawesome/styles/component/visually-hidden.styles.ts +19 -0
  30. package/dist/collection/assets/webawesome/styles/layers.css +21 -0
  31. package/dist/collection/assets/webawesome/styles/native.css +1439 -0
  32. package/dist/collection/assets/webawesome/styles/themes/awesome.css +614 -0
  33. package/dist/collection/assets/webawesome/styles/themes/default.css +373 -0
  34. package/dist/collection/assets/webawesome/styles/themes/shoelace.css +648 -0
  35. package/dist/collection/assets/webawesome/styles/utilities/align-items.css +44 -0
  36. package/dist/collection/assets/webawesome/styles/utilities/border-radius.css +20 -0
  37. package/dist/collection/assets/webawesome/styles/utilities/flex-wrap.css +16 -0
  38. package/dist/collection/assets/webawesome/styles/utilities/fouce.css +17 -0
  39. package/dist/collection/assets/webawesome/styles/utilities/gap.css +56 -0
  40. package/dist/collection/assets/webawesome/styles/utilities/justify-content.css +32 -0
  41. package/dist/collection/assets/webawesome/styles/utilities/layout.css +159 -0
  42. package/dist/collection/assets/webawesome/styles/utilities/placeholder.css +9 -0
  43. package/dist/collection/assets/webawesome/styles/utilities/prose.css +161 -0
  44. package/dist/collection/assets/webawesome/styles/utilities/scroll-lock.css +20 -0
  45. package/dist/collection/assets/webawesome/styles/utilities/size.css +21 -0
  46. package/dist/collection/assets/webawesome/styles/utilities/text.css +262 -0
  47. package/dist/collection/assets/webawesome/styles/utilities/variants.css +62 -0
  48. package/dist/collection/assets/webawesome/styles/utilities/visually-hidden.css +17 -0
  49. package/dist/collection/assets/webawesome/styles/utilities.css +17 -0
  50. package/dist/collection/assets/webawesome/styles/webawesome.css +11 -0
  51. package/dist/collection/collection-manifest.json +4 -0
  52. package/dist/collection/components/ogm-alerts/ogm-alerts.css +51 -0
  53. package/dist/collection/components/ogm-alerts/ogm-alerts.js +71 -0
  54. package/dist/collection/components/ogm-alerts/ogm-alerts.test.js +26 -0
  55. package/dist/collection/components/ogm-attributes/ogm-attributes.css +68 -0
  56. package/dist/collection/components/ogm-attributes/ogm-attributes.js +112 -0
  57. package/dist/collection/components/ogm-image/ogm-image.css +1 -1
  58. package/dist/collection/components/ogm-image/ogm-image.js +70 -20
  59. package/dist/collection/components/ogm-map/ogm-map.css +43 -44
  60. package/dist/collection/components/ogm-map/ogm-map.js +229 -91
  61. package/dist/collection/components/ogm-menubar/ogm-menubar.css +16 -14
  62. package/dist/collection/components/ogm-menubar/ogm-menubar.js +3 -5
  63. package/dist/collection/components/ogm-menubar/ogm-menubar.test.js +28 -3
  64. package/dist/collection/components/ogm-metadata/ogm-metadata.css +6 -492
  65. package/dist/collection/components/ogm-metadata/ogm-metadata.js +3 -5
  66. package/dist/collection/components/ogm-metadata/ogm-metadata.test.js +1 -1
  67. package/dist/collection/components/ogm-preview/ogm-preview.css +16 -0
  68. package/dist/collection/components/ogm-preview/ogm-preview.js +144 -0
  69. package/dist/collection/components/ogm-preview/ogm-preview.test.js +37 -0
  70. package/dist/collection/components/ogm-previews/ogm-previews.css +45 -0
  71. package/dist/collection/components/ogm-previews/ogm-previews.js +162 -0
  72. package/dist/collection/components/ogm-previews/ogm-previews.test.js +83 -0
  73. package/dist/collection/components/ogm-settings/ogm-settings.js +7 -9
  74. package/dist/collection/components/ogm-sidebar/ogm-sidebar.css +50 -503
  75. package/dist/collection/components/ogm-sidebar/ogm-sidebar.js +8 -12
  76. package/dist/collection/components/ogm-viewer/ogm-viewer.css +4 -974
  77. package/dist/collection/components/ogm-viewer/ogm-viewer.js +69 -32
  78. package/dist/collection/lib/errors.js +137 -0
  79. package/dist/collection/lib/errors.test.js +96 -0
  80. package/dist/collection/lib/previewers/cog-deck.js +14 -9
  81. package/dist/collection/lib/previewers/cog.js +3 -3
  82. package/dist/collection/lib/previewers/geojson.js +3 -2
  83. package/dist/collection/lib/previewers/maplibre.js +9 -15
  84. package/dist/collection/lib/previewers/pmtiles-raster.js +1 -1
  85. package/dist/collection/lib/previewers/pmtiles-vector.js +7 -1
  86. package/dist/collection/lib/previewers/raster.js +1 -1
  87. package/dist/collection/lib/previewers/vector.js +65 -30
  88. package/dist/collection/lib/previewers/wms.js +1 -1
  89. package/dist/collection/lib/record.js +1 -1
  90. package/dist/collection/lib/record.test.js +1 -1
  91. package/dist/collection/lib/references.js +0 -65
  92. package/dist/collection/lib/references.test.js +0 -150
  93. package/dist/collection/lib/sources/cog.js +5 -2
  94. package/dist/collection/lib/sources/geojson.js +9 -2
  95. package/dist/collection/lib/sources/iiif-manifest.js +54 -0
  96. package/dist/collection/lib/sources/iiif-manifest.test.js +110 -0
  97. package/dist/collection/lib/sources/iiif.js +11 -0
  98. package/dist/collection/lib/sources/maplibre.js +8 -0
  99. package/dist/collection/lib/sources/openindexmap.js +3 -0
  100. package/dist/collection/lib/sources/pmtiles.js +10 -4
  101. package/dist/collection/lib/sources/raster.js +3 -3
  102. package/dist/collection/lib/sources/source.js +26 -7
  103. package/dist/collection/lib/sources/tilejson.js +7 -1
  104. package/dist/collection/lib/sources/tms.js +3 -0
  105. package/dist/collection/lib/sources/vector.js +3 -3
  106. package/dist/collection/lib/sources/wms.js +6 -11
  107. package/dist/collection/lib/sources/xyz.js +3 -0
  108. package/dist/collection/lib/themes/maplibre.js +46 -0
  109. package/dist/collection/lib/themes/theme.js +23 -0
  110. package/dist/esm/{index-BdFT_kJa.js → index-DTLAlWKQ.js} +66 -3
  111. package/dist/esm/{lerc-DFDScDrO.js → lerc-CB_Jcu33.js} +2 -2
  112. package/dist/esm/loader.js +3 -3
  113. package/dist/esm/{ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-settings.ogm-sidebar.ogm-viewer-r5w_49n3.js → ogm-alerts.ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-preview.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-Dlc6USWB.js} +13840 -6977
  114. package/dist/esm/ogm-alerts_11.entry.js +2 -0
  115. package/dist/esm/ogm-viewer.js +3 -3
  116. package/dist/ogm-viewer/assets/icons/exclamation-triangle-fill.svg +3 -0
  117. package/dist/ogm-viewer/assets/webawesome/styles/color/palettes/base.css +44 -0
  118. package/dist/ogm-viewer/assets/webawesome/styles/color/palettes/bright.css +145 -0
  119. package/dist/ogm-viewer/assets/webawesome/styles/color/palettes/default.css +146 -0
  120. package/dist/ogm-viewer/assets/webawesome/styles/color/palettes/shoelace.css +146 -0
  121. package/dist/ogm-viewer/assets/webawesome/styles/color/variants/brand.css +162 -0
  122. package/dist/ogm-viewer/assets/webawesome/styles/color/variants/danger.css +162 -0
  123. package/dist/ogm-viewer/assets/webawesome/styles/color/variants/neutral.css +162 -0
  124. package/dist/ogm-viewer/assets/webawesome/styles/color/variants/success.css +162 -0
  125. package/dist/ogm-viewer/assets/webawesome/styles/color/variants/warning.css +162 -0
  126. package/dist/ogm-viewer/assets/webawesome/styles/color/variants.css +5 -0
  127. package/dist/ogm-viewer/assets/webawesome/styles/component/form-control.styles.d.ts +2 -0
  128. package/dist/ogm-viewer/assets/webawesome/styles/component/form-control.styles.ts +43 -0
  129. package/dist/ogm-viewer/assets/webawesome/styles/component/host.styles.d.ts +2 -0
  130. package/dist/ogm-viewer/assets/webawesome/styles/component/host.styles.ts +18 -0
  131. package/dist/ogm-viewer/assets/webawesome/styles/component/segmented-field.styles.d.ts +19 -0
  132. package/dist/ogm-viewer/assets/webawesome/styles/component/segmented-field.styles.ts +92 -0
  133. package/dist/ogm-viewer/assets/webawesome/styles/component/size.styles.d.ts +2 -0
  134. package/dist/ogm-viewer/assets/webawesome/styles/component/size.styles.ts +26 -0
  135. package/dist/ogm-viewer/assets/webawesome/styles/component/variants.styles.d.ts +2 -0
  136. package/dist/ogm-viewer/assets/webawesome/styles/component/variants.styles.ts +69 -0
  137. package/dist/ogm-viewer/assets/webawesome/styles/component/visually-hidden.styles.d.ts +2 -0
  138. package/dist/ogm-viewer/assets/webawesome/styles/component/visually-hidden.styles.ts +19 -0
  139. package/dist/ogm-viewer/assets/webawesome/styles/layers.css +21 -0
  140. package/dist/ogm-viewer/assets/webawesome/styles/native.css +1439 -0
  141. package/dist/ogm-viewer/assets/webawesome/styles/themes/awesome.css +614 -0
  142. package/dist/ogm-viewer/assets/webawesome/styles/themes/default.css +373 -0
  143. package/dist/ogm-viewer/assets/webawesome/styles/themes/shoelace.css +648 -0
  144. package/dist/ogm-viewer/assets/webawesome/styles/utilities/align-items.css +44 -0
  145. package/dist/ogm-viewer/assets/webawesome/styles/utilities/border-radius.css +20 -0
  146. package/dist/ogm-viewer/assets/webawesome/styles/utilities/flex-wrap.css +16 -0
  147. package/dist/ogm-viewer/assets/webawesome/styles/utilities/fouce.css +17 -0
  148. package/dist/ogm-viewer/assets/webawesome/styles/utilities/gap.css +56 -0
  149. package/dist/ogm-viewer/assets/webawesome/styles/utilities/justify-content.css +32 -0
  150. package/dist/ogm-viewer/assets/webawesome/styles/utilities/layout.css +159 -0
  151. package/dist/ogm-viewer/assets/webawesome/styles/utilities/placeholder.css +9 -0
  152. package/dist/ogm-viewer/assets/webawesome/styles/utilities/prose.css +161 -0
  153. package/dist/ogm-viewer/assets/webawesome/styles/utilities/scroll-lock.css +20 -0
  154. package/dist/ogm-viewer/assets/webawesome/styles/utilities/size.css +21 -0
  155. package/dist/ogm-viewer/assets/webawesome/styles/utilities/text.css +262 -0
  156. package/dist/ogm-viewer/assets/webawesome/styles/utilities/variants.css +62 -0
  157. package/dist/ogm-viewer/assets/webawesome/styles/utilities/visually-hidden.css +17 -0
  158. package/dist/ogm-viewer/assets/webawesome/styles/utilities.css +17 -0
  159. package/dist/ogm-viewer/assets/webawesome/styles/webawesome.css +11 -0
  160. package/dist/ogm-viewer/ogm-viewer.esm.js +1 -1
  161. package/dist/ogm-viewer/p-7936ccc9.entry.js +1 -0
  162. package/dist/ogm-viewer/p-CWtUrqsM.js +2822 -0
  163. package/dist/ogm-viewer/p-DTLAlWKQ.js +2 -0
  164. package/dist/ogm-viewer/{p-BeA3REDA.js → p-DqRDI3AC.js} +1 -1
  165. package/dist/types/components/ogm-alerts/ogm-alerts.d.ts +6 -0
  166. package/dist/types/components/ogm-alerts/ogm-alerts.test.d.ts +1 -0
  167. package/dist/types/components/ogm-attributes/ogm-attributes.d.ts +10 -0
  168. package/dist/types/components/ogm-image/ogm-image.d.ts +8 -3
  169. package/dist/types/components/ogm-map/ogm-map.d.ts +38 -14
  170. package/dist/types/components/ogm-menubar/ogm-menubar.d.ts +1 -1
  171. package/dist/types/components/ogm-metadata/ogm-metadata.d.ts +1 -1
  172. package/dist/types/components/ogm-preview/ogm-preview.d.ts +13 -0
  173. package/dist/types/components/ogm-preview/ogm-preview.test.d.ts +1 -0
  174. package/dist/types/components/ogm-previews/ogm-previews.d.ts +12 -0
  175. package/dist/types/components/ogm-previews/ogm-previews.test.d.ts +1 -0
  176. package/dist/types/components/ogm-settings/ogm-settings.d.ts +2 -2
  177. package/dist/types/components/ogm-sidebar/ogm-sidebar.d.ts +1 -1
  178. package/dist/types/components/ogm-viewer/ogm-viewer.d.ts +17 -14
  179. package/dist/types/components.d.ts +122 -18
  180. package/dist/types/lib/errors.d.ts +13 -0
  181. package/dist/types/lib/errors.test.d.ts +1 -0
  182. package/dist/types/lib/previewers/cog-deck.d.ts +7 -3
  183. package/dist/types/lib/previewers/cog.d.ts +2 -2
  184. package/dist/types/lib/previewers/maplibre.d.ts +7 -9
  185. package/dist/types/lib/previewers/pmtiles-vector.d.ts +1 -0
  186. package/dist/types/lib/previewers/vector.d.ts +3 -15
  187. package/dist/types/lib/record.d.ts +1 -1
  188. package/dist/types/lib/references.d.ts +0 -8
  189. package/dist/types/lib/sources/cog.d.ts +3 -2
  190. package/dist/types/lib/sources/geojson.d.ts +3 -2
  191. package/dist/types/lib/sources/iiif-manifest.d.ts +12 -0
  192. package/dist/types/lib/sources/iiif-manifest.test.d.ts +1 -0
  193. package/dist/types/lib/sources/iiif.d.ts +5 -0
  194. package/dist/types/lib/sources/maplibre.d.ts +7 -0
  195. package/dist/types/lib/sources/openindexmap.d.ts +1 -0
  196. package/dist/types/lib/sources/pmtiles.d.ts +6 -4
  197. package/dist/types/lib/sources/raster.d.ts +3 -3
  198. package/dist/types/lib/sources/source.d.ts +7 -7
  199. package/dist/types/lib/sources/tilejson.d.ts +3 -2
  200. package/dist/types/lib/sources/tms.d.ts +1 -0
  201. package/dist/types/lib/sources/vector.d.ts +4 -5
  202. package/dist/types/lib/sources/wms.d.ts +4 -3
  203. package/dist/types/lib/sources/xyz.d.ts +1 -0
  204. package/dist/types/lib/themes/maplibre.d.ts +27 -0
  205. package/dist/types/lib/themes/theme.d.ts +7 -0
  206. package/package.json +3 -2
  207. package/dist/cjs/ogm-image_7.cjs.entry.js +0 -14
  208. package/dist/collection/lib/previewers/index.js +0 -36
  209. package/dist/collection/lib/sources/index.js +0 -29
  210. package/dist/esm/ogm-image_7.entry.js +0 -2
  211. package/dist/ogm-viewer/p-0b81e024.entry.js +0 -1
  212. package/dist/ogm-viewer/p-33R3d9fV.js +0 -1950
  213. package/dist/ogm-viewer/p-BdFT_kJa.js +0 -2
  214. package/dist/types/lib/previewers/index.d.ts +0 -3
  215. package/dist/types/lib/sources/index.d.ts +0 -3
@@ -1,60 +1,84 @@
1
1
  import { h } from "@stencil/core";
2
- import { Protocol as PMTilesProtocol } from "pmtiles";
3
2
  import maplibregl from "maplibre-gl";
3
+ import { Protocol as PMTilesProtocol } from "pmtiles";
4
+ import CogSource from "../../lib/sources/cog";
5
+ import GeoJSONSource from "../../lib/sources/geojson";
6
+ import OpenIndexMapSource from "../../lib/sources/openindexmap";
7
+ import PMTilesSource from "../../lib/sources/pmtiles";
8
+ import RasterSource from "../../lib/sources/raster";
9
+ import WmsSource from "../../lib/sources/wms";
4
10
  import { getElement } from "../../lib/elements";
5
- import { getSources } from "../../lib/sources";
6
- import { getMapPreviewers } from "../../lib/previewers";
11
+ import { referenceError } from "../../lib/errors";
12
+ import CogPreviewer from "../../lib/previewers/cog";
13
+ import GeoJSONPreviewer from "../../lib/previewers/geojson";
14
+ import OpenIndexMapPreviewer from "../../lib/previewers/openindexmap";
15
+ import PMTilesRasterPreviewer from "../../lib/previewers/pmtiles-raster";
16
+ import PMTilesVectorPreviewer from "../../lib/previewers/pmtiles-vector";
17
+ import RasterPreviewer from "../../lib/previewers/raster";
18
+ import WmsPreviewer from "../../lib/previewers/wms";
19
+ import MapLibreTheme from "../../lib/themes/maplibre";
7
20
  // Register PMTiles protocol
8
21
  const protocol = new PMTilesProtocol();
9
22
  maplibregl.addProtocol('pmtiles', protocol.tile);
10
23
  export class OgmMap {
11
24
  el;
12
- record;
25
+ source;
13
26
  theme;
14
- previewOpacity = 100;
15
27
  padding = 0;
16
28
  mapIdle;
17
29
  mapLoading;
18
- // MapLibre map instance
30
+ previewError;
31
+ // Guards against reporting more than one error per load attempt
32
+ errorReported = false;
33
+ // MapLibre map instance and popup instance for feature info display
19
34
  map;
35
+ mapTheme;
36
+ popup = undefined;
37
+ attributesEl;
38
+ hoveredFeature = undefined;
20
39
  // Container element reference for fullscreen
21
40
  containerEl;
22
- // Previewers for the currently previewed sources
23
- previewers = [];
41
+ // Previewer for the currently previewed source
42
+ previewer = undefined;
24
43
  // Set up the mapLibre map and event bindings on load
25
44
  componentDidLoad() {
45
+ this.mapTheme = new MapLibreTheme(this.el);
26
46
  this.map = new maplibregl.Map({
27
47
  container: getElement(this.el, '#map'),
28
48
  attributionControl: false,
29
49
  cooperativeGestures: true,
30
- style: this.baseMapStyle,
50
+ style: this.mapTheme.getBaseMapStyle(),
31
51
  center: [0, 0],
32
52
  zoom: 2,
33
53
  minZoom: 2,
34
54
  });
35
55
  this.getContainer();
36
56
  this.addControls();
37
- this.map.on('idle', () => this.mapIdle.emit());
38
- this.map.on('load', () => this.previewRecord(this.record));
57
+ this.map.on('load', () => this.previewSource(this.source));
58
+ this.map.on('mousemove', this.handleHover.bind(this));
59
+ this.map.on('click', this.handleClick.bind(this));
60
+ this.map.on('error', this.handleMapError.bind(this));
39
61
  // View as a globe with atmosphere effects
40
62
  this.map.on('style.load', () => {
41
- this.map.setProjection({
42
- type: 'globe',
43
- });
44
- this.map.setSky({
45
- 'sky-color': '#199EF3',
46
- 'sky-horizon-blend': 0.5,
47
- 'horizon-color': '#ffffff',
48
- 'horizon-fog-blend': 0.5,
49
- 'fog-color': '#0000ff',
50
- 'fog-ground-blend': 0.5,
51
- 'atmosphere-blend': ['interpolate', ['linear'], ['zoom'], 0, 1, 10, 1, 12, 0],
52
- });
63
+ this.map.setProjection({ type: 'globe' });
64
+ this.map.setSky(this.mapTheme.getSkyStyle());
53
65
  });
66
+ // Keep attributes outside Stencil render pipeline so that MapLibre can
67
+ // use the HTML directly for the popup content
68
+ this.attributesEl = document.createElement('ogm-attributes');
69
+ this.attributesEl.features = [];
70
+ }
71
+ // Clean up the map to prevent warnings/errors when removed from the DOM
72
+ disconnectedCallback() {
73
+ if (this.map)
74
+ this.map.remove();
54
75
  }
55
76
  // Find the container element for the map (used for fullscreen control)
56
77
  getContainer() {
57
- const containerEl = this.el.parentElement?.parentElement;
78
+ const viewerEl = document.querySelector('ogm-viewer');
79
+ if (!viewerEl)
80
+ throw new Error('Could not find ogm-viewer element');
81
+ const containerEl = getElement(viewerEl, '.container');
58
82
  if (!containerEl)
59
83
  throw new Error('Could not find map container element');
60
84
  this.containerEl = containerEl;
@@ -74,33 +98,84 @@ export class OgmMap {
74
98
  },
75
99
  }));
76
100
  }
77
- async previewRecord(record) {
78
- if (!record)
101
+ // Get the appropriate previewer for our source
102
+ async getMapPreviewer(map) {
103
+ const style = this.mapTheme.getStyle();
104
+ if (this.source instanceof OpenIndexMapSource)
105
+ return new OpenIndexMapPreviewer(this.source, map, style);
106
+ else if (this.source instanceof GeoJSONSource)
107
+ return new GeoJSONPreviewer(this.source, map, style);
108
+ else if (this.source instanceof PMTilesSource) {
109
+ if (await this.source.isVector())
110
+ return new PMTilesVectorPreviewer(this.source, map, style);
111
+ else
112
+ return new PMTilesRasterPreviewer(this.source, map, style);
113
+ }
114
+ else if (this.source instanceof WmsSource)
115
+ return new WmsPreviewer(this.source, map, style);
116
+ else if (this.source instanceof CogSource)
117
+ return new CogPreviewer(this.source, map, style);
118
+ else if (this.source instanceof RasterSource)
119
+ return new RasterPreviewer(this.source, map, style);
120
+ }
121
+ async previewSource(source) {
122
+ // Do nothing if we didn't get passed a source
123
+ if (!source)
79
124
  return;
125
+ // Fresh load attempt: allow one error to be reported again for this source
126
+ this.errorReported = false;
127
+ // Indicate loading state so we can show the spinner
80
128
  this.mapLoading.emit();
81
- // Clear any existing preview layers and sources
82
- await Promise.all(this.previewers.map(previewer => previewer.clearPreview()));
83
- this.previewers = [];
84
- // Create sources and previewers using references
85
- const sources = getSources(record);
86
- const previewOptions = { fillColor: this.fillColor, lineColor: this.lineColor, opacity: this.previewOpacity / 100 };
87
- const previewers = await getMapPreviewers(sources, this.map, previewOptions);
88
- // Preview each source
89
- for (const previewer of previewers) {
90
- this.previewers.push(previewer);
91
- await previewer.preview();
129
+ try {
130
+ // Close popup if one is open
131
+ this.clearFeatureSelection();
132
+ this.destroyPopup();
133
+ // Clear existing preview
134
+ if (this.previewer) {
135
+ await this.previewer.clearPreview();
136
+ this.previewer = undefined;
137
+ }
138
+ // Get the appropriate previewer for our source and preview it
139
+ this.previewer = await this.getMapPreviewer(this.map);
140
+ if (!this.previewer)
141
+ throw new Error(`No previewer found for source: ${source.constructor.name}`);
142
+ await this.previewer.preview();
143
+ // Fit to bounds from the record; the spinner stays up until the map finishes moving
144
+ const bounds = await this.source.getBounds();
145
+ if (bounds)
146
+ await this.fitMapBounds(bounds);
147
+ }
148
+ catch (error) {
149
+ console.error(`Error previewing source ${source.url}:`, error);
150
+ if (!this.errorReported) {
151
+ this.errorReported = true;
152
+ this.previewError.emit(referenceError(error, source.label(), source.url));
153
+ }
92
154
  }
93
- // Fit to bounds from the record
94
- const bounds = record.getBounds();
95
- if (bounds)
96
- await this.fitMapBounds(bounds);
97
- this.mapIdle.emit();
155
+ finally {
156
+ this.mapIdle.emit();
157
+ }
158
+ }
159
+ // Surface MapLibre errors tied to the current preview source, skipping the
160
+ // noise from basemap/glyph/sprite loads, and deduped to a single alert per load attempt.
161
+ handleMapError(event) {
162
+ if (this.errorReported || !this.source || !this.previewer)
163
+ return;
164
+ if (event.sourceId !== this.previewer.sourceId)
165
+ return;
166
+ this.errorReported = true;
167
+ this.previewError.emit(referenceError(event.error, this.source.label(), this.source.url));
98
168
  }
99
- // Fit the map to the provided bounds
169
+ // Fit the map to the given bounds; resolve once the move finishes. Guard the case where the bounds
170
+ // can't produce a camera - e.g. sidebar padding wider than the viewport, or a hidden, zero-size
171
+ // inactive tab panel - because then fitBounds won't move, 'moveend' never fires, and awaiting this
172
+ // promise (and the loading state that depends on it) would hang forever.
100
173
  async fitMapBounds(bounds) {
174
+ if (!this.map.cameraForBounds(bounds))
175
+ return;
101
176
  return new Promise(resolve => {
102
177
  this.map.once('moveend', () => resolve());
103
- this.map.fitBounds(bounds, { padding: this.padding });
178
+ this.map.fitBounds(bounds);
104
179
  });
105
180
  }
106
181
  // When padding is changed, move the map over to make room for the sidebar
@@ -111,20 +186,80 @@ export class OgmMap {
111
186
  async easeMapTo(options) {
112
187
  return await this.map.easeTo(options);
113
188
  }
114
- // Base map style based on the theme
115
- get baseMapStyle() {
116
- return this.theme === 'dark' ? 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json' : 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';
189
+ // Use the crosshair cursor if there's something to inspect
190
+ handleHover(event) {
191
+ const features = this.map.queryRenderedFeatures(event.point, { layers: this.previewLayers });
192
+ if (features.length > 0) {
193
+ this.map.getCanvas().style.cursor = 'crosshair';
194
+ this.hoverFeature(features[0]);
195
+ }
196
+ else {
197
+ this.map.getCanvas().style.cursor = '';
198
+ this.clearHoveredFeature();
199
+ }
200
+ }
201
+ // Show the attributes popup on click
202
+ handleClick(event) {
203
+ // Clear any existing popup and feature selection
204
+ this.clearFeatureSelection();
205
+ this.destroyPopup();
206
+ // Check if we have feature(s) to inspect at the clicked point
207
+ const features = this.map.queryRenderedFeatures(event.point, { layers: this.previewLayers });
208
+ if (features.length === 0)
209
+ return;
210
+ // Create and populate the attributes popup and select the first feature if multiple
211
+ this.attributesEl.features = features;
212
+ this.createPopup(event.lngLat);
213
+ this.selectFeature(features[0]);
214
+ }
215
+ // Listen to selection events from the popup and highlight the selected feature
216
+ handleFeatureSelected(event) {
217
+ this.clearFeatureSelection();
218
+ const feature = event.detail;
219
+ this.selectFeature(feature);
220
+ }
221
+ // Reset styling of all features to unselected state
222
+ clearFeatureSelection() {
223
+ this.attributesEl.features.forEach(feature => {
224
+ this.map.setFeatureState({ source: feature.source, id: feature.id, sourceLayer: feature.sourceLayer }, { selected: false });
225
+ });
226
+ this.attributesEl.features = [];
227
+ }
228
+ // Set styling of a single feature to selected state
229
+ selectFeature(feature) {
230
+ this.map.setFeatureState({ source: feature.source, id: feature.id, sourceLayer: feature.sourceLayer }, { selected: true });
231
+ }
232
+ // Set styling of a single feature to hovered state
233
+ hoverFeature(feature) {
234
+ this.clearHoveredFeature();
235
+ this.hoveredFeature = feature;
236
+ this.map.setFeatureState({ source: feature.source, id: feature.id, sourceLayer: feature.sourceLayer }, { hover: true });
237
+ }
238
+ // Clear the hovered feature state
239
+ clearHoveredFeature() {
240
+ if (this.hoveredFeature) {
241
+ this.map.setFeatureState({ source: this.hoveredFeature.source, id: this.hoveredFeature.id, sourceLayer: this.hoveredFeature.sourceLayer }, { hover: false });
242
+ this.hoveredFeature = undefined;
243
+ }
117
244
  }
118
- // Fill colors for vector data based on the theme
119
- get fillColor() {
120
- return window.getComputedStyle(this.el).getPropertyValue('--sl-color-primary-500');
245
+ // Create a new popup and set its content and location
246
+ createPopup(location) {
247
+ this.popup = new maplibregl.Popup({ maxWidth: 'none' }).setDOMContent(this.attributesEl).setLngLat(location).addTo(this.map);
248
+ this.popup.on('close', this.clearFeatureSelection.bind(this));
121
249
  }
122
- // Line/stroke color for vector data based on the theme
123
- get lineColor() {
124
- return window.getComputedStyle(this.el).getPropertyValue('--sl-color-neutral-900');
250
+ // Remove popup from the map and clear the reference
251
+ destroyPopup() {
252
+ if (this.popup) {
253
+ this.popup.remove();
254
+ this.popup = undefined;
255
+ }
256
+ }
257
+ // Get the IDs of all layers in the previewers
258
+ get previewLayers() {
259
+ return this.previewer?.layerIds || [];
125
260
  }
126
261
  render() {
127
- return h("div", { key: 'f56397cdabf7610e7bf796920e34c8ae6b9943a9', id: "map", class: `sl-theme-${this.theme}` });
262
+ return h("div", { key: '9519610af20287a4d7e28470c715accc5ffb8430', id: "map", class: `wa-${this.theme}` });
128
263
  }
129
264
  static get is() { return "ogm-map"; }
130
265
  static get encapsulation() { return "shadow"; }
@@ -140,18 +275,16 @@ export class OgmMap {
140
275
  }
141
276
  static get properties() {
142
277
  return {
143
- "record": {
278
+ "source": {
144
279
  "type": "unknown",
145
280
  "mutable": false,
146
281
  "complexType": {
147
- "original": "OgmRecord",
148
- "resolved": "OgmRecord",
282
+ "original": "Source",
283
+ "resolved": "Source",
149
284
  "references": {
150
- "OgmRecord": {
151
- "location": "import",
152
- "path": "../../lib/record",
153
- "id": "src/lib/record.ts::OgmRecord",
154
- "referenceLocation": "OgmRecord"
285
+ "Source": {
286
+ "location": "global",
287
+ "id": "global::Source"
155
288
  }
156
289
  }
157
290
  },
@@ -183,26 +316,6 @@ export class OgmMap {
183
316
  "reflect": false,
184
317
  "attribute": "theme"
185
318
  },
186
- "previewOpacity": {
187
- "type": "number",
188
- "mutable": false,
189
- "complexType": {
190
- "original": "number",
191
- "resolved": "number",
192
- "references": {}
193
- },
194
- "required": false,
195
- "optional": false,
196
- "docs": {
197
- "tags": [],
198
- "text": ""
199
- },
200
- "getter": false,
201
- "setter": false,
202
- "reflect": false,
203
- "attribute": "preview-opacity",
204
- "defaultValue": "100"
205
- },
206
319
  "padding": {
207
320
  "type": "number",
208
321
  "mutable": false,
@@ -256,13 +369,35 @@ export class OgmMap {
256
369
  "resolved": "void",
257
370
  "references": {}
258
371
  }
372
+ }, {
373
+ "method": "previewError",
374
+ "name": "previewError",
375
+ "bubbles": true,
376
+ "cancelable": true,
377
+ "composed": true,
378
+ "docs": {
379
+ "tags": [],
380
+ "text": ""
381
+ },
382
+ "complexType": {
383
+ "original": "PreviewError",
384
+ "resolved": "PreviewError",
385
+ "references": {
386
+ "PreviewError": {
387
+ "location": "import",
388
+ "path": "../../lib/errors",
389
+ "id": "src/lib/errors.ts::PreviewError",
390
+ "referenceLocation": "PreviewError"
391
+ }
392
+ }
393
+ }
259
394
  }];
260
395
  }
261
396
  static get methods() {
262
397
  return {
263
398
  "easeMapTo": {
264
399
  "complexType": {
265
- "signature": "(options: EaseToOptions) => Promise<maplibregl.Map>",
400
+ "signature": "(options: maplibregl.EaseToOptions) => Promise<maplibregl.Map>",
266
401
  "parameters": [{
267
402
  "name": "options",
268
403
  "type": "AnimationOptions & CenterZoomBearing & { pitch?: number; roll?: number; elevation?: number; } & { delayEndEvents?: number; padding?: number | PaddingOptions; around?: LngLatLike; easeId?: string; noMoveStart?: boolean; }",
@@ -276,12 +411,6 @@ export class OgmMap {
276
411
  "maplibregl": {
277
412
  "location": "global",
278
413
  "id": "global::maplibregl"
279
- },
280
- "EaseToOptions": {
281
- "location": "import",
282
- "path": "maplibre-gl",
283
- "id": "node_modules::EaseToOptions",
284
- "referenceLocation": "EaseToOptions"
285
414
  }
286
415
  },
287
416
  "return": "Promise<Map$1>"
@@ -296,11 +425,20 @@ export class OgmMap {
296
425
  static get elementRef() { return "el"; }
297
426
  static get watchers() {
298
427
  return [{
299
- "propName": "record",
300
- "methodName": "previewRecord"
428
+ "propName": "source",
429
+ "methodName": "previewSource"
301
430
  }, {
302
431
  "propName": "padding",
303
432
  "methodName": "onPaddingChange"
304
433
  }];
305
434
  }
435
+ static get listeners() {
436
+ return [{
437
+ "name": "featureSelected",
438
+ "method": "handleFeatureSelected",
439
+ "target": "body",
440
+ "capture": false,
441
+ "passive": false
442
+ }];
443
+ }
306
444
  }
@@ -1,38 +1,40 @@
1
1
  .menubar {
2
2
  display: flex;
3
3
  align-items: center;
4
- gap: var(--sl-spacing-small);
5
- padding: var(--sl-spacing-x-small);
6
- background-color: var(--sl-panel-background-color);
7
- border-top: calc(var(--sl-panel-border-width) * 2) solid var(--accent-color);
8
- border-bottom: var(--sl-panel-border-width) solid var(--sl-panel-border-color);
4
+ gap: var(--wa-space-s);
5
+ padding: var(--wa-space-xs);
6
+ background-color: var(--wa-color-surface-raised);
7
+ border-top: calc(var(--wa-panel-border-width) * 2) solid var(--accent-color);
8
+ border-bottom: var(--wa-panel-border-width) solid var(--wa-color-surface-border);
9
9
 
10
10
  /* shadow above map and sidebar */
11
11
  position: relative;
12
12
  z-index: 701;
13
- box-shadow: 0 2px 6px 0px var(--sl-overlay-background-color);
13
+ box-shadow: 0 2px 6px 0px var(--wa-color-shadow);
14
14
  }
15
15
 
16
16
  /* Truncate long titles with an ellipsis */
17
17
  .title {
18
18
  flex: auto;
19
- font-size: var(--sl-font-size-large);
20
- color: var(--sl-color-neutral-800);
19
+ font-size: var(--wa-font-size-l);
20
+ color: var(--wa-color-text-normal);
21
21
  text-overflow: ellipsis;
22
22
  overflow: hidden;
23
23
  white-space: nowrap;
24
24
  }
25
25
 
26
- .menu-button {
27
- font-size: var(--sl-font-size-x-large);
26
+ /* Size the icon glyph directly rather than the button's own font-size, which would inflate
27
+ the button's whole clickable box (wa-button scales its control height off font-size) */
28
+ .menu-button wa-icon {
29
+ font-size: var(--wa-font-size-xl);
28
30
  }
29
31
 
30
32
  .restricted-icon {
31
- color: var(--sl-color-danger-600);
32
- font-size: var(--sl-font-size-large);
33
+ color: var(--wa-color-danger-on-normal);
34
+ font-size: var(--wa-font-size-l);
33
35
  }
34
36
 
35
37
  .loading-spinner {
36
- font-size: var(--sl-font-size-large);
37
- margin: 0 var(--sl-spacing-x-small);
38
+ font-size: var(--wa-font-size-l);
39
+ margin: 0 var(--wa-space-xs);
38
40
  }
@@ -5,7 +5,7 @@ export class OgmMenubar {
5
5
  sidebarToggled;
6
6
  loading = false;
7
7
  render() {
8
- return (h("div", { key: 'b9f3fa82a91afe42eacafa5c6f4e7041107f9317', class: `menubar ${this.theme && `sl-theme-${this.theme}`}` }, h("sl-icon-button", { key: '299008820e057cdadafa6be1c40cc51d99f1afb5', name: "list", label: "Open sidebar", class: "menu-button", onclick: this.sidebarToggled.emit }), this.record?.restricted && (h("sl-tooltip", { key: 'c676c4cf06c136d6ca055b567f4318f2678b323a', content: "Restricted access" }, h("sl-icon", { key: 'bbad7996a1e27d694309d8e5dd8146ba4cb053e0', name: "lock-fill", label: "Restricted access", class: "restricted-icon" }))), h("div", { key: 'ad8ba00f9b3a6b93fcf248d217ecc99f798645a8', class: "title" }, this.record?.title), this.loading && h("sl-spinner", { key: 'fa5b8f92939601e0afed5bfa85b99aad4db221f5', class: "loading-spinner" })));
8
+ return (h("div", { key: '437b8f1b6f44844f1db4460c6597c0a68479a8f7', class: `menubar ${this.theme && `wa-${this.theme}`}` }, h("wa-button", { key: 'baf0a0baa96fcf3f114fdf47b4446b90df862c6b', appearance: "plain", class: "menu-button", onclick: this.sidebarToggled.emit }, h("wa-icon", { key: 'b4e1c07bd7284dbd191997a4b270d12c1e1d5b63', name: "list", label: "Open sidebar", canvas: "auto" })), this.record?.restricted && (h(h.Fragment, null, h("wa-tooltip", { key: '4e8d318982e7bd8af99b320e4810b23d0c00b264', for: "restricted-icon" }, "Restricted access"), h("wa-icon", { key: '6da389ec7b55697d203dd2935af192ae64c0622b', id: "restricted-icon", name: "lock-fill", label: "Restricted access", class: "restricted-icon", canvas: "auto" }))), h("div", { key: 'd118cbb176a44ae761d82c7adff2b0fa31cbe87e', class: "title" }, this.record?.title), this.loading && h("wa-spinner", { key: '5bc6c254a1fa5b4b4bb82ec8fc1cc04ff1457a31', class: "loading-spinner" })));
9
9
  }
10
10
  static get is() { return "ogm-menubar"; }
11
11
  static get encapsulation() { return "shadow"; }
@@ -29,10 +29,8 @@ export class OgmMenubar {
29
29
  "resolved": "OgmRecord",
30
30
  "references": {
31
31
  "OgmRecord": {
32
- "location": "import",
33
- "path": "../../lib/record",
34
- "id": "src/lib/record.ts::OgmRecord",
35
- "referenceLocation": "OgmRecord"
32
+ "location": "global",
33
+ "id": "global::OgmRecord"
36
34
  }
37
35
  }
38
36
  },
@@ -1,5 +1,5 @@
1
1
  import { render, describe, it, expect, h } from "@stencil/vitest";
2
- import { OgmRecord } from "../../lib/record";
2
+ import OgmRecord from "../../lib/record";
3
3
  describe('ogm-menubar', () => {
4
4
  describe('with a record', () => {
5
5
  it('renders the record title', async () => {
@@ -17,7 +17,9 @@ describe('ogm-menubar', () => {
17
17
  <ogm-menubar class="hydrated">
18
18
  <mock:shadow-root>
19
19
  <div class="menubar undefined">
20
- <sl-icon-button name="list" label="Open sidebar" class="menu-button"></sl-icon-button>
20
+ <wa-button appearance="plain" class="menu-button">
21
+ <wa-icon name="list" label="Open sidebar" canvas="auto"></wa-icon>
22
+ </wa-button>
21
23
  <div class="title">
22
24
  Coho Salmon Watersheds: San Francisco Bay Area, California, 2011
23
25
  </div>
@@ -34,7 +36,9 @@ describe('ogm-menubar', () => {
34
36
  <ogm-menubar class="hydrated">
35
37
  <mock:shadow-root>
36
38
  <div class="menubar undefined">
37
- <sl-icon-button name="list" label="Open sidebar" class="menu-button"></sl-icon-button>
39
+ <wa-button appearance="plain" class="menu-button">
40
+ <wa-icon name="list" label="Open sidebar" canvas="auto"></wa-icon>
41
+ </wa-button>
38
42
  <div class="title"></div>
39
43
  </div>
40
44
  </mock:shadow-root>
@@ -42,4 +46,25 @@ describe('ogm-menubar', () => {
42
46
  `);
43
47
  });
44
48
  });
49
+ describe('loading indicator', () => {
50
+ it('shows the spinner while loading', async () => {
51
+ const { root } = await render(h("ogm-menubar", { loading: true }));
52
+ const shadowRoot = root.shadowRoot;
53
+ expect(shadowRoot.querySelector('.loading-spinner')).not.toBeNull();
54
+ });
55
+ it('hides the spinner when not loading', async () => {
56
+ const { root } = await render(h("ogm-menubar", { loading: false }));
57
+ const shadowRoot = root.shadowRoot;
58
+ expect(shadowRoot.querySelector('.loading-spinner')).toBeNull();
59
+ });
60
+ it('toggles the spinner when the loading prop changes', async () => {
61
+ const { root, setProps } = await render(h("ogm-menubar", { loading: false }));
62
+ const shadowRoot = root.shadowRoot;
63
+ expect(shadowRoot.querySelector('.loading-spinner')).toBeNull();
64
+ await setProps({ loading: true });
65
+ expect(shadowRoot.querySelector('.loading-spinner')).not.toBeNull();
66
+ await setProps({ loading: false });
67
+ expect(shadowRoot.querySelector('.loading-spinner')).toBeNull();
68
+ });
69
+ });
45
70
  });