@canopy-iiif/app 1.6.22 → 1.8.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/ui/dist/index.mjs CHANGED
@@ -4068,6 +4068,636 @@ function MapPoint() {
4068
4068
  return null;
4069
4069
  }
4070
4070
  MapPoint.displayName = "MapPoint";
4071
+
4072
+ // ui/src/content/gallery/Gallery.jsx
4073
+ import React40 from "react";
4074
+
4075
+ // ui/src/utils/manifestReferences.js
4076
+ import React39 from "react";
4077
+ var CONTEXT_KEY2 = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
4078
+ function getGlobalRoot() {
4079
+ if (typeof globalThis !== "undefined") return globalThis;
4080
+ if (typeof global !== "undefined") return global;
4081
+ if (typeof window !== "undefined") return window;
4082
+ return {};
4083
+ }
4084
+ function getPageContext() {
4085
+ const root = getGlobalRoot();
4086
+ if (root && root[CONTEXT_KEY2]) return root[CONTEXT_KEY2];
4087
+ const ctx = React39.createContext({
4088
+ navigation: null,
4089
+ page: null,
4090
+ site: null,
4091
+ primaryNavigation: []
4092
+ });
4093
+ if (root) root[CONTEXT_KEY2] = ctx;
4094
+ return ctx;
4095
+ }
4096
+ function normalizeManifestId(raw) {
4097
+ if (!raw) return "";
4098
+ try {
4099
+ const url = new URL(String(raw));
4100
+ url.hash = "";
4101
+ const params = Array.from(url.searchParams.entries()).sort(
4102
+ (a, b) => a[0].localeCompare(b[0]) || a[1].localeCompare(b[1])
4103
+ );
4104
+ url.search = "";
4105
+ params.forEach(([key, value]) => url.searchParams.append(key, value));
4106
+ return url.toString();
4107
+ } catch (_) {
4108
+ return String(raw || "").trim();
4109
+ }
4110
+ }
4111
+ var PageContextFallback = React39.createContext(null);
4112
+ var referencedModule = null;
4113
+ function getReferencedModule() {
4114
+ if (typeof window !== "undefined") return null;
4115
+ if (referencedModule) return referencedModule;
4116
+ try {
4117
+ const globalReq = typeof globalThis !== "undefined" && globalThis.__canopyRequire || null;
4118
+ if (typeof globalReq === "function") {
4119
+ referencedModule = globalReq("../../../lib/components/referenced.js");
4120
+ return referencedModule;
4121
+ }
4122
+ } catch (_) {
4123
+ referencedModule = null;
4124
+ }
4125
+ return referencedModule;
4126
+ }
4127
+ function useReferencedManifestMap() {
4128
+ const PageContext = getPageContext() || PageContextFallback;
4129
+ const pageContext = React39.useContext(PageContext);
4130
+ const referencedItems = pageContext && pageContext.page && Array.isArray(pageContext.page.referencedItems) ? pageContext.page.referencedItems : [];
4131
+ return React39.useMemo(() => {
4132
+ const map = /* @__PURE__ */ new Map();
4133
+ referencedItems.forEach((item) => {
4134
+ if (!item) return;
4135
+ const id = item.id || item.href;
4136
+ if (!id) return;
4137
+ const normalized = normalizeManifestId(id);
4138
+ if (normalized) map.set(normalized, item);
4139
+ map.set(String(id), item);
4140
+ });
4141
+ return map;
4142
+ }, [referencedItems]);
4143
+ }
4144
+ function resolveManifestReferences(ids, manifestMap) {
4145
+ if (!Array.isArray(ids) || !ids.length || !manifestMap) return [];
4146
+ const seen = /* @__PURE__ */ new Set();
4147
+ const out = [];
4148
+ ids.forEach((value) => {
4149
+ if (!value) return;
4150
+ const normalized = normalizeManifestId(value);
4151
+ if (!normalized || seen.has(normalized)) return;
4152
+ seen.add(normalized);
4153
+ const record = manifestMap.get(normalized) || manifestMap.get(String(value));
4154
+ if (!record) return;
4155
+ out.push({
4156
+ id: record.id || record.href || value,
4157
+ href: record.href || null,
4158
+ title: record.title || record.label || record.href || value,
4159
+ summary: record.summary || "",
4160
+ thumbnail: record.thumbnail || null,
4161
+ thumbnailWidth: record.thumbnailWidth,
4162
+ thumbnailHeight: record.thumbnailHeight,
4163
+ type: record.type || "work",
4164
+ metadata: Array.isArray(record.metadata) && record.metadata.length ? record.metadata : record.summary ? [record.summary] : []
4165
+ });
4166
+ });
4167
+ return out;
4168
+ }
4169
+ function resolveReferencedManifests(ids, manifestMap) {
4170
+ if (!Array.isArray(ids) || !ids.length) return [];
4171
+ const order = [];
4172
+ const seen = /* @__PURE__ */ new Set();
4173
+ ids.forEach((value) => {
4174
+ const normalized = normalizeManifestId(value);
4175
+ if (!normalized || seen.has(normalized)) return;
4176
+ seen.add(normalized);
4177
+ order.push(normalized);
4178
+ });
4179
+ if (!order.length) return [];
4180
+ const manifestRecords = /* @__PURE__ */ new Map();
4181
+ if (manifestMap) {
4182
+ const resolved = resolveManifestReferences(ids, manifestMap);
4183
+ resolved.forEach((entry) => {
4184
+ const key = normalizeManifestId(entry && (entry.id || entry.href));
4185
+ if (key && !manifestRecords.has(key)) {
4186
+ manifestRecords.set(key, entry);
4187
+ }
4188
+ });
4189
+ }
4190
+ const missing = order.filter((key) => key && !manifestRecords.has(key));
4191
+ if (missing.length) {
4192
+ const referencedHelpers = getReferencedModule();
4193
+ const buildReferencedItems = referencedHelpers == null ? void 0 : referencedHelpers.buildReferencedItems;
4194
+ const normalizeList = referencedHelpers == null ? void 0 : referencedHelpers.normalizeReferencedManifestList;
4195
+ if (typeof buildReferencedItems === "function") {
4196
+ let fallbackItems = [];
4197
+ try {
4198
+ const normalizedMissing = typeof normalizeList === "function" ? normalizeList(missing) : missing;
4199
+ if (normalizedMissing && normalizedMissing.length) {
4200
+ fallbackItems = buildReferencedItems(normalizedMissing) || [];
4201
+ }
4202
+ } catch (_) {
4203
+ fallbackItems = [];
4204
+ }
4205
+ fallbackItems.forEach((item) => {
4206
+ if (!item) return;
4207
+ const fallbackKey = normalizeManifestId(item.id || item.href);
4208
+ if (!fallbackKey || manifestRecords.has(fallbackKey)) return;
4209
+ manifestRecords.set(fallbackKey, {
4210
+ id: item.id || item.href || fallbackKey,
4211
+ href: item.href || null,
4212
+ title: item.title || item.href || "",
4213
+ summary: item.summary || "",
4214
+ thumbnail: item.thumbnail || null,
4215
+ thumbnailWidth: item.thumbnailWidth,
4216
+ thumbnailHeight: item.thumbnailHeight,
4217
+ type: item.type || "work",
4218
+ metadata: Array.isArray(item.metadata) && item.metadata.length ? item.metadata : item.summary ? [item.summary] : []
4219
+ });
4220
+ });
4221
+ }
4222
+ }
4223
+ return order.map((key) => manifestRecords.get(key)).filter(Boolean);
4224
+ }
4225
+
4226
+ // ui/src/content/gallery/Gallery.jsx
4227
+ var INLINE_SCRIPT = `(() => {
4228
+ if (typeof window === 'undefined') return;
4229
+ if (window.__canopyGalleryBound) return;
4230
+ window.__canopyGalleryBound = true;
4231
+ const focusableSelector =
4232
+ 'a[href],area[href],button:not([disabled]),input:not([disabled]):not([type="hidden"]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
4233
+ const raf =
4234
+ (window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) ||
4235
+ function (cb) {
4236
+ return window.setTimeout(cb, 0);
4237
+ };
4238
+ let activeModal = null;
4239
+
4240
+ function isVisible(node) {
4241
+ return !!(node && (node.offsetWidth || node.offsetHeight || node.getClientRects().length));
4242
+ }
4243
+
4244
+ function getFocusable(modal) {
4245
+ if (!modal) return [];
4246
+ return Array.prototype.slice
4247
+ .call(modal.querySelectorAll(focusableSelector))
4248
+ .filter((node) => !node.hasAttribute('disabled') && node.getAttribute('aria-hidden') !== 'true' && isVisible(node));
4249
+ }
4250
+
4251
+ function escapeSelector(value) {
4252
+ if (window.CSS && typeof window.CSS.escape === 'function') {
4253
+ return window.CSS.escape(value);
4254
+ }
4255
+ return value.replace(/"/g, '\\"');
4256
+ }
4257
+
4258
+ function findTrigger(modal) {
4259
+ if (!modal || !modal.id) return null;
4260
+ try {
4261
+ return document.querySelector('[data-canopy-gallery-trigger="' + escapeSelector(modal.id) + '"]');
4262
+ } catch (_) {
4263
+ return null;
4264
+ }
4265
+ }
4266
+
4267
+ function focusInitial(modal) {
4268
+ if (!modal) return;
4269
+ const focusables = getFocusable(modal);
4270
+ const target = focusables[0] || modal;
4271
+ raf(() => {
4272
+ try {
4273
+ target.focus({preventScroll: true});
4274
+ } catch (_) {
4275
+ try {
4276
+ target.focus();
4277
+ } catch (err) {}
4278
+ }
4279
+ });
4280
+ }
4281
+
4282
+ function lockScroll() {
4283
+ document.body && document.body.setAttribute('data-canopy-gallery-locked', '1');
4284
+ }
4285
+
4286
+ function unlockScroll() {
4287
+ document.body && document.body.removeAttribute('data-canopy-gallery-locked');
4288
+ }
4289
+
4290
+ function handleKeydown(event) {
4291
+ if (!activeModal) return;
4292
+ if (event.key === 'Escape' || event.key === 'Esc') {
4293
+ event.preventDefault();
4294
+ const closeId = activeModal.getAttribute('data-canopy-gallery-close');
4295
+ if (closeId) {
4296
+ const targetHash = '#' + closeId;
4297
+ if (window.location.hash !== targetHash) {
4298
+ window.location.hash = targetHash;
4299
+ } else {
4300
+ window.location.hash = targetHash;
4301
+ }
4302
+ } else {
4303
+ window.location.hash = '';
4304
+ }
4305
+ return;
4306
+ }
4307
+ if (event.key !== 'Tab') return;
4308
+ const focusables = getFocusable(activeModal);
4309
+ if (!focusables.length) {
4310
+ event.preventDefault();
4311
+ activeModal.focus();
4312
+ return;
4313
+ }
4314
+ const first = focusables[0];
4315
+ const last = focusables[focusables.length - 1];
4316
+ const current = document.activeElement;
4317
+ if (event.shiftKey) {
4318
+ if (current === first || !activeModal.contains(current)) {
4319
+ event.preventDefault();
4320
+ last.focus();
4321
+ }
4322
+ return;
4323
+ }
4324
+ if (current === last || !activeModal.contains(current)) {
4325
+ event.preventDefault();
4326
+ first.focus();
4327
+ }
4328
+ }
4329
+
4330
+ function setActiveModal(modal) {
4331
+ if (modal) {
4332
+ if (!activeModal) {
4333
+ lockScroll();
4334
+ document.addEventListener('keydown', handleKeydown, true);
4335
+ } else if (activeModal !== modal) {
4336
+ activeModal.removeAttribute('data-canopy-gallery-active');
4337
+ }
4338
+ activeModal = modal;
4339
+ modal.setAttribute('data-canopy-gallery-active', '1');
4340
+ focusInitial(modal);
4341
+ return;
4342
+ }
4343
+ if (!activeModal) return;
4344
+ const previous = activeModal;
4345
+ activeModal = null;
4346
+ previous.removeAttribute('data-canopy-gallery-active');
4347
+ unlockScroll();
4348
+ document.removeEventListener('keydown', handleKeydown, true);
4349
+ const closeTarget = previous.getAttribute('data-canopy-gallery-close');
4350
+ if (closeTarget) {
4351
+ const closeHash = '#' + closeTarget;
4352
+ if (window.location.hash === closeHash) {
4353
+ try {
4354
+ if (window.history && typeof window.history.replaceState === 'function') {
4355
+ window.history.replaceState('', document.title, window.location.pathname + window.location.search);
4356
+ } else {
4357
+ window.location.hash = '';
4358
+ }
4359
+ } catch (_) {}
4360
+ }
4361
+ }
4362
+ const trigger = findTrigger(previous);
4363
+ if (trigger && typeof trigger.focus === 'function') {
4364
+ raf(() => {
4365
+ try {
4366
+ trigger.focus({preventScroll: true});
4367
+ } catch (_) {
4368
+ try {
4369
+ trigger.focus();
4370
+ } catch (err) {}
4371
+ }
4372
+ });
4373
+ }
4374
+ }
4375
+
4376
+ function modalFromHash() {
4377
+ const id = window.location.hash.replace(/^#/, '');
4378
+ if (!id) return null;
4379
+ const candidate = document.getElementById(id);
4380
+ if (!candidate) return null;
4381
+ return candidate.hasAttribute('data-canopy-gallery-modal') ? candidate : null;
4382
+ }
4383
+
4384
+ function syncFromHash() {
4385
+ const modal = modalFromHash();
4386
+ if (modal) {
4387
+ setActiveModal(modal);
4388
+ } else {
4389
+ setActiveModal(null);
4390
+ }
4391
+ }
4392
+
4393
+ window.addEventListener('hashchange', syncFromHash);
4394
+ window.addEventListener('pageshow', syncFromHash);
4395
+ syncFromHash();
4396
+ })()`;
4397
+ var galleryInstanceCounter = 0;
4398
+ function nextGalleryInstanceId() {
4399
+ galleryInstanceCounter += 1;
4400
+ return `canopy-gallery-${galleryInstanceCounter}`;
4401
+ }
4402
+ function slugify(value, fallback) {
4403
+ if (!value) return fallback;
4404
+ const normalized = String(value).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").trim();
4405
+ if (!normalized) return fallback;
4406
+ return normalized;
4407
+ }
4408
+ function ensureArray2(value) {
4409
+ if (!value && value !== 0) return [];
4410
+ return Array.isArray(value) ? value : [value];
4411
+ }
4412
+ function normalizeOrder(value) {
4413
+ const normalized = String(value || "default").toLowerCase();
4414
+ return normalized === "random" ? "random" : "default";
4415
+ }
4416
+ function shuffleItems(list) {
4417
+ const copy = list.slice();
4418
+ for (let i = copy.length - 1; i > 0; i -= 1) {
4419
+ const j = Math.floor(Math.random() * (i + 1));
4420
+ const temp = copy[i];
4421
+ copy[i] = copy[j];
4422
+ copy[j] = temp;
4423
+ }
4424
+ return copy;
4425
+ }
4426
+ function renderMetaList(meta, className) {
4427
+ const entries = ensureArray2(meta).filter((entry) => entry || entry === 0);
4428
+ if (!entries.length) return null;
4429
+ return /* @__PURE__ */ React40.createElement("ul", { className, role: "list" }, entries.map((entry, index) => /* @__PURE__ */ React40.createElement("li", { key: `meta-${index}` }, entry)));
4430
+ }
4431
+ function renderPreview(props = {}) {
4432
+ const source = typeof props.media === "string" && props.media || props.thumbnail || props.src || props.image && props.image.src || props.image;
4433
+ if (source) {
4434
+ const alt = props.thumbnailAlt || props.imageAlt || props.alt || props.title || "";
4435
+ const width = props.thumbnailWidth || props.imageWidth || props.width;
4436
+ const height = props.thumbnailHeight || props.imageHeight || props.height;
4437
+ return /* @__PURE__ */ React40.createElement(
4438
+ "img",
4439
+ {
4440
+ src: source,
4441
+ alt,
4442
+ width,
4443
+ height,
4444
+ loading: "lazy"
4445
+ }
4446
+ );
4447
+ }
4448
+ return /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__placeholder", "aria-hidden": "true" });
4449
+ }
4450
+ function normalizeItem(child, index, galleryId, manifestMap) {
4451
+ var _a;
4452
+ if (!React40.isValidElement(child)) return null;
4453
+ if (child.type !== GalleryItem && ((_a = child.type) == null ? void 0 : _a.displayName) !== "GalleryItem")
4454
+ return null;
4455
+ const props = child.props || {};
4456
+ const manifestValues = Array.isArray(props.referencedManifests) ? props.referencedManifests : props.manifest ? [props.manifest] : Array.isArray(props.manifests) ? props.manifests : [];
4457
+ const manifests = resolveReferencedManifests(manifestValues, manifestMap);
4458
+ const normalizedProps = { ...props };
4459
+ if (!normalizedProps.title) {
4460
+ const manifestWithTitle = manifests.find(
4461
+ (manifest) => manifest && manifest.title
4462
+ );
4463
+ if (manifestWithTitle && manifestWithTitle.title) {
4464
+ normalizedProps.title = manifestWithTitle.title;
4465
+ }
4466
+ }
4467
+ if (!normalizedProps.summary || !normalizedProps.description) {
4468
+ const manifestWithSummary = manifests.find(
4469
+ (manifest) => manifest && manifest.summary
4470
+ );
4471
+ if (manifestWithSummary && manifestWithSummary.summary) {
4472
+ if (!normalizedProps.summary) {
4473
+ normalizedProps.summary = manifestWithSummary.summary;
4474
+ }
4475
+ if (!normalizedProps.description) {
4476
+ normalizedProps.description = manifestWithSummary.summary;
4477
+ }
4478
+ }
4479
+ }
4480
+ if (!normalizedProps.thumbnail && !normalizedProps.media && !normalizedProps.image) {
4481
+ const manifestWithThumb = manifests.find(
4482
+ (manifest) => manifest && manifest.thumbnail
4483
+ );
4484
+ if (manifestWithThumb && manifestWithThumb.thumbnail) {
4485
+ normalizedProps.thumbnail = manifestWithThumb.thumbnail;
4486
+ if (manifestWithThumb.thumbnailWidth != null && normalizedProps.thumbnailWidth == null && normalizedProps.imageWidth == null && normalizedProps.width == null) {
4487
+ normalizedProps.thumbnailWidth = manifestWithThumb.thumbnailWidth;
4488
+ }
4489
+ if (manifestWithThumb.thumbnailHeight != null && normalizedProps.thumbnailHeight == null && normalizedProps.imageHeight == null && normalizedProps.height == null) {
4490
+ normalizedProps.thumbnailHeight = manifestWithThumb.thumbnailHeight;
4491
+ }
4492
+ }
4493
+ }
4494
+ const rawSlug = normalizedProps.slug || normalizedProps.id || normalizedProps.title || `item-${index + 1}`;
4495
+ const slug = slugify(rawSlug, `item-${index + 1}`);
4496
+ const modalId = `${galleryId}-modal-${slug}-${index + 1}`;
4497
+ const triggerLabel = normalizedProps.triggerLabel || normalizedProps.buttonLabel || normalizedProps.linkLabel || `Open popup for ${normalizedProps.title || `item ${index + 1}`}`;
4498
+ const modalTitleId = `${modalId}-title`;
4499
+ const needsDescriptionId = normalizedProps.popupDescription || normalizedProps.modalDescription || normalizedProps.description || normalizedProps.summary;
4500
+ const modalDescriptionId = needsDescriptionId ? `${modalId}-description` : null;
4501
+ const manifestLinks = manifests.map((manifest) => {
4502
+ if (!manifest) return null;
4503
+ const href = manifest.href || manifest.id || manifest.slug;
4504
+ if (!href) return null;
4505
+ return {
4506
+ id: manifest.id || manifest.slug || href,
4507
+ href,
4508
+ title: manifest.title || manifest.label || href
4509
+ };
4510
+ }).filter(Boolean);
4511
+ return {
4512
+ index,
4513
+ key: child.key != null ? child.key : `${galleryId}-item-${index}`,
4514
+ props: normalizedProps,
4515
+ modalId,
4516
+ slug,
4517
+ modalTitleId,
4518
+ modalDescriptionId,
4519
+ triggerLabel,
4520
+ manifests: manifestLinks
4521
+ };
4522
+ }
4523
+ function buildCaptionContent(itemProps) {
4524
+ if (itemProps.caption) return itemProps.caption;
4525
+ const kicker = itemProps.kicker || itemProps.label || itemProps.eyebrow;
4526
+ const summary = itemProps.summary || itemProps.description;
4527
+ return /* @__PURE__ */ React40.createElement(React40.Fragment, null, kicker ? /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__kicker" }, kicker) : null, itemProps.title ? /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__title-text" }, itemProps.title) : null, summary ? /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__summary" }, summary) : null, renderMetaList(
4528
+ itemProps.meta,
4529
+ "canopy-gallery__meta canopy-gallery__meta--caption"
4530
+ ));
4531
+ }
4532
+ function GalleryModal({ item, total, closeTargetId, prevTarget, nextTarget }) {
4533
+ const {
4534
+ props,
4535
+ modalId,
4536
+ modalTitleId,
4537
+ modalDescriptionId,
4538
+ triggerLabel,
4539
+ index,
4540
+ manifests
4541
+ } = item;
4542
+ const kicker = props.kicker || props.label || props.eyebrow;
4543
+ const summary = props.popupDescription || props.modalDescription || props.description || props.summary || null;
4544
+ const modalTitle = props.popupTitle || props.modalTitle || props.title || `Item ${index + 1}`;
4545
+ const preview = renderPreview(props);
4546
+ return /* @__PURE__ */ React40.createElement(
4547
+ "div",
4548
+ {
4549
+ id: modalId,
4550
+ className: "canopy-gallery__modal",
4551
+ role: "dialog",
4552
+ "aria-modal": "true",
4553
+ "aria-labelledby": modalTitleId,
4554
+ "aria-describedby": modalDescriptionId || void 0,
4555
+ tabIndex: -1,
4556
+ "data-canopy-gallery-modal": "true",
4557
+ "data-canopy-gallery-close": closeTargetId
4558
+ },
4559
+ /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-scrim" }, /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-panel" }, /* @__PURE__ */ React40.createElement("header", { className: "canopy-gallery__modal-header" }, preview ? /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-thumb", "aria-hidden": "true" }, preview) : null, /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-text" }, kicker ? /* @__PURE__ */ React40.createElement("p", { className: "canopy-gallery__modal-kicker" }, kicker) : null, /* @__PURE__ */ React40.createElement("h3", { id: modalTitleId, className: "canopy-gallery__modal-title" }, modalTitle), summary ? /* @__PURE__ */ React40.createElement(
4560
+ "p",
4561
+ {
4562
+ id: modalDescriptionId || void 0,
4563
+ className: "canopy-gallery__modal-summary"
4564
+ },
4565
+ summary
4566
+ ) : null, renderMetaList(
4567
+ props.meta,
4568
+ "canopy-gallery__meta canopy-gallery__meta--modal"
4569
+ ))), /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modal-body" }, props.children, manifests && manifests.length ? /* @__PURE__ */ React40.createElement("section", { className: "canopy-gallery__referenced" }, /* @__PURE__ */ React40.createElement("h4", null, "Referenced works"), /* @__PURE__ */ React40.createElement("ul", { role: "list" }, manifests.map((manifest) => /* @__PURE__ */ React40.createElement("li", { key: manifest.id || manifest.href }, /* @__PURE__ */ React40.createElement("a", { href: manifest.href }, manifest.title || manifest.href))))) : null), /* @__PURE__ */ React40.createElement("footer", { className: "canopy-gallery__modal-footer" }, /* @__PURE__ */ React40.createElement(
4570
+ "a",
4571
+ {
4572
+ className: "canopy-gallery__modal-close",
4573
+ href: `#${closeTargetId}`,
4574
+ "aria-label": `Close popup for ${modalTitle}`
4575
+ },
4576
+ "Close"
4577
+ ), total > 1 ? /* @__PURE__ */ React40.createElement(
4578
+ "nav",
4579
+ {
4580
+ className: "canopy-gallery__modal-nav",
4581
+ "aria-label": "Gallery popup navigation"
4582
+ },
4583
+ /* @__PURE__ */ React40.createElement(
4584
+ "a",
4585
+ {
4586
+ className: "canopy-gallery__modal-nav-link canopy-gallery__modal-nav-link--prev",
4587
+ href: `#${prevTarget}`,
4588
+ "aria-label": `Show previous popup before ${modalTitle}`
4589
+ },
4590
+ "Prev"
4591
+ ),
4592
+ /* @__PURE__ */ React40.createElement(
4593
+ "a",
4594
+ {
4595
+ className: "canopy-gallery__modal-nav-link canopy-gallery__modal-nav-link--next",
4596
+ href: `#${nextTarget}`,
4597
+ "aria-label": `Show next popup after ${modalTitle}`
4598
+ },
4599
+ "Next"
4600
+ )
4601
+ ) : null)))
4602
+ );
4603
+ }
4604
+ function GalleryFigure({ item }) {
4605
+ const { props, modalId, triggerLabel } = item;
4606
+ return /* @__PURE__ */ React40.createElement(
4607
+ "figure",
4608
+ {
4609
+ className: "canopy-gallery__item",
4610
+ "data-gallery-item-index": item.index
4611
+ },
4612
+ /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__media" }, renderPreview(props)),
4613
+ /* @__PURE__ */ React40.createElement("figcaption", { className: "canopy-gallery__caption" }, buildCaptionContent(props)),
4614
+ /* @__PURE__ */ React40.createElement(
4615
+ "a",
4616
+ {
4617
+ className: "canopy-gallery__trigger",
4618
+ href: `#${modalId}`,
4619
+ "aria-haspopup": "dialog",
4620
+ "aria-controls": modalId,
4621
+ "aria-label": triggerLabel,
4622
+ "data-canopy-gallery-trigger": modalId
4623
+ },
4624
+ /* @__PURE__ */ React40.createElement("span", { className: "canopy-gallery__trigger-label" }, triggerLabel)
4625
+ )
4626
+ );
4627
+ }
4628
+ function GalleryContent({ children, flex = false }) {
4629
+ const contentClassName = [
4630
+ "canopy-gallery-item__content",
4631
+ flex ? "canopy-gallery-item__content_flex" : null
4632
+ ].filter(Boolean).join(" ");
4633
+ return /* @__PURE__ */ React40.createElement("div", { className: contentClassName }, children);
4634
+ }
4635
+ function GalleryItem() {
4636
+ return null;
4637
+ }
4638
+ GalleryItem.displayName = "GalleryItem";
4639
+ function normalizePopupSize(value) {
4640
+ const normalized = String(value || "full").toLowerCase();
4641
+ return normalized === "medium" ? "medium" : "full";
4642
+ }
4643
+ function Gallery({
4644
+ children,
4645
+ id,
4646
+ title,
4647
+ description,
4648
+ popupSize = "full",
4649
+ order = "default",
4650
+ className = "",
4651
+ style = {}
4652
+ }) {
4653
+ const manifestMap = useReferencedManifestMap();
4654
+ const galleryId = id ? String(id) : nextGalleryInstanceId();
4655
+ const HeadingTag = "h3";
4656
+ const closeTargetId = `${galleryId}-close`;
4657
+ const childArray = React40.Children.toArray(children);
4658
+ const items = childArray.map((child, index) => normalizeItem(child, index, galleryId, manifestMap)).filter(Boolean);
4659
+ if (!items.length) return null;
4660
+ const popupMode = normalizePopupSize(popupSize);
4661
+ const orderMode = normalizeOrder(order);
4662
+ const orderedItems = orderMode === "random" ? shuffleItems(items) : items;
4663
+ const rootClassName = [
4664
+ "canopy-gallery",
4665
+ popupMode === "medium" ? "canopy-gallery--popup-medium" : "canopy-gallery--popup-full",
4666
+ className
4667
+ ].filter(Boolean).join(" ");
4668
+ return /* @__PURE__ */ React40.createElement("section", { className: rootClassName, style, "data-canopy-gallery": "true" }, /* @__PURE__ */ React40.createElement(
4669
+ "div",
4670
+ {
4671
+ id: closeTargetId,
4672
+ className: "canopy-gallery__close-anchor",
4673
+ "aria-hidden": "true",
4674
+ tabIndex: -1
4675
+ }
4676
+ ), (title || description) && /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__header" }, title ? /* @__PURE__ */ React40.createElement(HeadingTag, { className: "canopy-gallery__heading" }, title) : null, description ? /* @__PURE__ */ React40.createElement("p", { className: "canopy-gallery__description" }, description) : null), /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__grid" }, orderedItems.map((item) => /* @__PURE__ */ React40.createElement(GalleryFigure, { key: item.key, item }))), /* @__PURE__ */ React40.createElement("div", { className: "canopy-gallery__modals" }, orderedItems.map((item, index) => {
4677
+ const total = orderedItems.length;
4678
+ const prevIndex = (index - 1 + total) % total;
4679
+ const nextIndex = (index + 1) % total;
4680
+ return /* @__PURE__ */ React40.createElement(
4681
+ GalleryModal,
4682
+ {
4683
+ key: `${item.modalId}-modal`,
4684
+ item,
4685
+ total,
4686
+ closeTargetId,
4687
+ prevTarget: orderedItems[prevIndex].modalId,
4688
+ nextTarget: orderedItems[nextIndex].modalId
4689
+ }
4690
+ );
4691
+ })), /* @__PURE__ */ React40.createElement(
4692
+ "script",
4693
+ {
4694
+ "data-canopy-gallery-script": "true",
4695
+ dangerouslySetInnerHTML: { __html: INLINE_SCRIPT }
4696
+ }
4697
+ ));
4698
+ }
4699
+ Gallery.Item = GalleryItem;
4700
+ Gallery.Content = GalleryContent;
4071
4701
  export {
4072
4702
  ArticleCard,
4073
4703
  Button,
@@ -4080,6 +4710,9 @@ export {
4080
4710
  Card,
4081
4711
  Container,
4082
4712
  MarkdownTable as DocsMarkdownTable,
4713
+ Gallery,
4714
+ GalleryContent,
4715
+ GalleryItem,
4083
4716
  GoogleAnalytics,
4084
4717
  Grid,
4085
4718
  GridItem,