geoblacklight 5.2.0 → 5.4.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +4 -4
  3. data/app/assets/stylesheets/geoblacklight/modules/item.scss +20 -3
  4. data/app/components/geoblacklight/accordion_component.html.erb +16 -0
  5. data/app/components/geoblacklight/accordion_component.rb +11 -0
  6. data/app/components/geoblacklight/attribute_table_component.html.erb +15 -17
  7. data/app/components/geoblacklight/document/sidebar_component.html.erb +3 -0
  8. data/app/components/geoblacklight/document/sidebar_component.rb +7 -0
  9. data/app/components/geoblacklight/download_links_component.html.erb +3 -0
  10. data/app/components/geoblacklight/download_links_component.rb +1 -1
  11. data/app/components/geoblacklight/index_map_inspect_component.html.erb +4 -3
  12. data/app/components/geoblacklight/item_map_viewer_component.rb +2 -2
  13. data/app/components/geoblacklight/location_leaflet_map_component.rb +2 -2
  14. data/app/components/geoblacklight/relations_component.html.erb +1 -1
  15. data/app/components/geoblacklight/static_map_component.rb +2 -2
  16. data/app/controllers/download_controller.rb +6 -0
  17. data/app/controllers/relation_controller.rb +1 -1
  18. data/app/helpers/geoblacklight_helper.rb +31 -3
  19. data/app/javascript/geoblacklight/controllers/leaflet_viewer_controller.js +35 -6
  20. data/app/javascript/geoblacklight/controllers/openlayers_viewer_controller.js +33 -2
  21. data/app/javascript/geoblacklight/leaflet/basemaps.js +0 -32
  22. data/app/javascript/geoblacklight/leaflet/controls/sidebar.js +47 -0
  23. data/app/javascript/geoblacklight/leaflet/inspection.js +5 -4
  24. data/app/javascript/geoblacklight/openlayers/basemaps.js +0 -20
  25. data/app/javascript/geoblacklight/openlayers/layers.js +1 -1
  26. data/app/models/concerns/geoblacklight/solr_document.rb +19 -2
  27. data/app/views/catalog/_metadata.html.erb +3 -0
  28. data/config/importmap.rb +4 -10
  29. data/config/locales/geoblacklight.en.yml +1 -0
  30. data/geoblacklight.gemspec +2 -2
  31. data/lib/generators/geoblacklight/assets/importmap_generator.rb +10 -7
  32. data/lib/generators/geoblacklight/assets/vite_generator.rb +9 -2
  33. data/lib/generators/geoblacklight/assets_generator.rb +2 -2
  34. data/lib/generators/geoblacklight/templates/assets/application.scss +1 -1
  35. data/lib/generators/geoblacklight/templates/catalog_controller.rb +4 -5
  36. data/lib/generators/geoblacklight/templates/settings.yml +20 -0
  37. data/lib/geoblacklight/deprecated_configuration.rb +665 -0
  38. data/lib/geoblacklight/download.rb +3 -0
  39. data/lib/geoblacklight/engine.rb +13 -0
  40. data/lib/geoblacklight/references.rb +6 -1
  41. data/lib/geoblacklight/relation/ancestors.rb +3 -0
  42. data/lib/geoblacklight/relation/descendants.rb +3 -0
  43. data/lib/geoblacklight/relation/relation_response.rb +4 -1
  44. data/lib/geoblacklight/version.rb +1 -1
  45. data/lib/geoblacklight.rb +16 -0
  46. data/lib/tasks/geoblacklight.rake +9 -0
  47. data/package.json +4 -7
  48. data/schema/geoblacklight-schema-aardvark.json +2 -2
  49. data/solr/conf/solrconfig.xml +10 -10
  50. data/spec/features/configurable_basemap_spec.rb +68 -0
  51. data/spec/features/full_screen_control_spec.rb +4 -6
  52. data/spec/features/relations_spec.rb +2 -3
  53. data/spec/features/saved_searches_spec.rb +1 -8
  54. data/spec/lib/geoblacklight/deprecated_configuration_spec.rb +680 -0
  55. data/spec/solr_config_spec.rb +41 -0
  56. data/spec/spec_helper.rb +24 -1
  57. data/vite.config.ts +0 -3
  58. metadata +15 -10
  59. data/test/leaflet/layers.test.js +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32b1f6b2ee7ddbd7ba16adc14ddad99cb87a9294004dfe7337ed0084626d1202
