blacklight-spotlight 4.7.1 → 5.0.0.pre.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -12
  3. data/Rakefile +8 -1
  4. data/app/assets/javascripts/spotlight/application.js +0 -1
  5. data/app/assets/javascripts/spotlight/spotlight.esm.js +3620 -3847
  6. data/app/assets/javascripts/spotlight/spotlight.esm.js.map +1 -1
  7. data/app/assets/javascripts/spotlight/spotlight.js +3620 -3852
  8. data/app/assets/javascripts/spotlight/spotlight.js.map +1 -1
  9. data/app/assets/stylesheets/spotlight/_accessibility.scss +0 -9
  10. data/app/assets/stylesheets/spotlight/_autocomplete.scss +49 -0
  11. data/app/assets/stylesheets/spotlight/_blacklight_configuration.scss +0 -1
  12. data/app/assets/stylesheets/spotlight/_blacklight_overrides.scss +1 -6
  13. data/app/assets/stylesheets/spotlight/_browse.scss +2 -2
  14. data/app/assets/stylesheets/spotlight/_catalog.scss +40 -41
  15. data/app/assets/stylesheets/spotlight/_curation.scss +1 -1
  16. data/app/assets/stylesheets/spotlight/_exhibit_admin.scss +7 -0
  17. data/app/assets/stylesheets/spotlight/_exhibits_index.scss +8 -5
  18. data/app/assets/stylesheets/spotlight/_featured_browse_categories_block.scss +3 -3
  19. data/app/assets/stylesheets/spotlight/_header.scss +13 -0
  20. data/app/assets/stylesheets/spotlight/_mixins.scss +3 -4
  21. data/app/assets/stylesheets/spotlight/_nestable.scss +2 -12
  22. data/app/assets/stylesheets/spotlight/_pages.scss +11 -9
  23. data/app/assets/stylesheets/spotlight/_report_a_problem.scss +1 -3
  24. data/app/assets/stylesheets/spotlight/_sir-trevor_overrides.scss +2 -2
  25. data/app/assets/stylesheets/spotlight/_spotlight.scss +2 -1
  26. data/app/assets/stylesheets/spotlight/_tag_selector.scss +34 -0
  27. data/app/assets/stylesheets/spotlight/_variables.scss +0 -8
  28. data/app/components/spotlight/analytics/dashboard_component.html.erb +3 -3
  29. data/app/components/spotlight/breadcrumbs_component.html.erb +13 -19
  30. data/app/components/spotlight/bulk_action_component.rb +1 -1
  31. data/app/components/spotlight/document_component.rb +1 -1
  32. data/app/components/spotlight/save_search_component.rb +1 -1
  33. data/app/components/spotlight/select_image_component.html.erb +17 -0
  34. data/app/components/spotlight/select_image_component.rb +24 -0
  35. data/app/components/spotlight/skip_link_component.rb +16 -0
  36. data/app/components/spotlight/tag_selector_component.html.erb +40 -0
  37. data/app/components/spotlight/tag_selector_component.rb +41 -0
  38. data/app/components/spotlight/tag_selector_component.yml +6 -0
  39. data/app/components/spotlight/title_component.html.erb +8 -0
  40. data/app/components/spotlight/title_component.rb +22 -0
  41. data/app/controllers/spotlight/accessibility_controller.rb +2 -2
  42. data/app/controllers/spotlight/catalog_controller.rb +7 -2
  43. data/app/controllers/spotlight/contact_email_controller.rb +8 -2
  44. data/app/controllers/spotlight/languages_controller.rb +9 -4
  45. data/app/helpers/spotlight/application_helper.rb +7 -0
  46. data/app/helpers/spotlight/crop_helper.rb +4 -0
  47. data/app/helpers/spotlight/meta_helper.rb +59 -36
  48. data/app/javascript/spotlight/admin/blacklight_configuration.js +1 -1
  49. data/app/javascript/spotlight/admin/block_mixins/autocompleteable.js +70 -34
  50. data/app/javascript/spotlight/admin/blocks/block.js +1 -0
  51. data/app/javascript/spotlight/admin/blocks/browse_block.js +8 -12
  52. data/app/javascript/spotlight/admin/blocks/browse_group_categories_block.js +14 -18
  53. data/app/javascript/spotlight/admin/blocks/pages_block.js +6 -10
  54. data/app/javascript/spotlight/admin/blocks/resources_block.js +33 -15
  55. data/app/javascript/spotlight/admin/blocks/solr_documents_base_block.js +11 -6
  56. data/app/javascript/spotlight/admin/blocks/solr_documents_embed_block.js +1 -0
  57. data/app/javascript/spotlight/admin/blocks/uploaded_items_block.js +4 -3
  58. data/app/javascript/spotlight/admin/copy_email_addresses.js +2 -0
  59. data/app/javascript/spotlight/admin/crop.js +45 -17
  60. data/app/javascript/spotlight/admin/croppable.js +8 -1
  61. data/app/javascript/spotlight/admin/croppable_modal.js +68 -0
  62. data/app/javascript/spotlight/admin/exhibits.js +15 -10
  63. data/app/javascript/spotlight/admin/form_observer.js +1 -1
  64. data/app/javascript/spotlight/admin/index.js +0 -10
  65. data/app/javascript/spotlight/admin/locks.js +15 -5
  66. data/app/javascript/spotlight/admin/pages.js +1 -1
  67. data/app/javascript/spotlight/admin/search_typeahead.js +62 -55
  68. data/app/javascript/spotlight/admin/spotlight_nestable.js +173 -50
  69. data/app/javascript/spotlight/admin/visibility_toggle.js +1 -11
  70. data/app/javascript/spotlight/controllers/index.js +8 -0
  71. data/app/javascript/spotlight/controllers/tag_selector_controller.js +203 -0
  72. data/app/javascript/spotlight/core.js +4 -6
  73. data/app/javascript/spotlight/index.js +2 -0
  74. data/app/javascript/spotlight/user/browse_group_categories.js +2 -0
  75. data/app/javascript/spotlight/user/carousel.js +3 -1
  76. data/app/javascript/spotlight/user/index.js +0 -2
  77. data/app/models/sir_trevor_rails/block.rb +5 -4
  78. data/app/models/sir_trevor_rails/blocks/solr_documents_block.rb +1 -1
  79. data/app/models/sir_trevor_rails/blocks/solr_documents_embed_block.rb +1 -1
  80. data/app/models/sir_trevor_rails/blocks/uploaded_items_block.rb +1 -1
  81. data/app/models/spotlight/page_configurations.rb +1 -1
  82. data/app/views/catalog/_add_tags.html.erb +2 -2
  83. data/app/views/catalog/_change_visibility.html.erb +1 -1
  84. data/app/views/catalog/_remove_tags.html.erb +2 -2
  85. data/app/views/layouts/spotlight/base.html.erb +24 -13
  86. data/app/views/layouts/spotlight/spotlight.html.erb +6 -6
  87. data/app/views/shared/_masthead.html.erb +4 -31
  88. data/app/views/shared/_site_sidebar.html.erb +1 -1
  89. data/app/views/shared/_user_util_links.html.erb +3 -1
  90. data/app/views/spotlight/accessibility/alt_text.html.erb +2 -2
  91. data/app/views/spotlight/admin_users/index.html.erb +3 -3
  92. data/app/views/spotlight/appearances/edit.html.erb +1 -1
  93. data/app/views/spotlight/browse/_search_box.html.erb +8 -8
  94. data/app/views/spotlight/browse/show.html.erb +1 -1
  95. data/app/views/spotlight/bulk_updates/_download.html.erb +1 -1
  96. data/app/views/spotlight/bulk_updates/_upload.html.erb +1 -1
  97. data/app/views/spotlight/catalog/_admin_header.html.erb +1 -1
  98. data/app/views/spotlight/catalog/_edit_default.html.erb +2 -1
  99. data/app/views/spotlight/catalog/select_image.html.erb +1 -0
  100. data/app/views/spotlight/contacts/_form.html.erb +1 -1
  101. data/app/views/spotlight/exhibits/_contact.html.erb +5 -6
  102. data/app/views/spotlight/exhibits/_delete.html.erb +1 -1
  103. data/app/views/spotlight/exhibits/_languages.html.erb +3 -2
  104. data/app/views/spotlight/featured_images/_form.html.erb +6 -2
  105. data/app/views/spotlight/featured_images/_upload_form.html.erb +1 -1
  106. data/app/views/spotlight/metadata_configurations/_metadata_field.html.erb +1 -1
  107. data/app/views/spotlight/metadata_configurations/edit.html.erb +6 -6
  108. data/app/views/spotlight/pages/show.html.erb +1 -1
  109. data/app/views/spotlight/resources/csv_upload/_form.html.erb +1 -1
  110. data/app/views/spotlight/resources/upload/_form.html.erb +1 -1
  111. data/app/views/spotlight/roles/index.html.erb +1 -1
  112. data/app/views/spotlight/searches/_form.html.erb +1 -1
  113. data/app/views/spotlight/shared/_dd3_item.html.erb +1 -1
  114. data/app/views/spotlight/sir_trevor/blocks/_browse_group_categories_block.html.erb +1 -1
  115. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_block.html.erb +1 -1
  116. data/app/views/spotlight/sir_trevor/blocks/_solr_documents_carousel_block.html.erb +1 -1
  117. data/app/views/spotlight/sir_trevor/blocks/_uploaded_items_block.html.erb +1 -1
  118. data/app/views/spotlight/tags/index.html.erb +2 -3
  119. data/app/views/spotlight/translations/_import.html.erb +2 -2
  120. data/config/importmap.rb +5 -0
  121. data/config/locales/spotlight.en.yml +2 -0
  122. data/config/routes.rb +5 -3
  123. data/lib/generators/spotlight/assets/generator_common_utilities.rb +36 -0
  124. data/lib/generators/spotlight/assets/importmap_generator.rb +87 -0
  125. data/lib/generators/spotlight/assets/propshaft_generator.rb +96 -0
  126. data/lib/generators/spotlight/assets_generator.rb +22 -0
  127. data/lib/generators/spotlight/install_generator.rb +8 -36
  128. data/lib/generators/spotlight/scaffold_resource_generator.rb +1 -1
  129. data/lib/generators/spotlight/templates/assets/spotlight.scss +6 -0
  130. data/lib/generators/spotlight/templates/javascript/jquery-shim.js +1 -0
  131. data/lib/spotlight/engine.rb +7 -6
  132. data/lib/spotlight/version.rb +1 -1
  133. data/spec/support/features/capybara_wait_metadata_helper.rb +13 -0
  134. data/spec/support/features/test_features_helpers.rb +16 -30
  135. data/vendor/assets/javascripts/tiny-slider.js +3 -0
  136. metadata +35 -87
  137. data/app/assets/stylesheets/spotlight/#_accessibility.scss# +0 -12
  138. data/app/javascript/spotlight/admin/checkbox_submit.js +0 -75
  139. data/app/javascript/spotlight/admin/exhibit_tag_autocomplete.js +0 -39
  140. data/app/javascript/spotlight/user/report_a_problem.js +0 -30
  141. data/app/views/spotlight/browse/_tophat.html.erb +0 -1
  142. data/app/views/spotlight/catalog/_tophat_default.html.erb +0 -1
  143. data/app/views/spotlight/home_pages/_tophat.html.erb +0 -2
  144. data/app/views/spotlight/pages/_tophat.html.erb +0 -1
  145. data/lib/generators/spotlight/templates/spotlight.js +0 -1
  146. data/lib/generators/spotlight/templates/spotlight.scss +0 -5
  147. data/spec/support/features/capybara_default_max_wait_metadata_helper.rb +0 -20
  148. data/vendor/assets/javascripts/bootstrap-tagsinput.js +0 -530
  149. data/vendor/assets/javascripts/jquery.serializejson.js +0 -234
  150. data/vendor/assets/javascripts/nestable.js +0 -645
  151. data/vendor/assets/javascripts/sir-trevor.js +0 -23508
  152. data/vendor/assets/javascripts/typeahead.bundle.min.js +0 -7
  153. data/vendor/assets/stylesheets/bootstrap-tagsinput.css +0 -46
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotlight
4
+ # Component to select section of image
5
+ class SelectImageComponent < ViewComponent::Base
6
+ def initialize(index_id, block_item_id)
7
+ super
8
+ @index_id = index_id
9
+ @block_item_id = block_item_id
10
+ end
11
+
12
+ def render?
13
+ true
14
+ end
15
+
16
+ def initial_crop_selection
17
+ Spotlight::Engine.config.thumbnail_initial_crop_selection
18
+ end
19
+
20
+ def help_text
21
+ t(:'spotlight.pages.form.instructions_html')
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotlight
4
+ # Blacklight Skip Link Component with conditional search link for Spotlight
5
+ class SkipLinkComponent < Blacklight::SkipLinkComponent
6
+ def initialize(render_search_link: true)
7
+ @render_search_link = render_search_link
8
+
9
+ super
10
+ end
11
+
12
+ def link_to_search
13
+ super if @render_search_link
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,40 @@
1
+ <div data-controller="tag-selector" class="tag-selector" data-tag-selector-translations-value="<%= translation_data.to_json %>" data-tag-selector-tags-value="<%= selected_tags %>">
2
+ <% if form.nil? %>
3
+ <%= text_field_tag field_name, selected_tags_value, class: 'tag-selector-input', placeholder: t('.no_js_placeholder'), data: { tag_selector_target: "tagsField" } %>
4
+ <% else %>
5
+ <%= form.text_field field_name, value: selected_tags_value, class: 'tag-selector-input', placeholder: t('.no_js_placeholder'), data: { tag_selector_target: "tagsField" } %>
6
+ <% end %>
7
+ <input type="hidden" value="<%= selected_tags_value %>" data-tag-selector-target="initialTags">
8
+
9
+ <div class='mb-3'>
10
+ <div class="tag-selection-wrapper" data-tag-selector-target="tagControlWrapper">
11
+ <div class="dropdown w-100 mb-3 d-inline-block" data-action="click@window->tag-selector#clickOutside">
12
+ <div data-tag-selector-target="tagSearchDropdown">
13
+ <div data-tag-selector-target="selectedTags"></div>
14
+ <div data-tag-selector-target="tagSearchInputWrapper" class="border rounded-bottom tag-selection-search-bar d-flex">
15
+ <button type="button" aria-label="toggle dropdown" class="btn btn-link text-secondary me-1 px-2" data-action="click->tag-selector#tagDropdown">
16
+ <span><%= search_icon_svg %></span>
17
+ </button>
18
+ <input class="flex-grow-1" data-action="input->tag-selector#search input->tag-selector#updateSearchResultsPlaceholder input->tag-selector#updateTagToAdd focus->tag-selector#tagDropdown keydown->tag-selector#handleKeydown"
19
+ data-tag-selector-target="tagSearch" placeholder="<%= t('.search') %>" aria-label="<%= t('.search') %>">
20
+ <button type="button" aria-label="toggle dropdown" class="dropdown-toggle btn btn-link text-secondary" data-bs-toggle="dropdown" data-action="click->tag-selector#tagDropdown"></button>
21
+ </div>
22
+ </div>
23
+ <div data-tag-selector-target="dropdownContent" class="dropdown-content d-none tags-group border rounded">
24
+ <% all_tags.each do |tag| %>
25
+ <label class="d-block">
26
+ <input type="checkbox" <%= 'checked' if selected?(tag) %> data-action="click->tag-selector#tagUpdate" data-tag-selector-target="searchResultTags" data-tag="<%= tag %>">
27
+ <%= tag %>
28
+ </label>
29
+ <% end %>
30
+ <% if all_tags.empty? %>
31
+ <label class="no-results"><%= t('.no_results') %></label>
32
+ <% end %>
33
+ <label class="d-none" data-tag-selector-target="addNewTagWrapper">
34
+ <input type="checkbox" disabled data-action="click->tag-selector#tagCreate" data-tag-selector-target="newTag" data-tag=""> Add new tag
35
+ </label>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </div>
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotlight
4
+ # Displays a tag selection input
5
+ # This uses a plain text input that acts-as-taggable-on expects.
6
+ class TagSelectorComponent < ViewComponent::Base
7
+ # selected_tags_value is a comma delimited string of tags
8
+ def initialize(field_name:, all_tags:, selected_tags_value: nil, form: nil)
9
+ @form = form
10
+ @field_name = field_name
11
+ @selected_tags_value = selected_tags_value || ''
12
+ @all_tags = all_tags&.sort_by { |tag| (tag.respond_to?(:name) ? tag.name : tag).downcase }
13
+
14
+ super
15
+ end
16
+
17
+ def selected_tags
18
+ selected_tags_value.split(',').map(&:strip)
19
+ end
20
+
21
+ def search_icon_svg
22
+ render Blacklight::Icons::SearchComponent.new
23
+ end
24
+
25
+ private
26
+
27
+ # To pass to the JS
28
+ def translation_data
29
+ {
30
+ add_new_tag: t('.add_new_tag'),
31
+ remove: t('.remove')
32
+ }
33
+ end
34
+
35
+ def selected?(tag)
36
+ selected_tags.include?(tag.respond_to?(:name) ? tag.name : tag)
37
+ end
38
+
39
+ attr_reader :form, :field_name, :selected_tags_value, :all_tags
40
+ end
41
+ end
@@ -0,0 +1,6 @@
1
+ en:
2
+ add_new_tag: "Add a new tag"
3
+ no_js_placeholder: "Enter tags delimited by commas"
4
+ no_results: "No results found"
5
+ remove: "Remove"
6
+ search: "Type to search and add tags"
@@ -0,0 +1,8 @@
1
+ <div class="container site-title-container">
2
+ <div class="site-title-wrapper">
3
+ <a href="<%= helpers.current_exhibit ? helpers.spotlight.exhibit_path(helpers.current_exhibit) : helpers.spotlight.exhibits_path %>" class="link-unstyled">
4
+ <%= title %>
5
+ <%= subtitle %>
6
+ </a>
7
+ </div>
8
+ </div>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spotlight
4
+ # Draws the title in the masthead
5
+ class TitleComponent < ViewComponent::Base
6
+ def initialize(title:, subtitle:)
7
+ @title = title
8
+ @subtitle = subtitle
9
+ super
10
+ end
11
+
12
+ def title
13
+ tag.h1 @title, class: 'site-title h2'
14
+ end
15
+
16
+ def subtitle
17
+ return unless @subtitle
18
+
19
+ tag.small(@subtitle, class: 'd-none d-md-block py-2 fs-4')
20
+ end
21
+ end
22
+ end
@@ -9,7 +9,7 @@ module Spotlight
9
9
 
