@canopy-iiif/app 1.4.2 → 1.4.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canopy-iiif/app",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Mat Jordan <mat@northwestern.edu>",
package/ui/dist/index.mjs CHANGED
@@ -2071,7 +2071,7 @@ function MarkdownTable({ className = "", ...rest }) {
2071
2071
  // ui/src/docs/Diagram.jsx
2072
2072
  import React32 from "react";
2073
2073
  function CanopyDiagram() {
2074
- return /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React32.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 105 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Collection A"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React32.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React32.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Collection B"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React32.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React32.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React32.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Automated content"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React32.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React32.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React32.createElement("li", null, "Author narratives & tours"), /* @__PURE__ */ React32.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Search index"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React32.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React32.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React32.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Work pages"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React32.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React32.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React32.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React32.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React32.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React32.createElement("li", null, "Optional annotation dataset"))))));
2074
+ return /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React32.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 105 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Collection A"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React32.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React32.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Collection B"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React32.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React32.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React32.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Automated content"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React32.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React32.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React32.createElement("li", null, "Author narratives"), /* @__PURE__ */ React32.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Search index"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React32.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React32.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React32.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React32.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React32.createElement("span", { className: "canopy-diagram__section-summary" }, "The output is a lightweight bundle of HTML, CSS, JS, and JSON assets that can deploy anywhere."), /* @__PURE__ */ React32.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Work pages"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React32.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React32.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React32.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React32.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React32.createElement("article", null, /* @__PURE__ */ React32.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React32.createElement("ul", null, /* @__PURE__ */ React32.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React32.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React32.createElement("li", null, "Optional annotation dataset"))))));
2075
2075
  }
2076
2076
 
2077
2077
  // ui/src/content/timeline/Timeline.jsx
@@ -3029,30 +3029,38 @@ function renderPopup(marker) {
3029
3029
  if (!marker || typeof document === "undefined") return null;
3030
3030
  const container = document.createElement("div");
3031
3031
  let root = null;
3032
- try {
3033
- root = createRoot(container);
3034
- root.render(/* @__PURE__ */ React35.createElement(MapPopupContent, { marker }));
3035
- } catch (error) {
3036
- if (root) {
3037
- try {
3038
- root.unmount();
3039
- } catch (_) {
3032
+ let hadError = false;
3033
+ const render = () => {
3034
+ if (hadError) return;
3035
+ try {
3036
+ if (!root) root = createRoot(container);
3037
+ root.render(/* @__PURE__ */ React35.createElement(MapPopupContent, { marker }));
3038
+ } catch (error) {
3039
+ hadError = true;
3040
+ if (root) {
3041
+ try {
3042
+ root.unmount();
3043
+ } catch (_) {
3044
+ }
3045
+ root = null;
3040
3046
  }
3041
- root = null;
3047
+ const fallbackTitle = marker.title || marker.summary || marker.href || "Location";
3048
+ container.innerHTML = `<div class="canopy-map__popup"><div class="canopy-map__popup-body"><span class="canopy-map__popup-title">${escapeHtml(fallbackTitle)}</span></div></div>`;
3042
3049
  }
3043
- const fallbackTitle = marker.title || marker.summary || marker.href || "Location";
3044
- container.innerHTML = `<div class="canopy-map__popup"><div class="canopy-map__popup-body"><span class="canopy-map__popup-title">${escapeHtml(fallbackTitle)}</span></div></div>`;
3045
- }
3050
+ };
3051
+ const destroy = () => {
3052
+ if (!root) return;
3053
+ try {
3054
+ root.unmount();
3055
+ } catch (_) {
3056
+ }
3057
+ root = null;
3058
+ };
3059
+ render();
3046
3060
  return {
3047
3061
  element: container,
3048
- destroy() {
3049
- if (!root) return;
3050
- try {
3051
- root.unmount();
3052
- } catch (_) {
3053
- }
3054
- root = null;
3055
- }
3062
+ render,
3063
+ destroy
3056
3064
  };
3057
3065
  }
3058
3066
  function normalizeCustomMarkers(points) {
@@ -3339,6 +3347,7 @@ function Map2({
3339
3347
  } catch (_) {
3340
3348
  }
3341
3349
  const bounds = [];
3350
+ const popupCleanups = [];
3342
3351
  allMarkers.forEach((marker) => {
3343
3352
  if (!marker || !Number.isFinite(marker.lat) || !Number.isFinite(marker.lng)) return;
3344
3353
  const latlng = leafletLib.latLng(marker.lat, marker.lng);
@@ -3349,23 +3358,20 @@ function Map2({
3349
3358
  if (popup && popup.element) {
3350
3359
  try {
3351
3360
  leafletMarker.bindPopup(popup.element);
3352
- if (typeof popup.destroy === "function") {
3353
- let disposed = false;
3354
- const cleanup = () => {
3355
- if (disposed) return;
3356
- disposed = true;
3361
+ if (typeof popup.render === "function") {
3362
+ leafletMarker.on("popupopen", popup.render);
3363
+ }
3364
+ popupCleanups.push(() => {
3365
+ if (typeof popup.render === "function") {
3357
3366
  try {
3358
- leafletMarker.off("popupclose", cleanup);
3359
- leafletMarker.off("popupremove", cleanup);
3360
- leafletMarker.off("remove", cleanup);
3367
+ leafletMarker.off("popupopen", popup.render);
3361
3368
  } catch (_) {
3362
3369
  }
3370
+ }
3371
+ if (typeof popup.destroy === "function") {
3363
3372
  popup.destroy();
3364
- };
3365
- leafletMarker.on("popupclose", cleanup);
3366
- leafletMarker.on("popupremove", cleanup);
3367
- leafletMarker.on("remove", cleanup);
3368
- }
3373
+ }
3374
+ });
3369
3375
  } catch (_) {
3370
3376
  if (typeof popup.destroy === "function") {
3371
3377
  popup.destroy();
@@ -3415,6 +3421,14 @@ function Map2({
3415
3421
  } catch (_) {
3416
3422
  }
3417
3423
  }
3424
+ return () => {
3425
+ popupCleanups.forEach((cleanup) => {
3426
+ try {
3427
+ cleanup();
3428
+ } catch (_) {
3429
+ }
3430
+ });
3431
+ };
3418
3432
  }, [allMarkers, defaultCenter, defaultZoom, leafletLib]);
3419
3433
  const isLoadingMarkers = iiifTargets.loading || navState.loading;
3420
3434
  const hasMarkers = allMarkers.length > 0;