@canopy-iiif/app 1.10.3 → 1.10.5

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.
@@ -4692,6 +4692,214 @@ function ReferencedManifestCard({
4692
4692
  );
4693
4693
  }
4694
4694
 
4695
+ // ui/src/utils/keyLegend.js
4696
+ var DEFAULT_ACCENT_HEX = "#2563eb";
4697
+ function normalizeHex(value) {
4698
+ if (!value) return "";
4699
+ let input = String(value).trim();
4700
+ if (!input) return "";
4701
+ if (input.startsWith("var(")) return input;
4702
+ if (/^#[0-9a-f]{3}$/i.test(input)) {
4703
+ return "#" + input.replace(/^#/, "").split("").map((ch) => ch + ch).join("").toLowerCase();
4704
+ }
4705
+ if (/^#[0-9a-f]{6}$/i.test(input)) return input.toLowerCase();
4706
+ return "";
4707
+ }
4708
+ function hexToRgb(hex) {
4709
+ if (!hex) return null;
4710
+ const normalized = normalizeHex(hex);
4711
+ if (!normalized || normalized.startsWith("var(")) return null;
4712
+ const int = parseInt(normalized.slice(1), 16);
4713
+ if (Number.isNaN(int)) return null;
4714
+ return {
4715
+ r: int >> 16 & 255,
4716
+ g: int >> 8 & 255,
4717
+ b: int & 255
4718
+ };
4719
+ }
4720
+ function rgbToHsl({ r, g, b }) {
4721
+ const rn = r / 255;
4722
+ const gn = g / 255;
4723
+ const bn = b / 255;
4724
+ const max = Math.max(rn, gn, bn);
4725
+ const min = Math.min(rn, gn, bn);
4726
+ let h = 0;
4727
+ let s = 0;
4728
+ const l = (max + min) / 2;
4729
+ const delta = max - min;
4730
+ if (delta !== 0) {
4731
+ s = l > 0.5 ? delta / (2 - max - min) : delta / (max + min);
4732
+ switch (max) {
4733
+ case rn:
4734
+ h = (gn - bn) / delta + (gn < bn ? 6 : 0);
4735
+ break;
4736
+ case gn:
4737
+ h = (bn - rn) / delta + 2;
4738
+ break;
4739
+ case bn:
4740
+ h = (rn - gn) / delta + 4;
4741
+ break;
4742
+ default:
4743
+ break;
4744
+ }
4745
+ h /= 6;
4746
+ }
4747
+ return { h: h * 360, s: s * 100, l: l * 100 };
4748
+ }
4749
+ function hslToHex(h, s, l) {
4750
+ const sat = s / 100;
4751
+ const light = l / 100;
4752
+ const c = (1 - Math.abs(2 * light - 1)) * sat;
4753
+ const hh = h / 60;
4754
+ const x = c * (1 - Math.abs(hh % 2 - 1));
4755
+ let r = 0;
4756
+ let g = 0;
4757
+ let b = 0;
4758
+ if (hh >= 0 && hh < 1) {
4759
+ r = c;
4760
+ g = x;
4761
+ } else if (hh >= 1 && hh < 2) {
4762
+ r = x;
4763
+ g = c;
4764
+ } else if (hh >= 2 && hh < 3) {
4765
+ g = c;
4766
+ b = x;
4767
+ } else if (hh >= 3 && hh < 4) {
4768
+ g = x;
4769
+ b = c;
4770
+ } else if (hh >= 4 && hh < 5) {
4771
+ r = x;
4772
+ b = c;
4773
+ } else {
4774
+ r = c;
4775
+ b = x;
4776
+ }
4777
+ const m = light - c / 2;
4778
+ const rn = Math.round((r + m) * 255);
4779
+ const gn = Math.round((g + m) * 255);
4780
+ const bn = Math.round((b + m) * 255);
4781
+ const toHex = (value) => value.toString(16).padStart(2, "0");
4782
+ return `#${toHex(rn)}${toHex(gn)}${toHex(bn)}`;
4783
+ }
4784
+ function rotateHue(baseHue, degrees) {
4785
+ return (baseHue + degrees + 360) % 360;
4786
+ }
4787
+ function resolveAccentHex() {
4788
+ let value = "";
4789
+ try {
4790
+ if (typeof window !== "undefined") {
4791
+ const styles = window.getComputedStyle(document.documentElement);
4792
+ value = styles.getPropertyValue("--color-accent-default");
4793
+ }
4794
+ } catch (_) {
4795
+ }
4796
+ const normalized = normalizeHex(value);
4797
+ return normalized && !normalized.startsWith("var(") ? normalized : DEFAULT_ACCENT_HEX;
4798
+ }
4799
+ function generateLegendColors(count) {
4800
+ if (!count || count <= 0) return [];
4801
+ const colors = [];
4802
+ const baseHex = resolveAccentHex();
4803
+ const accentVar = `var(--color-accent-default, ${baseHex})`;
4804
+ colors.push(accentVar);
4805
+ if (count === 1) return colors;
4806
+ const rgb = hexToRgb(baseHex);
4807
+ const baseHsl = rgb ? rgbToHsl(rgb) : { h: 220, s: 85, l: 56 };
4808
+ const rotations = [180, 120, -120, 60, -60, 90, -90, 30, -30];
4809
+ const needed = count - 1;
4810
+ for (let i = 0; i < needed; i += 1) {
4811
+ const angle = rotations[i] != null ? rotations[i] : 360 / (needed + 1) * (i + 1);
4812
+ const rotatedHue = rotateHue(baseHsl.h, angle);
4813
+ const hex = hslToHex(rotatedHue, baseHsl.s, baseHsl.l);
4814
+ colors.push(hex);
4815
+ }
4816
+ return colors;
4817
+ }
4818
+ function createLookupStore() {
4819
+ try {
4820
+ if (typeof globalThis !== "undefined" && typeof globalThis.Map === "function") {
4821
+ return new globalThis.Map();
4822
+ }
4823
+ } catch (_) {
4824
+ }
4825
+ try {
4826
+ if (typeof window !== "undefined" && typeof window.Map === "function") {
4827
+ return new window.Map();
4828
+ }
4829
+ } catch (_) {
4830
+ }
4831
+ const store = /* @__PURE__ */ Object.create(null);
4832
+ return {
4833
+ has(key) {
4834
+ return Object.prototype.hasOwnProperty.call(store, key);
4835
+ },
4836
+ get(key) {
4837
+ return store[key];
4838
+ },
4839
+ set(key, value) {
4840
+ store[key] = value;
4841
+ return this;
4842
+ }
4843
+ };
4844
+ }
4845
+ function normalizeText(value) {
4846
+ if (value == null) return "";
4847
+ try {
4848
+ return String(value).trim();
4849
+ } catch (_) {
4850
+ return "";
4851
+ }
4852
+ }
4853
+ function normalizeKeyValue(entry) {
4854
+ var _a, _b, _c;
4855
+ if (!entry) return "";
4856
+ const value = (_c = (_b = (_a = entry.id) != null ? _a : entry.value) != null ? _b : entry.key) != null ? _c : entry.slug;
4857
+ return normalizeText(value);
4858
+ }
4859
+ function normalizeKeyLabel(entry) {
4860
+ var _a, _b, _c;
4861
+ if (!entry) return "";
4862
+ const label = (_c = (_b = (_a = entry.label) != null ? _a : entry.name) != null ? _b : entry.title) != null ? _c : entry.text;
4863
+ return normalizeText(label);
4864
+ }
4865
+ function normalizeKeyLegendEntries(entries, options = {}) {
4866
+ if (!Array.isArray(entries)) return [];
4867
+ const resolveVariant = typeof options.resolveVariant === "function" ? options.resolveVariant : null;
4868
+ const variantProp = options.variantProp || "variant";
4869
+ return entries.map((entry) => {
4870
+ if (!entry) return null;
4871
+ const keyValue = normalizeKeyValue(entry);
4872
+ const label = normalizeKeyLabel(entry);
4873
+ if (!keyValue || !label) return null;
4874
+ const normalized = {
4875
+ keyValue,
4876
+ label
4877
+ };
4878
+ if (resolveVariant) {
4879
+ const variant = resolveVariant(entry);
4880
+ if (variant) normalized[variantProp] = variant;
4881
+ }
4882
+ return normalized;
4883
+ }).filter(Boolean);
4884
+ }
4885
+ function buildKeyLegend(entries) {
4886
+ if (!Array.isArray(entries) || !entries.length) {
4887
+ return { groups: [], lookup: null };
4888
+ }
4889
+ const lookup = createLookupStore();
4890
+ const palette = generateLegendColors(entries.length);
4891
+ const groups = entries.map((entry, index) => {
4892
+ const color = palette[index] || palette[0] || DEFAULT_ACCENT_HEX;
4893
+ const group = {
4894
+ ...entry,
4895
+ color
4896
+ };
4897
+ lookup.set(entry.keyValue, group);
4898
+ return group;
4899
+ });
4900
+ return { groups, lookup };
4901
+ }
4902
+
4695
4903
  // ui/src/content/timeline/Timeline.jsx
4696
4904
  var DAY_MS = 24 * 60 * 60 * 1e3;
4697
4905
  var DEFAULT_TRACK_HEIGHT = 640;
@@ -4829,6 +5037,7 @@ function sanitizePoints(points) {
4829
5037
  label: meta.label || "",
4830
5038
  timestamp: Number.isFinite(timestamp) ? timestamp : null
4831
5039
  },
5040
+ keyValue: point.keyValue ? String(point.keyValue).trim() : "",
4832
5041
  manifests,
4833
5042
  resources
4834
5043
  };
@@ -4859,7 +5068,7 @@ function resolveTrackHeight(height, pointCount) {
4859
5068
  }
4860
5069
  return fallback;
4861
5070
  }