10
10
  def alt_text
11
11
  # Sort by newest except for the homepage, which is always first
12
- pages_with_alt = @exhibit.pages.order(Arel.sql('id = 1 DESC, created_at DESC')).select { |elem| elem.content.any?(&:alt_text?) }
12
+ pages_with_alt = @exhibit.pages.order(Arel.sql('id = 1 DESC, created_at DESC')).select { |elem| elem.content.any?(&:supports_alt_text?) }
13
13
  @pages = pages_with_alt.map { |page| get_alt_info(page) }
14
14
  @has_alt_text = @pages.sum { |page| page[:has_alt_text] }
15
15
  @total_alt_items = @pages.sum { |page| page[:can_have_alt_text] }
@@ -23,7 +23,7 @@ module Spotlight
23
23
  can_have_alt_text = 0
24
24
  has_alt_text = 0
25
25
  page.content.each do |content|
26
- next unless content.alt_text?
26
+ next unless content.supports_alt_text?
27
27
 
28
28
  content.item&.each_value do |item|
29
29
  can_have_alt_text += 1
@@ -30,7 +30,6 @@ module Spotlight
30
30
  if Blacklight::VERSION > '8'
31
31
  blacklight_config.show.document_component = Spotlight::DocumentComponent
32
32
  else
33
- blacklight_config.show.partials.unshift 'tophat'
34
33
  blacklight_config.show.partials.unshift 'curation_mode_toggle'