4
- data.tar.gz: 3dfb956c7f0f3236671a65d9b3b8aea8135f473a388236b8879b0fdca064e310
3
+ metadata.gz: 1ece9d6b515fa64010cbc158c72b17ae35e019a842e83700e68650f7d6cb0a98
4
+ data.tar.gz: 268d83c2130ce59ac765ec0764164e3c081d7e62beedfe8494968ce8a74ba22c
5
5
  SHA512:
6
- metadata.gz: a8f1c491b83df2cf7c3ca22b8239c718697eb9e9aa3596255e2bd2c6b59ee85d79ee04bbc70c8fac9fe9bd20f9d9ed56519184898d4e6bfa21adc3a2b7261728
7
- data.tar.gz: 24f5d3148d558ffe6a2c8845419228bc51bf64d7277fa1ae7aad8e920341851cf02a91cb699cc7080dd7a27ea6c62d415d91620b0744b92bd9d8148cbe917362
6
+ metadata.gz: ee73a4e5fb22a94dd6b0f6010ec8259b360ebec2b99c007545b1bfedecc52a9e4296eee9d40af1d125d8771e11d15d9997bd9c8ac3deec5d05630fac542c0cad
7
+ data.tar.gz: e8658148c4a9289b38ed5258ad50b9538f8fea43d2d1733d0d05bf46fa0fe5e1868c291f62d0a771d3f3165b829d109fff5d6700e06638c871fe15c444bf38b7
@@ -39,13 +39,13 @@ jobs:
39
39
  matrix:
40
40
  ruby_version: ["3.2", "3.3", "3.4"]
41
41
  rails_version: ["7.2.3", "8.1.1"]
42
- asset_pipeline: ["vite"]
43
- additional_engine_cart_rails_options: ["--js rollup"]
42
+ asset_pipeline: ["importmap"]
43
+ additional_engine_cart_rails_options: ["--js importmap"]
44
44
  include:
45
45
  - ruby_version: "3.4"
46
46
  rails_version: "8.1.1"
47
- asset_pipeline: "importmap"
48
- additional_engine_cart_rails_options: "--js importmap"
47
+ asset_pipeline: "vite"
48
+ additional_engine_cart_rails_options: "--js rollup"
49
49
 
50
50
  name: test ruby ${{ matrix.ruby_version }} / rails ${{ matrix.rails_version }} / ${{ matrix.asset_pipeline }}
51
51
  steps:
@@ -49,13 +49,30 @@
49
49
  -webkit-line-clamp: 8;
50
50
  }
51
51
 
52
- #table-container{
53
- overflow:scroll;
54
- max-height: 450px;
52
+ #table-container {
55
53
  margin-bottom: 1rem;
56
54
 
57
55
  table {
58
56
  margin-bottom: 0px;
59
57
  }
60
58
  }
59
+
60
+ .attribute-data {
61
+ --bs-accordion-active-bg: none;
62
+ }
63
+
64
+ .sidebar-container {
65
+ width: 25vw;
66
+ overflow: scroll;
67
+ overscroll-behavior: contain;
68
+
69
+ img {
70
+ width: 100%;
71
+ }
72
+
73
+ @media screen and (max-width: breakpoint-max(sm)) {
74
+ width: calc(100vw - 100px);
75
+ max-height: 20vh!important;
76
+ }
77
+ }
61
78
  }
