@canopy-iiif/app 1.4.1 → 1.4.3
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 +1 -1
- package/ui/dist/index.mjs +208 -93
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +207 -142
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/components/_map.scss +54 -2
- package/ui/styles/components/search/_results.scss +2 -2
- package/ui/styles/index.css +47 -4
package/package.json
CHANGED
package/ui/dist/index.mjs
CHANGED
|
@@ -2075,7 +2075,7 @@ function CanopyDiagram() {
|
|
|
2075
2075
|
}
|
|
2076
2076
|
|
|
2077
2077
|
// ui/src/content/timeline/Timeline.jsx
|
|
2078
|
-
import
|
|
2078
|
+
import React34 from "react";
|
|
2079
2079
|
|
|
2080
2080
|
// ui/src/content/timeline/date-utils.js
|
|
2081
2081
|
var FALLBACK_LOCALE = (() => {
|
|
@@ -2227,6 +2227,56 @@ function clampProgress(value) {
|
|
|
2227
2227
|
return value;
|
|
2228
2228
|
}
|
|
2229
2229
|
|
|
2230
|
+
// ui/src/layout/ReferencedManifestCard.jsx
|
|
2231
|
+
import React33 from "react";
|
|
2232
|
+
function normalizeMetadata(metadata, summary) {
|
|
2233
|
+
if (Array.isArray(metadata) && metadata.length) {
|
|
2234
|
+
return metadata.filter(Boolean);
|
|
2235
|
+
}
|
|
2236
|
+
if (summary) return [summary];
|
|
2237
|
+
return [];
|
|
2238
|
+
}
|
|
2239
|
+
function ReferencedManifestCard({
|
|
2240
|
+
manifest = null,
|
|
2241
|
+
href,
|
|
2242
|
+
title,
|
|
2243
|
+
summary,
|
|
2244
|
+
metadata,
|
|
2245
|
+
thumbnail,
|
|
2246
|
+
type,
|
|
2247
|
+
className = "",
|
|
2248
|
+
...rest
|
|
2249
|
+
}) {
|
|
2250
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2251
|
+
const record = manifest || {};
|
|
2252
|
+
const resolvedHref = (_a = href != null ? href : record.href) != null ? _a : "";
|
|
2253
|
+
const resolvedTitle = (_c = (_b = title != null ? title : record.title) != null ? _b : record.href) != null ? _c : "";
|
|
2254
|
+
const resolvedSummary = (_d = summary != null ? summary : record.summary) != null ? _d : "";
|
|
2255
|
+
const resolvedMetadata = normalizeMetadata(
|
|
2256
|
+
metadata != null ? metadata : record.metadata,
|
|
2257
|
+
resolvedSummary
|
|
2258
|
+
);
|
|
2259
|
+
const resolvedThumbnail = (_e = thumbnail != null ? thumbnail : record.thumbnail) != null ? _e : null;
|
|
2260
|
+
const resolvedType = (_f = type != null ? type : record.type) != null ? _f : "work";
|
|
2261
|
+
const classes = [
|
|
2262
|
+
"canopy-referenced-manifest-card",
|
|
2263
|
+
className
|
|
2264
|
+
].filter(Boolean).join(" ");
|
|
2265
|
+
return /* @__PURE__ */ React33.createElement(
|
|
2266
|
+
TeaserCard,
|
|
2267
|
+
{
|
|
2268
|
+
href: resolvedHref || void 0,
|
|
2269
|
+
title: resolvedTitle || resolvedHref || "",
|
|
2270
|
+
summary: resolvedSummary,
|
|
2271
|
+
metadata: resolvedMetadata,
|
|
2272
|
+
thumbnail: resolvedThumbnail,
|
|
2273
|
+
type: resolvedType,
|
|
2274
|
+
className: classes,
|
|
2275
|
+
...rest
|
|
2276
|
+
}
|
|
2277
|
+
);
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2230
2280
|
// ui/src/content/timeline/Timeline.jsx
|
|
2231
2281
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2232
2282
|
var DEFAULT_TRACK_HEIGHT = 640;
|
|
@@ -2394,24 +2444,14 @@ function TimelineConnector({ side, isActive, highlight }) {
|
|
|
2394
2444
|
"canopy-timeline__connector-dot",
|
|
2395
2445
|
highlight || isActive ? "is-active" : ""
|
|
2396
2446
|
].filter(Boolean).join(" ");
|
|
2397
|
-
return /* @__PURE__ */
|
|
2447
|
+
return /* @__PURE__ */ React34.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React34.createElement(React34.Fragment, null, /* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React34.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React34.createElement(React34.Fragment, null, /* @__PURE__ */ React34.createElement("span", { className: dotClasses }), /* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__connector-line" })));
|
|
2398
2448
|
}
|
|
2399
2449
|
function renderResourceSection(point) {
|
|
2400
2450
|
if (!point) return null;
|
|
2401
2451
|
const manifestCards = Array.isArray(point.manifests) ? point.manifests.filter(Boolean) : [];
|
|
2402
2452
|
const legacyResources = Array.isArray(point.resources) ? point.resources.filter(Boolean) : [];
|
|
2403
2453
|
if (!manifestCards.length && !legacyResources.length) return null;
|
|
2404
|
-
return /* @__PURE__ */
|
|
2405
|
-
TeaserCard,
|
|
2406
|
-
{
|
|
2407
|
-
href: manifest.href,
|
|
2408
|
-
title: manifest.title || manifest.href,
|
|
2409
|
-
summary: manifest.summary,
|
|
2410
|
-
metadata: Array.isArray(manifest.metadata) && manifest.metadata.length ? manifest.metadata : manifest.summary ? [manifest.summary] : [],
|
|
2411
|
-
thumbnail: manifest.thumbnail,
|
|
2412
|
-
type: manifest.type || "work"
|
|
2413
|
-
}
|
|
2414
|
-
))), legacyResources.map((resource, idx) => /* @__PURE__ */ React33.createElement("div", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React33.createElement(
|
|
2454
|
+
return /* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__resources" }, /* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__resources-list" }, manifestCards.map((manifest) => /* @__PURE__ */ React34.createElement("div", { key: manifest.id || manifest.href }, /* @__PURE__ */ React34.createElement(ReferencedManifestCard, { manifest }))), legacyResources.map((resource, idx) => /* @__PURE__ */ React34.createElement("div", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React34.createElement(
|
|
2415
2455
|
TeaserCard,
|
|
2416
2456
|
{
|
|
2417
2457
|
href: resource.href,
|
|
@@ -2436,26 +2476,26 @@ function Timeline({
|
|
|
2436
2476
|
...rest
|
|
2437
2477
|
}) {
|
|
2438
2478
|
const payloadPoints = payload && Array.isArray(payload.points) ? payload.points : null;
|
|
2439
|
-
const rawPoints =
|
|
2479
|
+
const rawPoints = React34.useMemo(() => {
|
|
2440
2480
|
if (Array.isArray(pointsProp) && pointsProp.length) return pointsProp;
|
|
2441
2481
|
if (payloadPoints && payloadPoints.length) return payloadPoints;
|
|
2442
2482
|
return [];
|
|
2443
2483
|
}, [pointsProp, payloadPoints]);
|
|
2444
|
-
const sanitizedPoints =
|
|
2484
|
+
const sanitizedPoints = React34.useMemo(
|
|
2445
2485
|
() => sanitizePoints(rawPoints),
|
|
2446
2486
|
[rawPoints]
|
|
2447
2487
|
);
|
|
2448
2488
|
const localeValue = payload && payload.locale ? payload.locale : localeProp;
|
|
2449
|
-
const baseLocale =
|
|
2489
|
+
const baseLocale = React34.useMemo(
|
|
2450
2490
|
() => createLocale(localeValue),
|
|
2451
2491
|
[localeValue]
|
|
2452
2492
|
);
|
|
2453
2493
|
const rangeInput = payload && payload.range ? payload.range : rangeProp || {};
|
|
2454
|
-
const rangeOverrides =
|
|
2494
|
+
const rangeOverrides = React34.useMemo(
|
|
2455
2495
|
() => deriveRangeOverrides(sanitizedPoints, rangeInput),
|
|
2456
2496
|
[sanitizedPoints, rangeInput]
|
|
2457
2497
|
);
|
|
2458
|
-
const effectiveRange =
|
|
2498
|
+
const effectiveRange = React34.useMemo(
|
|
2459
2499
|
() => normalizeRange({
|
|
2460
2500
|
...rangeOverrides,
|
|
2461
2501
|
locale: baseLocale
|
|
@@ -2464,7 +2504,7 @@ function Timeline({
|
|
|
2464
2504
|
);
|
|
2465
2505
|
const spanStart = effectiveRange.startDate.getTime();
|
|
2466
2506
|
const span = effectiveRange.span;
|
|
2467
|
-
const pointsWithPosition =
|
|
2507
|
+
const pointsWithPosition = React34.useMemo(() => {
|
|
2468
2508
|
if (!sanitizedPoints.length) return [];
|
|
2469
2509
|
return sanitizedPoints.map((point, index) => {
|
|
2470
2510
|
const timestamp = point.meta.timestamp;
|
|
@@ -2478,29 +2518,29 @@ function Timeline({
|
|
|
2478
2518
|
};
|
|
2479
2519
|
});
|
|
2480
2520
|
}, [sanitizedPoints, spanStart, span]);
|
|
2481
|
-
const [activeId, setActiveId] =
|
|
2521
|
+
const [activeId, setActiveId] = React34.useState(
|
|
2482
2522
|
() => getActivePointId(pointsWithPosition)
|
|
2483
2523
|
);
|
|
2484
|
-
|
|
2524
|
+
React34.useEffect(() => {
|
|
2485
2525
|
setActiveId(getActivePointId(pointsWithPosition));
|
|
2486
2526
|
}, [pointsWithPosition]);
|
|
2487
2527
|
const thresholdValue = typeof thresholdProp === "number" ? thresholdProp : payload && payload.threshold != null ? payload.threshold : null;
|
|
2488
2528
|
const stepsValue = typeof steps === "number" ? Number(steps) : payload && typeof payload.steps === "number" ? Number(payload.steps) : null;
|
|
2489
|
-
const thresholdMs =
|
|
2529
|
+
const thresholdMs = React34.useMemo(
|
|
2490
2530
|
() => getThresholdMs(thresholdValue, effectiveRange.granularity),
|
|
2491
2531
|
[thresholdValue, effectiveRange.granularity]
|
|
2492
2532
|
);
|
|
2493
|
-
const groupedEntries =
|
|
2533
|
+
const groupedEntries = React34.useMemo(
|
|
2494
2534
|
() => buildGroupedEntries(pointsWithPosition, thresholdMs, {
|
|
2495
2535
|
granularity: effectiveRange.granularity,
|
|
2496
2536
|
locale: baseLocale
|
|
2497
2537
|
}),
|
|
2498
2538
|
[pointsWithPosition, thresholdMs, effectiveRange.granularity, baseLocale]
|
|
2499
2539
|
);
|
|
2500
|
-
const [expandedGroupIds, setExpandedGroupIds] =
|
|
2540
|
+
const [expandedGroupIds, setExpandedGroupIds] = React34.useState(
|
|
2501
2541
|
() => /* @__PURE__ */ new Set()
|
|
2502
2542
|
);
|
|
2503
|
-
|
|
2543
|
+
React34.useEffect(() => {
|
|
2504
2544
|
setExpandedGroupIds((prev) => {
|
|
2505
2545
|
if (!prev || prev.size === 0) return prev;
|
|
2506
2546
|
const validIds = new Set(
|
|
@@ -2515,7 +2555,7 @@ function Timeline({
|
|
|
2515
2555
|
return changed ? next : prev;
|
|
2516
2556
|
});
|
|
2517
2557
|
}, [groupedEntries]);
|
|
2518
|
-
const toggleGroup =
|
|
2558
|
+
const toggleGroup = React34.useCallback((groupId) => {
|
|
2519
2559
|
setExpandedGroupIds((prev) => {
|
|
2520
2560
|
const next = new Set(prev || []);
|
|
2521
2561
|
if (next.has(groupId)) next.delete(groupId);
|
|
@@ -2538,7 +2578,7 @@ function Timeline({
|
|
|
2538
2578
|
point.id === activeId ? "is-active" : "",
|
|
2539
2579
|
point.highlight ? "is-highlighted" : ""
|
|
2540
2580
|
].filter(Boolean).join(" ");
|
|
2541
|
-
const connector = /* @__PURE__ */
|
|
2581
|
+
const connector = /* @__PURE__ */ React34.createElement(
|
|
2542
2582
|
TimelineConnector,
|
|
2543
2583
|
{
|
|
2544
2584
|
side: point.side,
|
|
@@ -2546,9 +2586,9 @@ function Timeline({
|
|
|
2546
2586
|
highlight: point.highlight
|
|
2547
2587
|
}
|
|
2548
2588
|
);
|
|
2549
|
-
const body = /* @__PURE__ */
|
|
2589
|
+
const body = /* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
|
|
2550
2590
|
const resourceSection = renderResourceSection(point);
|
|
2551
|
-
return /* @__PURE__ */
|
|
2591
|
+
return /* @__PURE__ */ React34.createElement(
|
|
2552
2592
|
"div",
|
|
2553
2593
|
{
|
|
2554
2594
|
key: point.id,
|
|
@@ -2556,7 +2596,7 @@ function Timeline({
|
|
|
2556
2596
|
style: wrapperStyle,
|
|
2557
2597
|
role: "listitem"
|
|
2558
2598
|
},
|
|
2559
|
-
point.side === "left" ? /* @__PURE__ */
|
|
2599
|
+
point.side === "left" ? /* @__PURE__ */ React34.createElement(React34.Fragment, null, /* @__PURE__ */ React34.createElement("div", { className: cardClasses }, body, resourceSection), connector) : /* @__PURE__ */ React34.createElement(React34.Fragment, null, connector, /* @__PURE__ */ React34.createElement("div", { className: cardClasses }, body, resourceSection))
|
|
2560
2600
|
);
|
|
2561
2601
|
}
|
|
2562
2602
|
function renderGroupEntry(entry) {
|
|
@@ -2567,7 +2607,7 @@ function Timeline({
|
|
|
2567
2607
|
const wrapperStyle = { top: `${entry.progress * 100}%` };
|
|
2568
2608
|
const isExpanded = expandedGroupIds.has(entry.id);
|
|
2569
2609
|
const hasActivePoint = entry.points.some((point) => point.id === activeId);
|
|
2570
|
-
const connector = /* @__PURE__ */
|
|
2610
|
+
const connector = /* @__PURE__ */ React34.createElement(
|
|
2571
2611
|
TimelineConnector,
|
|
2572
2612
|
{
|
|
2573
2613
|
side: entry.side,
|
|
@@ -2581,7 +2621,7 @@ function Timeline({
|
|
|
2581
2621
|
hasActivePoint ? "is-active" : ""
|
|
2582
2622
|
].filter(Boolean).join(" ");
|
|
2583
2623
|
const countLabel = `${entry.count} event${entry.count > 1 ? "s" : ""}`;
|
|
2584
|
-
const header = /* @__PURE__ */
|
|
2624
|
+
const header = /* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__group-header" }, /* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__group-summary" }, /* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__point-date" }, entry.label), /* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__group-count" }, countLabel)), /* @__PURE__ */ React34.createElement(
|
|
2585
2625
|
"button",
|
|
2586
2626
|
{
|
|
2587
2627
|
type: "button",
|
|
@@ -2591,7 +2631,7 @@ function Timeline({
|
|
|
2591
2631
|
},
|
|
2592
2632
|
isExpanded ? "Hide details" : "Show details"
|
|
2593
2633
|
));
|
|
2594
|
-
const groupPoints = isExpanded ? /* @__PURE__ */
|
|
2634
|
+
const groupPoints = isExpanded ? /* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__group-points" }, entry.points.map((point) => /* @__PURE__ */ React34.createElement(
|
|
2595
2635
|
"button",
|
|
2596
2636
|
{
|
|
2597
2637
|
key: point.id,
|
|
@@ -2602,11 +2642,11 @@ function Timeline({
|
|
|
2602
2642
|
].filter(Boolean).join(" "),
|
|
2603
2643
|
onClick: () => setActiveId(point.id)
|
|
2604
2644
|
},
|
|
2605
|
-
/* @__PURE__ */
|
|
2606
|
-
/* @__PURE__ */
|
|
2645
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label),
|
|
2646
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__group-point-title" }, point.title)
|
|
2607
2647
|
))) : null;
|
|
2608
|
-
const groupCard = /* @__PURE__ */
|
|
2609
|
-
return /* @__PURE__ */
|
|
2648
|
+
const groupCard = /* @__PURE__ */ React34.createElement("div", { className: groupClasses }, header, groupPoints);
|
|
2649
|
+
return /* @__PURE__ */ React34.createElement(
|
|
2610
2650
|
"div",
|
|
2611
2651
|
{
|
|
2612
2652
|
key: entry.id,
|
|
@@ -2614,17 +2654,17 @@ function Timeline({
|
|
|
2614
2654
|
style: wrapperStyle,
|
|
2615
2655
|
role: "listitem"
|
|
2616
2656
|
},
|
|
2617
|
-
entry.side === "left" ? /* @__PURE__ */
|
|
2657
|
+
entry.side === "left" ? /* @__PURE__ */ React34.createElement(React34.Fragment, null, groupCard, connector) : /* @__PURE__ */ React34.createElement(React34.Fragment, null, connector, groupCard)
|
|
2618
2658
|
);
|
|
2619
2659
|
}
|
|
2620
|
-
return /* @__PURE__ */
|
|
2660
|
+
return /* @__PURE__ */ React34.createElement("section", { className: containerClasses, ...rest }, title ? /* @__PURE__ */ React34.createElement("h2", { className: "canopy-timeline__title" }, title) : null, description ? /* @__PURE__ */ React34.createElement("p", { className: "canopy-timeline__description" }, description) : null, rangeLabel ? /* @__PURE__ */ React34.createElement("p", { className: "canopy-timeline__range", "aria-live": "polite" }, rangeLabel) : null, /* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__body" }, /* @__PURE__ */ React34.createElement(
|
|
2621
2661
|
"div",
|
|
2622
2662
|
{
|
|
2623
2663
|
className: "canopy-timeline__list",
|
|
2624
2664
|
role: "list",
|
|
2625
2665
|
style: { minHeight: trackHeight }
|
|
2626
2666
|
},
|
|
2627
|
-
/* @__PURE__ */
|
|
2667
|
+
/* @__PURE__ */ React34.createElement("div", { className: "canopy-timeline__spine", "aria-hidden": "true" }),
|
|
2628
2668
|
renderSteps(stepsValue, effectiveRange),
|
|
2629
2669
|
groupedEntries.map((entry) => {
|
|
2630
2670
|
if (entry.type === "group") return renderGroupEntry(entry);
|
|
@@ -2639,7 +2679,7 @@ function renderSteps(stepSize, range) {
|
|
|
2639
2679
|
const markers = [];
|
|
2640
2680
|
if (startYear < endYear) {
|
|
2641
2681
|
markers.push(
|
|
2642
|
-
/* @__PURE__ */
|
|
2682
|
+
/* @__PURE__ */ React34.createElement(
|
|
2643
2683
|
"span",
|
|
2644
2684
|
{
|
|
2645
2685
|
key: "timeline-step-start",
|
|
@@ -2647,12 +2687,12 @@ function renderSteps(stepSize, range) {
|
|
|
2647
2687
|
style: { top: "0%" },
|
|
2648
2688
|
"aria-hidden": "true"
|
|
2649
2689
|
},
|
|
2650
|
-
/* @__PURE__ */
|
|
2651
|
-
/* @__PURE__ */
|
|
2690
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__step-line" }),
|
|
2691
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__step-label" }, startYear)
|
|
2652
2692
|
)
|
|
2653
2693
|
);
|
|
2654
2694
|
markers.push(
|
|
2655
|
-
/* @__PURE__ */
|
|
2695
|
+
/* @__PURE__ */ React34.createElement(
|
|
2656
2696
|
"span",
|
|
2657
2697
|
{
|
|
2658
2698
|
key: "timeline-step-end",
|
|
@@ -2660,8 +2700,8 @@ function renderSteps(stepSize, range) {
|
|
|
2660
2700
|
style: { top: "100%" },
|
|
2661
2701
|
"aria-hidden": "true"
|
|
2662
2702
|
},
|
|
2663
|
-
/* @__PURE__ */
|
|
2664
|
-
/* @__PURE__ */
|
|
2703
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__step-line" }),
|
|
2704
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__step-label" }, endYear)
|
|
2665
2705
|
)
|
|
2666
2706
|
);
|
|
2667
2707
|
}
|
|
@@ -2671,7 +2711,7 @@ function renderSteps(stepSize, range) {
|
|
|
2671
2711
|
const progress = (timestamp - range.startDate.getTime()) / range.span;
|
|
2672
2712
|
if (progress <= 0 || progress >= 1) continue;
|
|
2673
2713
|
markers.push(
|
|
2674
|
-
/* @__PURE__ */
|
|
2714
|
+
/* @__PURE__ */ React34.createElement(
|
|
2675
2715
|
"span",
|
|
2676
2716
|
{
|
|
2677
2717
|
key: `timeline-step-${year}`,
|
|
@@ -2679,8 +2719,8 @@ function renderSteps(stepSize, range) {
|
|
|
2679
2719
|
style: { top: `calc(${progress * 100}% - 0.5px)` },
|
|
2680
2720
|
"aria-hidden": "true"
|
|
2681
2721
|
},
|
|
2682
|
-
/* @__PURE__ */
|
|
2683
|
-
/* @__PURE__ */
|
|
2722
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__step-line" }),
|
|
2723
|
+
/* @__PURE__ */ React34.createElement("span", { className: "canopy-timeline__step-label" }, year)
|
|
2684
2724
|
)
|
|
2685
2725
|
);
|
|
2686
2726
|
}
|
|
@@ -2694,7 +2734,8 @@ function TimelinePoint() {
|
|
|
2694
2734
|
TimelinePoint.displayName = "TimelinePoint";
|
|
2695
2735
|
|
|
2696
2736
|
// ui/src/content/map/Map.jsx
|
|
2697
|
-
import
|
|
2737
|
+
import React35 from "react";
|
|
2738
|
+
import { createRoot } from "react-dom/client";
|
|
2698
2739
|
var DEFAULT_TILE_LAYERS = [
|
|
2699
2740
|
{
|
|
2700
2741
|
name: "OpenStreetMap",
|
|
@@ -2947,35 +2988,80 @@ function escapeHtml(value) {
|
|
|
2947
2988
|
if (value == null) return "";
|
|
2948
2989
|
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
2949
2990
|
}
|
|
2950
|
-
function
|
|
2951
|
-
if (!marker) return
|
|
2991
|
+
function MapPopupContent({ marker }) {
|
|
2992
|
+
if (!marker) return null;
|
|
2952
2993
|
const title = marker.title || marker.manifestTitle || "";
|
|
2953
2994
|
const summary = marker.summary || marker.manifestSummary || "";
|
|
2954
2995
|
const href = marker.href ? withBasePath(marker.href) : "";
|
|
2955
2996
|
const thumbnail = marker.thumbnail || "";
|
|
2956
2997
|
const thumbWidth = marker.thumbnailWidth;
|
|
2957
2998
|
const thumbHeight = marker.thumbnailHeight;
|
|
2958
|
-
const
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2999
|
+
const manifestLinks = Array.isArray(marker.manifests) ? marker.manifests.filter((entry) => entry && (entry.href || entry.title)) : [];
|
|
3000
|
+
const normalizedManifests = manifestLinks.map((manifest) => ({
|
|
3001
|
+
...manifest,
|
|
3002
|
+
href: manifest.href ? withBasePath(manifest.href) : manifest.href || ""
|
|
3003
|
+
}));
|
|
3004
|
+
return /* @__PURE__ */ React35.createElement("div", { className: "canopy-map__popup" }, thumbnail ? /* @__PURE__ */ React35.createElement("div", { className: "canopy-map__popup-media" }, /* @__PURE__ */ React35.createElement(
|
|
3005
|
+
"img",
|
|
3006
|
+
{
|
|
3007
|
+
src: thumbnail,
|
|
3008
|
+
alt: "",
|
|
3009
|
+
loading: "lazy",
|
|
3010
|
+
width: typeof thumbWidth === "number" && thumbWidth > 0 ? thumbWidth : void 0,
|
|
3011
|
+
height: typeof thumbHeight === "number" && thumbHeight > 0 ? thumbHeight : void 0
|
|
3012
|
+
}
|
|
3013
|
+
)) : null, /* @__PURE__ */ React35.createElement("div", { className: "canopy-map__popup-body" }, title ? href ? /* @__PURE__ */ React35.createElement("a", { href, className: "canopy-map__popup-title" }, title) : /* @__PURE__ */ React35.createElement("span", { className: "canopy-map__popup-title" }, title) : null, summary ? /* @__PURE__ */ React35.createElement("p", { className: "canopy-map__popup-summary" }, summary) : null, marker.detailsHtml ? /* @__PURE__ */ React35.createElement(
|
|
3014
|
+
"div",
|
|
3015
|
+
{
|
|
3016
|
+
className: "canopy-map__popup-details",
|
|
3017
|
+
dangerouslySetInnerHTML: { __html: marker.detailsHtml }
|
|
3018
|
+
}
|
|
3019
|
+
) : null, !summary && !marker.detailsHtml && href && !title ? /* @__PURE__ */ React35.createElement("a", { href, className: "canopy-map__popup-link" }, "View item") : null, normalizedManifests.length ? /* @__PURE__ */ React35.createElement("div", { className: "canopy-map__popup-manifests" }, /* @__PURE__ */ React35.createElement("div", { className: "canopy-map__popup-manifests-list" }, normalizedManifests.map((manifest, index) => /* @__PURE__ */ React35.createElement(
|
|
3020
|
+
"div",
|
|
3021
|
+
{
|
|
3022
|
+
key: manifest.id || manifest.href || `manifest-${index}`,
|
|
3023
|
+
className: "canopy-map__popup-manifests-item"
|
|
3024
|
+
},
|
|
3025
|
+
/* @__PURE__ */ React35.createElement(ReferencedManifestCard, { manifest })
|
|
3026
|
+
)))) : null));
|
|
3027
|
+
}
|
|
3028
|
+
function renderPopup(marker) {
|
|
3029
|
+
if (!marker || typeof document === "undefined") return null;
|
|
3030
|
+
const container = document.createElement("div");
|
|
3031
|
+
let root = null;
|
|
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;
|
|
3046
|
+
}
|
|
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>`;
|
|
3049
|
+
}
|
|
3050
|
+
};
|
|
3051
|
+
const destroy = () => {
|
|
3052
|
+
if (!root) return;
|
|
3053
|
+
try {
|
|
3054
|
+
root.unmount();
|
|
3055
|
+
} catch (_) {
|
|
3056
|
+
}
|
|
3057
|
+
root = null;
|
|
3058
|
+
};
|
|
3059
|
+
render();
|
|
3060
|
+
return {
|
|
3061
|
+
element: container,
|
|
3062
|
+
render,
|
|
3063
|
+
destroy
|
|
3064
|
+
};
|
|
2979
3065
|
}
|
|
2980
3066
|
function normalizeCustomMarkers(points) {
|
|
2981
3067
|
if (!Array.isArray(points)) return [];
|
|
@@ -2995,6 +3081,7 @@ function normalizeCustomMarkers(points) {
|
|
|
2995
3081
|
thumbnail: point.thumbnail || "",
|
|
2996
3082
|
thumbnailWidth: point.thumbnailWidth,
|
|
2997
3083
|
thumbnailHeight: point.thumbnailHeight,
|
|
3084
|
+
manifests: Array.isArray(point.manifests) ? point.manifests : [],
|
|
2998
3085
|
type: "custom"
|
|
2999
3086
|
};
|
|
3000
3087
|
}).filter(Boolean);
|
|
@@ -3072,26 +3159,26 @@ function Map2({
|
|
|
3072
3159
|
defaultCenter = null,
|
|
3073
3160
|
defaultZoom = null
|
|
3074
3161
|
} = {}) {
|
|
3075
|
-
const containerRef =
|
|
3076
|
-
const mapRef =
|
|
3077
|
-
const layerRef =
|
|
3078
|
-
const [leafletLib, setLeafletLib] =
|
|
3079
|
-
const [leafletError, setLeafletError] =
|
|
3162
|
+
const containerRef = React35.useRef(null);
|
|
3163
|
+
const mapRef = React35.useRef(null);
|
|
3164
|
+
const layerRef = React35.useRef(null);
|
|
3165
|
+
const [leafletLib, setLeafletLib] = React35.useState(() => resolveGlobalLeaflet());
|
|
3166
|
+
const [leafletError, setLeafletError] = React35.useState(null);
|
|
3080
3167
|
const datasetInfo = navDataset && typeof navDataset === "object" ? navDataset : null;
|
|
3081
3168
|
const datasetHref = datasetInfo && datasetInfo.href || "/api/navplace.json";
|
|
3082
3169
|
const datasetVersion = datasetInfo && datasetInfo.version;
|
|
3083
3170
|
const datasetHasFeatures = !!(datasetInfo && datasetInfo.hasFeatures);
|
|
3084
|
-
const [navState, setNavState] =
|
|
3171
|
+
const [navState, setNavState] = React35.useState(() => ({
|
|
3085
3172
|
loading: false,
|
|
3086
3173
|
error: null,
|
|
3087
3174
|
markers: []
|
|
3088
3175
|
}));
|
|
3089
|
-
const [iiifTargets, setIiifTargets] =
|
|
3176
|
+
const [iiifTargets, setIiifTargets] = React35.useState(() => ({
|
|
3090
3177
|
loading: false,
|
|
3091
3178
|
error: null,
|
|
3092
3179
|
keys: []
|
|
3093
3180
|
}));
|
|
3094
|
-
|
|
3181
|
+
React35.useEffect(() => {
|
|
3095
3182
|
if (!iiifContent) {
|
|
3096
3183
|
setIiifTargets({ loading: false, error: null, keys: [] });
|
|
3097
3184
|
return;
|
|
@@ -3139,7 +3226,7 @@ function Map2({
|
|
|
3139
3226
|
const navTargets = iiifTargets.keys || [];
|
|
3140
3227
|
const navTargetsKey = navTargets.join("|");
|
|
3141
3228
|
const shouldFetchNav = datasetHasFeatures && navTargets.length > 0;
|
|
3142
|
-
|
|
3229
|
+
React35.useEffect(() => {
|
|
3143
3230
|
if (!shouldFetchNav) {
|
|
3144
3231
|
setNavState({ loading: false, error: null, markers: [] });
|
|
3145
3232
|
return void 0;
|
|
@@ -3171,7 +3258,7 @@ function Map2({
|
|
|
3171
3258
|
cancelled = true;
|
|
3172
3259
|
};
|
|
3173
3260
|
}, [datasetHref, datasetVersion, navTargetsKey, shouldFetchNav]);
|
|
3174
|
-
|
|
3261
|
+
React35.useEffect(() => {
|
|
3175
3262
|
if (leafletLib) return;
|
|
3176
3263
|
let cancelled = false;
|
|
3177
3264
|
waitForLeaflet().then((lib) => {
|
|
@@ -3183,7 +3270,7 @@ function Map2({
|
|
|
3183
3270
|
cancelled = true;
|
|
3184
3271
|
};
|
|
3185
3272
|
}, [leafletLib]);
|
|
3186
|
-
const navMatchMap =
|
|
3273
|
+
const navMatchMap = React35.useMemo(() => {
|
|
3187
3274
|
const matchMap = createMarkerMap();
|
|
3188
3275
|
(navState.markers || []).forEach((marker) => {
|
|
3189
3276
|
if (!marker || !Array.isArray(marker.matchKeys)) return;
|
|
@@ -3194,7 +3281,7 @@ function Map2({
|
|
|
3194
3281
|
});
|
|
3195
3282
|
return matchMap;
|
|
3196
3283
|
}, [navState.markers]);
|
|
3197
|
-
const normalizedCustom =
|
|
3284
|
+
const normalizedCustom = React35.useMemo(() => {
|
|
3198
3285
|
return normalizeCustomMarkers(customPoints).map((point) => {
|
|
3199
3286
|
if (!point || point.thumbnail || !point.href) return point;
|
|
3200
3287
|
const match = navMatchMap.get(normalizeKey(point.href));
|
|
@@ -3209,11 +3296,11 @@ function Map2({
|
|
|
3209
3296
|
};
|
|
3210
3297
|
});
|
|
3211
3298
|
}, [customPoints, navMatchMap]);
|
|
3212
|
-
const allMarkers =
|
|
3299
|
+
const allMarkers = React35.useMemo(() => {
|
|
3213
3300
|
return [...navState.markers || [], ...normalizedCustom];
|
|
3214
3301
|
}, [navState.markers, normalizedCustom]);
|
|
3215
|
-
const clusterOptions =
|
|
3216
|
-
|
|
3302
|
+
const clusterOptions = React35.useMemo(() => buildClusterOptions(leafletLib), [leafletLib]);
|
|
3303
|
+
React35.useEffect(() => {
|
|
3217
3304
|
if (!containerRef.current || mapRef.current || !leafletLib) return void 0;
|
|
3218
3305
|
const map = leafletLib.map(containerRef.current, {
|
|
3219
3306
|
zoomControl: true,
|
|
@@ -3251,7 +3338,7 @@ function Map2({
|
|
|
3251
3338
|
layerRef.current = null;
|
|
3252
3339
|
};
|
|
3253
3340
|
}, [tileLayers, scrollWheelZoom, cluster, clusterOptions, leafletLib]);
|
|
3254
|
-
|
|
3341
|
+
React35.useEffect(() => {
|
|
3255
3342
|
const map = mapRef.current;
|
|
3256
3343
|
const layer = layerRef.current;
|
|
3257
3344
|
if (!map || !layer || !leafletLib) return;
|
|
@@ -3260,6 +3347,7 @@ function Map2({
|
|
|
3260
3347
|
} catch (_) {
|
|
3261
3348
|
}
|
|
3262
3349
|
const bounds = [];
|
|
3350
|
+
const popupCleanups = [];
|
|
3263
3351
|
allMarkers.forEach((marker) => {
|
|
3264
3352
|
if (!marker || !Number.isFinite(marker.lat) || !Number.isFinite(marker.lng)) return;
|
|
3265
3353
|
const latlng = leafletLib.latLng(marker.lat, marker.lng);
|
|
@@ -3267,11 +3355,30 @@ function Map2({
|
|
|
3267
3355
|
const icon = buildMarkerIcon(marker, leafletLib);
|
|
3268
3356
|
const leafletMarker = leafletLib.marker(latlng, icon ? { icon } : void 0);
|
|
3269
3357
|
const popup = renderPopup(marker);
|
|
3270
|
-
if (popup) {
|
|
3358
|
+
if (popup && popup.element) {
|
|
3271
3359
|
try {
|
|
3272
|
-
leafletMarker.bindPopup(popup);
|
|
3360
|
+
leafletMarker.bindPopup(popup.element);
|
|
3361
|
+
if (typeof popup.render === "function") {
|
|
3362
|
+
leafletMarker.on("popupopen", popup.render);
|
|
3363
|
+
}
|
|
3364
|
+
popupCleanups.push(() => {
|
|
3365
|
+
if (typeof popup.render === "function") {
|
|
3366
|
+
try {
|
|
3367
|
+
leafletMarker.off("popupopen", popup.render);
|
|
3368
|
+
} catch (_) {
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
if (typeof popup.destroy === "function") {
|
|
3372
|
+
popup.destroy();
|
|
3373
|
+
}
|
|
3374
|
+
});
|
|
3273
3375
|
} catch (_) {
|
|
3376
|
+
if (typeof popup.destroy === "function") {
|
|
3377
|
+
popup.destroy();
|
|
3378
|
+
}
|
|
3274
3379
|
}
|
|
3380
|
+
} else if (popup && typeof popup.destroy === "function") {
|
|
3381
|
+
popup.destroy();
|
|
3275
3382
|
}
|
|
3276
3383
|
try {
|
|
3277
3384
|
layer.addLayer(leafletMarker);
|
|
@@ -3314,6 +3421,14 @@ function Map2({
|
|
|
3314
3421
|
} catch (_) {
|
|
3315
3422
|
}
|
|
3316
3423
|
}
|
|
3424
|
+
return () => {
|
|
3425
|
+
popupCleanups.forEach((cleanup) => {
|
|
3426
|
+
try {
|
|
3427
|
+
cleanup();
|
|
3428
|
+
} catch (_) {
|
|
3429
|
+
}
|
|
3430
|
+
});
|
|
3431
|
+
};
|
|
3317
3432
|
}, [allMarkers, defaultCenter, defaultZoom, leafletLib]);
|
|
3318
3433
|
const isLoadingMarkers = iiifTargets.loading || navState.loading;
|
|
3319
3434
|
const hasMarkers = allMarkers.length > 0;
|
|
@@ -3327,14 +3442,14 @@ function Map2({
|
|
|
3327
3442
|
].filter(Boolean).join(" ");
|
|
3328
3443
|
const statusLabel = leafletError ? leafletError.message || "Failed to load map library" : !leafletLib ? "Loading map\u2026" : iiifTargets.error ? iiifTargets.error : datasetUnavailable ? "Map data is unavailable for this site." : navState.error ? navState.error : isLoadingMarkers ? "Loading map data\u2026" : !iiifContent && !hasCustomPoints ? "Add iiifContent or MapPoint markers to populate this map." : !hasMarkers ? "No map locations available." : "";
|
|
3329
3444
|
const showStatus = Boolean(statusLabel);
|
|
3330
|
-
return /* @__PURE__ */
|
|
3445
|
+
return /* @__PURE__ */ React35.createElement("div", { className: rootClass, id: id || void 0, style: style || void 0 }, /* @__PURE__ */ React35.createElement(
|
|
3331
3446
|
"div",
|
|
3332
3447
|
{
|
|
3333
3448
|
ref: containerRef,
|
|
3334
3449
|
className: "canopy-map__canvas",
|
|
3335
3450
|
style: { height: height || "600px" }
|
|
3336
3451
|
}
|
|
3337
|
-
), showStatus ? /* @__PURE__ */
|
|
3452
|
+
), showStatus ? /* @__PURE__ */ React35.createElement("div", { className: "canopy-map__status", "aria-live": "polite" }, statusLabel) : null);
|
|
3338
3453
|
}
|
|
3339
3454
|
|
|
3340
3455
|
// ui/src/content/map/MapPoint.jsx
|