35
34
  end
36
35
  end
@@ -135,6 +134,12 @@ module Spotlight
135
134
  end
136
135
  end
137
136
 
137
+ def select_image
138
+ @index_id = params[:index_id]
139
+ @block_item_id = params[:block_item_id]
140
+ respond_to :html
141
+ end
142
+
138
143
  protected
139
144
 
140
145
  def load_document
@@ -194,7 +199,7 @@ module Spotlight
194
199
  def solr_document_params
195
200
  params.require(:solr_document).permit(:exhibit_tag_list,
196
201
  uploaded_resource: [:url],
197
- sidecar: [:public, { data: [editable_solr_document_params] }])
202
+ sidecar: [:public, { data: editable_solr_document_params }])
198
203
  end
199
204
 
200
205
  def editable_solr_document_params
@@ -12,13 +12,19 @@ module Spotlight
12
12
 
13
13
  def destroy
14
14
  @contact_email.destroy
15
- render json: { success: true, error: nil }
15
+ respond_to do |format|
16
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(@contact_email) }
17
+ format.json { render json: { success: true, error: nil } }
18
+ end
16
19
  end
17
20
 
18
21
  private
19
22
 
20
23
  def record_not_found(_error)
21
- render json: { success: false, error: 'Not Found' }, status: :not_found
24
+ respond_to do |format|
25
+ format.turbo_stream { head :not_found }
26
+ format.json { render json: { success: false, error: 'Not Found' }, status: :not_found }
27
+ end
22
28
  end