4862
- function TimelineConnector({ side, isActive, highlight }) {
5071
+ function TimelineConnector({ side, isActive, highlight, color }) {
4863
5072
  const connectorClasses = [
4864
5073
  "canopy-timeline__connector",
4865
5074
  side === "left" ? "canopy-timeline__connector--left" : "canopy-timeline__connector--right"
@@ -4868,7 +5077,8 @@ function TimelineConnector({ side, isActive, highlight }) {
4868
5077
  "canopy-timeline__connector-dot",
4869
5078
  highlight || isActive ? "is-active" : ""
4870
5079
  ].filter(Boolean).join(" ");
4871
- return /* @__PURE__ */ React37.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React37.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement("span", { className: dotClasses }), /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__connector-line" })));
5080
+ const connectorStyle = color ? { "--canopy-timeline-point-color": color } : void 0;
5081
+ return /* @__PURE__ */ React37.createElement("span", { className: connectorClasses, "aria-hidden": "true", style: connectorStyle }, side === "left" ? /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React37.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React37.createElement(React37.Fragment, null, /* @__PURE__ */ React37.createElement("span", { className: dotClasses }), /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__connector-line" })));
4872
5082
  }
4873
5083
  function renderResourceSection(point) {
4874
5084
  if (!point) return null;
@@ -4898,6 +5108,9 @@ function Timeline({
4898
5108
  align = ALIGN_OPTIONS.CENTER,
4899
5109
  steps = null,
4900
5110
  points: pointsProp,
5111
+ keyConfig = [],
5112
+ timelineKey = [],
5113
+ legend = [],
4901
5114
  __canopyTimeline: payload = null,
4902
5115
  ...rest
4903
5116
  }) {
@@ -4911,6 +5124,35 @@ function Timeline({
4911
5124
  () => sanitizePoints(rawPoints),
4912
5125
  [rawPoints]
4913
5126
  );
5127
+ const resolvedKeyInput = React37.useMemo(() => {
5128
+ if (Array.isArray(keyConfig) && keyConfig.length) return keyConfig;
5129
+ if (Array.isArray(timelineKey) && timelineKey.length) return timelineKey;
5130
+ if (Array.isArray(legend) && legend.length) return legend;
5131
+ return [];
5132
+ }, [keyConfig, timelineKey, legend]);
5133
+ const normalizedLegendConfig = React37.useMemo(
5134
+ () => normalizeKeyLegendEntries(resolvedKeyInput),
5135
+ [resolvedKeyInput]
5136
+ );
5137
+ const timelineKeyData = React37.useMemo(
5138
+ () => buildKeyLegend(normalizedLegendConfig),
5139
+ [normalizedLegendConfig]
5140
+ );
5141
+ const timelineKeyGroups = timelineKeyData.groups;
5142
+ const timelineKeyLookup = timelineKeyData.lookup;
5143
+ const getLegendMeta = React37.useCallback(
5144
+ (value) => {
5145
+ if (!value || !timelineKeyLookup || typeof timelineKeyLookup.get !== "function") {
5146
+ return null;
5147
+ }
5148
+ try {
5149
+ return timelineKeyLookup.get(value) || null;
5150
+ } catch (_) {
5151
+ return null;
5152
+ }
5153
+ },
5154
+ [timelineKeyLookup]
5155
+ );
4914
5156
  const localeValue = payload && payload.locale ? payload.locale : localeProp;
4915
5157
  const baseLocale = React37.useMemo(
4916
5158
  () => createLocale(localeValue),
@@ -4941,13 +5183,22 @@ function Timeline({
4941
5183
  const fallbackProgress = sanitizedPoints.length > 1 ? index / (sanitizedPoints.length - 1) : 0;
4942
5184
  const progress = useUniformSpacing ? fallbackProgress : Number.isFinite(timestamp) ? clampProgress((timestamp - spanStart) / span) : fallbackProgress;
4943
5185
  const side = enforcedSide || point.side || (index % 2 === 0 ? "left" : "right");
5186
+ const keyMeta = point.keyValue ? getLegendMeta(point.keyValue) : null;
4944
5187
  return {
4945
5188
  ...point,
4946
5189
  progress,
4947
- side
5190
+ side,
5191
+ keyMeta
4948
5192
  };
4949
5193
  });
4950
- }, [sanitizedPoints, spanStart, span, useUniformSpacing, enforcedSide]);
5194
+ }, [
5195
+ sanitizedPoints,
5196
+ spanStart,
5197
+ span,
5198
+ useUniformSpacing,
5199
+ enforcedSide,
5200
+ getLegendMeta
5201
+ ]);
4951
5202
  const [activeId, setActiveId] = React37.useState(
4952
5203
  () => getActivePointId(pointsWithPosition)
4953
5204
  );
@@ -5000,6 +5251,7 @@ function Timeline({
5000
5251
  className
5001
5252
  ].filter(Boolean).join(" ");
5002
5253
  const rangeLabel = formatRangeLabel(effectiveRange);
5254
+ const hasKeyLegend = timelineKeyGroups.length > 0;
5003
5255
  function renderPointEntry(point) {
5004
5256
  if (!point) return null;
5005
5257
  const wrapperClasses = [
@@ -5012,12 +5264,14 @@ function Timeline({
5012
5264
  point.id === activeId ? "is-active" : "",
5013
5265
  point.highlight ? "is-highlighted" : ""
5014
5266
  ].filter(Boolean).join(" ");
5267
+ const pointColor = point.keyMeta && point.keyMeta.color ? point.keyMeta.color : null;
5015
5268
  const connector = /* @__PURE__ */ React37.createElement(
5016
5269
  TimelineConnector,
5017
5270
  {
5018
5271
  side: point.side,
5019
5272
  isActive: point.id === activeId,
5020
- highlight: point.highlight
5273
+ highlight: point.highlight,
5274
+ color: pointColor
5021
5275
  }
5022
5276
  );
5023
5277
  const body = /* @__PURE__ */ React37.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
@@ -5041,12 +5295,24 @@ function Timeline({
5041
5295
  const wrapperStyle = { top: `${entry.progress * 100}%` };
5042
5296
  const isExpanded = expandedGroupIds.has(entry.id);
5043
5297
  const hasActivePoint = entry.points.some((point) => point.id === activeId);
5298
+ const groupKeyMeta = (() => {
5299
+ if (!entry.points || !entry.points.length) return null;
5300
+ const firstPoint = entry.points[0];
5301
+ if (!firstPoint || !firstPoint.keyValue) return null;
5302
+ const sameKey = entry.points.every(
5303
+ (point) => point && point.keyValue === firstPoint.keyValue
5304
+ );
5305
+ if (!sameKey) return null;
5306
+ return firstPoint.keyMeta || getLegendMeta(firstPoint.keyValue);
5307
+ })();
5308
+ const groupColor = groupKeyMeta && groupKeyMeta.color ? groupKeyMeta.color : null;
5044
5309
  const connector = /* @__PURE__ */ React37.createElement(
5045
5310
  TimelineConnector,
5046
5311
  {
5047
5312
  side: entry.side,
5048
5313
  isActive: hasActivePoint,
5049
- highlight: hasActivePoint
5314
+ highlight: hasActivePoint,
5315
+ color: groupColor
5050
5316
  }
5051
5317
  );
5052
5318
  const groupClasses = [
@@ -5104,7 +5370,22 @@ function Timeline({
5104
5370
  if (entry.type === "group") return renderGroupEntry(entry);
5105
5371
  return renderPointEntry(entry.point);
5106
5372
  })
5107
- )));
5373
+ )), hasKeyLegend ? /* @__PURE__ */ React37.createElement("div", { className: "canopy-timeline__key", "aria-label": "Timeline key" }, /* @__PURE__ */ React37.createElement("ul", { className: "canopy-timeline__key-list" }, timelineKeyGroups.map((group) => /* @__PURE__ */ React37.createElement(
5374
+ "li",
5375
+ {
5376
+ key: group.keyValue || group.label,
5377
+ className: "canopy-timeline__key-item"
5378
+ },
5379
+ /* @__PURE__ */ React37.createElement(
5380
+ "span",
5381
+ {
5382
+ className: "canopy-timeline__key-dot",
5383
+ "aria-hidden": "true",
5384
+ style: { backgroundColor: group.color || void 0 }
5385
+ }
5386
+ ),
5387
+ /* @__PURE__ */ React37.createElement("span", { className: "canopy-timeline__key-label" }, group.label)
5388
+ )))) : null);
5108
5389
  }
5109
5390
  function renderSteps(stepSize, range) {
5110
5391
  if (!Number.isFinite(stepSize) || stepSize <= 0 || !range) return null;
@@ -5327,6 +5608,14 @@ function resolveReferencedManifests(ids, manifestMap) {
5327
5608
  }
5328
5609
 
5329
5610
  // ui/src/content/timeline/MdxTimeline.jsx
5611
+ function normalizeReactKey(value) {
5612
+ if (value == null) return "";
5613
+ const str = String(value);
5614
+ if (/^\.[0-9]+$/.test(str)) return "";
5615
+ if (str.startsWith(".$")) return str.slice(2);
5616
+ if (str.startsWith(".")) return str.slice(1);
5617
+ return str;
5618
+ }
5330
5619
  function normalizeResource(resource, index) {
5331
5620
  if (!resource) return null;
5332
5621
  const href = resource.href || resource.id || "";
@@ -5341,14 +5630,17 @@ function normalizeResource(resource, index) {
5341
5630
  };
5342
5631
  }
5343
5632
  function normalizePoint(child, index, options) {
5344
- var _a, _b, _c, _d, _e, _f;
5633
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
5345
5634
  if (!React39.isValidElement(child)) return null;
5346
5635
  if (child.type !== TimelinePoint && ((_a = child.type) == null ? void 0 : _a.displayName) !== "TimelinePoint")
5347
5636
  return null;
5348
5637
  const props = child.props || {};
5349
5638
  const id = props.id || `timeline-point-${index}`;
5639
+ const normalizedNodeKey = normalizeReactKey(child.key);
5640
+ const keyValueProp = (_f = (_e = (_d = (_c = (_b = props.timelineKey) != null ? _b : props.pointKey) != null ? _c : props.keyValue) != null ? _d : props.group) != null ? _e : props.category) != null ? _f : null;
5641
+ const normalizedKeyValue = keyValueProp != null && keyValueProp !== "" ? String(keyValueProp).trim() : normalizedNodeKey;
5350
5642
  const granularity = props.precision || props.granularity || options && options.range && options.range.granularity || "year";
5351
- const value = (_f = (_e = (_d = (_c = (_b = props.date) != null ? _b : props.value) != null ? _c : props.timestamp) != null ? _d : props.year) != null ? _e : props.label) != null ? _f : props.title;
5643
+ const value = (_k = (_j = (_i = (_h = (_g = props.date) != null ? _g : props.value) != null ? _h : props.timestamp) != null ? _i : props.year) != null ? _j : props.label) != null ? _k : props.title;
5352
5644
  const meta = buildPointMetadata({
5353
5645
  value,
5354
5646
  granularity,
@@ -5380,7 +5672,8 @@ function normalizePoint(child, index, options) {
5380
5672
  },
5381
5673
  detailsHtml,
5382
5674
  resources,
5383
- manifests
5675
+ manifests,
5676
+ keyValue: normalizedKeyValue
5384
5677
  };
5385
5678
  }
5386
5679
  function serializeProps(props, payload, locale) {
@@ -5449,7 +5742,7 @@ function normalizeNumber(value) {
5449
5742
  const num = Number(value);
5450
5743
  return Number.isFinite(num) ? num : null;
5451
5744
  }
5452
- function normalizeReactKey(value) {
5745
+ function normalizeReactKey2(value) {
5453
5746
  if (value == null) return "";
5454
5747
  const str = String(value);
5455
5748
  if (/^\.[0-9]+$/.test(str)) return "";
@@ -5474,7 +5767,7 @@ function renderDetailsHtml(children) {
5474
5767
  return "";
5475
5768
  }
5476
5769
  }
5477
- function normalizeKeyLabel(value) {
5770
+ function normalizeKeyLabel2(value) {
5478
5771
  if (value == null) return "";
5479
5772
  try {
5480
5773
  const label = String(value).trim();
@@ -5514,9 +5807,9 @@ function normalizeCustomPoint(child, index, manifestMap) {
5514
5807
  let thumbnailWidth = normalizeNumber(props.thumbnailWidth);
5515
5808
  let thumbnailHeight = normalizeNumber(props.thumbnailHeight);
5516
5809
  const rawNodeKey = child && child.key != null ? child.key : null;
5517
- const normalizedNodeKey = normalizeReactKey(rawNodeKey);
5810
+ const normalizedNodeKey = normalizeReactKey2(rawNodeKey);
5518
5811
  const keyValue = props.mapKey || props.group || props.key || normalizedNodeKey;
5519
- const keyLabelFromProps = normalizeKeyLabel(props.keyLabel || props.legend || props.groupLabel);
5812
+ const keyLabelFromProps = normalizeKeyLabel2(props.keyLabel || props.legend || props.groupLabel);
5520
5813
  const detailsHtml = renderDetailsHtml(props.children);
5521
5814
  const manifestValues = Array.isArray(props.referencedManifests) ? props.referencedManifests : props.manifest ? [props.manifest] : Array.isArray(props.manifests) ? props.manifests : [];
5522
5815
  const manifests = resolveReferencedManifests(manifestValues, manifestMap);
@@ -5955,6 +6248,55 @@ var INLINE_SCRIPT2 = `(() => {
5955
6248
  emitModalState(previous, 'close');
5956
6249
  }
5957
6250
 
6251
+ function closeModalFromBackground(modal) {
6252
+ if (!modal) return;
6253
+ const closeId = modal.getAttribute('data-canopy-gallery-close');
6254
+ const targetHash = closeId ? '#' + closeId : '';
6255
+ if (targetHash) {
6256
+ if (window.location.hash === targetHash) {
6257
+ try {
6258
+ window.location.hash = targetHash;
6259
+ } catch (_) {}
6260
+ } else {
6261
+ window.location.hash = targetHash;
6262
+ }
6263
+ } else {
6264
+ window.location.hash = '';
6265
+ }
6266
+ }
6267
+
6268
+ function shouldCloseFromBackground(event, modal) {
6269
+ if (!event || !modal) return false;
6270
+ const target = event.target;
6271
+ if (!target) return false;
6272
+ const panel = modal.querySelector('.canopy-gallery__modal-panel');
6273
+ if (panel && panel.contains(target)) return false;
6274
+ const actions = modal.querySelector('.canopy-gallery__modal-actions');
6275
+ if (actions && actions.contains(target)) return false;
6276
+ if (target === modal) return true;
6277
+ const scrim = modal.querySelector('.canopy-gallery__modal-scrim');
6278
+ if (scrim && scrim.contains(target)) return true;
6279
+ return false;
6280
+ }
6281
+
6282
+ function bindModalDismissal(modal) {
6283
+ if (!modal || modal.getAttribute('data-canopy-gallery-dismiss-bound') === '1') {
6284
+ return;
6285
+ }
6286
+ modal.setAttribute('data-canopy-gallery-dismiss-bound', '1');
6287
+ modal.addEventListener('click', function (event) {
6288
+ if (!shouldCloseFromBackground(event, modal)) return;
6289
+ event.preventDefault();
6290
+ closeModalFromBackground(modal);
6291
+ });
6292
+ }
6293
+
6294
+ function bindModalDismissals() {
6295
+ const modals = document.querySelectorAll('[data-canopy-gallery-modal]');
6296
+ if (!modals || !modals.length) return;
6297
+ Array.prototype.forEach.call(modals, bindModalDismissal);
6298
+ }
6299
+
5958
6300
  function modalFromHash() {
5959
6301
  const id = window.location.hash.replace(/^#/, '');
5960
6302
  if (!id) return null;
@@ -6238,10 +6580,12 @@ var INLINE_SCRIPT2 = `(() => {
6238
6580
  window.addEventListener('pageshow', function () {
6239
6581
  syncFromHash();
6240
6582
  bindGalleryNavs();
6583
+ bindModalDismissals();
6241
6584
  refreshGalleryNavs({reveal: true});
6242
6585
  });
6243
6586
  syncFromHash();
6244
6587
  bindGalleryNavs();
6588
+ bindModalDismissals();
6245
6589
  refreshGalleryNavs({reveal: true});
6246
6590
  })()`;
6247
6591
  var galleryInstanceCounter = 0;
@@ -6609,7 +6953,7 @@ function GalleryItem() {
6609
6953
  GalleryItem.displayName = "GalleryItem";
6610
6954
  function normalizePopupSize(value) {
6611
6955
  const normalized = String(value || "full").toLowerCase();
6612
- return normalized === "medium" ? "medium" : "full";
6956
+ return normalized === "window" ? "window" : "full";
6613
6957
  }
6614
6958
  function Gallery({
6615
6959
  children,
@@ -6633,7 +6977,7 @@ function Gallery({
6633
6977
  const orderedItems = orderMode === "random" ? shuffleItems(items) : items;
6634
6978
  const rootClassName = [
6635
6979
  "canopy-gallery",
6636
- popupMode === "medium" ? "canopy-gallery--popup-medium" : "canopy-gallery--popup-full",
6980
+ popupMode === "window" ? "canopy-gallery--popup-window" : "canopy-gallery--popup-full",
6637
6981
  className
6638
6982
  ].filter(Boolean).join(" ");
6639
6983
  const navGroupName = `${galleryId}-nav`;