arclight 0.0.1 → 1.6.2

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 (204) hide show
  1. checksums.yaml +5 -5
  2. data/.all-contributorsrc +450 -0
  3. data/.codeclimate.yml +5 -0
  4. data/.eslintrc +22 -0
  5. data/.github/workflows/ruby.yml +51 -0
  6. data/.gitignore +7 -0
  7. data/.rspec +0 -1
  8. data/.rubocop.yml +521 -0
  9. data/.rubocop_todo.yml +122 -0
  10. data/.solr_wrapper +6 -0
  11. data/CONTRIBUTING.md +43 -0
  12. data/CONTRIBUTORS.md +79 -0
  13. data/Gemfile +41 -0
  14. data/LICENSE.txt +1 -0
  15. data/README.md +100 -12
  16. data/Rakefile +13 -3
  17. data/app/assets/images/arclight/logo.png +0 -0
  18. data/app/assets/images/blacklight/bookmark.svg +1 -0
  19. data/app/assets/images/blacklight/collection.svg +5 -0
  20. data/app/assets/images/blacklight/compact.svg +1 -0
  21. data/app/assets/images/blacklight/container.svg +5 -0
  22. data/app/assets/images/blacklight/ead.svg +1 -0
  23. data/app/assets/images/blacklight/file.svg +5 -0
  24. data/app/assets/images/blacklight/folder.svg +1 -0
  25. data/app/assets/images/blacklight/list.svg +1 -0
  26. data/app/assets/images/blacklight/minus.svg +1 -0
  27. data/app/assets/images/blacklight/online.svg +5 -0
  28. data/app/assets/images/blacklight/pdf.svg +1 -0
  29. data/app/assets/images/blacklight/plus.svg +1 -0
  30. data/app/assets/images/blacklight/repository.svg +1 -0
  31. data/app/assets/javascripts/arclight/arclight.js +4 -0
  32. data/app/assets/javascripts/arclight/oembed_controller.js +58 -0
  33. data/app/assets/javascripts/arclight/truncate_controller.js +27 -0
  34. data/app/assets/stylesheets/arclight/application.scss +14 -0
  35. data/app/assets/stylesheets/arclight/build.scss +4 -0
  36. data/app/assets/stylesheets/arclight/modules/collection_search.scss +3 -0
  37. data/app/assets/stylesheets/arclight/modules/hierarchy_and_online_contents.scss +155 -0
  38. data/app/assets/stylesheets/arclight/modules/highlights.scss +11 -0
  39. data/app/assets/stylesheets/arclight/modules/icons.scss +20 -0
  40. data/app/assets/stylesheets/arclight/modules/layout.scss +220 -0
  41. data/app/assets/stylesheets/arclight/modules/mastheads.scss +42 -0
  42. data/app/assets/stylesheets/arclight/modules/repositories.scss +25 -0
  43. data/app/assets/stylesheets/arclight/modules/repository_card.scss +9 -0
  44. data/app/assets/stylesheets/arclight/modules/search_form.scss +9 -0
  45. data/app/assets/stylesheets/arclight/modules/search_results.scss +250 -0
  46. data/app/assets/stylesheets/arclight/modules/show_collection.scss +38 -0
  47. data/app/assets/stylesheets/arclight/modules/truncator.scss +58 -0
  48. data/app/assets/stylesheets/arclight/responsive.scss +13 -0
  49. data/app/assets/stylesheets/arclight/variables.scss +25 -0
  50. data/app/components/arclight/access_component.html.erb +14 -0
  51. data/app/components/arclight/access_component.rb +25 -0
  52. data/app/components/arclight/bookmark_component.html.erb +25 -0
  53. data/app/components/arclight/bookmark_component.rb +9 -0
  54. data/app/components/arclight/breadcrumb_component.rb +50 -0
  55. data/app/components/arclight/breadcrumbs_hierarchy_component.html.erb +34 -0
  56. data/app/components/arclight/breadcrumbs_hierarchy_component.rb +25 -0
  57. data/app/components/arclight/collection_context_component.html.erb +12 -0
  58. data/app/components/arclight/collection_context_component.rb +27 -0
  59. data/app/components/arclight/collection_info_component.html.erb +28 -0
  60. data/app/components/arclight/collection_info_component.rb +26 -0
  61. data/app/components/arclight/collection_sidebar_component.html.erb +15 -0
  62. data/app/components/arclight/collection_sidebar_component.rb +39 -0
  63. data/app/components/arclight/constraints_component.html.erb +7 -0
  64. data/app/components/arclight/constraints_component.rb +17 -0
  65. data/app/components/arclight/document_collection_context_component.html.erb +29 -0
  66. data/app/components/arclight/document_collection_context_component.rb +28 -0
  67. data/app/components/arclight/document_collection_hierarchy_component.html.erb +50 -0
  68. data/app/components/arclight/document_collection_hierarchy_component.rb +62 -0
  69. data/app/components/arclight/document_component.html.erb +39 -0
  70. data/app/components/arclight/document_component.rb +49 -0
  71. data/app/components/arclight/document_components_hierarchy_component.html.erb +20 -0
  72. data/app/components/arclight/document_components_hierarchy_component.rb +34 -0
  73. data/app/components/arclight/document_download_component.html.erb +24 -0
  74. data/app/components/arclight/document_download_component.rb +71 -0
  75. data/app/components/arclight/embed_component.html.erb +10 -0
  76. data/app/components/arclight/embed_component.rb +43 -0
  77. data/app/components/arclight/expand_hierarchy_button_component.html.erb +5 -0
  78. data/app/components/arclight/expand_hierarchy_button_component.rb +16 -0
  79. data/app/components/arclight/group_component.html.erb +34 -0
  80. data/app/components/arclight/group_component.rb +23 -0
  81. data/app/components/arclight/header_component.html.erb +5 -0
  82. data/app/components/arclight/header_component.rb +10 -0
  83. data/app/components/arclight/index_metadata_field_component.html.erb +16 -0
  84. data/app/components/arclight/index_metadata_field_component.rb +20 -0
  85. data/app/components/arclight/masthead_component.html.erb +17 -0
  86. data/app/components/arclight/masthead_component.rb +10 -0
  87. data/app/components/arclight/metadata_section_component.html.erb +13 -0
  88. data/app/components/arclight/metadata_section_component.rb +22 -0
  89. data/app/components/arclight/oembed_viewer_component.html.erb +5 -0
  90. data/app/components/arclight/oembed_viewer_component.rb +16 -0
  91. data/app/components/arclight/online_content_filter_component.html.erb +15 -0
  92. data/app/components/arclight/online_content_filter_component.rb +17 -0
  93. data/app/components/arclight/online_status_indicator_component.rb +19 -0
  94. data/app/components/arclight/repository_breadcrumb_component.html.erb +10 -0
  95. data/app/components/arclight/repository_breadcrumb_component.rb +17 -0
  96. data/app/components/arclight/repository_location_component.html.erb +20 -0
  97. data/app/components/arclight/repository_location_component.rb +10 -0
  98. data/app/components/arclight/search_bar_component.html.erb +27 -0
  99. data/app/components/arclight/search_bar_component.rb +32 -0
  100. data/app/components/arclight/search_result_breadcrumbs_component.html.erb +6 -0
  101. data/app/components/arclight/search_result_breadcrumbs_component.rb +37 -0
  102. data/app/components/arclight/search_result_component.html.erb +20 -0
  103. data/app/components/arclight/search_result_component.rb +18 -0
  104. data/app/components/arclight/search_result_title_component.html.erb +15 -0
  105. data/app/components/arclight/search_result_title_component.rb +15 -0
  106. data/app/components/arclight/sidebar_component.html.erb +9 -0
  107. data/app/components/arclight/sidebar_component.rb +19 -0
  108. data/app/components/arclight/upper_metadata_layout_component.rb +10 -0
  109. data/app/components/blacklight/icons/bookmark_component.rb +12 -0
  110. data/app/components/blacklight/icons/collection_component.rb +14 -0
  111. data/app/components/blacklight/icons/compact_component.rb +12 -0
  112. data/app/components/blacklight/icons/container_component.rb +14 -0
  113. data/app/components/blacklight/icons/ead_component.rb +12 -0
  114. data/app/components/blacklight/icons/file_component.rb +14 -0
  115. data/app/components/blacklight/icons/folder_component.rb +12 -0
  116. data/app/components/blacklight/icons/online_component.rb +15 -0
  117. data/app/components/blacklight/icons/pdf_component.rb +12 -0
  118. data/app/components/blacklight/icons/repository_component.rb +12 -0
  119. data/app/controllers/arclight/repositories_controller.rb +40 -0
  120. data/app/helpers/arclight/ead_format_helpers.rb +309 -0
  121. data/app/helpers/arclight/field_config_helpers.rb +26 -0
  122. data/app/helpers/arclight_helper.rb +108 -0
  123. data/app/models/arclight/document_downloads.rb +122 -0
  124. data/app/models/arclight/parent.rb +24 -0
  125. data/app/models/arclight/parents.rb +39 -0
  126. data/app/models/arclight/requests/aeon_external_request.rb +42 -0
  127. data/app/models/arclight/requests/aeon_web_ead.rb +52 -0
  128. data/app/models/arclight/requests/google_form.rb +49 -0
  129. data/app/models/concerns/arclight/catalog.rb +44 -0
  130. data/app/models/concerns/arclight/search_behavior.rb +47 -0
  131. data/app/models/concerns/arclight/solr_document.rb +160 -0
  132. data/app/presenters/arclight/index_presenter.rb +10 -0
  133. data/app/presenters/arclight/show_presenter.rb +54 -0
  134. data/app/views/arclight/.keep +0 -0
  135. data/app/views/arclight/_requests.html.erb +7 -0
  136. data/app/views/arclight/repositories/_repository.html.erb +43 -0
  137. data/app/views/arclight/repositories/index.html.erb +9 -0
  138. data/app/views/arclight/repositories/show.html.erb +37 -0
  139. data/app/views/arclight/requests/_aeon_external_request_endpoint.html.erb +8 -0
  140. data/app/views/arclight/requests/_aeon_web_ead.html.erb +6 -0
  141. data/app/views/arclight/requests/_google_form.html.erb +10 -0
  142. data/app/views/catalog/_document_list.html.erb +8 -0
  143. data/app/views/catalog/_group.html.erb +4 -0
  144. data/app/views/catalog/_group_toggle.html.erb +10 -0
  145. data/app/views/catalog/_search_results_header.html.erb +3 -0
  146. data/app/views/catalog/hierarchy.html.erb +19 -0
  147. data/app/views/catalog/index.html.erb +17 -0
  148. data/app/views/shared/_breadcrumbs.html.erb +3 -0
  149. data/app/views/shared/_main_menu_links.html.erb +6 -0
  150. data/arclight.gemspec +29 -11
  151. data/bin/rails +13 -0
  152. data/config/breadcrumbs.rb +24 -0
  153. data/config/i18n-tasks.yml +134 -0
  154. data/config/importmap.rb +3 -0
  155. data/config/locales/arclight.en.yml +100 -0
  156. data/config/repositories.yml +0 -0
  157. data/config/routes.rb +6 -0
  158. data/docker-compose.yml +16 -0
  159. data/lib/arclight/digital_object.rb +27 -0
  160. data/lib/arclight/engine.rb +47 -0
  161. data/lib/arclight/exceptions.rb +19 -0
  162. data/lib/arclight/hash_absolute_xpath.rb +62 -0
  163. data/lib/arclight/level_label.rb +46 -0
  164. data/lib/arclight/missing_id_strategy.rb +21 -0
  165. data/lib/arclight/normalized_date.rb +49 -0
  166. data/lib/arclight/normalized_id.rb +30 -0
  167. data/lib/arclight/normalized_title.rb +33 -0
  168. data/lib/arclight/repository.rb +116 -0
  169. data/lib/arclight/routes/hierarchy.rb +19 -0
  170. data/lib/arclight/routes.rb +8 -0
  171. data/lib/arclight/traject/ead2_component_config.rb +335 -0
  172. data/lib/arclight/traject/ead2_config.rb +316 -0
  173. data/lib/arclight/traject/nokogiri_namespaceless_reader.rb +22 -0
  174. data/lib/arclight/version.rb +3 -1
  175. data/lib/arclight/year_range.rb +111 -0
  176. data/lib/arclight.rb +11 -1
  177. data/lib/generators/arclight/install_generator.rb +163 -0
  178. data/lib/generators/arclight/templates/arclight.scss +6 -0
  179. data/lib/generators/arclight/templates/catalog_controller.rb +423 -0
  180. data/lib/generators/arclight/templates/config/downloads.yml +25 -0
  181. data/lib/generators/arclight/templates/config/locales/arclight.en.yml +61 -0
  182. data/lib/generators/arclight/templates/config/repositories.yml +53 -0
  183. data/lib/generators/arclight/update_generator.rb +22 -0
  184. data/lib/tasks/index.rake +84 -0
  185. data/package.json +30 -0
  186. data/solr/conf/_rest_managed.json +3 -0
  187. data/solr/conf/admin-extra.html +31 -0
  188. data/solr/conf/elevate.xml +36 -0
  189. data/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  190. data/solr/conf/protwords.txt +21 -0
  191. data/solr/conf/schema.xml +400 -0
  192. data/solr/conf/solrconfig.xml +328 -0
  193. data/solr/conf/spellings.txt +2 -0
  194. data/solr/conf/stopwords.txt +58 -0
  195. data/solr/conf/stopwords_en.txt +58 -0
  196. data/solr/conf/synonyms.txt +31 -0
  197. data/solr/conf/xslt/example.xsl +132 -0
  198. data/solr/conf/xslt/example_atom.xsl +67 -0
  199. data/solr/conf/xslt/example_rss.xsl +66 -0
  200. data/solr/conf/xslt/luke.xsl +337 -0
  201. data/tasks/arclight.rake +79 -0
  202. data/template.rb +10 -0
  203. metadata +440 -20
  204. data/.travis.yml +0 -5
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Draw the links to the collection info in the sidebar
5
+ class CollectionSidebarComponent < ViewComponent::Base
6
+ def initialize(document:, partials:, collection_presenter:)
7
+ super()
8
+
9
+ @document = document
10
+ @partials = Array(partials)
11
+ @collection_presenter = collection_presenter
12
+ end
13
+
14
+ attr_reader :document, :partials, :collection_presenter
15
+
16
+ def has_section?(section)
17
+ # Access field data comes from repositories.yml not from solr, so handle it in a different way.
18
+ return true if section == :access_field
19
+
20
+ collection_presenter.with_field_group(section).fields_to_render.any?
21
+ end
22
+
23
+ def document_section_path(section)
24
+ [document_path, section_anchor(section)].join
25
+ end
26
+
27
+ def section_label(section)
28
+ t("arclight.views.show.sections.#{section}")
29
+ end
30
+
31
+ def document_path
32
+ @document_path ||= solr_document_path(document.root)
33
+ end
34
+
35
+ def section_anchor(section)
36
+ "##{t("arclight.views.show.sections.#{section}").parameterize}"
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ <% if repository %>
2
+ <div class="al-repository-card">
3
+ <%= render repository %>
4
+ </div>
5
+ <% end %>
6
+
7
+ <%= render Blacklight::ConstraintsComponent.new(**@kwargs) %>
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Extend the upstream constraints with breadcrumbs and
5
+ # repository context information
6
+ class ConstraintsComponent < Blacklight::ConstraintsComponent
7
+ def initialize(**kwargs)
8
+ super
9
+
10
+ @kwargs = kwargs
11
+ end
12
+
13
+ def repository
14
+ @repository ||= helpers.repository_faceted_on
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ <%= tag.tr(
2
+ id: @document.id,
3
+ data: {
4
+ 'document-id': @document.id.to_s.parameterize,
5
+ 'document-counter': @counter,
6
+ },
7
+ itemscope: true,
8
+ itemtype: @document.itemtype,
9
+ class: classes.join(' ')) do %>
10
+ <td data-document-id="<%= document.id %>">
11
+ <div class="index_title document-title-heading" data-turbo="false">
12
+ <%= helpers.link_to_document document, counter: @counter %>
13
+ <% if document.children? %>
14
+ <span class="badge badge-pill bg-secondary badge-secondary al-number-of-children-badge"><%= document.number_of_children %><span class="sr-only visually-hidden"><%= t(:'arclight.views.index.number_of_components', count: document.number_of_children) %></span></span>
15
+ <% end %>
16
+ </div>
17
+ </td>
18
+
19
+ <%= tag.td class: 'text-muted' do %>
20
+ <%= document.containers.join(', ') %>
21
+ <% end %>
22
+
23
+ <%= tag.td class: 'text-end' do %>
24
+ <%= helpers.render_index_doc_actions document, wrapping_class: 'd-inline-flex justify-content-end' %>
25
+ <% end %>
26
+ </div>
27
+
28
+ <%= render Arclight::IndexMetadataFieldComponent.with_collection(@presenter&.field_presenters.select { |field| field.field_config.collection_context }, classes: ['col pl-0 my-0']) %>
29
+ <% end %>
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Display a single document in the collection
5
+ class DocumentCollectionContextComponent < Arclight::SearchResultComponent
6
+ # @param [SolrDocument] document
7
+ def initialize(document: nil, blacklight_config: nil, **kwargs)
8
+ super(document: document, **kwargs)
9
+ @blacklight_config = blacklight_config
10
+ end
11
+
12
+ attr_reader :blacklight_config
13
+
14
+ def classes
15
+ (super - ['row'] + ['al-collection-context']).flatten
16
+ end
17
+
18
+ private
19
+
20
+ def online_status
21
+ render online_status_component.new(document: @document)
22
+ end
23
+
24
+ def online_status_component
25
+ blacklight_config.show.online_status_component || Arclight::OnlineStatusIndicatorComponent
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,50 @@
1
+ <%= content_tag :li,
2
+ id: "#{@document.id}-hierarchy-item",
3
+ data: {
4
+ 'document-id': @document.id.to_s.parameterize,
5
+ 'document-counter': @counter,
6
+ },
7
+ itemscope: true,
8
+ itemtype: @document.itemtype,
9
+ class: (classes.flatten + ['al-collection-context', ('al-hierarchy-highlight' if current_target?)].compact).join(' ') do %>
10
+ <div class="documentHeader" data-document-id="<%= document.id %>">
11
+ <% if document.children? %>
12
+ <%= link_to('',
13
+ "#collapsible-hierarchy-#{document.id}",
14
+ class: "al-toggle-view-children#{ ' collapsed' unless show_expanded?}",
15
+ aria: {
16
+ label: t('arclight.hierarchy.view_all'),
17
+ expanded: show_expanded?
18
+ },
19
+ data: {
20
+ bs_toggle: 'collapse',
21
+ toggle: 'collapse'
22
+ }
23
+ )
24
+ %>
25
+ <% end %>
26
+ <div class="index_title document-title-heading" data-turbo="false">
27
+ <% if current_target? %>
28
+ <%= document.normalized_title %>
29
+ <% else %>
30
+ <%= helpers.link_to_document document, counter: @counter %>
31
+ <% end %>
32
+ <% if document.children? %>
33
+ <span class="badge badge-pill bg-secondary badge-secondary al-number-of-children-badge"><%= document.number_of_children %><span class="sr-only visually-hidden"><%= t(:'arclight.views.index.number_of_components', count: document.number_of_children) %></span></span>
34
+ <% end %>
35
+ <%= online_status %>
36
+ </div>
37
+ <%= content_tag('div', class: 'al-document-container text-muted') do %>
38
+ <%= document.containers.join(', ') %>
39
+ <% end if document.containers.present? %>
40
+ </div>
41
+
42
+ <%= render Arclight::IndexMetadataFieldComponent.with_collection(@presenter&.field_presenters.select { |field| field.field_config.collection_context }, classes: ['col pl-0 my-0']) %>
43
+ <% if document.number_of_children > 0 %>
44
+ <%= content_tag(:div, id: "collapsible-hierarchy-#{document.id}",
45
+ class: "collapse al-collection-context-collapsible al-hierarchy-level-#{document.component_level} #{'show' if show_expanded?}"
46
+ ) do %>
47
+ <%= render Arclight::DocumentComponentsHierarchyComponent.new(document: @document, target_index: target_index) %>
48
+ <% end %>
49
+ <% end %>
50
+ <% end %>
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Display a single document in the collection hierarchy
5
+ class DocumentCollectionHierarchyComponent < Arclight::SearchResultComponent
6
+ # @param [Blacklight::DocumentPresenter] document
7
+ # @param [String] nest_path determines which element to highlight
8
+ def initialize(document: nil, nest_path: nil, blacklight_config: nil, **kwargs)
9
+ super(document: document, **kwargs)
10
+ @nest_path = nest_path
11
+ @blacklight_config = blacklight_config
12
+ end
13
+
14
+ attr_reader :blacklight_config
15
+
16
+ def classes
17
+ super - ['row']
18
+ end
19
+
20
+ # we want to eager-load this document's children if we're in the
21
+ # target document's component hierarchy
22
+ def show_expanded?
23
+ within_original_tree?
24
+ end
25
+
26
+ # Solr nest paths are constructed using the path name and an index, e.g.
27
+ # `/components#5/components#3`.
28
+ #
29
+ # @return [String] the targeted index for this level of the hierarchy
30
+ def target_index
31
+ return -1 unless within_original_tree?
32
+
33
+ remaining_path = nest_path.sub("#{@document.nest_path}/", '')
34
+ current_component, _rest = remaining_path.split('/', 2)
35
+ _name, index = current_component.split('#', 2)
36
+
37
+ index&.to_i
38
+ end
39
+
40
+ private
41
+
42
+ # We're in the targeted document's original tree if the target nest path
43
+ # includes this document's path.
44
+ def within_original_tree?
45
+ nest_path&.start_with? "#{@document.nest_path}/"
46
+ end
47
+
48
+ def current_target?
49
+ nest_path == @document.nest_path
50
+ end
51
+
52
+ def online_status
53
+ render online_status_component.new(document: @document)
54
+ end
55
+
56
+ def online_status_component
57
+ blacklight_config.show.online_status_component || Arclight::OnlineStatusIndicatorComponent
58
+ end
59
+
60
+ attr_reader :nest_path
61
+ end
62
+ end
@@ -0,0 +1,39 @@
1
+ <div class='d-md-flex justify-content-between al-show'>
2
+ <div class='al-show-breadcrumb'>
3
+ <nav aria-label="breadcrumb">
4
+ <%= render breadcrumb_component.new(presenter: presenter) %>
5
+ </nav>
6
+ </div>
7
+ </div>
8
+
9
+ <div class="title-container">
10
+ <%= content_tag :h1 do %>
11
+ <%= document.normalized_title %>
12
+ <% end %>
13
+ <%= render 'arclight/requests', document: document %>
14
+ <%= render Arclight::BookmarkComponent.new document: document, action: bookmark_config %>
15
+ <%= toggle_sidebar %>
16
+ <%= online_filter %>
17
+ </div>
18
+
19
+ <%= render Arclight::MetadataSectionComponent.with_collection(component_metadata_partials,
20
+ metadata_attr: { layout: Arclight::UpperMetadataLayoutComponent },
21
+ presenter: presenter) unless document.collection? %>
22
+
23
+ <%= embed %>
24
+
25
+ <% if document.collection? %>
26
+ <div id="metadata">
27
+ <h2 class="sr-only visually-hidden"><%= t 'arclight.views.show.context' %></h2>
28
+ <%= render Arclight::MetadataSectionComponent.with_collection(metadata_partials,
29
+ metadata_attr: { layout: Arclight::UpperMetadataLayoutComponent },
30
+ presenter: presenter, heading: true) %>
31
+ </div>
32
+ <% elsif document.children? %>
33
+ <div id="contents">
34
+ <h2 class="al-show-sub-heading"><%= t 'arclight.views.show.contents' %></h2>
35
+ <%= helpers.turbo_frame_tag "al-hierarchy-#{document.id}-document", loading: 'lazy', src: helpers.hierarchy_solr_document_path(id: document.id, paginate: true, key: '-document', per_page: 50) %>
36
+ </div>
37
+ <% end %>
38
+
39
+ <%= access %>
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Render a single document
5
+ class DocumentComponent < Blacklight::DocumentComponent
6
+ attr_reader :document
7
+
8
+ def online_content?
9
+ document.online_content? && (document.collection? || document.children?)
10
+ end
11
+
12
+ def blacklight_config
13
+ presenter.configuration
14
+ end
15
+
16
+ # @return [Blacklight::Configuration::ToolConfig] the configuration for the bookmark
17
+ def bookmark_config
18
+ blacklight_config.index.document_actions.arclight_bookmark_control
19
+ end
20
+
21
+ def breadcrumb_component
22
+ blacklight_config.show.breadcrumb_component || Arclight::BreadcrumbsHierarchyComponent
23
+ end
24
+
25
+ def metadata_partials
26
+ blacklight_config.show.metadata_partials || []
27
+ end
28
+
29
+ def component_metadata_partials
30
+ blacklight_config.show.component_metadata_partials || []
31
+ end
32
+
33
+ def online_filter
34
+ render Arclight::OnlineContentFilterComponent.new(document: document)
35
+ end
36
+
37
+ def access
38
+ render (blacklight_config.show.access_component || Arclight::AccessComponent).new(presenter: presenter)
39
+ end
40
+
41
+ def toggle_sidebar
42
+ button_tag(t('arclight.views.show.toggle_sidebar'),
43
+ type: :button,
44
+ class: 'btn btn-sm btn-secondary d-lg-none sidebar-toggle',
45
+ data: { bs_toggle: 'offcanvas', bs_target: '#sidebar' },
46
+ aria: { controls: 'sidebar' })
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ <% if paginate? && @target_index >= 0 && @target_index >= @maximum_left_gap %>
2
+ <%# render the hierarchy as: outer (left) window ... window ... because our current document hierarchy is so far down the list it'd be buried %>
3
+ <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-left", loading: 'lazy', src: hierarchy_path(limit: @left_outer_window, key: '-left') %>
4
+ <%= tag.turbo_frame id: "al-hierarchy-#{@document.id}-gap" do %>
5
+ <%= render expand_hierarchy_component.new(path: hierarchy_path(offset: @left_outer_window, limit: @target_index - @left_outer_window - (@window / 2), key: '-gap')) %>
6
+ <% end %>
7
+ <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-window", src: hierarchy_path(offset: @target_index - (@window / 2), limit: @window, key: '-window') %>
8
+ <%= tag.turbo_frame id: "al-hierarchy-#{@document.id}-right" do %>
9
+ <%= render expand_hierarchy_component.new(path: hierarchy_path(offset: @target_index + (@window / 2), key: '-right')) %>
10
+ <% end %>
11
+ <% elsif paginate? %>
12
+ <%# render the first N documents, and let the user expand the remaining if desired %>
13
+ <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-sidebar", loading: ('lazy' unless @target_index >= 0), src: hierarchy_path(limit: @maximum_left_gap, key: '-sidebar') %>
14
+ <%= tag.turbo_frame id: "al-hierarchy-#{@document.id}-right" do %>
15
+ <%= render expand_hierarchy_component.new(path: hierarchy_path(offset: @maximum_left_gap, key: '-right')) %>
16
+ <% end %>
17
+ <% else %>
18
+ <%# there aren't enough to bother paginating, so load them all at once %>
19
+ <%= helpers.turbo_frame_tag "al-hierarchy-#{@document.id}-sidebar", loading: ('lazy' unless @target_index >= 0), src: hierarchy_path(key: '-sidebar') %>
20
+ <% end %>
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Display a document's constituent components with appropriate lazy-loading
5
+ # to keep the page load time reasonable.
6
+ class DocumentComponentsHierarchyComponent < ViewComponent::Base
7
+ # rubocop:disable Metrics/ParameterLists
8
+ def initialize(document: nil, target_index: -1, minimum_pagination_size: 20, left_outer_window: 3, maximum_left_gap: 10, window: 10)
9
+ super()
10
+
11
+ @document = document
12
+ @target_index = target_index&.to_i || -1
13
+ @minimum_pagination_size = minimum_pagination_size
14
+ @left_outer_window = left_outer_window
15
+ @maximum_left_gap = maximum_left_gap
16
+ @window = window
17
+ end
18
+ # rubocop:enable Metrics/ParameterLists
19
+
20
+ def paginate?
21
+ @document.number_of_children > @minimum_pagination_size
22
+ end
23
+
24
+ def hierarchy_path(**kwargs)
25
+ helpers.hierarchy_solr_document_path(id: @document.id, hierarchy: true, nest_path: params[:nest_path], **kwargs)
26
+ end
27
+
28
+ def expand_hierarchy_component
29
+ blacklight_config.show.expand_hierarchy_component || Arclight::ExpandHierarchyButtonComponent
30
+ end
31
+
32
+ delegate :blacklight_config, to: :helpers
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ <div class="al-show-actions-box-downloads-container">
2
+ <% case files.size %>
3
+ <% when 2 %>
4
+ <div class="dropdown">
5
+ <button class="btn btn-secondary btn-sm dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
6
+ <%= t("arclight.views.show.download.default") %>
7
+ </button>
8
+ <ul class="dropdown-menu">
9
+ <% files.each do |file| %>
10
+ <li><%= link_to(file.href, class: "dropdown-item", **@link_options) do %>
11
+ <%= icon_for(file.type) %> <%= dropdown_label(file) %>
12
+ <% end %>
13
+ </li>
14
+ <% end %>
15
+ </ul>
16
+ </div>
17
+ <% when 1 %>
18
+ <% files.each do |file| %>
19
+ <%= link_to(file.href, class: "btn btn-secondary btn-sm", **@link_options) do %>
20
+ <%= download_icon %> <%= label(file) %>
21
+ <% end %>
22
+ <% end %>
23
+ <% end %>
24
+ </div>
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # ViewComponent for rendering a single document download link
5
+ class DocumentDownloadComponent < ViewComponent::Base
6
+ def initialize(downloads:, **kwargs)
7
+ super()
8
+
9
+ @downloads = downloads
10
+ @link_options = kwargs
11
+ end
12
+
13
+ attr_reader :downloads
14
+
15
+ delegate :files, to: :downloads
16
+
17
+ def render?
18
+ files.present?
19
+ end
20
+
21
+ # i18n-tasks-use t('arclight.views.show.download.multiple.pdf')
22
+ # i18n-tasks-use t('arclight.views.show.download.multiple.ead')
23
+ def dropdown_label(file)
24
+ t(file.type, scope: 'arclight.views.show.download.multiple')
25
+ end
26
+
27
+ # i18n-tasks-use t('arclight.views.show.download.single.pdf')
28
+ # i18n-tasks-use t('arclight.views.show.download.single.ead')
29
+ def label(file)
30
+ t(file.type, scope: 'arclight.views.show.download.single')
31
+ end
32
+
33
+ # From https://icons.getbootstrap.com/icons/download/
34
+ def download_icon
35
+ icon = <<~HTML
36
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16">
37
+ <path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z"/>
38
+ <path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z"/>
39
+ </svg>
40
+ HTML
41
+ icon.html_safe # rubocop:disable Rails/OutputSafety
42
+ end
43
+
44
+ # This is an extension point where one could configure a different icon per file type
45
+ def icon_for(type)
46
+ public_send("#{type}_icon")
47
+ end
48
+
49
+ # From https://icons.getbootstrap.com/icons/file-earmark-pdf/
50
+ def pdf_icon
51
+ icon = <<~HTML
52
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-pdf" viewBox="0 0 16 16">
53
+ <path d="M14 14V4.5L9.5 0H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2zM9.5 3A1.5 1.5 0 0 0 11 4.5h2V14a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h5.5v2z"/>
54
+ <path d="M4.603 14.087a.81.81 0 0 1-.438-.42c-.195-.388-.13-.776.08-1.102.198-.307.526-.568.897-.787a7.68 7.68 0 0 1 1.482-.645 19.697 19.697 0 0 0 1.062-2.227 7.269 7.269 0 0 1-.43-1.295c-.086-.4-.119-.796-.046-1.136.075-.354.274-.672.65-.823.192-.077.4-.12.602-.077a.7.7 0 0 1 .477.365c.088.164.12.356.127.538.007.188-.012.396-.047.614-.084.51-.27 1.134-.52 1.794a10.954 10.954 0 0 0 .98 1.686 5.753 5.753 0 0 1 1.334.05c.364.066.734.195.96.465.12.144.193.32.2.518.007.192-.047.382-.138.563a1.04 1.04 0 0 1-.354.416.856.856 0 0 1-.51.138c-.331-.014-.654-.196-.933-.417a5.712 5.712 0 0 1-.911-.95 11.651 11.651 0 0 0-1.997.406 11.307 11.307 0 0 1-1.02 1.51c-.292.35-.609.656-.927.787a.793.793 0 0 1-.58.029zm1.379-1.901c-.166.076-.32.156-.459.238-.328.194-.541.383-.647.547-.094.145-.096.25-.04.361.01.022.02.036.026.044a.266.266 0 0 0 .035-.012c.137-.056.355-.235.635-.572a8.18 8.18 0 0 0 .45-.606zm1.64-1.33a12.71 12.71 0 0 1 1.01-.193 11.744 11.744 0 0 1-.51-.858 20.801 20.801 0 0 1-.5 1.05zm2.446.45c.15.163.296.3.435.41.24.19.407.253.498.256a.107.107 0 0 0 .07-.015.307.307 0 0 0 .094-.125.436.436 0 0 0 .059-.2.095.095 0 0 0-.026-.063c-.052-.062-.2-.152-.518-.209a3.876 3.876 0 0 0-.612-.053zM8.078 7.8a6.7 6.7 0 0 0 .2-.828c.031-.188.043-.343.038-.465a.613.613 0 0 0-.032-.198.517.517 0 0 0-.145.04c-.087.035-.158.106-.196.283-.04.192-.03.469.046.822.024.111.054.227.09.346z"/>
55
+ </svg>
56
+ HTML
57
+ icon.html_safe # rubocop:disable Rails/OutputSafety
58
+ end
59
+
60
+ # From https://icons.getbootstrap.com/icons/file-earmark-code/
61
+ def ead_icon
62
+ icon = <<~HTML
63
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-file-earmark-code" viewBox="0 0 16 16">
64
+ <path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
65
+ <path d="M8.646 6.646a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L10.293 9 8.646 7.354a.5.5 0 0 1 0-.708zm-1.292 0a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0 0 .708l2 2a.5.5 0 0 0 .708-.708L5.707 9l1.647-1.646a.5.5 0 0 0 0-.708z"/>
66
+ </svg>
67
+ HTML
68
+ icon.html_safe # rubocop:disable Rails/OutputSafety
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ <div id="embed" class="mb-2">
2
+ <h2 class="al-show-sub-heading"><%= t('arclight.views.show.embedded_content') %></h2>
3
+ <%= render Arclight::OembedViewerComponent.with_collection(embeddable_resources, document: @document) %>
4
+
5
+ <% linked_resources.each do |resource| %>
6
+ <div class="al-digital-object">
7
+ <%= link_to(resource.label, resource.href) %>
8
+ </div>
9
+ <% end %>
10
+ </div>
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Render digital object links for a document
5
+ class EmbedComponent < ViewComponent::Base
6
+ def initialize(document:, presenter:, **kwargs) # rubocop:disable Lint/UnusedMethodArgument
7
+ super()
8
+
9
+ @document = document
10
+ @presenter = presenter
11
+ end
12
+
13
+ def render?
14
+ resources.any?
15
+ end
16
+
17
+ def embeddable_resources
18
+ resources.first(1).select { |object| embeddable?(object) }
19
+ end
20
+
21
+ def linked_resources
22
+ resources - embeddable_resources
23
+ end
24
+
25
+ def resources
26
+ @resources ||= @document.digital_objects || []
27
+ end
28
+
29
+ def depth
30
+ @document.parents.length || 0
31
+ end
32
+
33
+ def embeddable?(object)
34
+ exclude_patterns.none? do |pattern|
35
+ object.href =~ pattern
36
+ end
37
+ end
38
+
39
+ def exclude_patterns
40
+ Arclight::Engine.config.oembed_resource_exclude_patterns
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ <ul>
2
+ <li>
3
+ <%= expand_link %>
4
+ </li>
5
+ </ul>
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Component for rendering an expand button in the hierarchy view
5
+ class ExpandHierarchyButtonComponent < Blacklight::Component
6
+ def initialize(path:, classes: 'btn btn-secondary btn-sm')
7
+ super()
8
+ @path = path
9
+ @classes = classes
10
+ end
11
+
12
+ def expand_link
13
+ link_to t('arclight.views.show.expand'), @path, class: @classes
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ <div class='al-grouped-title-bar'>
2
+ <div class='row'>
3
+ <div class='col-md-12'>
4
+ <% if document.repository_config.present? %>
5
+ <div class='al-grouped-repository breadcrumb-links'>
6
+ <%= link_to(document.repository_config.name, helpers.arclight_engine.repository_path(document.repository_config.slug)) %>
7
+ </div>
8
+ <% end %>
9
+ <h3><%= helpers.link_to_document document %></h3>
10
+ <% document.extent.each do |extent| %>
11
+ <%= tag.span extent, class: 'al-document-extent badge' unless compact? %>
12
+ <% end %>
13
+ <dl>
14
+ <%= render Arclight::IndexMetadataFieldComponent.with_collection(presenter.field_presenters.select { |field| !compact? || field.field_config.compact }) %>
15
+ </dl>
16
+ </div>
17
+ </div>
18
+ </div>
19
+
20
+ <div class="grouped-documents">
21
+ <div class="al-grouped-more">
22
+ <% if @group.total > 3 %>
23
+ <%= t('arclight.views.index.top_group_results', count: 3) %>
24
+ <%= link_to(
25
+ t('arclight.views.index.all_group_results', count: @group.total),
26
+ search_within_collection_url)
27
+ %>
28
+ <% else %>
29
+ <%= t('arclight.views.index.group_results_count', count: @group.total) %>
30
+ <% end %>
31
+ </div>
32
+
33
+ <%= helpers.render_document_index @group.docs %>
34
+ </div>
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Render a collection document for a
5
+ # grouped search result view
6
+ class GroupComponent < Blacklight::Document::GroupComponent
7
+ def compact?
8
+ helpers.document_index_view_type.to_s == 'compact'
9
+ end
10
+
11
+ def document
12
+ @document ||= @group.docs.first.collection
13
+ end
14
+
15
+ def presenter
16
+ @presenter ||= Arclight::ShowPresenter.new(document, helpers).with_field_group('group_header_field')
17
+ end
18
+
19
+ def search_within_collection_url
20
+ search_catalog_path(helpers.search_without_group.deep_merge(f: { collection: [document.collection_name] }))
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,5 @@
1
+ <header>
2
+ <%= top_bar %>
3
+ <%= masthead %>
4
+ <%= search_bar %>
5
+ </header>
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Arclight
4
+ # Provides a header with a masthead
5
+ class HeaderComponent < Blacklight::HeaderComponent
6
+ def masthead
7
+ render Arclight::MastheadComponent.new
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ <div class="row">
2
+ <%= tag.dt @field.label, class: 'visually-hidden' %>
3
+ <% if truncate? %>
4
+ <%= tag.dd(class: @classes + ['truncator'], data: { controller: 'arclight-truncate' }) do %>
5
+ <%= tag.div @field.render, class: 'content', data: { arclight_truncate_target: 'content' } %>
6
+ <%= button_tag(type: :button, class: 'btn btn-sm btn-link px-0',
7
+ data: { action: 'click->arclight-truncate#trigger' }) do %>
8
+ <%= tag.span t('arclight.truncation.view_more'), class: 'view-more' %>
9
+ <%= tag.span t('arclight.truncation.view_less'), class: 'view-less' %>
10
+ <%= tag.span(class: 'icon') %>
11
+ <% end %>
12
+ <% end %>
13
+ <% else %>
14
+ <%= tag.dd @field.render, class: @classes %>
15
+ <% end %>
16
+ </div>