@@ -0,0 +1,16 @@
1
+ <div data-sidebar="<%= Settings.LEAFLET.SIDEBAR %>">
2
+ <div class="accordion attribute-data<% if Settings.LEAFLET.SIDEBAR %> sidebar-container<% end %>">
3
+ <div class="accordion-item">
4
+ <h2 class="accordion-header" id="accordionHeading">
5
+ <button class="accordion-button p-2 border-none bg-none" type="button" data-bs-toggle="collapse" data-bs-target="#<%= @id %>" aria-expanded="true" aria-controls="<%= @id %>">
6
+ <%= @title %>
7
+ </button>
8
+ </h2>
9
+ <div id="<%= @id %>" class="accordion-collapse collapse show" aria-labelledby="accordionHeading" data-bs-parent="#accordionHeading">
10
+ <div class="accordion-body p-0">
11
+ <%= content %>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </div>
16
+ </div>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geoblacklight
4
+ class AccordionComponent < ViewComponent::Base
5
+ def initialize(id:, title:)
6
+ @id = id
7
+ @title = title
8
+ super()
9
+ end
10
+ end
11
+ end
@@ -1,17 +1,15 @@
1
- <div class='row'>
2
- <div id='table-container' class='col-md-12'>
3
- <table id="attribute-table" class="table table-hover table-condensed table-striped table-bordered col-md-12">
4
- <thead>
5
- <tr>
6
- <th><%= t('geoblacklight.show.attribute') %></th>
7
- <th><%= t('geoblacklight.show.value') %></th>
8
- </tr>
9
- </thead>
10
- <tbody class='attribute-table-body'>
11
- <tr>
12
- <td class='default-text' colspan='2'><em><%= t('geoblacklight.show.click_map') %></em></td>
13
- </tr>
14
- </tbody>
15
- </table>
16
- </div>
17
- </div>
1
+ <%= render Geoblacklight::AccordionComponent.new(id: 'attribute-table', title: t('geoblacklight.show.table_name')) do %>
2
+ <table id="attribute-table" class="table table-hover table-condensed table-striped mb-0 collapse show">
3
+ <thead>
4
+ <tr>
5
+ <th><%= t('geoblacklight.show.attribute') %></th>
6
+ <th><%= t('geoblacklight.show.value') %></th>
7
+ </tr>
8
+ </thead>
9
+ <tbody class='attribute-table-body'>
10
+ <tr>
11
+ <td class='default-text' colspan='2'><em><%= t('geoblacklight.show.click_map') %></em></td>
12
+ </tr>
13
+ </tbody>
14
+ </table>
15
+ <% end %>
@@ -1,3 +1,5 @@
1
+ <%# Rendered by GeoBlacklight itself, so the deprecated methods it calls are silenced here; an application that overrides them still gets warned when it calls them. %>
2
+ <% Geoblacklight.deprecation.silence do %>
1
3
  <%= render_static_map_component %>
2
4
  <%= render_show_tools %>
3
5
 
@@ -10,3 +12,4 @@
10
12
  <%= render_more_like_this %>
11
13
 
12
14
  <%= render "relations_container", document: document %>
15
+ <% end %>
@@ -3,10 +3,12 @@
3
3
  module Geoblacklight
4
4
  module Document
5
5
  class SidebarComponent < Blacklight::Document::SidebarComponent
6
+ # @deprecated
6
7
  def render_static_map_component
7
8
  render Geoblacklight::StaticMapComponent.new(document:)
8
9
  end
9
10
 
11
+ # @deprecated
10
12
  def sidebar_buttons
11
13
  [
12
14
  Geoblacklight::WebServicesLinkComponent.new(document:),
@@ -15,9 +17,14 @@ module Geoblacklight
15
17
  ]
16
18
  end
17
19
 
20
+ # @deprecated
18
21
  def render_more_like_this
19
22
  render Blacklight::Document::MoreLikeThisComponent.new(document:)
20
23
  end
24
+ Geoblacklight.deprecation.deprecate_methods(Geoblacklight::Document::SidebarComponent,
25
+ render_static_map_component: "GeoBlacklight 6 builds the sidebar from a single #panels method",
26
+ sidebar_buttons: "GeoBlacklight 6 builds the sidebar from a single #panels method",
27
+ render_more_like_this: "GeoBlacklight 6 builds the sidebar from a single #panels method")
21
28
  end
22
29
  end
23
30
  end
@@ -1,3 +1,5 @@
1
+ <%# Removed in GeoBlacklight 6. The deprecated download API it calls is silenced here so that a stock application is not warned once per request. %>
2
+ <% Geoblacklight.deprecation.silence do %>
1
3
  <div data-controller="downloads" class="downloads-sidebar">
2
4
  <%# the dropdown-toggle class gives us the little arrow %>
3
5
  <button type="button" class="btn btn-primary dropdown-toggle sidebar-btn" id="downloads-button" data-bs-toggle="collapse" data-bs-target="#downloads-collapse" aria-expanded="false" aria-controls="downloads-collapse">
@@ -29,3 +31,4 @@
29
31
  </div>
30
32
  </div>
31
33
  </div>
34
+ <% end %>
@@ -11,7 +11,7 @@ module Geoblacklight
11
11
  end
12
12
 
13
13
  def render?
14
- downloadable? && (document.direct_download.present? || document.iiif_download.present? || document.download_types.present?)
14
+ Geoblacklight.deprecation.silence { downloadable? && (document.direct_download.present? || document.iiif_download.present? || document.download_types.present?) }
15
15
  end
16
16
 
17
17
  def downloadable?