23
29
  end
24
30
  end
@@ -33,10 +33,15 @@ module Spotlight
33
33
  def destroy
34
34
  @language.destroy
35
35
 
36
- redirect_to(
37
- spotlight.edit_exhibit_path(@exhibit, tab: 'language'),
38
- notice: t(:'helpers.submit.language.destroyed', model: @language.model_name.human.downcase)
39
- )
36
+ respond_to do |format|
37
+ format.turbo_stream { render turbo_stream: turbo_stream.remove(@language) }
38
+ format.html do
39
+ redirect_to(
40
+ spotlight.edit_exhibit_path(@exhibit, tab: 'language'),
41
+ notice: t(:'helpers.submit.language.destroyed', model: @language.model_name.human.downcase)
42
+ )
43
+ end
44
+ end
40
45
  end
41
46
 
42
47
  private
@@ -30,6 +30,13 @@ module Spotlight
30
30
  current_site.title.presence
31
31
  end
32
32
 
33
+ def content?(field)
34
+ return content_for?(field) unless Rails.configuration.action_view.annotate_rendered_view_with_filenames && content_for(field).present?
35
+
36
+ stripped_content = content_for(field).gsub(/<!-- BEGIN .+? -->/, '').gsub(/<!-- END .+? -->/, '').strip
37
+ stripped_content.present?
38
+ end
39
+
33
40
  # Returns the url for the current page in the new locale. This may be
