diff --git a/app/assets/stylesheets/_base.scss b/app/assets/stylesheets/_base.scss
index 46df279..6859400 100644
--- a/app/assets/stylesheets/_base.scss
+++ b/app/assets/stylesheets/_base.scss
@@ -131,3 +131,11 @@ mark, .mark {
margin-bottom: 2rem;
padding-top: 1rem;
}
+
+/* Navbars */
+
+.sticky-toolbar-top {
+ position: sticky !important;
+ top: 0;
+ z-index: 2;
+}
diff --git a/app/assets/stylesheets/_collection.scss b/app/assets/stylesheets/_collection.scss
index f26f6b0..ba634b4 100644
--- a/app/assets/stylesheets/_collection.scss
+++ b/app/assets/stylesheets/_collection.scss
@@ -8,12 +8,20 @@
position: relative;
margin-bottom: .5rem;
border-bottom: solid 1px var.$color-teal-300;
- box-shadow: 0px 3px 3px -3px rgba(23, 130, 166, 0.58);
- background-image: linear-gradient(to top, var.$gray-cool, var.$gray-white, var.$near-white, var.$color-background-primary);
+ box-shadow: 0 3px 3px -3px rgba(23, 130, 166, 0.58);
+ background-image: linear-gradient(
+ to top, var.$gray-cool, var.$gray-white, var.$near-white, var.$color-background-primary
+ );
+ padding-top: 1rem;
+ padding-bottom: 0.5rem;
.al-request {
margin-right: 2rem;
- margin-bottom: 1rem;
+ margin-bottom: 0.5rem;
+ }
+
+ .btn-primary {
+ margin-right: 0.5rem;
}
}
@@ -80,3 +88,27 @@
.al-show-actions-box svg.bi {
color: var.$color-blue-500;
}
+
+.collection-name-wrapper {
+ margin-top: 1rem;
+ position: relative;
+}
+
+.collection-name-wrapper a {
+ text-decoration: none;
+ font-weight: 700;
+ line-height: 1.25;
+ font-size: 1.35rem;
+}
+
+.al-collection-content-icon {
+ font-size: 1.5rem;
+}
+
+span.breadcrumb-text {
+ font-size: 1rem;
+}
+
+.breadcrumb-item + .breadcrumb-item::before {
+ content: "\00BB";
+}
diff --git a/app/assets/stylesheets/_contents.scss b/app/assets/stylesheets/_contents.scss
new file mode 100644
index 0000000..26407b2
--- /dev/null
+++ b/app/assets/stylesheets/_contents.scss
@@ -0,0 +1,55 @@
+@use "variables" as var;
+
+#contents .documents li {
+ background-color: unset !important;
+}
+
+.contents-action {
+ min-width: 2rem;
+ min-height: 2rem;
+
+ i {
+ font-size: 2rem;
+ }
+}
+
+.nested-contents-container {
+ margin-left: 3rem;
+}
+
+.contents-item-card {
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.blended {
+ color: var.$color-neutral-100;
+ opacity: 0.0;
+}
+
+#contents button[aria-expanded="true"] {
+ i::before {
+ content: "\F2E6" !important;
+ }
+}
+
+#contents button[aria-expanded="false"] {
+ i::before {
+ content: "\F4FA" !important;
+ }
+}
+
+.btn-outline-request {
+ border: 1px solid var.$color-neutral-100;
+ color: var.$color-blue-400;
+}
+
+.button-placeholder {
+ padding: 0.875rem;
+}
+
+#about-this-level {
+ background-color: #f5f5f5;
+ border-top: 1px dotted #cccccc;
+ padding: 1rem;
+}
diff --git a/app/assets/stylesheets/_search_results.scss b/app/assets/stylesheets/_search_results.scss
index f66c426..b44de36 100644
--- a/app/assets/stylesheets/_search_results.scss
+++ b/app/assets/stylesheets/_search_results.scss
@@ -193,10 +193,6 @@ article.document .blacklight-icons.blacklight-icons-online {
}
}
-.truncator button {
- display: unset;
-}
-
#content .al-grouped-results .al-grouped-title-bar {
background-color: var.$facet-gray;
border: 1px solid rgba(0, 0, 0, 0.075);
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 1904a75..ad41cc7 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -3,6 +3,7 @@
@use "banners";
@use "buttons";
@use "card";
+@use "contents";
@use "collection";
@use "context_navigation";
@use "footer";
diff --git a/app/components/um_aeon_request_checkbox_component.html.erb b/app/components/um_aeon_request_checkbox_component.html.erb
new file mode 100644
index 0000000..1a11d81
--- /dev/null
+++ b/app/components/um_aeon_request_checkbox_component.html.erb
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+ Request "<%= document.aeon_item_sub_title_visually_hidden %>"
+
+
diff --git a/app/components/um_aeon_request_checkbox_component.rb b/app/components/um_aeon_request_checkbox_component.rb
new file mode 100644
index 0000000..901874f
--- /dev/null
+++ b/app/components/um_aeon_request_checkbox_component.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class UmAeonRequestCheckboxComponent < ViewComponent::Base
+ def initialize(document:)
+ @document = document
+ end
+
+ attr_reader :document
+end
diff --git a/app/components/um_breadcrumbs_hierarchy_component.html.erb b/app/components/um_breadcrumbs_hierarchy_component.html.erb
new file mode 100644
index 0000000..bd029fd
--- /dev/null
+++ b/app/components/um_breadcrumbs_hierarchy_component.html.erb
@@ -0,0 +1,20 @@
+
+
+
+
+ <%= blacklight_icon :collection, classes: 'al-collection-content-icon' %>
+ <%= link_to collection.label, solr_document_path(collection.id) %>
+
+
+ <% parents_under_collection.each do |parent| %>
+
+ <%= link_to parent.label, solr_document_path(parent.id) %>
+
+ <% end %>
+
+ <%= @presenter.heading %>
+
+
+
+
+
diff --git a/app/components/um_breadcrumbs_hierarchy_component.rb b/app/components/um_breadcrumbs_hierarchy_component.rb
new file mode 100644
index 0000000..c179f5b
--- /dev/null
+++ b/app/components/um_breadcrumbs_hierarchy_component.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class UmBreadcrumbsHierarchyComponent < Arclight::BreadcrumbsHierarchyComponent
+ def render?
+ document.present? && !document.collection?
+ end
+end
diff --git a/app/components/um_collection_sidebar_component.html.erb b/app/components/um_collection_sidebar_component.html.erb
index 0f4b2bd..c769e08 100644
--- a/app/components/um_collection_sidebar_component.html.erb
+++ b/app/components/um_collection_sidebar_component.html.erb
@@ -11,5 +11,19 @@
%>
<% end %>
+
+ <%= link_to "Contents",
+ "#{document_path}#contents",
+ class: 'nav-link pl-0 ps-0 py-1',
+ data: { turbolinks: 'false' }
+ %>
+
+
+ <%= link_to t('arclight.views.show.sections.access_field'),
+ "#{document_path}#using-these-materials",
+ class: 'nav-link pl-0 ps-0 py-1',
+ data: { turbolinks: 'false' }
+ %>
+
\ No newline at end of file
diff --git a/app/components/um_contents_document_collection_hierarchy_component.html.erb b/app/components/um_contents_document_collection_hierarchy_component.html.erb
new file mode 100644
index 0000000..98b6676
--- /dev/null
+++ b/app/components/um_contents_document_collection_hierarchy_component.html.erb
@@ -0,0 +1,77 @@
+
+
+
+ <% if document.children? %>
+ <%= content_tag(
+ :button,
+ class: "btn btn-light contents-action",
+ aria: { label: t("arclight.hierarchy.view_all"), expanded: start_expanded? },
+ data: {
+ bs_toggle: "collapse",
+ toggle: "collapse",
+ bs_target: "#collapsible-contents-hierarchy-#{document.id}"
+ }
+ ) do %>
+
+ <% end %>
+ <% else %>
+
+ <% end %>
+
+
+
+ <% if document.collection_has_requestable_components? and document.is_checkbox_requestable? %>
+
+ <%= render UmAeonRequestCheckboxComponent.new(document: document) %>
+
+ <% end %>
+
+
+
+ <% if helpers.is_interesting_component?(document) %>
+ <%= helpers.link_to_document document %>
+ <% else %>
+ <%= presenter.label %>
+ <% end %>
+ <% if document.children? %>
+
+ <%= document.number_of_children %> <%= t(:'um_arclight.views.index.number_of_components', count: document.number_of_children) %>
+
+ <% end %>
+
+ <%= content_tag("p", "aria-roledescription" => "subtitle") do %>
+ <% document.extent.each do |extent_chunk| %>
+ <%= extent_chunk %>
+ <% end %>
+ <% end unless document.extent.empty? %>
+
+
+ <%= tag.span class: "al-document-container container-wrapper col-auto" do %>
+ <%= presenter.document.containers.join(", ") %>
+ <% end if presenter.document.containers.present? %>
+ <%= helpers.render_index_doc_actions document, wrapping_class: "col-auto" %>
+
+ <%= content_tag('div', class: 'al-document-creator') do %>
+ <%= document.creator %>
+ <% end if document.creator %>
+
<%= abstract_or_scope %>
+
+
+ <% if document.children? %>
+ <%= content_tag(
+ :div,
+ id: "collapsible-contents-hierarchy-#{document.id}",
+ class: "collapse #{'show' if start_expanded?}"
+ ) do %>
+
+ <%= helpers.turbo_frame_tag(
+ "al-hierarchy-#{document.id}-document",
+ loading: 'lazy',
+ src: helpers.hierarchy_solr_document_path(
+ id: document.id, key: '-document', contents: true
+ )
+ ) %>
+
+ <% end %>
+ <% end %>
+
diff --git a/app/components/um_contents_document_collection_hierarchy_component.rb b/app/components/um_contents_document_collection_hierarchy_component.rb
new file mode 100644
index 0000000..612b249
--- /dev/null
+++ b/app/components/um_contents_document_collection_hierarchy_component.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class UmContentsDocumentCollectionHierarchyComponent < Arclight::DocumentCollectionHierarchyComponent
+ def abstract_or_scope
+ render Arclight::IndexMetadataFieldComponent.new(
+ field: Blacklight::FieldPresenter.new(
+ view_context,
+ document,
+ Blacklight::Configuration::Field.new(
+ field: "abstract_or_scope",
+ key: "abstract_or_scope",
+ accessor: true,
+ truncate: true,
+ helper_method: :render_html_tags
+ )
+ )
+ )
+ end
+
+ def start_expanded?
+ document.number_of_children == 1
+ end
+end
diff --git a/app/components/um_document_component.html.erb b/app/components/um_document_component.html.erb
index 05086ce..7c20792 100644
--- a/app/components/um_document_component.html.erb
+++ b/app/components/um_document_component.html.erb
@@ -1,4 +1,4 @@
-
+<%= render breadcrumb_component.new(presenter: presenter) %>
+
<%= content_tag :h1 do %>
<%= document.normalized_title %>
@@ -32,10 +34,18 @@
metadata_attr: { layout: UmUpperMetadataLayoutComponent },
presenter: presenter, heading: true, classes: "") %>
-<% elsif document.children? %>
+<% end %>
+
+<% if document.children? %>
<%= t 'arclight.views.show.contents' %>
- <%= 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) %>
+ <%= helpers.turbo_frame_tag(
+ "al-hierarchy-#{document.id}-document",
+ loading: 'lazy',
+ src: helpers.hierarchy_solr_document_path(
+ id: document.id, key: '-document', paginate: true, contents: true
+ )
+ ) %>
<% end %>
diff --git a/app/controllers/catalog_controller.rb b/app/controllers/catalog_controller.rb
index 969b669..6772e97 100644
--- a/app/controllers/catalog_controller.rb
+++ b/app/controllers/catalog_controller.rb
@@ -83,7 +83,7 @@ class CatalogController < ApplicationController
# config.show.title_field = 'title_display'
config.show.document_component = UmDocumentComponent
config.show.sidebar_component = UmSidebarComponent
- config.show.breadcrumb_component = Arclight::BreadcrumbsHierarchyComponent
+ config.show.breadcrumb_component = UmBreadcrumbsHierarchyComponent
config.show.embed_component = Arclight::EmbedComponent
config.show.access_component = UmAccessComponent
config.show.online_status_component = Arclight::OnlineStatusIndicatorComponent
@@ -96,7 +96,6 @@ class CatalogController < ApplicationController
background_field
related_field
indexed_terms_field
- access_field
]
config.show.collection_access_items = %i[
diff --git a/app/helpers/um_arclight_helper.rb b/app/helpers/um_arclight_helper.rb
index 3a915bb..cbb0a5d 100644
--- a/app/helpers/um_arclight_helper.rb
+++ b/app/helpers/um_arclight_helper.rb
@@ -10,4 +10,21 @@ def formatted_last_indexed(timestamp)
date = DateTime.parse(timestamp)
date.strftime("%F")
end
+
+ # Keys are the display keys registered in the Blacklight config.
+ # Fields are the Solr document keys associated with the display keys.
+ SKIPPABLE_KEYS = [
+ "physdesc",
+ "creators",
+ "abstract",
+ "scopecontent",
+ "extent",
+ "note",
+ "odd"
+ ]
+ def is_interesting_component?(document)
+ blacklight_config.component_fields.find do |key, field_config|
+ SKIPPABLE_KEYS.exclude?(field_config.key) && document.fetch(field_config.field, nil).present?
+ end || document.is_linkable?
+ end
end
diff --git a/app/javascript/aeonform.js b/app/javascript/aeonform.js
new file mode 100644
index 0000000..c9dd75b
--- /dev/null
+++ b/app/javascript/aeonform.js
@@ -0,0 +1,251 @@
+/* aeonform.js */
+
+const CHECKBOX_SELECTOR = 'input[type="checkbox"][name="Request"]';
+const FORM_ID = "EADRequestFormId";
+const FALLBACK_BUCKET = "null-collection";
+
+const state = {
+ collectionId: null,
+ selected: new Set(),
+ items: new Map(),
+};
+
+const storageKeys = () => ({
+ selected: `${state.collectionId}_selectedItems`,
+ items: `${state.collectionId}_collectionItems`,
+});
+
+function collectionIdFromIdentifier(identifier) {
+ const idx = identifier?.indexOf("_aspace_") ?? -1;
+ return idx > 0 ? identifier.slice(0, idx) : null;
+}
+
+function currentCollectionId() {
+ const idno = document.querySelector(`#${FORM_ID} input[name="idno"]`)?.value;
+ if (idno) return idno;
+
+ const checkbox = document.querySelector(CHECKBOX_SELECTOR);
+ const fromCheckbox = checkbox && collectionIdFromIdentifier(checkbox.value);
+ if (fromCheckbox) return fromCheckbox;
+
+ const doc = document.querySelector("[data-document-id]");
+ const fromDoc = doc && collectionIdFromIdentifier(doc.dataset.documentId);
+ if (fromDoc) return fromDoc;
+
+ if (checkbox) {
+ console.warn(
+ "aeonform: Request checkboxes present but no collection could be resolved; " +
+ "using shared fallback bucket."
+ );
+ }
+ return FALLBACK_BUCKET;
+}
+
+function assertSingleCollection() {
+ const ids = new Set(
+ [...document.querySelectorAll(CHECKBOX_SELECTOR)]
+ .map((cb) => collectionIdFromIdentifier(cb.value))
+ .filter(Boolean)
+ );
+ if (ids.size > 1) {
+ console.warn("aeonform: page mixes collections:", [...ids]);
+ }
+}
+
+function readJSON(key, fallback) {
+ try {
+ return JSON.parse(sessionStorage.getItem(key)) ?? fallback;
+ } catch {
+ return fallback;
+ }
+}
+
+function loadState() {
+ state.collectionId = currentCollectionId();
+ const keys = storageKeys();
+ state.selected = new Set(readJSON(keys.selected, []));
+ state.items = new Map(Object.entries(readJSON(keys.items, {})));
+}
+
+let warnedPersistenceFailure = false;
+function saveState() {
+ const keys = storageKeys();
+ try {
+ sessionStorage.setItem(keys.selected, JSON.stringify([...state.selected]));
+ sessionStorage.setItem(keys.items, JSON.stringify(Object.fromEntries(state.items)));
+ } catch {
+ // Continue with in-memory state when session storage is unavailable.
+ if (!warnedPersistenceFailure) {
+ warnedPersistenceFailure = true;
+ console.warn("aeonform: sessionStorage write failed; selections won't persist across navigation.");
+ }
+ }
+}
+
+function ensureCurrentCollection() {
+ if (state.collectionId !== currentCollectionId()) {
+ loadState();
+ restoreCheckboxes();
+ updateCount();
+ }
+}
+
+function harvestItemMetadata() {
+ let dirty = false;
+
+ document.querySelectorAll(CHECKBOX_SELECTOR).forEach((checkbox) => {
+ const identifier = checkbox.value;
+ if (state.items.has(identifier)) return;
+
+ const suffix = `_${identifier}`;
+ const metadata = {};
+ checkbox.closest("label")?.querySelectorAll("input").forEach((input) => {
+ if (input.name && input.name.endsWith(suffix)) {
+ metadata[input.name] = input.value;
+ }
+ });
+
+ state.items.set(identifier, metadata);
+ dirty = true;
+ });
+
+ if (dirty) saveState();
+}
+
+function updateCount() {
+ const span = document.getElementById("selected-items-count");
+ if (!span) return;
+
+ const count = state.selected.size;
+ span.innerHTML = count > 0
+ ? `${count} ` + `Request ${count} ${count === 1 ? "item" : "items"} `
+ : ""
+}
+
+function restoreCheckboxes(root = document) {
+ root.querySelectorAll(CHECKBOX_SELECTOR).forEach((checkbox) => {
+ checkbox.checked = state.selected.has(checkbox.value);
+ });
+}
+
+function clearAll() {
+ document.querySelectorAll(CHECKBOX_SELECTOR).forEach((cb) => { cb.checked = false; });
+ state.selected.clear();
+ state.items.clear();
+ saveState();
+ updateCount();
+}
+
+function hiddenInput(name, value) {
+ const input = document.createElement("input");
+ input.type = "hidden";
+ input.name = name;
+ input.value = value;
+ return input;
+}
+
+function submitAeonRequest(sourceForm) {
+ ensureCurrentCollection();
+
+ if (state.selected.size === 0) {
+ alert(
+ "Please select one or more items to request.\n" +
+ "(You may need to scroll down to see the checkboxes)"
+ );
+ return;
+ }
+
+ document.querySelectorAll(".aeon-submitted-form").forEach((f) => f.remove());
+
+ const form = sourceForm.cloneNode(true);
+ form.id = `${FORM_ID}-${Date.now()}`;
+ form.classList.add("aeon-submitted-form");
+ form.hidden = true;
+ form.querySelectorAll("[id]").forEach((el) => el.removeAttribute("id"));
+
+ // Avoids duplicate/stale request values).
+ form.querySelectorAll('input[name="Request"]').forEach((el) => el.remove());
+
+ state.selected.forEach((identifier) => {
+ form.append(hiddenInput("Request", identifier));
+ Object.entries(state.items.get(identifier) ?? {}).forEach(([name, value]) => {
+ form.append(hiddenInput(name, value));
+ });
+ });
+
+ document.body.append(form);
+ form.submit();
+ clearAll();
+}
+
+document.addEventListener("change", (event) => {
+ const checkbox = event.target.closest?.(CHECKBOX_SELECTOR);
+ if (!checkbox) return;
+
+ ensureCurrentCollection();
+ harvestItemMetadata();
+
+ if (checkbox.checked) {
+ state.selected.add(checkbox.value);
+ } else {
+ state.selected.delete(checkbox.value);
+ }
+
+ saveState();
+ updateCount();
+});
+
+document.addEventListener("submit", (event) => {
+ const form = event.target;
+ if (form.id !== FORM_ID) return;
+
+ event.preventDefault();
+ submitAeonRequest(form);
+});
+
+document.addEventListener("click", (event) => {
+ if (event.target.closest("[data-aeon-submit]")) {
+ event.preventDefault();
+ document.getElementById(FORM_ID)?.requestSubmit();
+ return;
+ }
+ if (event.target.closest("[data-aeon-clear]")) {
+ event.preventDefault();
+ clearAll();
+ }
+});
+
+function initialize() {
+ loadState();
+ assertSingleCollection();
+ harvestItemMetadata();
+ restoreCheckboxes();
+ updateCount();
+}
+
+document.addEventListener("turbo:load", initialize);
+
+if (document.readyState !== "loading") {
+ initialize();
+} else {
+ document.addEventListener("DOMContentLoaded", initialize);
+}
+
+document.addEventListener("turbo:render", () => {
+ ensureCurrentCollection();
+ restoreCheckboxes();
+ updateCount();
+});
+
+document.addEventListener("turbo:before-cache", () => {
+ const span = document.getElementById("selected-items-count");
+ if (span) span.innerHTML = "";
+ document.querySelectorAll(CHECKBOX_SELECTOR).forEach((cb) => { cb.checked = false; });
+});
+
+document.addEventListener("turbo:frame-load", (event) => {
+ ensureCurrentCollection();
+ harvestItemMetadata();
+ restoreCheckboxes(event.target);
+ updateCount();
+});
diff --git a/app/javascript/application.js b/app/javascript/application.js
index 50b93b4..011f78d 100644
--- a/app/javascript/application.js
+++ b/app/javascript/application.js
@@ -4,7 +4,8 @@ import "controllers"
import * as bootstrap from "bootstrap"
import githubAutoCompleteElement from "@github/auto-complete-element"
import Blacklight from "blacklight"
-
import "arclight"
+import "aeonform"
import BlacklightRangeLimit from "blacklight-range-limit";
BlacklightRangeLimit.init({onLoadHandler: Blacklight.onLoad });
+
diff --git a/app/javascript/arclight/truncate_controller.js b/app/javascript/arclight/truncate_controller.js
new file mode 100644
index 0000000..817417e
--- /dev/null
+++ b/app/javascript/arclight/truncate_controller.js
@@ -0,0 +1,25 @@
+import { Controller } from '@hotwired/stimulus'
+export default class extends Controller {
+ static targets = ['content']
+ connect() {
+ this._check = this.checkTruncation.bind(this)
+ requestAnimationFrame(this._check)
+ window.addEventListener('resize', this._check)
+ }
+ disconnect() {
+ window.removeEventListener('resize', this._check)
+ }
+ checkTruncation() {
+ const el = this.contentTarget
+ const isTruncated = el.scrollHeight > el.clientHeight + 1
+ if (isTruncated) {
+ this.element.classList.add('truncated')
+ } else {
+ this.element.classList.remove('truncated')
+ this.element.classList.remove('expanded')
+ }
+ }
+ trigger() {
+ this.element.classList.toggle('expanded')
+ }
+}
diff --git a/app/models/solr_document.rb b/app/models/solr_document.rb
index d9264fb..f88c6ef 100644
--- a/app/models/solr_document.rb
+++ b/app/models/solr_document.rb
@@ -14,6 +14,10 @@ class SolrDocument
# Recommendation: Use field names from Dublin Core
use_extension(Blacklight::Document::DublinCore)
+ def component?
+ parent_ids.present?
+ end
+
def physloc
fetch("collection_physloc_tesim", [])[0]
end
@@ -31,14 +35,75 @@ def repository_id
end
def has_online_content?
- fetch("has_online_content_ssim", [])[0]
+ online_content?
end
def restrictions
fetch("accessrestrict_html_tesm", [])[0]
end
+ def accessrestrict
+ fetch("accessrestrict_tesim", [])
+ end
+
+ def userestrict
+ fetch("userestrict_tesim", [])
+ end
+
+ def phystech
+ fetch("phystech_tesim", [])
+ end
+
+ def restricted_component?
+ component? && (accessrestrict.present? || userestrict.present? || phystech.present?)
+ end
+
def document_id
fetch("ead_ssi", nil)&.strip
end
+
+ def is_linkable?
+ online_content? || number_of_children > 0 || restricted_component?
+ end
+
+ def collection_has_requestable_components?
+ repository_config.request_config_present_for_type?("aeon_hidden_form_request")
+ end
+
+ def container_types
+ fetch("container_types_ssim", [])
+ end
+
+ def is_checkbox_requestable?
+ config_present = repository_config.request_config_present_for_type?("aeon_hidden_form_request")
+ container_requestable = container_types.any? do |container_type|
+ %w[box folder reel map-case tube object volume bundle].any? do |type|
+ container_type.casecmp(type) == 0
+ end
+ end
+ config_present && !container_types.empty? && container_requestable
+ end
+
+ # TODO: should the Aeon stuff live in a separate class?
+ def aeon_item_sub_title_value
+ subtitle = ActionController::Base.helpers.strip_tags(normalized_title)
+ subtitle += " (#{extent.join(", ")})" if extent
+ subtitle
+ end
+
+ def aeon_item_sub_title_visually_hidden
+ aeon_item_sub_title_value
+ end
+
+ def aeon_item_volume_value
+ containers.join(", ")
+ end
+
+ def aeon_item_citation_value
+ reference # May need to fall back to the ID
+ end
+
+ def aeon_item_info_1_value
+ accessrestrict
+ end
end
diff --git a/app/views/arclight/requests/_aeon_hidden_form_request.html.erb b/app/views/arclight/requests/_aeon_hidden_form_request.html.erb
index b48edad..c70e315 100644
--- a/app/views/arclight/requests/_aeon_hidden_form_request.html.erb
+++ b/app/views/arclight/requests/_aeon_hidden_form_request.html.erb
@@ -18,6 +18,6 @@
<% end %>
-Request
-Clear selected
+Request
+Clear selected
diff --git a/app/views/catalog/hierarchy.html.erb b/app/views/catalog/hierarchy.html.erb
index 87b40ef..c5a0e13 100644
--- a/app/views/catalog/hierarchy.html.erb
+++ b/app/views/catalog/hierarchy.html.erb
@@ -8,6 +8,12 @@
blacklight_config: blacklight_config,
nest_path: params[:nest_path]) %>
+ <% elsif params[:contents].present? %>
+
+ <%= render UmContentsDocumentCollectionHierarchyComponent.with_collection(
+ presenters, blacklight_config: blacklight_config, nest_path: params[:nest_path]
+ ) %>
+
<% else %>
<%= render Arclight::DocumentCollectionContextComponent.with_collection(presenters,
diff --git a/config/importmap.rb b/config/importmap.rb
index 6f29700..dc7d2fa 100644
--- a/config/importmap.rb
+++ b/config/importmap.rb
@@ -12,10 +12,11 @@
pin "arclight", to: "arclight/arclight.js"
# TODO: We may be able to move these to a single importmap for arclight.
pin "arclight/oembed_controller", to: "arclight/oembed_controller.js"
-pin "arclight/truncate_controller", to: "arclight/truncate_controller.js"
# chart.js is dependency of blacklight-range-limit, currently is not working
# as vendored importmaps, but instead must be pinned to CDN. You may want to update
# versions perioidically.
pin "chart.js", to: "https://ga.jspm.io/npm:chart.js@4.2.0/dist/chart.js"
# single dependency of chart.js:
pin "@kurkle/color", to: "https://ga.jspm.io/npm:@kurkle/color@0.3.2/dist/color.esm.js"
+pin "aeonform"
+pin "arclight/truncate_controller", to: "arclight/truncate_controller.js"
diff --git a/lib/um_arclight/traject/ead2_component_config.rb b/lib/um_arclight/traject/ead2_component_config.rb
index a738ad9..d79e214 100644
--- a/lib/um_arclight/traject/ead2_component_config.rb
+++ b/lib/um_arclight/traject/ead2_component_config.rb
@@ -299,6 +299,12 @@
to_field "#{selector}_tesim", extract_xpath("./did/#{selector}")
end
+to_field "container_types_ssim" do |record, accumulator|
+ record.xpath("./did/container[normalize-space(@type)]").each do |node|
+ accumulator << node["type"]
+ end
+end
+
# =============================
# Each component child document
#
diff --git a/lib/um_arclight/traject/ead2_config.rb b/lib/um_arclight/traject/ead2_config.rb
index f8a66d9..964715d 100644
--- a/lib/um_arclight/traject/ead2_config.rb
+++ b/lib/um_arclight/traject/ead2_config.rb
@@ -304,6 +304,21 @@
to_field "authors_creators_tesim", extract_xpath("/ead/eadheader/filedesc/titlestmt//author") # search plain text
to_field "authors_creators_ssm", extract_xpath("/ead/eadheader/filedesc/titlestmt//author", to_text: false) # show render_html_tags
+# Used for evaluating xpath components to find
+class NokogiriXpathExtensions
+ def is_component(node_set)
+ node_set.find_all do |node|
+ component_elements = (1..12).map { |i| "c#{'%02d' % i}" }
+ component_elements.push "c"
+ component_elements.include? node.name
+ end
+ end
+end
+
+to_field "child_component_count_isi" do |record, accumulator|
+ accumulator << record.xpath("/ead/archdesc/dsc/*[is_component(.)]", NokogiriXpathExtensions.new).count
+end
+
# =============================
# Each component child document
#