@@ -1,4 +1,5 @@
1
- <div class='row'>
2
- <div class='viewer-information col-sm-12'>
1
+ <%= render Geoblacklight::AccordionComponent.new(id: 'index-map-attributes', title: t('geoblacklight.show.table_name')) do %>
2
+ <div class='viewer-information px-3'>
3
+ <em><%= t('geoblacklight.show.click_map') %></em>
3
4
  </div>
4
- </div>
5
+ <% end %>
@@ -84,12 +84,12 @@ module Geoblacklight
84
84
  data: {
85
85
  "controller" => viewer_name,
86
86
  "#{viewer_name}-available-value" => helpers.document_available?(@document),
87
- "#{viewer_name}-basemap-value" => helpers.geoblacklight_basemap,
87
+ "#{viewer_name}-basemap-value" => Geoblacklight.deprecation.silence { helpers.geoblacklight_basemap },
88
88
  "#{viewer_name}-protocol-value" => protocol,
89
89
  "#{viewer_name}-url-value" => @document.viewer_endpoint,
90
90
  "#{viewer_name}-map-geom-value" => @document.geometry.geojson,
91
91
  "#{viewer_name}-layer-id-value" => @document.wxs_identifier,
92
- "#{viewer_name}-options-value" => helpers.leaflet_options,
92
+ "#{viewer_name}-options-value" => Geoblacklight.deprecation.silence { helpers.leaflet_options },
93
93
  "#{viewer_name}-page-value" => params[:action]&.upcase,
94
94
  "#{viewer_name}-draw-initial-bounds-value" => true
95
95
  }.compact)
@@ -26,7 +26,7 @@ module Geoblacklight
26
26
 
27
27
  # Add top-level geosearch control to leaflet options if configured
28
28
  def leaflet_options
29
- options = helpers.leaflet_options.deep_dup
29
+ options = Geoblacklight.deprecation.silence { helpers.leaflet_options }.deep_dup
30
30
  options["CONTROLS"] = {"Geosearch" => @geosearch} if @geosearch
31
31
  options.to_json
32
32
  end
@@ -37,7 +37,7 @@ module Geoblacklight
37
37
  class: @classes,