34
41
  # overridden in downstream applications where our naive use of `url_for`
35
42
  # is insufficient to generate the expected routes
@@ -34,5 +34,9 @@ module Spotlight
34
34
  def form_prefix(f)
35
35
  f.object_name.parameterize(separator: '_')
36
36
  end
37
+
38
+ def input_prefix(f)
39
+ f.object_name
40
+ end
37
41
  end
38
42
  end
@@ -3,17 +3,37 @@
3
3
  module Spotlight
4
4
  # HTML <meta> tag helpers
5
5
  module MetaHelper
6
+ def description(description)
7
+ content_for(:meta) { ActionController::Base.helpers.tag.meta(name: 'description', content: description) } if description
8
+ end
9
+
10
+ def card(type, &block)
11
+ card = {}
12
+ block.call(card) if block_given?
13
+ content_for(:meta) { build_tags(card, type) }
14
+ end
15
+
16
+ # rubocop:disable Rails/OutputSafety
17
+ def build_tags(attributes, tag_field)
18
+ type_fields = { 'og' => 'property', 'twitter' => 'name' }
19
+ attributes.map do |key, value|
20
+ ActionController::Base.helpers.tag.meta("#{type_fields[tag_field]}": "#{tag_field}:#{key}", content: value) if value
21
+ end.compact.join("\n").html_safe
22
+ end
23
+ # rubocop:enable Rails/OutputSafety
24
+
6
25
  def add_exhibit_meta_content
