@canopy-iiif/app 1.11.1 → 1.11.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.
- package/lib/components/hero-slider-runtime.js +64 -46
- package/lib/components/slider-runtime-entry.js +0 -3
- package/package.json +1 -1
- package/ui/dist/server.mjs +59 -40
- package/ui/dist/server.mjs.map +2 -2
- package/ui/styles/components/_interstitial-hero.scss +23 -11
- package/ui/styles/index.css +22 -11
|
@@ -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
|
});
|
package/package.json
CHANGED
package/ui/dist/server.mjs
CHANGED
|
@@ -1182,7 +1182,6 @@ function Hero({
|
|
|
1182
1182
|
item,
|
|
1183
1183
|
index,
|
|
1184
1184
|
random = true,
|
|
1185
|
-
transition = "fade",
|
|
1186
1185
|
headline,
|
|
1187
1186
|
description,
|
|
1188
1187
|
links = [],
|
|
@@ -1308,15 +1307,6 @@ function Hero({
|
|
|
1308
1307
|
backgroundClassName,
|
|
1309
1308
|
className
|
|
1310
1309
|
].filter(Boolean).join(" ");
|
|
1311
|
-
const normalizedTransition = (() => {
|
|
1312
|
-
try {
|
|
1313
|
-
const raw = transition == null ? "" : String(transition);
|
|
1314
|
-
const normalized = raw.trim().toLowerCase();
|
|
1315
|
-
return normalized === "slide" ? "slide" : "fade";
|
|
1316
|
-
} catch (_) {
|
|
1317
|
-
return "fade";
|
|
1318
|
-
}
|
|
1319
|
-
})();
|
|
1320
1310
|
const renderSlide = (slide, idx, { showVeil = true, captionVariant = "overlay" } = {}) => {
|
|
1321
1311
|
const safeHref = applyBasePath(slide.href || "#");
|
|
1322
1312
|
const isStaticCaption = captionVariant === "static";
|
|
@@ -1351,38 +1341,68 @@ function Hero({
|
|
|
1351
1341
|
);
|
|
1352
1342
|
};
|
|
1353
1343
|
if (isStaticCaption) {
|
|
1354
|
-
return /* @__PURE__ */ React12.createElement(
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1344
|
+
return /* @__PURE__ */ React12.createElement(
|
|
1345
|
+
"div",
|
|
1346
|
+
{
|
|
1347
|
+
className: "canopy-interstitial__slide",
|
|
1348
|
+
key: safeHref || idx,
|
|
1349
|
+
role: "group",
|
|
1350
|
+
"aria-roledescription": "slide",
|
|
1351
|
+
"aria-label": `${idx + 1} of ${orderedSlides.length}`
|
|
1352
|
+
},
|
|
1353
|
+
wrapWithLink(
|
|
1354
|
+
/* @__PURE__ */ React12.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__media-frame" }, /* @__PURE__ */ React12.createElement(
|
|
1355
|
+
"img",
|
|
1356
|
+
{
|
|
1357
|
+
...buildImageProps(
|
|
1358
|
+
"canopy-interstitial__media canopy-interstitial__media--static"
|
|
1359
|
+
)
|
|
1360
|
+
}
|
|
1361
|
+
)) : null, slide.title ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__caption canopy-interstitial__caption--static" }, /* @__PURE__ */ React12.createElement("span", { className: "canopy-interstitial__caption-link" }, slide.title)) : null)
|
|
1362
|
+
)
|
|
1363
|
+
);
|
|
1364
1364
|
}
|
|
1365
|
-
return /* @__PURE__ */ React12.createElement(
|
|
1366
|
-
|
|
1367
|
-
|
|
1365
|
+
return /* @__PURE__ */ React12.createElement(
|
|
1366
|
+
"div",
|
|
1367
|
+
{
|
|
1368
|
+
className: "canopy-interstitial__slide",
|
|
1369
|
+
key: safeHref || idx,
|
|
1370
|
+
role: "group",
|
|
1371
|
+
"aria-roledescription": "slide",
|
|
1372
|
+
"aria-label": `${idx + 1} of ${orderedSlides.length}`
|
|
1373
|
+
},
|
|
1374
|
+
wrapWithLink(
|
|
1375
|
+
/* @__PURE__ */ React12.createElement("article", { className: paneClassName }, slide.thumbnail ? /* @__PURE__ */ React12.createElement("img", { ...buildImageProps("canopy-interstitial__media") }) : null, showVeil ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__veil", "aria-hidden": "true" }) : null, slide.title ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__caption" }, /* @__PURE__ */ React12.createElement("span", { className: "canopy-interstitial__caption-link" }, slide.title)) : null)
|
|
1376
|
+
)
|
|
1377
|
+
);
|
|
1368
1378
|
};
|
|
1369
|
-
const renderSlider = (options = {}) => /* @__PURE__ */ React12.createElement(
|
|
1370
|
-
"
|
|
1371
|
-
{
|
|
1372
|
-
type: "button",
|
|
1373
|
-
"aria-label": "Previous slide",
|
|
1374
|
-
className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--prev swiper-button-prev"
|
|
1375
|
-
},
|
|
1376
|
-
/* @__PURE__ */ React12.createElement(PrevArrowIcon, null)
|
|
1377
|
-
), /* @__PURE__ */ React12.createElement(
|
|
1378
|
-
"button",
|
|
1379
|
+
const renderSlider = (options = {}) => /* @__PURE__ */ React12.createElement(
|
|
1380
|
+
"div",
|
|
1379
1381
|
{
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1382
|
+
className: "canopy-interstitial__slider",
|
|
1383
|
+
role: "region",
|
|
1384
|
+
"aria-roledescription": "carousel",
|
|
1385
|
+
"aria-label": overlayTitle || "Featured content"
|
|
1383
1386
|
},
|
|
1384
|
-
/* @__PURE__ */ React12.createElement(
|
|
1385
|
-
|
|
1387
|
+
/* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__slide-wrapper" }, orderedSlides.map((slide, idx) => renderSlide(slide, idx, options))),
|
|
1388
|
+
/* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__nav" }, /* @__PURE__ */ React12.createElement(
|
|
1389
|
+
"button",
|
|
1390
|
+
{
|
|
1391
|
+
type: "button",
|
|
1392
|
+
"aria-label": "Previous slide",
|
|
1393
|
+
className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--prev"
|
|
1394
|
+
},
|
|
1395
|
+
/* @__PURE__ */ React12.createElement(PrevArrowIcon, null)
|
|
1396
|
+
), /* @__PURE__ */ React12.createElement(
|
|
1397
|
+
"button",
|
|
1398
|
+
{
|
|
1399
|
+
type: "button",
|
|
1400
|
+
"aria-label": "Next slide",
|
|
1401
|
+
className: "canopy-interstitial__nav-btn canopy-interstitial__nav-btn--next"
|
|
1402
|
+
},
|
|
1403
|
+
/* @__PURE__ */ React12.createElement(NextArrowIcon, null)
|
|
1404
|
+
))
|
|
1405
|
+
);
|
|
1386
1406
|
const overlayContent = /* @__PURE__ */ React12.createElement(React12.Fragment, null, overlayTitle ? /* @__PURE__ */ React12.createElement("h1", { className: "canopy-interstitial__headline" }, overlayTitle) : null, derivedDescription ? /* @__PURE__ */ React12.createElement("p", { className: "canopy-interstitial__description" }, derivedDescription) : null, finalOverlayLinks.length ? /* @__PURE__ */ React12.createElement(ButtonWrapper, { className: "canopy-interstitial__actions" }, finalOverlayLinks.map((link) => /* @__PURE__ */ React12.createElement(
|
|
1387
1407
|
Button,
|
|
1388
1408
|
{
|
|
@@ -1401,11 +1421,10 @@ function Hero({
|
|
|
1401
1421
|
};
|
|
1402
1422
|
if (!isBreadcrumbVariant) {
|
|
1403
1423
|
sectionProps["data-canopy-hero-slider"] = "1";
|
|
1404
|
-
sectionProps["data-transition"] = normalizedTransition;
|
|
1405
1424
|
} else {
|
|
1406
1425
|
sectionProps["data-canopy-hero-variant"] = "breadcrumb";
|
|
1407
1426
|
}
|
|
1408
|
-
return /* @__PURE__ */ React12.createElement("section", { ...sectionProps }, isBreadcrumbVariant ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__layout canopy-interstitial__layout--breadcrumb" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__body" }, breadcrumbNode, overlayContent))) : /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__layout" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__body" }, overlayContent)), /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__media-group" }, renderSlider({ showVeil: false, captionVariant: "static" }))));
|
|
1427
|
+
return /* @__PURE__ */ React12.createElement("section", { ...sectionProps }, isBreadcrumbVariant ? /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__layout canopy-interstitial__layout--breadcrumb" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__body" }, breadcrumbNode, overlayContent))) : /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__layout" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__panel" }, /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__body" }, overlayContent)), /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__media-group" }, renderSlider({ showVeil: false, captionVariant: "static" }), /* @__PURE__ */ React12.createElement("div", { className: "canopy-interstitial__pagination" }))));
|
|
1409
1428
|
}
|
|
1410
1429
|
|
|
1411
1430
|
// ui/src/layout/SubNavigation.jsx
|