38
38
  data: {
39
39
  "controller" => "leaflet-viewer",
40
- "leaflet-viewer-basemap-value" => helpers.geoblacklight_basemap,
40
+ "leaflet-viewer-basemap-value" => Geoblacklight.deprecation.silence { helpers.geoblacklight_basemap },
41
41
  "leaflet-viewer-map-geom-value" => search_bbox || @map_geometry,
42
42
  "leaflet-viewer-data-map-value" => @data_map,
43
43
  "leaflet-viewer-page-value" => params[:action]&.upcase,
@@ -7,7 +7,7 @@
7
7
  <% relationship_type_results['docs'][0..2].map { |hash| SolrDocument.new(hash) }.each do |doc| %>
8
8
  <li class="list-group-item border-bottom-0">
9
9
  <%= link_to solr_document_path(doc) do %>
10
- <%= helpers.relations_icon(doc, rel_type_info.icon) unless rel_type_info.icon.nil? %>
10
+ <%= Geoblacklight.deprecation.silence { helpers.relations_icon(doc, rel_type_info.icon) } unless rel_type_info.icon.nil? %>
11
11
  <%= doc.title %>
12
12
  <% end %>
13
13
  </li>
@@ -18,13 +18,13 @@ module Geoblacklight
18
18
  end
19
19
 
20
20
  def viewer_tag
21
- leaflet_options = helpers.leaflet_options.deep_dup
21
+ leaflet_options = Geoblacklight.deprecation.silence { helpers.leaflet_options }.deep_dup
22
22
  leaflet_options.SLEEP.SLEEP = false
23
23
  tag.div(nil,
24
24
  id: "static-map",
25
25
  data: {
26
26
  "controller" => "leaflet-viewer",
27
- "leaflet-viewer-basemap-value" => helpers.geoblacklight_basemap,
27
+ "leaflet-viewer-basemap-value" => Geoblacklight.deprecation.silence { helpers.geoblacklight_basemap },
28
28
  "leaflet-viewer-page-value" => "STATIC_MAP",
29
29
  "leaflet-viewer-map-geom-value" => @document.geometry.geojson,
30
30
  "leaflet-viewer-options-value" => leaflet_options,
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DownloadController < ApplicationController
4
+ before_action do
5
+ Geoblacklight.deprecation.warn(
6
+ "DownloadController is deprecated; GeoBlacklight 6 removes the generated download subsystem: downloads are direct links only, rendered by Geoblacklight::Document::DownloadLinksComponent"
7
+ )
8
+ end
9
+
4
10
  # include Blacklight::SearchHelper
5
11
  include Blacklight::Catalog
6
12
 
@@ -6,7 +6,7 @@ class RelationController < ApplicationController
6
6
  copy_blacklight_config_from(CatalogController)
7
7
 
8
8
  def index
9
- @relations = Geoblacklight::Relation::RelationResponse.new(params[:id], repository)
9
+ @relations = Geoblacklight.deprecation.silence { Geoblacklight::Relation::RelationResponse.new(params[:id], repository) }
10
10
  render layout: !request.xhr?
11
11
  end
12
12
 
@@ -9,6 +9,7 @@ module GeoblacklightHelper
9
9
  # Blacklight catalog controller helper method to truncate field value to 150 chars
10
10
  # @param [SolrDocument] args
11
11
  # @return [String]
12
+ # @deprecated
12
13
  def snippit(args)
13
14
  truncate(Array(args[:value]).flatten.join(" "), length: 150)
14
15
  end
@@ -40,6 +41,7 @@ module GeoblacklightHelper
40
41
  # div. Arguments come from Blacklight::DocumentPresenter's
41
42
  # get_field_values method
42
43
  # @param [Hash] args from get_field_values
44
+ # @deprecated
43
45
  def render_value_as_truncate_abstract(args)
44
46
  tag.div class: "truncate-abstract" do
45
47
  Array(args[:value]).flatten.join(" ")
@@ -49,6 +51,7 @@ module GeoblacklightHelper
49
51
  ##
50
52
  # Selects the basemap used for map displays
51
53
  # @return [String]
54
+ # @deprecated
52
55
  def geoblacklight_basemap
53
56
  blacklight_config.basemap_provider || "positron"
54
57
  end
@@ -56,6 +59,7 @@ module GeoblacklightHelper
56
59
  ##
57
60
  # Returns a hash of the leaflet plugin settings to pass to the viewer.
58
61
  # @return[Hash]
62
+ # @deprecated
59
63
  def leaflet_options
60
64
  Settings.LEAFLET
61
65
  end
@@ -65,6 +69,7 @@ module GeoblacklightHelper
65
69
  # (Renders a partial when the metadata isn't available)
66
70
  # @param [Geoblacklight::Metadata::Base] metadata the metadata object
67
71
  # @return [String]
72
+ # @deprecated
68
73
  def render_transformed_metadata(metadata)
69
74
  render partial: "catalog/metadata/content", locals: {content: metadata.transform.html_safe}
70
75
  rescue Geoblacklight::MetadataTransformer::TransformError => transform_err
@@ -80,6 +85,7 @@ module GeoblacklightHelper
80
85
  # @param [SolrDocument] document the Solr Document for the item
81
86
  # @param [Geoblacklight::Metadata::Base] metadata the object for the metadata resource
82
87
  # @return [Boolean]
88
+ # @deprecated
83
89
  def first_metadata?(document, metadata)
84
90
  document.references.shown_metadata.first.type == metadata.type
85
91
  end
@@ -96,10 +102,22 @@ module GeoblacklightHelper
96
102
  end
97
103
 
98
104
  ## Returns the icon used based off a Settings strategy
105
+ # @deprecated
99
106
  def relations_icon(document, icon)
100
- icon_html = render Geoblacklight::HeaderIconsComponent.new(document: document, fields: [Settings.FIELDS.GEOM_TYPE]) if Settings.USE_GEOM_FOR_RELATIONS_ICON
101
- return icon_html unless !Settings.USE_GEOM_FOR_RELATIONS_ICON || icon_html.include?("icon-missing")
102
- geoblacklight_icon(icon, **{})
107
+ # If configured to use geometry type for relations icon
108
+ if Settings.USE_GEOM_FOR_RELATIONS_ICON
109
+ Geoblacklight.deprecation.warn("USE_GEOM_FOR_RELATIONS_ICON is deprecated and will be removed in GeoBlacklight 6")
110
+ icon_html = render Geoblacklight::HeaderIconsComponent.new(
111
+ document: document,
112
+ fields: [Settings.FIELDS.GEOM_TYPE]
113
+ )
114
+
115
+ # Use geometry-based icon if it's valid (not missing)
116
+ return icon_html unless icon_html.include?("icon-missing")
117
+ end
118
+
119
+ # Fall back to the default icon
120
+ geoblacklight_icon(icon)
103
121
  end
104
122
 
105
123
  ## Returns the data-page attribute value used as the JS map selector
@@ -111,4 +129,14 @@ module GeoblacklightHelper
111
129
  "index"
112
130
  end
113
131
  end
132
+
133
+ Geoblacklight.deprecation.deprecate_methods(GeoblacklightHelper,
134
+ snippit: "GeoBlacklight 6 removes it; its catalog_controller template no longer configures a " \
135
+ "truncated description index field",
136
+ render_value_as_truncate_abstract: "use Geoblacklight::MetadataDescriptionMarkdownComponent instead",
137
+ geoblacklight_basemap: "GeoBlacklight 6 reads the basemap from the viewer components themselves",
138
+ leaflet_options: "GeoBlacklight 6 reads the Leaflet options from the viewer components themselves",
139
+ render_transformed_metadata: "use Geoblacklight::MetadataComponent instead",
140
+ first_metadata?: "use Geoblacklight::MetadataComponent instead",
141
+ relations_icon: "use Geoblacklight::Relations::RelationComponent instead")
114
142
  end
@@ -1,5 +1,5 @@
1
1
  import { Icon, Control } from "leaflet";
2
- import "leaflet-fullscreen";
2
+ import { FullScreen } from "leaflet.fullscreen";
3
3
  import { layerGroup, polygon, map, tileLayer } from "leaflet";
4
4
  import { imageMapLayer } from "esri-leaflet";
5
5
  import { Controller } from "@hotwired/stimulus";
@@ -23,6 +23,7 @@ import {
23
23
  } from "geoblacklight/leaflet/layers";
24
24
  import { geoJSONToBounds } from "geoblacklight/leaflet/utils";
25
25
  import { DEFAULT_BOUNDS, DEFAULT_OPACITY, DEFAULT_GEOM_OVERLAY_OPTIONS } from "geoblacklight/leaflet/constants";
26
+ import sidebarControl from "geoblacklight/leaflet/controls/sidebar";
26
27
 
27
28
  export default class LeafletViewerController extends Controller {
28
29
  static values = {
@@ -109,13 +110,28 @@ export default class LeafletViewerController extends Controller {
109
110
 
110
111
  // Select the configured basemap to use
111
112
  getBasemap() {
112
- const basemapName = this.basemapValue || "positron";
113
- return tileLayer(basemaps[basemapName].url, {
114
- ...basemaps[basemapName],
113
+ const definition = this.basemapDefinition();
114
+ return tileLayer(definition.url, {
115
+ ...definition,
115
116
  detectRetina: this.optionsValue.LAYERS.DETECT_RETINA || false,
116
117
  });
117
118
  }
118
119
 
120
+ // The basemap to draw, with any BASEMAPS values from settings.yml merged over
121
+ // the shipped definition. This lets an application change a basemap's URL --
122
+ // to add a CARTO API key, for example -- without restating the rest of the
123
+ // definition, and lets it define basemaps of its own. Falls back to positron
124
+ // for a name that resolves to nothing rather than drawing no basemap.
125
+ basemapDefinition() {
126
+ const name = this.basemapValue || "positron";
127
+ const definition = { ...basemaps[name], ...this.basemapOverrides()[name] };
128
+ return definition.url ? definition : basemaps.positron;
129
+ }
130
+
131
+ basemapOverrides() {
132
+ return this.optionsValue.BASEMAPS || {};
133
+ }
134
+
119
135
  // Add the configured controls to the map
120
136
  addControls() {
121
137
  // Add any top-level controls (e.g. geosearch) first
@@ -146,7 +162,7 @@ export default class LeafletViewerController extends Controller {
146
162
  if (controlName == "Opacity")
147
163
  return new LayerOpacityControl(this.previewOverlay);
148
164
  if (controlName == "Fullscreen")
149
- return new Control.Fullscreen({ position: "topright", ...controlOptions });
165
+ return new FullScreen({ position: "bottomleft", ...controlOptions });
150
166
  if (controlName == "Geosearch")
151
167
  return new GeoSearchControl({ baseUrl: this.catalogBaseUrlValue, ...controlOptions });
152
168
  console.error(`Unsupported control name: "${controlName}"`);
@@ -179,10 +195,23 @@ export default class LeafletViewerController extends Controller {
179
195
  if (this.boundsOverlay) this.overlay.removeLayer(this.boundsOverlay);
180
196
  }
181
197
 
198
+ setUpSidebar() {
199
+ const sidebar = document.querySelector('[data-sidebar]')
200
+ if (!sidebar || sidebar.dataset.sidebar != 'true') return;
201
+ const control = new sidebarControl({
202
+ position: 'topright',
203
+ sidebar
204
+ });
205
+ this.map.addControl(control);
206
+ }
207
+
182
208
  addInspection() {
209
+ const inspectionProtocols = ['Wms', 'FeatureLayer', 'DynamicMapLayer', 'IndexMap']
210
+ if (!inspectionProtocols.includes(this.protocolValue)) return;
211
+ this.setUpSidebar();
183
212
  if (this.protocolValue == "Wms") return wmsInspection(this.map, this.urlValue, this.layerIdValue, this.previewOverlay);
184
213
  if (this.protocolValue == "FeatureLayer") return featureLayerInspection(this.map, this.previewOverlay);
185
- if (this.protocolValue == "DynamicMapLayer") return dynamicMapLayerInspection(this.map, this.previewOverlay, this.layerIdValue)
214
+ if (this.protocolValue == "DynamicMapLayer") return dynamicMapLayerInspection(this.map, this.previewOverlay, this.layerIdValue);
186
215
  // TODO: TiledMapLayer is converted but seems busted -- see layers.js. Don't know what to test it with, need a fixture.
187
216
  // if (this.protocolValue == "TiledMapLayer") return tiledMapLayerInspection(this.map, this.previewOverlay);
188
217
  }
@@ -14,6 +14,7 @@ export default class OpenlayersViewerController extends Controller {
14
14
  protocol: String,
15
15
  basemap: String,
16
16
  mapGeom: String,
17
+ options: Object,
17
18
  };
18
19
 
19
20
  async connect() {
@@ -55,11 +56,41 @@ export default class OpenlayersViewerController extends Controller {
55
56
 
56
57
  // Select the configured basemap to use
57
58
  getBasemap() {
58
- const basemap = basemaps[this.basemapValue || "positron"];
59
- const layer = new TileLayer({ source: new XYZ(basemap) });
59
+ const basemap = this.basemapDefinition();
60
+ const layer = new TileLayer({
61
+ source: new XYZ({
62
+ ...basemap,
63
+ url: this.normalizeUrl(basemap.url),
64
+ // The leaflet definitions spell this `attribution`; OpenLayers wants
65
+ // `attributions`. Accept either so one setting serves both viewers.
66
+ attributions: basemap.attributions || basemap.attribution,
67
+ }),
68
+ });
60
69
  return layer;
61
70
  }
62
71
 
72
+ // The basemap to draw, with any BASEMAPS values from settings.yml merged over
73
+ // the shipped definition. This lets an application change a basemap's URL --
74
+ // to add a CARTO API key, for example -- without restating the rest of the
75
+ // definition, and lets it define basemaps of its own. Falls back to positron
76
+ // for a name that resolves to nothing rather than drawing no basemap.
77
+ basemapDefinition() {
78
+ const name = this.basemapValue || "positron";
79
+ const definition = { ...basemaps[name], ...this.basemapOverrides()[name] };
80
+ return definition.url ? definition : basemaps.positron;
81
+ }
82
+
83
+ basemapOverrides() {
84
+ return this.optionsValue.BASEMAPS || {};
85
+ }
86
+
87
+ // Leaflet and OpenLayers spell tile URL templates differently, so accept the
88
+ // leaflet form a configured basemap is most likely to be written in: {s}
89
+ // becomes OpenLayers' subdomain range, and {retina} drops out.
90
+ normalizeUrl(url) {
91
+ return url.replace(/\{s\}/g, "{a-d}").replace(/\{retina\}/g, "");
92
+ }
93
+
63
94
  // Generate a layer based on the protocol
64
95
  getPreviewOverlay(protocol, url) {
65
96
  if (protocol === "Pmtiles") return pmTilesLayer(url);
@@ -23,38 +23,6 @@ export default {
23
23
  worldCopyJump: true,
24
24
  retina: "@2x",
25
25
  },
26
- worldAntique: {
27
- url: "https://cartocdn_{s}.global.ssl.fastly.net/base-antique/{z}/{x}/{y}{retina}.png",
28
- attribution:
29
- '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
30
- maxZoom: 18,
31
- worldCopyJump: true,
32
- retina: "@2x",
33
- },
34
- worldEco: {
35
- url: "https://cartocdn_{s}.global.ssl.fastly.net/base-eco/{z}/{x}/{y}{retina}.png",
36
- attribution:
37
- '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
38
- maxZoom: 18,
39
- worldCopyJump: true,
40
- retina: "@2x",
41
- },
42
- flatBlue: {
43
- url: "https://cartocdn_{s}.global.ssl.fastly.net/base-flatblue/{z}/{x}/{y}{retina}.png",
44
- attribution:
45
- '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
46
- maxZoom: 18,
47
- worldCopyJump: true,
48
- retina: "@2x",
49
- },
50
- midnightCommander: {
51
- url: "https://cartocdn_{s}.global.ssl.fastly.net/base-midnight/{z}/{x}/{y}{retina}.png",
52
- attribution:
53
- '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://carto.com/attributions">Carto</a>',
54
- maxZoom: 18,
55
- worldCopyJump: true,
56
- retina: "@2x",
57
- },
58
26
  openstreetmapHot: {
59
27
  url: "https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png",
60
28
  attribution:
@@ -0,0 +1,47 @@
1
+ import { Control } from "leaflet";
2
+
3
+ export default class sidebarControl extends Control {
4
+ constructor(options) {
5
+ const _options = { ...options };
6
+ super(_options);
7
+ }
8
+
9
+ onAdd(map) {
10
+ var div = L.DomUtil.create('div', 'leaflet-sidebar');
11
+ div.setAttribute("tabindex", "0");
12
+ const sidebarContainer = this.options.sidebar;
13
+
14
+ // Disable zoom when the mouse enters the sidebar
15
+ div.addEventListener('mouseenter', function() {
16
+ map.scrollWheelZoom.disable();
17
+ });
18
+
19
+ div.addEventListener('touchstart', function() {
20
+ map.touchZoom.disable();
21
+ map.dragging.disable();
22
+ });
23
+
24
+ // without this the map will try to find data about whatever is behind the sidebar
25
+ div.addEventListener('click', function(event) {
26
+ event.stopPropagation();
27
+ });
28
+
29
+ // Enable zoom when the mouse leaves the sidebar
30
+ div.addEventListener('touchend', function() {
31
+ map.touchZoom.enable();
32
+ map.dragging.enable();
33
+ });
34
+
35
+ // Enable zoom when the mouse leaes the sidebar
36
+ div.addEventListener('mouseleave', function() {
37
+ map.scrollWheelZoom.enable();
38
+ });
39
+
40
+ if (!sidebarContainer) { return };
41
+ // Make sure table height is same as map (with a little padding)
42
+ sidebarContainer.children[0].style.maxHeight = `${map._container.offsetHeight - 30}px`;
43
+ div.innerHTML = sidebarContainer.innerHTML;
44
+ sidebarContainer.remove();
45
+ return div;
46
+ }
47
+ }
@@ -1,4 +1,5 @@
1
1
  import { identifyFeatures } from "esri-leaflet";
2
+ import { polygon, geoJSON, circleMarker, layerGroup } from "leaflet";
2
3
  import { appendErrorMessage, appendLoadingMessage, getLayerOpacity, linkify } from "geoblacklight/leaflet/utils";
3
4
 
4
5
  export const wmsInspection = (map, url, layerId, layer) => {
@@ -66,15 +67,15 @@ export const overlayLayer = (map, data, layer) => {
66
67
  const color = map.options.selected_color;
67
68
 
68
69
  if (data._latlngs) {
69
- overlayLayer = L.polygon(data._latlngs, {color: color, weight: 2, layer: true, fillOpacity: opacity, addToOpacitySlider: true, highlightLayer: true });
70
+ overlayLayer = polygon(data._latlngs, {color: color, weight: 2, layer: true, fillOpacity: opacity, addToOpacitySlider: true, highlightLayer: true });
70
71
  } else {
71
- overlayLayer = L.geoJSON(data, {
72
+ overlayLayer = geoJSON(data, {
72
73
  color: color,
73
74
  opacity: opacity,
74
75
  addToOpacitySlider: true,
75
76
  highlightLayer: true,
76
77
  pointToLayer: function(feature, latlng) {
77
- return L.circleMarker(latlng, {
78
+ return circleMarker(latlng, {
78
79
  radius: 8,
79
80
  fillColor: color,
80
81
  color: color,
@@ -85,7 +86,7 @@ export const overlayLayer = (map, data, layer) => {
85
86
  }
86
87
  });
87
88
  }
88
- L.layerGroup([overlayLayer]).addTo(map);
89
+ layerGroup([overlayLayer]).addTo(map);
89
90
  }
90
91
 
91
92
  export const tiledMapLayerInspection = (map, layer) => {