7
26
  exhibit_twitter_card_content
8
27
  exhibit_opengraph_content
9
28
  end
10
29
 
11
30
  def exhibit_twitter_card_content
12
- twitter_card('summary') do |card|
13
- card.url exhibit_root_url(current_exhibit)
14
- card.title current_exhibit.title
15
- card.description current_exhibit.subtitle
16
- card.image meta_image if current_exhibit.thumbnail
31
+ card('twitter') do |card|
32
+ card['card'] = 'summary'
33
+ card['url'] = spotlight.exhibit_root_url(current_exhibit)
34
+ card['title'] = current_exhibit.title
35
+ card['description'] = current_exhibit.subtitle
36
+ card['image'] = meta_image if current_exhibit.thumbnail
17
37
  end
18
38
  end
19
39
 
@@ -22,10 +42,10 @@ module Spotlight
22
42
  end
23
43
 
24
44
  def exhibit_opengraph_content
25
- opengraph do |graph|
26
- graph.title current_exhibit.title
27
- graph.image meta_image if current_exhibit.thumbnail
28
- graph.site_name site_title
45
+ card('og') do |graph|
46
+ graph['title'] = current_exhibit.title
47
+ graph['image'] = meta_image if current_exhibit.thumbnail
48
+ graph['site_name'] = site_title
29
49
  end
30
50
  end
31
51
 
@@ -35,20 +55,21 @@ module Spotlight
35
55
  end
36
56
 
37
57
  def page_twitter_card_content(page)
38
- twitter_card('summary_large_image') do |card|
39
- card.title page.title
40
- card.image page.thumbnail.iiif_url if page.thumbnail
58
+ card('twitter') do |card|
59
+ card['card'] = 'summary_large_image'
60
+ card['title'] = page.title
61
+ card['image'] = page.thumbnail.iiif_url if page.thumbnail
41
62
  end
42
63
  end
43
64
 
44
65
  def page_opengraph_content(page)
45
- opengraph do |graph|
46
- graph.type 'article'
47
- graph.site_name application_name
48
- graph.title page.title
49
- graph.send('image', page.thumbnail.iiif_url) if page.thumbnail
50
- graph.send('article:published_time', page.created_at.iso8601)
51
- graph.send('article:modified_time', page.updated_at.iso8601)
66
+ card('og') do |graph|
67
+ graph['type'] = 'article'
68
+ graph['site_name'] = application_name
69
+ graph['title'] = page.title
70
+ graph['image'] = page.thumbnail.iiif_url if page.thumbnail
71
+ graph['article:published_time'] = page.created_at.iso8601
72
+ graph['article:modified_time'] = page.updated_at.iso8601
52
73
  end
53
74
  end
54
75
 
@@ -58,20 +79,21 @@ module Spotlight
58
79
  end
59
80
 
60
81
  def browse_twitter_card_content(browse)
61
- twitter_card('summary_large_image') do |card|
62
- card.title browse.title
63
- card.image browse.thumbnail.iiif_url if browse.thumbnail
82
+ card('twitter') do |card|
83
+ card['card'] = 'summary_large_image'
84
+ card['title'] = browse.title
85
+ card['image'] = browse.thumbnail.iiif_url if browse.thumbnail
64
86
  end
65
87
  end
66
88
 
67
89
  def browse_opengraph_content(browse)
68
- opengraph do |graph|
69
- graph.type 'article'
70
- graph.site_name application_name
71
- graph.title browse.title
72
- graph.send('image', browse.thumbnail.iiif_url) if browse.thumbnail
73
- graph.send('article:published_time', browse.created_at.iso8601)
74
- graph.send('article:modified_time', browse.updated_at.iso8601)
90
+ card('og') do |graph|
91
+ graph['type'] = 'article'
92
+ graph['site_name'] = application_name
93
+ graph['title'] = browse.title
94
+ graph['image'] = browse.thumbnail.iiif_url if browse.thumbnail
95
+ graph['article:published_time'] = browse.created_at.iso8601
96
+ graph['article:modified_time'] = browse.updated_at.iso8601
75
97
  end
76
98
  end
77
99
 
@@ -83,19 +105,20 @@ module Spotlight
83
105
  def document_twitter_card_content(document)
84
106
  presenter = document_presenter(document)
85
107
 
86
- twitter_card('summary_large_image') do |card|
87
- card.title presenter.heading
88
- card.image document.first(blacklight_config.index.thumbnail_field)
108
+ card('twitter') do |card|
109
+ card['card'] = 'summary_large_image'
110
+ card['title'] = presenter.heading
111
+ card['image'] = document.first(blacklight_config.index.thumbnail_field)
89
112
  end
