@canopy-iiif/app 1.11.1 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/build/search.js +1 -1
- package/lib/build/sitemap.js +1 -1
- package/lib/components/hero-slider-runtime.js +64 -46
- package/lib/components/slider-runtime-entry.js +0 -3
- package/lib/search/search-form-runtime.js +1 -1
- package/lib/search/search.js +1 -4
- package/package.json +1 -1
- package/ui/dist/index.mjs +5 -5
- package/ui/dist/index.mjs.map +1 -1
- package/ui/dist/server.mjs +64 -45
- package/ui/dist/server.mjs.map +2 -2
- package/ui/styles/components/_interstitial-hero.scss +23 -11
- package/ui/styles/index.css +22 -11
package/lib/build/search.js
CHANGED
|
@@ -138,7 +138,7 @@ async function writeFacetCollections(labelWhitelist, combined) {
|
|
|
138
138
|
}
|
|
139
139
|
const selfId = absoluteUrl(`/api/facet/${labelSlug}/${valueSlug}.json`);
|
|
140
140
|
const parentId = absoluteUrl(`/api/facet/${labelSlug}.json`);
|
|
141
|
-
const homepage = absoluteUrl(`/search?${encodeURIComponent(labelSlug)}=${encodeURIComponent(valueSlug)}`);
|
|
141
|
+
const homepage = absoluteUrl(`/search/index.html?${encodeURIComponent(labelSlug)}=${encodeURIComponent(valueSlug)}`);
|
|
142
142
|
const col = {
|
|
143
143
|
'@context': 'https://iiif.io/api/presentation/3/context.json',
|
|
144
144
|
id: selfId,
|
package/lib/build/sitemap.js
CHANGED
|
@@ -42,7 +42,7 @@ function collectAbsoluteUrls(iiifRecords, pageRecords) {
|
|
|
42
42
|
push(record.href);
|
|
43
43
|
});
|
|
44
44
|
// Ensure the search page is always present even though it is generated separately
|
|
45
|
-
push('/search.html');
|
|
45
|
+
push('/search/index.html');
|
|
46
46
|
return Array.from(urls.values()).sort((a, b) => a.localeCompare(b));
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Navigation, Pagination, Autoplay, EffectFade } from 'swiper/modules';
|
|
3
|
-
import 'swiper/css';
|
|
4
|
-
import 'swiper/css/navigation';
|
|
5
|
-
import 'swiper/css/pagination';
|
|
6
|
-
import 'swiper/css/effect-fade';
|
|
1
|
+
import EmblaCarousel from 'embla-carousel';
|
|
7
2
|
|
|
8
3
|
function ready(fn) {
|
|
9
4
|
if (typeof document === 'undefined') return;
|
|
@@ -16,61 +11,85 @@ function ready(fn) {
|
|
|
16
11
|
|
|
17
12
|
function initSlider(host) {
|
|
18
13
|
if (!host || host.__canopyHeroBound) return;
|
|
19
|
-
const
|
|
20
|
-
if (!
|
|
21
|
-
const prev = host.querySelector('.canopy-interstitial__nav-btn--prev');
|
|
22
|
-
const next = host.querySelector('.canopy-interstitial__nav-btn--next');
|
|
23
|
-
const pagination = host.querySelector('.canopy-interstitial__pagination');
|
|
24
|
-
const transitionAttr = (host.getAttribute && host.getAttribute('data-transition')) || 'fade';
|
|
25
|
-
const transition = transitionAttr && transitionAttr.toLowerCase() === 'slide' ? 'slide' : 'fade';
|
|
14
|
+
const viewport = host.querySelector('.canopy-interstitial__slider');
|
|
15
|
+
if (!viewport) return;
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if (transition === 'fade') baseModules.push(EffectFade);
|
|
30
|
-
const swiperInstance = new Swiper(slider, {
|
|
31
|
-
modules: baseModules,
|
|
32
|
-
loop: true,
|
|
33
|
-
slidesPerView: 1,
|
|
34
|
-
effect: transition,
|
|
35
|
-
fadeEffect: transition === 'fade' ? { crossFade: true } : undefined,
|
|
36
|
-
navigation: {
|
|
37
|
-
prevEl: prev || undefined,
|
|
38
|
-
nextEl: next || undefined,
|
|
39
|
-
},
|
|
40
|
-
pagination: {
|
|
41
|
-
el: pagination || undefined,
|
|
42
|
-
clickable: true,
|
|
43
|
-
},
|
|
44
|
-
autoplay: {
|
|
45
|
-
delay: 6000,
|
|
46
|
-
disableOnInteraction: false,
|
|
47
|
-
},
|
|
48
|
-
});
|
|
17
|
+
const slides = Array.from(viewport.querySelectorAll('.canopy-interstitial__slide'));
|
|
18
|
+
if (slides.length <= 1) {
|
|
49
19
|
host.__canopyHeroBound = true;
|
|
50
|
-
|
|
51
|
-
} catch (error) {
|
|
52
|
-
try {
|
|
53
|
-
console.warn('[canopy][hero] failed to initialise slider', error);
|
|
54
|
-
} catch (_) {}
|
|
20
|
+
return;
|
|
55
21
|
}
|
|
22
|
+
|
|
23
|
+
const paginationEl = host.querySelector('.canopy-interstitial__pagination');
|
|
24
|
+
const prevBtn = host.querySelector('.canopy-interstitial__nav-btn--prev');
|
|
25
|
+
const nextBtn = host.querySelector('.canopy-interstitial__nav-btn--next');
|
|
26
|
+
|
|
27
|
+
// Live region for screen-reader slide announcements
|
|
28
|
+
const liveEl = document.createElement('div');
|
|
29
|
+
liveEl.setAttribute('aria-live', 'polite');
|
|
30
|
+
liveEl.setAttribute('aria-atomic', 'true');
|
|
31
|
+
liveEl.className = 'canopy-interstitial__sr-live';
|
|
32
|
+
host.appendChild(liveEl);
|
|
33
|
+
|
|
34
|
+
const embla = EmblaCarousel(viewport, { loop: true, duration: 0 });
|
|
35
|
+
|
|
36
|
+
const announce = (idx) => {
|
|
37
|
+
liveEl.textContent = `Slide ${idx + 1} of ${slides.length}`;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (paginationEl) {
|
|
41
|
+
slides.forEach((_, i) => {
|
|
42
|
+
const dot = document.createElement('button');
|
|
43
|
+
dot.type = 'button';
|
|
44
|
+
dot.className =
|
|
45
|
+
'canopy-interstitial__dot' +
|
|
46
|
+
(i === 0 ? ' canopy-interstitial__dot--active' : '');
|
|
47
|
+
dot.setAttribute('aria-label', `Go to slide ${i + 1}`);
|
|
48
|
+
dot.setAttribute('aria-current', i === 0 ? 'true' : 'false');
|
|
49
|
+
dot.addEventListener('click', () => embla.scrollTo(i));
|
|
50
|
+
paginationEl.appendChild(dot);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
embla.on('select', () => {
|
|
54
|
+
const idx = embla.selectedScrollSnap();
|
|
55
|
+
announce(idx);
|
|
56
|
+
paginationEl.querySelectorAll('.canopy-interstitial__dot').forEach((dot, i) => {
|
|
57
|
+
const active = i === idx;
|
|
58
|
+
dot.classList.toggle('canopy-interstitial__dot--active', active);
|
|
59
|
+
dot.setAttribute('aria-current', active ? 'true' : 'false');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (prevBtn) prevBtn.addEventListener('click', () => embla.scrollPrev());
|
|
65
|
+
if (nextBtn) nextBtn.addEventListener('click', () => embla.scrollNext());
|
|
66
|
+
|
|
67
|
+
let timer = setInterval(() => embla.scrollNext(), 6000);
|
|
68
|
+
const stopAutoplay = () => { clearInterval(timer); timer = null; };
|
|
69
|
+
const startAutoplay = () => { if (!timer) timer = setInterval(() => embla.scrollNext(), 6000); };
|
|
70
|
+
|
|
71
|
+
embla.on('pointerDown', stopAutoplay);
|
|
72
|
+
embla.on('pointerUp', startAutoplay);
|
|
73
|
+
|
|
74
|
+
host.__canopyHeroBound = true;
|
|
56
75
|
}
|
|
57
76
|
|
|
58
77
|
function observeHosts() {
|
|
59
78
|
try {
|
|
60
|
-
|
|
79
|
+
new MutationObserver((mutations) => {
|
|
61
80
|
mutations.forEach((mutation) => {
|
|
62
81
|
mutation.addedNodes &&
|
|
63
82
|
mutation.addedNodes.forEach((node) => {
|
|
64
83
|
if (!(node instanceof Element)) return;
|
|
65
|
-
if (node.matches && node.matches('[data-canopy-hero-slider]'))
|
|
84
|
+
if (node.matches && node.matches('[data-canopy-hero-slider]'))
|
|
85
|
+
initSlider(node);
|
|
66
86
|
const inner = node.querySelectorAll
|
|
67
87
|
? node.querySelectorAll('[data-canopy-hero-slider]')
|
|
68
88
|
: [];
|
|
69
89
|
inner && inner.forEach && inner.forEach((el) => initSlider(el));
|
|
70
90
|
});
|
|
71
91
|
});
|
|
72
|
-
})
|
|
73
|
-
observer.observe(document.documentElement || document.body, {
|
|
92
|
+
}).observe(document.documentElement || document.body, {
|
|
74
93
|
childList: true,
|
|
75
94
|
subtree: true,
|
|
76
95
|
});
|
|
@@ -79,7 +98,6 @@ function observeHosts() {
|
|
|
79
98
|
|
|
80
99
|
ready(() => {
|
|
81
100
|
if (typeof document === 'undefined') return;
|
|
82
|
-
|
|
83
|
-
hosts.forEach((host) => initSlider(host));
|
|
101
|
+
document.querySelectorAll('[data-canopy-hero-slider]').forEach((host) => initSlider(host));
|
|
84
102
|
observeHosts();
|
|
85
103
|
});
|
|
@@ -105,7 +105,7 @@ function isOnSearchPage() {
|
|
|
105
105
|
let path = String(location.pathname || '');
|
|
106
106
|
if (base && path.startsWith(base)) path = path.slice(base.length);
|
|
107
107
|
if (path.endsWith('/')) path = path.slice(0, -1);
|
|
108
|
-
return path === '/search';
|
|
108
|
+
return path === '/search' || path === '/search/index.html';
|
|
109
109
|
} catch (_) {
|
|
110
110
|
return false;
|
|
111
111
|
}
|
package/lib/search/search.js
CHANGED
|
@@ -114,10 +114,7 @@ function getSearchRouteEntries() {
|
|
|
114
114
|
function resolveSearchOutputRelative(routeValue) {
|
|
115
115
|
const defaultRoute = getDefaultRoute('search') || 'search';
|
|
116
116
|
const trimmed = typeof routeValue === 'string' ? routeValue.trim().replace(/^\/+|\/+$/g, '') : '';
|
|
117
|
-
|
|
118
|
-
return `${trimmed || defaultRoute}.html`;
|
|
119
|
-
}
|
|
120
|
-
return path.join(trimmed, 'index.html');
|
|
117
|
+
return path.join(trimmed || defaultRoute, 'index.html');
|
|
121
118
|
}
|
|
122
119
|
|
|
123
120
|
async function ensureSearchRuntime() {
|
package/package.json
CHANGED
package/ui/dist/index.mjs
CHANGED
|
@@ -43458,7 +43458,7 @@ function isAbsoluteUrl(href) {
|
|
|
43458
43458
|
}
|
|
43459
43459
|
function resolveSearchPath(pathValue) {
|
|
43460
43460
|
let raw = typeof pathValue === "string" ? pathValue.trim() : "";
|
|
43461
|
-
if (!raw) raw = "/search";
|
|
43461
|
+
if (!raw) raw = "/search/index.html";
|
|
43462
43462
|
if (isAbsoluteUrl(raw)) return raw;
|
|
43463
43463
|
const normalizedPath = raw.startsWith("/") ? raw : `/${raw}`;
|
|
43464
43464
|
const base = readBasePath();
|
|
@@ -43477,7 +43477,7 @@ function SearchPanelForm(props = {}) {
|
|
|
43477
43477
|
placeholder,
|
|
43478
43478
|
buttonLabel,
|
|
43479
43479
|
label,
|
|
43480
|
-
searchPath = "/search",
|
|
43480
|
+
searchPath = "/search/index.html",
|
|
43481
43481
|
inputId: inputIdProp,
|
|
43482
43482
|
clearLabel
|
|
43483
43483
|
} = props || {};
|
|
@@ -43633,7 +43633,7 @@ function SearchPanel(props = {}) {
|
|
|
43633
43633
|
// eslint-disable-line no-unused-vars
|
|
43634
43634
|
buttonLabel: buttonLabelProp,
|
|
43635
43635
|
label,
|
|
43636
|
-
searchPath = "/search"
|
|
43636
|
+
searchPath = "/search/index.html"
|
|
43637
43637
|
} = props || {};
|
|
43638
43638
|
const { getString } = useLocale();
|
|
43639
43639
|
const placeholder = placeholderProp != null ? placeholderProp : getString("common.phrases.placeholder_search", "Search\u2026");
|
|
@@ -44627,7 +44627,7 @@ function CanopyHeader(props = {}) {
|
|
|
44627
44627
|
const defaultSearchRoute = siteDefaultRoutes && typeof siteDefaultRoutes.search === "string" ? siteDefaultRoutes.search : "search";
|
|
44628
44628
|
const trimmedSearchRoute = searchRouteValue ? searchRouteValue.replace(/^\/+|\/+$/g, "") : "";
|
|
44629
44629
|
const usesDirectorySearchRoute = trimmedSearchRoute && trimmedSearchRoute !== (defaultSearchRoute || "search");
|
|
44630
|
-
const normalizedSearchRoute = usesDirectorySearchRoute ? `/${trimmedSearchRoute}/` : `/${(trimmedSearchRoute || defaultSearchRoute || "search").replace(/^\/+/, "")}`;
|
|
44630
|
+
const normalizedSearchRoute = usesDirectorySearchRoute ? `/${trimmedSearchRoute}/` : `/${(trimmedSearchRoute || defaultSearchRoute || "search").replace(/^\/+/, "")}/index.html`;
|
|
44631
44631
|
const resolvedLanguageToggle = languageToggleProp === false ? null : languageToggleProp === true || typeof languageToggleProp === "undefined" ? siteLanguageToggle : languageToggleProp;
|
|
44632
44632
|
const resolvedSearchLabel = searchLabelProp != null ? searchLabelProp : getString("common.nouns.search", "Search");
|
|
44633
44633
|
const resolvedSearchPlaceholder = searchPlaceholderProp != null ? searchPlaceholderProp : getString("common.phrases.placeholder_search", "Search\u2026");
|
|
@@ -46316,7 +46316,7 @@ function MdxSearchFormModal(props = {}) {
|
|
|
46316
46316
|
// kept for backward compat; ignored by teaser form
|
|
46317
46317
|
buttonLabel: buttonLabelProp,
|
|
46318
46318
|
label,
|
|
46319
|
-
searchPath = "/search"
|
|
46319
|
+
searchPath = "/search/index.html"
|
|
46320
46320
|
} = props || {};
|
|
46321
46321
|
const { getString } = useLocale();
|
|
46322
46322
|
const placeholder = placeholderProp != null ? placeholderProp : getString("common.phrases.placeholder_search", "Search\u2026");
|