90
113
  end
91
114
 
92
115
  def document_opengraph_content(document)
93
116
  presenter = document_presenter(document)
94
117
 
95
- opengraph do |graph|
96
- graph.site_name application_name
97
- graph.title presenter.heading
98
- graph.send('image', document.first(blacklight_config.index.thumbnail_field))
118
+ card('og') do |graph|
119
+ graph['site_name'] = application_name
120
+ graph['title'] = presenter.heading
121
+ graph['image'] = document.first(blacklight_config.index.thumbnail_field)
99
122
  end
100
123
  end
101
124
  end
@@ -59,4 +59,4 @@ export default class {
59
59
  });
60
60
  });
61
61
  }
62
- }
62
+ }
@@ -1,3 +1,5 @@
1
+ import { fetchAutocompleteJSON } from 'spotlight/admin/search_typeahead';
2
+
1
3
  (function ($){
2
4
  SirTrevor.BlockMixins.Autocompleteable = {
3
5
  mixinName: "Autocompleteable",
@@ -7,59 +9,93 @@
7
9
  this.on("onRender", this.addAutocompletetoSirTrevorForm);
8
10
 
9
11
  if (this['autocomplete_url'] === undefined) {
10
- this.autocomplete_url = function() { return $('form[data-autocomplete-url]').data('autocomplete-url').replace("%25QUERY", "%QUERY"); };
12
+ this.autocomplete_url = function() { return $('form[data-autocomplete-url]').data('autocomplete-url'); };
13
+ }
14
+
15
+ if (this['autocomplete_fetch'] === undefined) {
16
+ this.autocomplete_fetch = this.fetchAutocompleteResults;
11
17
  }
12
18
 
13
19
  if (this['transform_autocomplete_results'] === undefined) {
14
20
  this.transform_autocomplete_results = (val) => val
15
21
  }
16
22
 
23
+ if (this['highlight'] === undefined) {
24
+ this.highlight = function(value) {
25
+ if (!value) return '';
26
+ const queryValue = this.getQueryValue().trim();
27
+ return queryValue ? value.replace(new RegExp(queryValue, 'gi'), '<strong>$&</strong>') : value;
28
+ }
29
+ }
30
+
17
31
  if (this['autocomplete_control'] === undefined) {
18
- this.autocomplete_control = function() { return `<input type="text" class="st-input-string form-control item-input-field" data-twitter-typeahead="true" placeholder="${i18n.t("blocks:autocompleteable:placeholder")}"/>` };
32
+ this.autocomplete_control = function() {
33
+ const autocompleteID = this.autocompleteID();
34
+ return `
35
+ <auto-complete src="${this.autocomplete_url()}" for="${autocompleteID}-popup" fetch-on-empty>
36
+ <input type="text" name="${autocompleteID}" placeholder="${i18n.t("blocks:autocompleteable:placeholder")}" data-default-typeahead>
37
+ <ul id="${autocompleteID}-popup"></ul>
38
+ <div id="${autocompleteID}-popup-feedback" class="sr-only visually-hidden"></div>
39
+ </auto-complete>
40
+ ` };
19
41
  }
20
42
 
21
- if (this['bloodhoundOptions'] === undefined) {
22
- this.bloodhoundOptions = function() {
23
- return {
24
- remote: {
25
- url: this.autocomplete_url(),
26
- filter: this.transform_autocomplete_results
27
- }
28
- };
29
- };
43
+ if (this['autocomplete_element_template'] === undefined) {
44
+ this.autocomplete_element_template = function(item) {
45
+ return `<li role="option" data-autocomplete-value="${item.id}">${this.autocomplete_template(item)}</li>`
46
+ }
30
47
  }
31
48
  },
32
49
 
33
- addAutocompletetoSirTrevorForm: function() {
34
- $('[data-twitter-typeahead]', this.inner).spotlightSearchTypeAhead({bloodhound: this.bloodhound(), template: this.autocomplete_template}).on('typeahead:selected typeahead:autocompleted', this.autocompletedHandler()).on( 'focus', function() {
35
- if($(this).val() === '') {
36
- $(this).data().ttTypeahead.input.trigger('queryChanged', '');
37
- }
38
- });
50
+ queryTokenizer: function(query) {
51
+ return query.trim().toLowerCase().split(/\s+/).filter(Boolean);
39
52
  },
40
53
 
41
- autocompletedHandler: function(e, data) {
42
- var context = this;
54
+ filterResults: function(data, query) {
55
+ const queryStrings = this.queryTokenizer(query);
56
+ return data.filter(item => {
57
+ const lowerTitle = item.title.toLowerCase();
58
+ return queryStrings.some(queryString => lowerTitle.includes(queryString));
59
+ });
60
+ },
43
61
 
44
- return function(e, data) {
45
- $(this).typeahead("val", "");
46
- $(this).val("");
62
+ fetchAutocompleteResults: async function(url) {
63
+ const result = await fetchAutocompleteJSON(url);
64
+ const transformed = this.transform_autocomplete_results(result);
65
+ this.fetchedData = {};
66
+ transformed.map(item => this.fetchedData[item.id] = item);
67
+ return transformed.map(item => this.autocomplete_element_template(item)).join('');
68
+ },
47
69
 
48
- context.createItemPanel($.extend(data, {display: "true"}));
70
+ fetchOnceAndFilterLocalResults: async function(url) {
71
+ if (this.fetchedData === undefined) {
72
+ await this.fetchAutocompleteResults(url);
49
73
  }
74
+ const query = url.searchParams.get('q');
75
+ const data = Object.values(this.fetchedData);
76
+ const filteredData = query ? this.filterResults(data, query) : data;
77
+ return filteredData.map(item => this.autocomplete_element_template(item)).join('');
78
+ },
79
+
80
+ autocompleteID: function() {
81
+ return this.blockID + '-autocomplete';
50
82
  },
51
83
 
52
- bloodhound: function() {
53
- var block = this;
54
- var results = new Bloodhound(Object.assign({
55
- datumTokenizer: function(d) {
56
- return Bloodhound.tokenizers.whitespace(d.title);
57
- },
58
- queryTokenizer: Bloodhound.tokenizers.whitespace,
59
- limit: 100,
60
- }, block.bloodhoundOptions()));
61
- results.initialize();
62
- return results;
84
+ getQueryValue: function() {
85
+ const completer = this.inner.querySelector("auto-complete > input");
86
+ return completer.value;
87
+ },
88
+
89
+ addAutocompletetoSirTrevorForm: function() {
90
+ const completer = this.inner.querySelector("auto-complete");
91
+ completer.fetchResult = this.autocomplete_fetch.bind(this);
92
+ completer.addEventListener('auto-complete-change', (e) => {
93
+ const data = this.fetchedData[e.relatedTarget.value];
94
+ if (e.relatedTarget.value && data) {
95
+ e.value = e.relatedTarget.value = '';
96
+ this.createItemPanel({ ...data, display: "true" });
97
+ }
98
+ });
63
99
  },
64
100
  },
65
101
 
@@ -1,4 +1,5 @@
1
1
  import Core from 'spotlight/core'
2
+
2
3
  (function ($){
3
4
  Core.Block = SirTrevor.Block.extend({
4
5
  scribeOptions: {
@@ -8,22 +8,18 @@ SirTrevor.Blocks.Browse = (function(){
8
8
  icon_name: "browse",
9
9
 
10
10
  autocomplete_url: function() {
11
- return $(this.inner).closest('form[data-autocomplete-exhibit-searches-path]').data('autocomplete-exhibit-searches-path').replace("%25QUERY", "%QUERY");
11
+ return document.getElementById(this.instanceID).closest('form[data-autocomplete-exhibit-searches-path]').dataset.autocompleteExhibitSearchesPath;
12
+ },
13
+
14
+ autocomplete_fetch: function(url) {
15
+ return this.fetchOnceAndFilterLocalResults(url);
12
16
  },
13
17
 
14
18
  autocomplete_template: function(obj) {
15
19
  const thumbnail = obj.thumbnail_image_url ? `<div class="document-thumbnail"><img class="img-thumbnail" src="${obj.thumbnail_image_url}" /></div>` : ''
20
+ const description = obj.description ? `<small>&nbsp;&nbsp;${obj.description}</small>` : '';
16
21
  return `<div class="autocomplete-item${!obj.published ? ' blacklight-private' : ''}">${thumbnail}
17
- <span class="autocomplete-title">${obj.full_title}</span><br/><small>&nbsp;&nbsp;${obj.description}</small></div>`
18
- },
19
-
20
- bloodhoundOptions: function() {
21
- return {
22
- prefetch: {
23
- url: this.autocomplete_url(),
24
- ttl: 0
25
- }
26
- };
22
+ <span class="autocomplete-title">${this.highlight(obj.full_title)}</span>${description}</div>`;
27
23
  },
28
24
 
29
25
  _itemPanel: function(data) {
@@ -36,7 +32,7 @@ SirTrevor.Blocks.Browse = (function(){
36
32
  }
37
33
  var resource_id = data.slug || data.id;
38
34
  var markup = `
39
- <li class="field form-inline dd-item dd3-item" data-resource-id="${resource_id}" data-id="${index}" id="${this.formId(index)}">
35
+ <li class="field dd-item dd3-item" data-resource-id="${resource_id}" data-id="${index}" id="${this.formId(index)}">
40
36
  <input type="hidden" name="item[${index}][id]" value="${resource_id}" />
41
37
  <input type="hidden" name="item[${index}][full_title]" value="${(data.full_title || data.title)}" />
42
38
  <input data-property="weight" type="hidden" name="item[${index}][weight]" value="${data.weight}" />