@canopy-iiif/app 0.12.3 → 0.12.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.
- package/lib/build/iiif.js +15 -1
- package/lib/build/mdx.js +101 -0
- package/lib/build/pages.js +7 -1
- package/lib/build/runtimes.js +1 -0
- package/lib/components/timeline-runtime.js +80 -0
- package/lib/search/search-form-runtime.js +1 -1
- package/package.json +1 -1
- package/ui/dist/index.mjs +701 -67
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +873 -61
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/components/_timeline.scss +272 -0
- package/ui/styles/components/index.scss +1 -0
- package/ui/styles/index.css +265 -0
package/ui/dist/index.mjs
CHANGED
|
@@ -1171,8 +1171,51 @@ function CanopyFooter({ className = "", children }) {
|
|
|
1171
1171
|
return /* @__PURE__ */ React15.createElement("footer", { className: footerClassName }, /* @__PURE__ */ React15.createElement("div", { className: "canopy-footer__inner" }, children));
|
|
1172
1172
|
}
|
|
1173
1173
|
|
|
1174
|
+
// ui/src/layout/TeaserCard.jsx
|
|
1175
|
+
import React16 from "react";
|
|
1176
|
+
function TeaserCard({
|
|
1177
|
+
href = "",
|
|
1178
|
+
title = "",
|
|
1179
|
+
metadata = [],
|
|
1180
|
+
summary = "",
|
|
1181
|
+
thumbnail = null,
|
|
1182
|
+
type = "work",
|
|
1183
|
+
className = "",
|
|
1184
|
+
...rest
|
|
1185
|
+
}) {
|
|
1186
|
+
const Tag = href ? "a" : "div";
|
|
1187
|
+
const classes = [
|
|
1188
|
+
"canopy-card",
|
|
1189
|
+
"canopy-card--teaser",
|
|
1190
|
+
"canopy-search-teaser__item",
|
|
1191
|
+
"canopy-teaser-card",
|
|
1192
|
+
className
|
|
1193
|
+
].filter(Boolean).join(" ");
|
|
1194
|
+
const showThumb = type === "work" && thumbnail;
|
|
1195
|
+
const metaLine = (Array.isArray(metadata) && metadata.length ? metadata.filter(Boolean) : summary ? [summary] : []).filter(Boolean).slice(0, 2).join(" \u2022 ");
|
|
1196
|
+
return /* @__PURE__ */ React16.createElement(
|
|
1197
|
+
Tag,
|
|
1198
|
+
{
|
|
1199
|
+
className: classes,
|
|
1200
|
+
href: href || void 0,
|
|
1201
|
+
"data-canopy-item": href ? "" : void 0,
|
|
1202
|
+
...rest
|
|
1203
|
+
},
|
|
1204
|
+
showThumb ? /* @__PURE__ */ React16.createElement("div", { className: "canopy-search-teaser__thumb" }, /* @__PURE__ */ React16.createElement(
|
|
1205
|
+
"img",
|
|
1206
|
+
{
|
|
1207
|
+
src: thumbnail,
|
|
1208
|
+
alt: "",
|
|
1209
|
+
loading: "lazy",
|
|
1210
|
+
className: "canopy-search-teaser__thumb-img"
|
|
1211
|
+
}
|
|
1212
|
+
)) : null,
|
|
1213
|
+
/* @__PURE__ */ React16.createElement("div", { className: "canopy-search-teaser__text" }, /* @__PURE__ */ React16.createElement("span", { className: "canopy-search-teaser__title" }, title || href || "Untitled"), metaLine ? /* @__PURE__ */ React16.createElement("span", { className: "canopy-search-teaser__meta" }, metaLine) : null)
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1174
1217
|
// ui/src/iiif/Viewer.jsx
|
|
1175
|
-
import
|
|
1218
|
+
import React17, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1176
1219
|
var DEFAULT_VIEWER_OPTIONS = {
|
|
1177
1220
|
showDownload: false,
|
|
1178
1221
|
showIIIFBadge: false,
|
|
@@ -1228,7 +1271,7 @@ var Viewer = (props) => {
|
|
|
1228
1271
|
} catch (_) {
|
|
1229
1272
|
json = "{}";
|
|
1230
1273
|
}
|
|
1231
|
-
return /* @__PURE__ */
|
|
1274
|
+
return /* @__PURE__ */ React17.createElement("div", { "data-canopy-viewer": "1", className: "not-prose" }, /* @__PURE__ */ React17.createElement(
|
|
1232
1275
|
"script",
|
|
1233
1276
|
{
|
|
1234
1277
|
type: "application/json",
|
|
@@ -1236,11 +1279,11 @@ var Viewer = (props) => {
|
|
|
1236
1279
|
}
|
|
1237
1280
|
));
|
|
1238
1281
|
}
|
|
1239
|
-
return /* @__PURE__ */
|
|
1282
|
+
return /* @__PURE__ */ React17.createElement(CloverViewer, { ...props, options: mergedOptions });
|
|
1240
1283
|
};
|
|
1241
1284
|
|
|
1242
1285
|
// ui/src/iiif/Slider.jsx
|
|
1243
|
-
import
|
|
1286
|
+
import React18, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
1244
1287
|
|
|
1245
1288
|
// ui/src/iiif/sliderOptions.js
|
|
1246
1289
|
var UNIT_TOKEN = "__canopySliderUnit";
|
|
@@ -1387,7 +1430,7 @@ var Slider = (props = {}) => {
|
|
|
1387
1430
|
} catch (_) {
|
|
1388
1431
|
json = "{}";
|
|
1389
1432
|
}
|
|
1390
|
-
return /* @__PURE__ */
|
|
1433
|
+
return /* @__PURE__ */ React18.createElement("div", { className: sliderClassName, "data-canopy-slider": "1" }, /* @__PURE__ */ React18.createElement(
|
|
1391
1434
|
"script",
|
|
1392
1435
|
{
|
|
1393
1436
|
type: "application/json",
|
|
@@ -1395,11 +1438,11 @@ var Slider = (props = {}) => {
|
|
|
1395
1438
|
}
|
|
1396
1439
|
));
|
|
1397
1440
|
}
|
|
1398
|
-
return /* @__PURE__ */
|
|
1441
|
+
return /* @__PURE__ */ React18.createElement(CloverSlider, { ...resolvedProps });
|
|
1399
1442
|
};
|
|
1400
1443
|
|
|
1401
1444
|
// ui/src/iiif/Scroll.jsx
|
|
1402
|
-
import
|
|
1445
|
+
import React19, { useEffect as useEffect4, useState as useState4 } from "react";
|
|
1403
1446
|
var Scroll = (props) => {
|
|
1404
1447
|
const [CloverScroll, setCloverScroll] = useState4(null);
|
|
1405
1448
|
useEffect4(() => {
|
|
@@ -1424,7 +1467,7 @@ var Scroll = (props) => {
|
|
|
1424
1467
|
} catch (_) {
|
|
1425
1468
|
json = "{}";
|
|
1426
1469
|
}
|
|
1427
|
-
return /* @__PURE__ */
|
|
1470
|
+
return /* @__PURE__ */ React19.createElement("div", { "data-canopy-scroll": "1", className: "not-prose" }, /* @__PURE__ */ React19.createElement(
|
|
1428
1471
|
"script",
|
|
1429
1472
|
{
|
|
1430
1473
|
type: "application/json",
|
|
@@ -1432,11 +1475,11 @@ var Scroll = (props) => {
|
|
|
1432
1475
|
}
|
|
1433
1476
|
));
|
|
1434
1477
|
}
|
|
1435
|
-
return /* @__PURE__ */
|
|
1478
|
+
return /* @__PURE__ */ React19.createElement(CloverScroll, { ...props });
|
|
1436
1479
|
};
|
|
1437
1480
|
|
|
1438
1481
|
// ui/src/iiif/Image.jsx
|
|
1439
|
-
import
|
|
1482
|
+
import React20, { useEffect as useEffect5, useState as useState5 } from "react";
|
|
1440
1483
|
var Image = (props = {}) => {
|
|
1441
1484
|
const [CloverImage, setCloverImage] = useState5(null);
|
|
1442
1485
|
const baseClass = "canopy-iiif-image";
|
|
@@ -1469,7 +1512,7 @@ var Image = (props = {}) => {
|
|
|
1469
1512
|
} catch (_) {
|
|
1470
1513
|
json = "{}";
|
|
1471
1514
|
}
|
|
1472
|
-
return /* @__PURE__ */
|
|
1515
|
+
return /* @__PURE__ */ React20.createElement("figure", { className: rootClassName }, /* @__PURE__ */ React20.createElement(
|
|
1473
1516
|
"div",
|
|
1474
1517
|
{
|
|
1475
1518
|
className: `${baseClass}__placeholder`,
|
|
@@ -1479,20 +1522,20 @@ var Image = (props = {}) => {
|
|
|
1479
1522
|
"--canopy-iiif-image-bg": backgroundColor
|
|
1480
1523
|
}
|
|
1481
1524
|
},
|
|
1482
|
-
/* @__PURE__ */
|
|
1525
|
+
/* @__PURE__ */ React20.createElement(
|
|
1483
1526
|
"script",
|
|
1484
1527
|
{
|
|
1485
1528
|
type: "application/json",
|
|
1486
1529
|
dangerouslySetInnerHTML: { __html: json }
|
|
1487
1530
|
}
|
|
1488
1531
|
)
|
|
1489
|
-
), caption && /* @__PURE__ */
|
|
1532
|
+
), caption && /* @__PURE__ */ React20.createElement("figcaption", { className: `${baseClass}__caption` }, caption));
|
|
1490
1533
|
}
|
|
1491
|
-
return /* @__PURE__ */
|
|
1534
|
+
return /* @__PURE__ */ React20.createElement(CloverImage, { ...props, className: rootClassName });
|
|
1492
1535
|
};
|
|
1493
1536
|
|
|
1494
1537
|
// ui/src/iiif/MdxRelatedItems.jsx
|
|
1495
|
-
import
|
|
1538
|
+
import React21 from "react";
|
|
1496
1539
|
function MdxRelatedItems(props) {
|
|
1497
1540
|
let json = "{}";
|
|
1498
1541
|
try {
|
|
@@ -1500,7 +1543,7 @@ function MdxRelatedItems(props) {
|
|
|
1500
1543
|
} catch (_) {
|
|
1501
1544
|
json = "{}";
|
|
1502
1545
|
}
|
|
1503
|
-
return /* @__PURE__ */
|
|
1546
|
+
return /* @__PURE__ */ React21.createElement("div", { "data-canopy-related-items": "1" }, /* @__PURE__ */ React21.createElement(
|
|
1504
1547
|
"script",
|
|
1505
1548
|
{
|
|
1506
1549
|
type: "application/json",
|
|
@@ -1510,7 +1553,7 @@ function MdxRelatedItems(props) {
|
|
|
1510
1553
|
}
|
|
1511
1554
|
|
|
1512
1555
|
// ui/src/search/MdxSearchResults.jsx
|
|
1513
|
-
import
|
|
1556
|
+
import React22 from "react";
|
|
1514
1557
|
function MdxSearchResults(props) {
|
|
1515
1558
|
let json = "{}";
|
|
1516
1559
|
try {
|
|
@@ -1518,11 +1561,11 @@ function MdxSearchResults(props) {
|
|
|
1518
1561
|
} catch (_) {
|
|
1519
1562
|
json = "{}";
|
|
1520
1563
|
}
|
|
1521
|
-
return /* @__PURE__ */
|
|
1564
|
+
return /* @__PURE__ */ React22.createElement("div", { "data-canopy-search-results": "1" }, /* @__PURE__ */ React22.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
1522
1565
|
}
|
|
1523
1566
|
|
|
1524
1567
|
// ui/src/search/SearchSummary.jsx
|
|
1525
|
-
import
|
|
1568
|
+
import React23 from "react";
|
|
1526
1569
|
function SearchSummary(props) {
|
|
1527
1570
|
let json = "{}";
|
|
1528
1571
|
try {
|
|
@@ -1530,11 +1573,11 @@ function SearchSummary(props) {
|
|
|
1530
1573
|
} catch (_) {
|
|
1531
1574
|
json = "{}";
|
|
1532
1575
|
}
|
|
1533
|
-
return /* @__PURE__ */
|
|
1576
|
+
return /* @__PURE__ */ React23.createElement("div", { "data-canopy-search-summary": "1" }, /* @__PURE__ */ React23.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
1534
1577
|
}
|
|
1535
1578
|
|
|
1536
1579
|
// ui/src/search/MdxSearchTabs.jsx
|
|
1537
|
-
import
|
|
1580
|
+
import React24 from "react";
|
|
1538
1581
|
function MdxSearchTabs(props) {
|
|
1539
1582
|
let json = "{}";
|
|
1540
1583
|
try {
|
|
@@ -1542,15 +1585,15 @@ function MdxSearchTabs(props) {
|
|
|
1542
1585
|
} catch (_) {
|
|
1543
1586
|
json = "{}";
|
|
1544
1587
|
}
|
|
1545
|
-
return /* @__PURE__ */
|
|
1588
|
+
return /* @__PURE__ */ React24.createElement("div", { "data-canopy-search-tabs": "1" }, /* @__PURE__ */ React24.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: json } }));
|
|
1546
1589
|
}
|
|
1547
1590
|
|
|
1548
1591
|
// ui/src/search/SearchResults.jsx
|
|
1549
|
-
import
|
|
1592
|
+
import React25 from "react";
|
|
1550
1593
|
function DefaultArticleTemplate({ record, query }) {
|
|
1551
1594
|
if (!record) return null;
|
|
1552
1595
|
const metadata = Array.isArray(record.metadata) ? record.metadata : [];
|
|
1553
|
-
return /* @__PURE__ */
|
|
1596
|
+
return /* @__PURE__ */ React25.createElement(
|
|
1554
1597
|
ArticleCard,
|
|
1555
1598
|
{
|
|
1556
1599
|
href: record.href,
|
|
@@ -1568,7 +1611,7 @@ function DefaultFigureTemplate({ record, thumbnailAspectRatio }) {
|
|
|
1568
1611
|
if (!record) return null;
|
|
1569
1612
|
const hasDims = Number.isFinite(Number(record.thumbnailWidth)) && Number(record.thumbnailWidth) > 0 && Number.isFinite(Number(record.thumbnailHeight)) && Number(record.thumbnailHeight) > 0;
|
|
1570
1613
|
const aspect = Number.isFinite(Number(thumbnailAspectRatio)) && Number(thumbnailAspectRatio) > 0 ? Number(thumbnailAspectRatio) : hasDims ? Number(record.thumbnailWidth) / Number(record.thumbnailHeight) : void 0;
|
|
1571
|
-
return /* @__PURE__ */
|
|
1614
|
+
return /* @__PURE__ */ React25.createElement(
|
|
1572
1615
|
Card,
|
|
1573
1616
|
{
|
|
1574
1617
|
href: record.href,
|
|
@@ -1589,7 +1632,7 @@ function SearchResults({
|
|
|
1589
1632
|
variant = "auto"
|
|
1590
1633
|
}) {
|
|
1591
1634
|
if (!results.length) {
|
|
1592
|
-
return /* @__PURE__ */
|
|
1635
|
+
return /* @__PURE__ */ React25.createElement("div", { className: "text-slate-600" }, /* @__PURE__ */ React25.createElement("em", null, "No results"));
|
|
1593
1636
|
}
|
|
1594
1637
|
const normalizedType = String(type || "all").toLowerCase();
|
|
1595
1638
|
const normalizedVariant = variant === "figure" || variant === "article" ? variant : "auto";
|
|
@@ -1597,9 +1640,9 @@ function SearchResults({
|
|
|
1597
1640
|
const FigureTemplate = templates && templates.figure ? templates.figure : DefaultFigureTemplate;
|
|
1598
1641
|
const ArticleTemplate = templates && templates.article ? templates.article : DefaultArticleTemplate;
|
|
1599
1642
|
if (isAnnotationView) {
|
|
1600
|
-
return /* @__PURE__ */
|
|
1643
|
+
return /* @__PURE__ */ React25.createElement("div", { id: "search-results", className: "space-y-4" }, results.map((r, i) => {
|
|
1601
1644
|
if (!r) return null;
|
|
1602
|
-
return /* @__PURE__ */
|
|
1645
|
+
return /* @__PURE__ */ React25.createElement(
|
|
1603
1646
|
ArticleTemplate,
|
|
1604
1647
|
{
|
|
1605
1648
|
key: r.id || i,
|
|
@@ -1617,20 +1660,20 @@ function SearchResults({
|
|
|
1617
1660
|
return !isWorkRecord(record) || normalizedType !== "work";
|
|
1618
1661
|
};
|
|
1619
1662
|
if (layout === "list") {
|
|
1620
|
-
return /* @__PURE__ */
|
|
1663
|
+
return /* @__PURE__ */ React25.createElement("div", { id: "search-results", className: "space-y-6" }, results.map((r, i) => {
|
|
1621
1664
|
if (shouldRenderAsArticle(r)) {
|
|
1622
|
-
return /* @__PURE__ */
|
|
1665
|
+
return /* @__PURE__ */ React25.createElement("div", { key: i, className: `search-result ${r && r.type}` }, /* @__PURE__ */ React25.createElement(ArticleTemplate, { record: r, query, layout }));
|
|
1623
1666
|
}
|
|
1624
1667
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
1625
1668
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
1626
|
-
return /* @__PURE__ */
|
|
1669
|
+
return /* @__PURE__ */ React25.createElement(
|
|
1627
1670
|
"div",
|
|
1628
1671
|
{
|
|
1629
1672
|
key: i,
|
|
1630
1673
|
className: `search-result ${r.type}`,
|
|
1631
1674
|
"data-thumbnail-aspect-ratio": aspect
|
|
1632
1675
|
},
|
|
1633
|
-
/* @__PURE__ */
|
|
1676
|
+
/* @__PURE__ */ React25.createElement(
|
|
1634
1677
|
FigureTemplate,
|
|
1635
1678
|
{
|
|
1636
1679
|
record: r,
|
|
@@ -1642,20 +1685,20 @@ function SearchResults({
|
|
|
1642
1685
|
);
|
|
1643
1686
|
}));
|
|
1644
1687
|
}
|
|
1645
|
-
return /* @__PURE__ */
|
|
1688
|
+
return /* @__PURE__ */ React25.createElement("div", { id: "search-results" }, /* @__PURE__ */ React25.createElement(Grid, null, results.map((r, i) => {
|
|
1646
1689
|
if (shouldRenderAsArticle(r)) {
|
|
1647
|
-
return /* @__PURE__ */
|
|
1690
|
+
return /* @__PURE__ */ React25.createElement(GridItem, { key: i, className: `search-result ${r && r.type}` }, /* @__PURE__ */ React25.createElement(ArticleTemplate, { record: r, query, layout }));
|
|
1648
1691
|
}
|
|
1649
1692
|
const hasDims = Number.isFinite(Number(r.thumbnailWidth)) && Number(r.thumbnailWidth) > 0 && Number.isFinite(Number(r.thumbnailHeight)) && Number(r.thumbnailHeight) > 0;
|
|
1650
1693
|
const aspect = hasDims ? Number(r.thumbnailWidth) / Number(r.thumbnailHeight) : void 0;
|
|
1651
|
-
return /* @__PURE__ */
|
|
1694
|
+
return /* @__PURE__ */ React25.createElement(
|
|
1652
1695
|
GridItem,
|
|
1653
1696
|
{
|
|
1654
1697
|
key: i,
|
|
1655
1698
|
className: `search-result ${r.type}`,
|
|
1656
1699
|
"data-thumbnail-aspect-ratio": aspect
|
|
1657
1700
|
},
|
|
1658
|
-
/* @__PURE__ */
|
|
1701
|
+
/* @__PURE__ */ React25.createElement(
|
|
1659
1702
|
FigureTemplate,
|
|
1660
1703
|
{
|
|
1661
1704
|
record: r,
|
|
@@ -1669,7 +1712,7 @@ function SearchResults({
|
|
|
1669
1712
|
}
|
|
1670
1713
|
|
|
1671
1714
|
// ui/src/search/SearchTabs.jsx
|
|
1672
|
-
import
|
|
1715
|
+
import React26, { useRef as useRef2, useState as useState6, useEffect as useEffect6 } from "react";
|
|
1673
1716
|
function SearchTabs({
|
|
1674
1717
|
type = "all",
|
|
1675
1718
|
onTypeChange,
|
|
@@ -1723,7 +1766,7 @@ function SearchTabs({
|
|
|
1723
1766
|
width: `${itemBoundingBox.width}px`
|
|
1724
1767
|
};
|
|
1725
1768
|
}
|
|
1726
|
-
return /* @__PURE__ */
|
|
1769
|
+
return /* @__PURE__ */ React26.createElement("div", { className: "canopy-search-tabs-wrapper" }, /* @__PURE__ */ React26.createElement(
|
|
1727
1770
|
"div",
|
|
1728
1771
|
{
|
|
1729
1772
|
role: "tablist",
|
|
@@ -1732,7 +1775,7 @@ function SearchTabs({
|
|
|
1732
1775
|
ref: wrapperRef,
|
|
1733
1776
|
onMouseLeave: resetHighlight
|
|
1734
1777
|
},
|
|
1735
|
-
/* @__PURE__ */
|
|
1778
|
+
/* @__PURE__ */ React26.createElement(
|
|
1736
1779
|
"div",
|
|
1737
1780
|
{
|
|
1738
1781
|
ref: highlightRef,
|
|
@@ -1744,7 +1787,7 @@ function SearchTabs({
|
|
|
1744
1787
|
const active = String(type).toLowerCase() === String(t).toLowerCase();
|
|
1745
1788
|
const cRaw = counts && Object.prototype.hasOwnProperty.call(counts, t) ? counts[t] : void 0;
|
|
1746
1789
|
const c = Number.isFinite(Number(cRaw)) ? Number(cRaw) : 0;
|
|
1747
|
-
return /* @__PURE__ */
|
|
1790
|
+
return /* @__PURE__ */ React26.createElement(
|
|
1748
1791
|
"button",
|
|
1749
1792
|
{
|
|
1750
1793
|
key: t,
|
|
@@ -1762,7 +1805,7 @@ function SearchTabs({
|
|
|
1762
1805
|
")"
|
|
1763
1806
|
);
|
|
1764
1807
|
})
|
|
1765
|
-
), hasFilters ? /* @__PURE__ */
|
|
1808
|
+
), hasFilters ? /* @__PURE__ */ React26.createElement(
|
|
1766
1809
|
"button",
|
|
1767
1810
|
{
|
|
1768
1811
|
type: "button",
|
|
@@ -1770,12 +1813,12 @@ function SearchTabs({
|
|
|
1770
1813
|
"aria-expanded": filtersOpen ? "true" : "false",
|
|
1771
1814
|
className: "inline-flex items-center gap-2 rounded-md border border-slate-200 bg-white px-3 py-1.5 text-sm font-medium text-slate-700 shadow-sm transition hover:border-brand-200 hover:bg-brand-50 hover:text-brand-700"
|
|
1772
1815
|
},
|
|
1773
|
-
/* @__PURE__ */
|
|
1816
|
+
/* @__PURE__ */ React26.createElement("span", null, filtersLabel, filterBadge)
|
|
1774
1817
|
) : null);
|
|
1775
1818
|
}
|
|
1776
1819
|
|
|
1777
1820
|
// ui/src/search/SearchFiltersDialog.jsx
|
|
1778
|
-
import
|
|
1821
|
+
import React27 from "react";
|
|
1779
1822
|
function toArray(input) {
|
|
1780
1823
|
if (!input) return [];
|
|
1781
1824
|
if (Array.isArray(input)) return input;
|
|
@@ -1814,20 +1857,20 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
1814
1857
|
const selectedValues = selected.get(String(slug)) || /* @__PURE__ */ new Set();
|
|
1815
1858
|
const checkboxId = (valueSlug) => `filter-${slug}-${valueSlug}`;
|
|
1816
1859
|
const hasSelection = selectedValues.size > 0;
|
|
1817
|
-
const [quickQuery, setQuickQuery] =
|
|
1860
|
+
const [quickQuery, setQuickQuery] = React27.useState("");
|
|
1818
1861
|
const hasQuery = quickQuery.trim().length > 0;
|
|
1819
|
-
const filteredValues =
|
|
1862
|
+
const filteredValues = React27.useMemo(
|
|
1820
1863
|
() => facetMatches(values, quickQuery),
|
|
1821
1864
|
[values, quickQuery]
|
|
1822
1865
|
);
|
|
1823
|
-
return /* @__PURE__ */
|
|
1866
|
+
return /* @__PURE__ */ React27.createElement(
|
|
1824
1867
|
"details",
|
|
1825
1868
|
{
|
|
1826
1869
|
className: "canopy-search-filters__facet",
|
|
1827
1870
|
open: hasSelection
|
|
1828
1871
|
},
|
|
1829
|
-
/* @__PURE__ */
|
|
1830
|
-
/* @__PURE__ */
|
|
1872
|
+
/* @__PURE__ */ React27.createElement("summary", { className: "canopy-search-filters__facet-summary" }, /* @__PURE__ */ React27.createElement("span", null, label), /* @__PURE__ */ React27.createElement("span", { className: "canopy-search-filters__facet-count" }, values.length)),
|
|
1873
|
+
/* @__PURE__ */ React27.createElement("div", { className: "canopy-search-filters__facet-content" }, /* @__PURE__ */ React27.createElement("div", { className: "canopy-search-filters__quick" }, /* @__PURE__ */ React27.createElement(
|
|
1831
1874
|
"input",
|
|
1832
1875
|
{
|
|
1833
1876
|
type: "search",
|
|
@@ -1837,7 +1880,7 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
1837
1880
|
className: "canopy-search-filters__quick-input",
|
|
1838
1881
|
"aria-label": `Filter ${label} values`
|
|
1839
1882
|
}
|
|
1840
|
-
), quickQuery ? /* @__PURE__ */
|
|
1883
|
+
), quickQuery ? /* @__PURE__ */ React27.createElement(
|
|
1841
1884
|
"button",
|
|
1842
1885
|
{
|
|
1843
1886
|
type: "button",
|
|
@@ -1845,11 +1888,11 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
1845
1888
|
className: "canopy-search-filters__quick-clear"
|
|
1846
1889
|
},
|
|
1847
1890
|
"Clear"
|
|
1848
|
-
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */
|
|
1891
|
+
) : null), hasQuery && !filteredValues.length ? /* @__PURE__ */ React27.createElement("p", { className: "canopy-search-filters__facet-notice" }, "No matches found.") : null, /* @__PURE__ */ React27.createElement("ul", { className: "canopy-search-filters__facet-list" }, filteredValues.map((entry) => {
|
|
1849
1892
|
const valueSlug = String(entry.slug || entry.value || "");
|
|
1850
1893
|
const isChecked = selectedValues.has(valueSlug);
|
|
1851
1894
|
const inputId = checkboxId(valueSlug);
|
|
1852
|
-
return /* @__PURE__ */
|
|
1895
|
+
return /* @__PURE__ */ React27.createElement("li", { key: valueSlug, className: "canopy-search-filters__facet-item" }, /* @__PURE__ */ React27.createElement(
|
|
1853
1896
|
"input",
|
|
1854
1897
|
{
|
|
1855
1898
|
id: inputId,
|
|
@@ -1861,15 +1904,15 @@ function FacetSection({ facet, selected, onToggle }) {
|
|
|
1861
1904
|
if (onToggle) onToggle(slug, valueSlug, nextChecked);
|
|
1862
1905
|
}
|
|
1863
1906
|
}
|
|
1864
|
-
), /* @__PURE__ */
|
|
1907
|
+
), /* @__PURE__ */ React27.createElement(
|
|
1865
1908
|
"label",
|
|
1866
1909
|
{
|
|
1867
1910
|
htmlFor: inputId,
|
|
1868
1911
|
className: "canopy-search-filters__facet-label"
|
|
1869
1912
|
},
|
|
1870
|
-
/* @__PURE__ */
|
|
1913
|
+
/* @__PURE__ */ React27.createElement("span", null, entry.value, " ", Number.isFinite(entry.doc_count) ? /* @__PURE__ */ React27.createElement("span", { className: "canopy-search-filters__facet-count" }, "(", entry.doc_count, ")") : null)
|
|
1871
1914
|
));
|
|
1872
|
-
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */
|
|
1915
|
+
}), !filteredValues.length && !hasQuery ? /* @__PURE__ */ React27.createElement("li", { className: "canopy-search-filters__facet-empty" }, "No values available.") : null))
|
|
1873
1916
|
);
|
|
1874
1917
|
}
|
|
1875
1918
|
function SearchFiltersDialog(props = {}) {
|
|
@@ -1891,7 +1934,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
1891
1934
|
(total, set) => total + set.size,
|
|
1892
1935
|
0
|
|
1893
1936
|
);
|
|
1894
|
-
|
|
1937
|
+
React27.useEffect(() => {
|
|
1895
1938
|
if (!open) return void 0;
|
|
1896
1939
|
if (typeof document === "undefined") return void 0;
|
|
1897
1940
|
const body = document.body;
|
|
@@ -1908,7 +1951,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
1908
1951
|
if (!open) return null;
|
|
1909
1952
|
const brandId = "canopy-modal-filters-label";
|
|
1910
1953
|
const subtitleText = subtitle != null ? subtitle : title;
|
|
1911
|
-
return /* @__PURE__ */
|
|
1954
|
+
return /* @__PURE__ */ React27.createElement(
|
|
1912
1955
|
CanopyModal,
|
|
1913
1956
|
{
|
|
1914
1957
|
id: "canopy-modal-filters",
|
|
@@ -1923,8 +1966,8 @@ function SearchFiltersDialog(props = {}) {
|
|
|
1923
1966
|
onBackgroundClick: () => onOpenChange && onOpenChange(false),
|
|
1924
1967
|
bodyClassName: "canopy-modal__body--filters"
|
|
1925
1968
|
},
|
|
1926
|
-
subtitleText ? /* @__PURE__ */
|
|
1927
|
-
/* @__PURE__ */
|
|
1969
|
+
subtitleText ? /* @__PURE__ */ React27.createElement("p", { className: "canopy-search-filters__subtitle" }, subtitleText) : null,
|
|
1970
|
+
/* @__PURE__ */ React27.createElement("div", { className: "canopy-search-filters__body" }, Array.isArray(facets) && facets.length ? /* @__PURE__ */ React27.createElement("div", { className: "canopy-search-filters__facets" }, facets.map((facet) => /* @__PURE__ */ React27.createElement(
|
|
1928
1971
|
FacetSection,
|
|
1929
1972
|
{
|
|
1930
1973
|
key: facet.slug || facet.label,
|
|
@@ -1932,8 +1975,8 @@ function SearchFiltersDialog(props = {}) {
|
|
|
1932
1975
|
selected: selectedMap,
|
|
1933
1976
|
onToggle
|
|
1934
1977
|
}
|
|
1935
|
-
))) : /* @__PURE__ */
|
|
1936
|
-
/* @__PURE__ */
|
|
1978
|
+
))) : /* @__PURE__ */ React27.createElement("p", { className: "canopy-search-filters__empty" }, "No filters are available for this collection.")),
|
|
1979
|
+
/* @__PURE__ */ React27.createElement("footer", { className: "canopy-search-filters__footer" }, /* @__PURE__ */ React27.createElement("div", null, activeCount ? `${activeCount} filter${activeCount === 1 ? "" : "s"} applied` : "No filters applied"), /* @__PURE__ */ React27.createElement("div", { className: "canopy-search-filters__footer-actions" }, /* @__PURE__ */ React27.createElement(
|
|
1937
1980
|
"button",
|
|
1938
1981
|
{
|
|
1939
1982
|
type: "button",
|
|
@@ -1944,7 +1987,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
1944
1987
|
className: "canopy-search-filters__button canopy-search-filters__button--secondary"
|
|
1945
1988
|
},
|
|
1946
1989
|
"Clear all"
|
|
1947
|
-
), /* @__PURE__ */
|
|
1990
|
+
), /* @__PURE__ */ React27.createElement(
|
|
1948
1991
|
"button",
|
|
1949
1992
|
{
|
|
1950
1993
|
type: "button",
|
|
@@ -1957,7 +2000,7 @@ function SearchFiltersDialog(props = {}) {
|
|
|
1957
2000
|
}
|
|
1958
2001
|
|
|
1959
2002
|
// ui/src/search-form/MdxSearchFormModal.jsx
|
|
1960
|
-
import
|
|
2003
|
+
import React28 from "react";
|
|
1961
2004
|
function MdxSearchFormModal(props = {}) {
|
|
1962
2005
|
const {
|
|
1963
2006
|
placeholder = "Search\u2026",
|
|
@@ -1973,21 +2016,609 @@ function MdxSearchFormModal(props = {}) {
|
|
|
1973
2016
|
const text = typeof label === "string" && label.trim() ? label.trim() : buttonLabel;
|
|
1974
2017
|
const resolvedSearchPath = resolveSearchPath(searchPath);
|
|
1975
2018
|
const data = { placeholder, hotkey, maxResults, groupOrder, label: text, searchPath: resolvedSearchPath };
|
|
1976
|
-
return /* @__PURE__ */
|
|
2019
|
+
return /* @__PURE__ */ React28.createElement("div", { "data-canopy-search-form": true, className: "flex-1 min-w-0" }, /* @__PURE__ */ React28.createElement("div", { className: "relative w-full" }, /* @__PURE__ */ React28.createElement(SearchPanelForm, { placeholder, buttonLabel, label, searchPath: resolvedSearchPath }), /* @__PURE__ */ React28.createElement(SearchPanelTeaserResults, null)), /* @__PURE__ */ React28.createElement("script", { type: "application/json", dangerouslySetInnerHTML: { __html: JSON.stringify(data) } }));
|
|
1977
2020
|
}
|
|
1978
2021
|
|
|
1979
2022
|
// ui/src/docs/MarkdownTable.jsx
|
|
1980
|
-
import
|
|
2023
|
+
import React29 from "react";
|
|
1981
2024
|
function MarkdownTable({ className = "", ...rest }) {
|
|
1982
2025
|
const merged = ["markdown-table", className].filter(Boolean).join(" ");
|
|
1983
|
-
return /* @__PURE__ */
|
|
2026
|
+
return /* @__PURE__ */ React29.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React29.createElement("table", { className: merged, ...rest }));
|
|
1984
2027
|
}
|
|
1985
2028
|
|
|
1986
2029
|
// ui/src/docs/Diagram.jsx
|
|
1987
|
-
import
|
|
2030
|
+
import React30 from "react";
|
|
1988
2031
|
function CanopyDiagram() {
|
|
1989
|
-
return /* @__PURE__ */
|
|
2032
|
+
return /* @__PURE__ */ React30.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React30.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React30.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React30.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 105 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React30.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Collection A"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React30.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React30.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Collection B"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React30.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React30.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React30.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React30.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React30.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React30.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React30.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React30.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, page content, and annotations before bundling the site."), /* @__PURE__ */ React30.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Automated content"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React30.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React30.createElement("li", null, "Customize page layout"))), /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React30.createElement("li", null, "Author narratives & tours"), /* @__PURE__ */ React30.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Search index"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React30.createElement("li", null, "Customize result layout"), /* @__PURE__ */ React30.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React30.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React30.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React30.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React30.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React30.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React30.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__ */ React30.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Work pages"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React30.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React30.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React30.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React30.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React30.createElement("article", null, /* @__PURE__ */ React30.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React30.createElement("ul", null, /* @__PURE__ */ React30.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React30.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React30.createElement("li", null, "Optional annotation dataset"))))));
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
// ui/src/content/timeline/Timeline.jsx
|
|
2036
|
+
import React31 from "react";
|
|
2037
|
+
|
|
2038
|
+
// ui/src/content/timeline/date-utils.js
|
|
2039
|
+
var FALLBACK_LOCALE = (() => {
|
|
2040
|
+
try {
|
|
2041
|
+
return new Intl.Locale("en-US");
|
|
2042
|
+
} catch (_) {
|
|
2043
|
+
return "en-US";
|
|
2044
|
+
}
|
|
2045
|
+
})();
|
|
2046
|
+
function createLocale(localeValue) {
|
|
2047
|
+
if (!localeValue) return FALLBACK_LOCALE;
|
|
2048
|
+
if (typeof Intl !== "undefined" && localeValue instanceof Intl.Locale)
|
|
2049
|
+
return localeValue;
|
|
2050
|
+
try {
|
|
2051
|
+
return new Intl.Locale(localeValue);
|
|
2052
|
+
} catch (_) {
|
|
2053
|
+
return FALLBACK_LOCALE;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
function parseMonthIndex(name = "", locale = FALLBACK_LOCALE) {
|
|
2057
|
+
const localeId = typeof locale === "string" ? locale : locale && locale.baseName ? locale.baseName : "en-US";
|
|
2058
|
+
const normalized = String(name || "").trim().toLocaleLowerCase(localeId);
|
|
2059
|
+
if (!normalized) return 0;
|
|
2060
|
+
const months = [
|
|
2061
|
+
"january",
|
|
2062
|
+
"february",
|
|
2063
|
+
"march",
|
|
2064
|
+
"april",
|
|
2065
|
+
"may",
|
|
2066
|
+
"june",
|
|
2067
|
+
"july",
|
|
2068
|
+
"august",
|
|
2069
|
+
"september",
|
|
2070
|
+
"october",
|
|
2071
|
+
"november",
|
|
2072
|
+
"december"
|
|
2073
|
+
];
|
|
2074
|
+
const idx = months.indexOf(normalized);
|
|
2075
|
+
return idx === -1 ? 0 : idx;
|
|
2076
|
+
}
|
|
2077
|
+
function parseStructuredInput(value) {
|
|
2078
|
+
if (!value || typeof value !== "object") return null;
|
|
2079
|
+
const { year, month, day } = value;
|
|
2080
|
+
if (!Number.isFinite(Number(year))) return null;
|
|
2081
|
+
const y = Number(year);
|
|
2082
|
+
const m = Number.isFinite(Number(month)) ? Number(month) - 1 : 0;
|
|
2083
|
+
const d = Number.isFinite(Number(day)) ? Number(day) : 1;
|
|
2084
|
+
return new Date(Date.UTC(y, Math.max(0, Math.min(11, m)), Math.max(1, Math.min(31, d))));
|
|
2085
|
+
}
|
|
2086
|
+
function createDateFromInput(value, { locale } = {}) {
|
|
2087
|
+
const loc = createLocale(locale);
|
|
2088
|
+
if (value instanceof Date) return new Date(value.getTime());
|
|
2089
|
+
if (Number.isFinite(Number(value))) {
|
|
2090
|
+
const year = Number(value);
|
|
2091
|
+
return new Date(Date.UTC(year, 0, 1));
|
|
2092
|
+
}
|
|
2093
|
+
if (value && typeof value === "object") {
|
|
2094
|
+
const structured = parseStructuredInput(value);
|
|
2095
|
+
if (structured) return structured;
|
|
2096
|
+
}
|
|
2097
|
+
const text = typeof value === "string" ? value.trim() : "";
|
|
2098
|
+
if (!text) return null;
|
|
2099
|
+
const isoYear = text.match(/^(\d{4})$/);
|
|
2100
|
+
if (isoYear) {
|
|
2101
|
+
return new Date(Date.UTC(Number(isoYear[1]), 0, 1));
|
|
2102
|
+
}
|
|
2103
|
+
const isoYearMonth = text.match(/^(\d{4})-(\d{2})$/);
|
|
2104
|
+
if (isoYearMonth) {
|
|
2105
|
+
return new Date(
|
|
2106
|
+
Date.UTC(Number(isoYearMonth[1]), Number(isoYearMonth[2]) - 1, 1)
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
const isoDate = text.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
2110
|
+
if (isoDate) {
|
|
2111
|
+
return new Date(
|
|
2112
|
+
Date.UTC(
|
|
2113
|
+
Number(isoDate[1]),
|
|
2114
|
+
Number(isoDate[2]) - 1,
|
|
2115
|
+
Number(isoDate[3])
|
|
2116
|
+
)
|
|
2117
|
+
);
|
|
2118
|
+
}
|
|
2119
|
+
const monthDayYear = text.match(/^([A-Za-z]+)\s+(\d{1,2})(?:,)?\s*(\d{4})$/);
|
|
2120
|
+
if (monthDayYear) {
|
|
2121
|
+
const month = parseMonthIndex(monthDayYear[1], loc);
|
|
2122
|
+
const day = Number(monthDayYear[2]);
|
|
2123
|
+
const year = Number(monthDayYear[3]);
|
|
2124
|
+
return new Date(Date.UTC(year, month, day));
|
|
2125
|
+
}
|
|
2126
|
+
const monthYear = text.match(/^([A-Za-z]+)\s+(\d{4})$/);
|
|
2127
|
+
if (monthYear) {
|
|
2128
|
+
const month = parseMonthIndex(monthYear[1], loc);
|
|
2129
|
+
const year = Number(monthYear[2]);
|
|
2130
|
+
return new Date(Date.UTC(year, month, 1));
|
|
2131
|
+
}
|
|
2132
|
+
const fallback = new Date(text);
|
|
2133
|
+
if (!Number.isNaN(fallback.getTime())) return fallback;
|
|
2134
|
+
const alt = new Date(Date.parse(text));
|
|
2135
|
+
if (!Number.isNaN(alt.getTime())) return alt;
|
|
2136
|
+
return null;
|
|
2137
|
+
}
|
|
2138
|
+
function formatDateLabel(date, { granularity = "day", locale } = {}) {
|
|
2139
|
+
if (!(date instanceof Date) || Number.isNaN(date.getTime())) return "";
|
|
2140
|
+
const loc = createLocale(locale);
|
|
2141
|
+
const baseName = typeof loc === "string" ? loc : loc.baseName || "en-US";
|
|
2142
|
+
const options = { year: "numeric" };
|
|
2143
|
+
if (granularity === "month" || granularity === "day") {
|
|
2144
|
+
options.month = "long";
|
|
2145
|
+
}
|
|
2146
|
+
if (granularity === "day") {
|
|
2147
|
+
options.day = "numeric";
|
|
2148
|
+
}
|
|
2149
|
+
options.timeZone = "UTC";
|
|
2150
|
+
try {
|
|
2151
|
+
const formatter = new Intl.DateTimeFormat(baseName, options);
|
|
2152
|
+
return formatter.format(date);
|
|
2153
|
+
} catch (_) {
|
|
2154
|
+
return date.toISOString().slice(0, 10);
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
function normalizeRange(range = {}) {
|
|
2158
|
+
const { start, end, granularity = "year", locale } = range || {};
|
|
2159
|
+
const loc = createLocale(locale);
|
|
2160
|
+
const startDate = createDateFromInput(start || /* @__PURE__ */ new Date(), { locale: loc }) || new Date(Date.UTC(0, 0, 1));
|
|
2161
|
+
let endDate = createDateFromInput(end, { locale: loc });
|
|
2162
|
+
if (!endDate) {
|
|
2163
|
+
const copy = new Date(startDate.getTime());
|
|
2164
|
+
copy.setUTCFullYear(copy.getUTCFullYear() + 1);
|
|
2165
|
+
endDate = copy;
|
|
2166
|
+
}
|
|
2167
|
+
if (endDate <= startDate) {
|
|
2168
|
+
const nextDay = new Date(startDate.getTime());
|
|
2169
|
+
nextDay.setUTCDate(nextDay.getUTCDate() + 1);
|
|
2170
|
+
endDate = nextDay;
|
|
2171
|
+
}
|
|
2172
|
+
const span = Math.max(1, endDate - startDate);
|
|
2173
|
+
return {
|
|
2174
|
+
startDate,
|
|
2175
|
+
endDate,
|
|
2176
|
+
span,
|
|
2177
|
+
granularity: granularity === "month" || granularity === "day" ? granularity : "year",
|
|
2178
|
+
locale: loc
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
function clampProgress(value) {
|
|
2182
|
+
if (!Number.isFinite(value)) return 0;
|
|
2183
|
+
if (value < 0) return 0;
|
|
2184
|
+
if (value > 1) return 1;
|
|
2185
|
+
return value;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2188
|
+
// ui/src/content/timeline/Timeline.jsx
|
|
2189
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
2190
|
+
function getThresholdMs(threshold, granularity) {
|
|
2191
|
+
const value = Number(threshold);
|
|
2192
|
+
if (!Number.isFinite(value) || value <= 0) return 0;
|
|
2193
|
+
if (granularity === "day") return value * DAY_MS;
|
|
2194
|
+
if (granularity === "month") return value * 30 * DAY_MS;
|
|
2195
|
+
return value * 365 * DAY_MS;
|
|
2196
|
+
}
|
|
2197
|
+
function buildGroupedEntries(points, thresholdMs, options) {
|
|
2198
|
+
if (!Array.isArray(points) || !points.length) return [];
|
|
2199
|
+
if (!thresholdMs) return points.map((point) => ({ type: "point", point }));
|
|
2200
|
+
const entries = [];
|
|
2201
|
+
let currentGroup = null;
|
|
2202
|
+
let groupCounter = 0;
|
|
2203
|
+
function flush() {
|
|
2204
|
+
if (!currentGroup) return;
|
|
2205
|
+
if (currentGroup.points.length > 1) {
|
|
2206
|
+
const firstPoint = currentGroup.points[0];
|
|
2207
|
+
entries.push({
|
|
2208
|
+
type: "group",
|
|
2209
|
+
id: `canopy-timeline-group-${groupCounter}-${currentGroup.start}`,
|
|
2210
|
+
points: currentGroup.points,
|
|
2211
|
+
progress: firstPoint.progress,
|
|
2212
|
+
side: firstPoint.side,
|
|
2213
|
+
label: formatGroupLabel(currentGroup.start, currentGroup.end, options),
|
|
2214
|
+
count: currentGroup.points.length
|
|
2215
|
+
});
|
|
2216
|
+
groupCounter += 1;
|
|
2217
|
+
} else {
|
|
2218
|
+
entries.push({ type: "point", point: currentGroup.points[0] });
|
|
2219
|
+
}
|
|
2220
|
+
currentGroup = null;
|
|
2221
|
+
}
|
|
2222
|
+
points.forEach((point) => {
|
|
2223
|
+
const timestamp = point && point.meta ? point.meta.timestamp : null;
|
|
2224
|
+
if (!Number.isFinite(timestamp)) {
|
|
2225
|
+
flush();
|
|
2226
|
+
if (point) entries.push({ type: "point", point });
|
|
2227
|
+
return;
|
|
2228
|
+
}
|
|
2229
|
+
if (!currentGroup) {
|
|
2230
|
+
currentGroup = {
|
|
2231
|
+
points: [point],
|
|
2232
|
+
start: timestamp,
|
|
2233
|
+
end: timestamp,
|
|
2234
|
+
last: timestamp
|
|
2235
|
+
};
|
|
2236
|
+
return;
|
|
2237
|
+
}
|
|
2238
|
+
const diff = Math.abs(timestamp - currentGroup.last);
|
|
2239
|
+
if (diff <= thresholdMs) {
|
|
2240
|
+
currentGroup.points.push(point);
|
|
2241
|
+
currentGroup.last = timestamp;
|
|
2242
|
+
if (timestamp < currentGroup.start) currentGroup.start = timestamp;
|
|
2243
|
+
if (timestamp > currentGroup.end) currentGroup.end = timestamp;
|
|
2244
|
+
} else {
|
|
2245
|
+
flush();
|
|
2246
|
+
currentGroup = {
|
|
2247
|
+
points: [point],
|
|
2248
|
+
start: timestamp,
|
|
2249
|
+
end: timestamp,
|
|
2250
|
+
last: timestamp
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
});
|
|
2254
|
+
flush();
|
|
2255
|
+
return entries;
|
|
2256
|
+
}
|
|
2257
|
+
function formatGroupLabel(startTs, endTs, options) {
|
|
2258
|
+
if (!Number.isFinite(startTs) || !Number.isFinite(endTs)) return "";
|
|
2259
|
+
const startLabel = formatDateLabel(new Date(startTs), options);
|
|
2260
|
+
const endLabel = formatDateLabel(new Date(endTs), options);
|
|
2261
|
+
if (!startLabel || !endLabel) return "";
|
|
2262
|
+
return startLabel === endLabel ? startLabel : `${startLabel} \u2013 ${endLabel}`;
|
|
2263
|
+
}
|
|
2264
|
+
function deriveRangeOverrides(points, range) {
|
|
2265
|
+
const timestamps = points.map((point) => point && point.meta ? point.meta.timestamp : null).filter((timestamp) => Number.isFinite(timestamp));
|
|
2266
|
+
if (!timestamps.length) return range || {};
|
|
2267
|
+
const earliest = Math.min(...timestamps);
|
|
2268
|
+
const latest = Math.max(...timestamps);
|
|
2269
|
+
return {
|
|
2270
|
+
...range,
|
|
2271
|
+
start: range && range.start ? range.start : new Date(earliest),
|
|
2272
|
+
end: range && range.end ? range.end : new Date(latest)
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
function getActivePointId(points) {
|
|
2276
|
+
const highlighted = points.find((point) => point && point.highlight);
|
|
2277
|
+
if (highlighted) return highlighted.id;
|
|
2278
|
+
return points.length ? points[0].id : null;
|
|
2279
|
+
}
|
|
2280
|
+
function formatRangeLabel(rangeInfo) {
|
|
2281
|
+
if (!rangeInfo) return "";
|
|
2282
|
+
const startLabel = formatDateLabel(rangeInfo.startDate, {
|
|
2283
|
+
granularity: rangeInfo.granularity,
|
|
2284
|
+
locale: rangeInfo.locale
|
|
2285
|
+
});
|
|
2286
|
+
const endLabel = formatDateLabel(rangeInfo.endDate, {
|
|
2287
|
+
granularity: rangeInfo.granularity,
|
|
2288
|
+
locale: rangeInfo.locale
|
|
2289
|
+
});
|
|
2290
|
+
if (!startLabel || !endLabel) return "";
|
|
2291
|
+
if (startLabel === endLabel) return startLabel;
|
|
2292
|
+
return `${startLabel} \u2013 ${endLabel}`;
|
|
2293
|
+
}
|
|
2294
|
+
function sanitizePoints(points) {
|
|
2295
|
+
if (!Array.isArray(points)) return [];
|
|
2296
|
+
return points.map((point, index) => {
|
|
2297
|
+
if (!point) return null;
|
|
2298
|
+
const meta = point.meta || {};
|
|
2299
|
+
const timestamp = Number(meta.timestamp);
|
|
2300
|
+
const manifests = Array.isArray(point.manifests) ? point.manifests.map((manifest) => manifest ? { ...manifest } : null).filter(Boolean) : [];
|
|
2301
|
+
const resources = Array.isArray(point.resources) ? point.resources.filter(Boolean) : [];
|
|
2302
|
+
return {
|
|
2303
|
+
...point,
|
|
2304
|
+
id: point.id || `timeline-point-${index}`,
|
|
2305
|
+
title: point.title || point.label || `Point ${index + 1}`,
|
|
2306
|
+
summary: point.summary || point.description || "",
|
|
2307
|
+
detailsHtml: point.detailsHtml || "",
|
|
2308
|
+
highlight: !!point.highlight,
|
|
2309
|
+
side: point.side === "left" || point.side === "right" ? point.side : null,
|
|
2310
|
+
meta: {
|
|
2311
|
+
label: meta.label || "",
|
|
2312
|
+
timestamp: Number.isFinite(timestamp) ? timestamp : null
|
|
2313
|
+
},
|
|
2314
|
+
manifests,
|
|
2315
|
+
resources
|
|
2316
|
+
};
|
|
2317
|
+
}).filter(Boolean);
|
|
2318
|
+
}
|
|
2319
|
+
function TimelineConnector({ side, isActive, highlight }) {
|
|
2320
|
+
const connectorClasses = [
|
|
2321
|
+
"canopy-timeline__connector",
|
|
2322
|
+
side === "left" ? "canopy-timeline__connector--left" : "canopy-timeline__connector--right"
|
|
2323
|
+
].filter(Boolean).join(" ");
|
|
2324
|
+
const dotClasses = [
|
|
2325
|
+
"canopy-timeline__connector-dot",
|
|
2326
|
+
highlight || isActive ? "is-active" : ""
|
|
2327
|
+
].filter(Boolean).join(" ");
|
|
2328
|
+
return /* @__PURE__ */ React31.createElement("span", { className: connectorClasses, "aria-hidden": "true" }, side === "left" ? /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__connector-line" }), /* @__PURE__ */ React31.createElement("span", { className: dotClasses })) : /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement("span", { className: dotClasses }), /* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__connector-line" })));
|
|
2329
|
+
}
|
|
2330
|
+
function renderResourceSection(point) {
|
|
2331
|
+
if (!point) return null;
|
|
2332
|
+
const manifestCards = Array.isArray(point.manifests) ? point.manifests.filter(Boolean) : [];
|
|
2333
|
+
const legacyResources = Array.isArray(point.resources) ? point.resources.filter(Boolean) : [];
|
|
2334
|
+
if (!manifestCards.length && !legacyResources.length) return null;
|
|
2335
|
+
return /* @__PURE__ */ React31.createElement("div", { className: "canopy-timeline__resources" }, /* @__PURE__ */ React31.createElement("ul", { className: "canopy-timeline__resources-list" }, manifestCards.map((manifest) => /* @__PURE__ */ React31.createElement("li", { key: manifest.id || manifest.href }, /* @__PURE__ */ React31.createElement(
|
|
2336
|
+
TeaserCard,
|
|
2337
|
+
{
|
|
2338
|
+
href: manifest.href,
|
|
2339
|
+
title: manifest.title || manifest.href,
|
|
2340
|
+
summary: manifest.summary,
|
|
2341
|
+
metadata: Array.isArray(manifest.metadata) && manifest.metadata.length ? manifest.metadata : manifest.summary ? [manifest.summary] : [],
|
|
2342
|
+
thumbnail: manifest.thumbnail,
|
|
2343
|
+
type: manifest.type || "work"
|
|
2344
|
+
}
|
|
2345
|
+
))), legacyResources.map((resource, idx) => /* @__PURE__ */ React31.createElement("li", { key: resource.id || resource.href || `legacy-${idx}` }, /* @__PURE__ */ React31.createElement(
|
|
2346
|
+
TeaserCard,
|
|
2347
|
+
{
|
|
2348
|
+
href: resource.href,
|
|
2349
|
+
title: resource.label || resource.title || resource.href,
|
|
2350
|
+
summary: resource.summary,
|
|
2351
|
+
thumbnail: resource.thumbnail,
|
|
2352
|
+
type: resource.type || "resource"
|
|
2353
|
+
}
|
|
2354
|
+
)))));
|
|
2355
|
+
}
|
|
2356
|
+
function Timeline({
|
|
2357
|
+
className = "",
|
|
2358
|
+
title,
|
|
2359
|
+
description,
|
|
2360
|
+
range: rangeProp,
|
|
2361
|
+
locale: localeProp = "en-US",
|
|
2362
|
+
height = 640,
|
|
2363
|
+
threshold: thresholdProp = null,
|
|
2364
|
+
steps = null,
|
|
2365
|
+
points: pointsProp,
|
|
2366
|
+
__canopyTimeline: payload = null,
|
|
2367
|
+
...rest
|
|
2368
|
+
}) {
|
|
2369
|
+
const payloadPoints = payload && Array.isArray(payload.points) ? payload.points : null;
|
|
2370
|
+
const rawPoints = React31.useMemo(() => {
|
|
2371
|
+
if (Array.isArray(pointsProp) && pointsProp.length) return pointsProp;
|
|
2372
|
+
if (payloadPoints && payloadPoints.length) return payloadPoints;
|
|
2373
|
+
return [];
|
|
2374
|
+
}, [pointsProp, payloadPoints]);
|
|
2375
|
+
const sanitizedPoints = React31.useMemo(
|
|
2376
|
+
() => sanitizePoints(rawPoints),
|
|
2377
|
+
[rawPoints]
|
|
2378
|
+
);
|
|
2379
|
+
const localeValue = payload && payload.locale ? payload.locale : localeProp;
|
|
2380
|
+
const baseLocale = React31.useMemo(() => createLocale(localeValue), [localeValue]);
|
|
2381
|
+
const rangeInput = payload && payload.range ? payload.range : rangeProp || {};
|
|
2382
|
+
const rangeOverrides = React31.useMemo(
|
|
2383
|
+
() => deriveRangeOverrides(sanitizedPoints, rangeInput),
|
|
2384
|
+
[sanitizedPoints, rangeInput]
|
|
2385
|
+
);
|
|
2386
|
+
const effectiveRange = React31.useMemo(
|
|
2387
|
+
() => normalizeRange({
|
|
2388
|
+
...rangeOverrides,
|
|
2389
|
+
locale: baseLocale
|
|
2390
|
+
}),
|
|
2391
|
+
[rangeOverrides, baseLocale]
|
|
2392
|
+
);
|
|
2393
|
+
const spanStart = effectiveRange.startDate.getTime();
|
|
2394
|
+
const span = effectiveRange.span;
|
|
2395
|
+
const pointsWithPosition = React31.useMemo(() => {
|
|
2396
|
+
if (!sanitizedPoints.length) return [];
|
|
2397
|
+
return sanitizedPoints.map((point, index) => {
|
|
2398
|
+
const timestamp = point.meta.timestamp;
|
|
2399
|
+
const fallbackProgress = sanitizedPoints.length > 1 ? index / (sanitizedPoints.length - 1) : 0;
|
|
2400
|
+
const progress = Number.isFinite(timestamp) ? clampProgress((timestamp - spanStart) / span) : fallbackProgress;
|
|
2401
|
+
const side = point.side || (index % 2 === 0 ? "left" : "right");
|
|
2402
|
+
return {
|
|
2403
|
+
...point,
|
|
2404
|
+
progress,
|
|
2405
|
+
side
|
|
2406
|
+
};
|
|
2407
|
+
});
|
|
2408
|
+
}, [sanitizedPoints, spanStart, span]);
|
|
2409
|
+
const [activeId, setActiveId] = React31.useState(
|
|
2410
|
+
() => getActivePointId(pointsWithPosition)
|
|
2411
|
+
);
|
|
2412
|
+
React31.useEffect(() => {
|
|
2413
|
+
setActiveId(getActivePointId(pointsWithPosition));
|
|
2414
|
+
}, [pointsWithPosition]);
|
|
2415
|
+
const thresholdValue = typeof thresholdProp === "number" ? thresholdProp : payload && payload.threshold != null ? payload.threshold : null;
|
|
2416
|
+
const stepsValue = typeof steps === "number" ? Number(steps) : payload && typeof payload.steps === "number" ? Number(payload.steps) : null;
|
|
2417
|
+
const thresholdMs = React31.useMemo(
|
|
2418
|
+
() => getThresholdMs(thresholdValue, effectiveRange.granularity),
|
|
2419
|
+
[thresholdValue, effectiveRange.granularity]
|
|
2420
|
+
);
|
|
2421
|
+
const groupedEntries = React31.useMemo(
|
|
2422
|
+
() => buildGroupedEntries(pointsWithPosition, thresholdMs, {
|
|
2423
|
+
granularity: effectiveRange.granularity,
|
|
2424
|
+
locale: baseLocale
|
|
2425
|
+
}),
|
|
2426
|
+
[pointsWithPosition, thresholdMs, effectiveRange.granularity, baseLocale]
|
|
2427
|
+
);
|
|
2428
|
+
const [expandedGroupIds, setExpandedGroupIds] = React31.useState(() => /* @__PURE__ */ new Set());
|
|
2429
|
+
React31.useEffect(() => {
|
|
2430
|
+
setExpandedGroupIds((prev) => {
|
|
2431
|
+
if (!prev || prev.size === 0) return prev;
|
|
2432
|
+
const validIds = new Set(
|
|
2433
|
+
groupedEntries.filter((entry) => entry.type === "group").map((entry) => entry.id)
|
|
2434
|
+
);
|
|
2435
|
+
const next = /* @__PURE__ */ new Set();
|
|
2436
|
+
let changed = false;
|
|
2437
|
+
prev.forEach((id) => {
|
|
2438
|
+
if (validIds.has(id)) next.add(id);
|
|
2439
|
+
else changed = true;
|
|
2440
|
+
});
|
|
2441
|
+
return changed ? next : prev;
|
|
2442
|
+
});
|
|
2443
|
+
}, [groupedEntries]);
|
|
2444
|
+
const toggleGroup = React31.useCallback((groupId) => {
|
|
2445
|
+
setExpandedGroupIds((prev) => {
|
|
2446
|
+
const next = new Set(prev || []);
|
|
2447
|
+
if (next.has(groupId)) next.delete(groupId);
|
|
2448
|
+
else next.add(groupId);
|
|
2449
|
+
return next;
|
|
2450
|
+
});
|
|
2451
|
+
}, []);
|
|
2452
|
+
const resolvedHeight = Number.isFinite(Number(height)) ? Number(height) : 640;
|
|
2453
|
+
const trackHeight = Math.max(resolvedHeight, pointsWithPosition.length * 220);
|
|
2454
|
+
const containerClasses = ["canopy-timeline", className].filter(Boolean).join(" ");
|
|
2455
|
+
const rangeLabel = formatRangeLabel(effectiveRange);
|
|
2456
|
+
function renderPointEntry(point) {
|
|
2457
|
+
if (!point) return null;
|
|
2458
|
+
const wrapperClasses = [
|
|
2459
|
+
"canopy-timeline__point-wrapper",
|
|
2460
|
+
point.side === "left" ? "canopy-timeline__point-wrapper--left" : "canopy-timeline__point-wrapper--right"
|
|
2461
|
+
].filter(Boolean).join(" ");
|
|
2462
|
+
const wrapperStyle = { top: `calc(${point.progress * 100}% - 1rem)` };
|
|
2463
|
+
const cardClasses = [
|
|
2464
|
+
"canopy-timeline__point",
|
|
2465
|
+
point.id === activeId ? "is-active" : "",
|
|
2466
|
+
point.highlight ? "is-highlighted" : ""
|
|
2467
|
+
].filter(Boolean).join(" ");
|
|
2468
|
+
const connector = /* @__PURE__ */ React31.createElement(
|
|
2469
|
+
TimelineConnector,
|
|
2470
|
+
{
|
|
2471
|
+
side: point.side,
|
|
2472
|
+
isActive: point.id === activeId,
|
|
2473
|
+
highlight: point.highlight
|
|
2474
|
+
}
|
|
2475
|
+
);
|
|
2476
|
+
const body = /* @__PURE__ */ React31.createElement("div", { className: "canopy-timeline__point-body" }, /* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__point-date" }, point.meta.label), /* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__point-title" }, point.title), point.summary ? /* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__point-summary" }, point.summary) : null);
|
|
2477
|
+
const resourceSection = renderResourceSection(point);
|
|
2478
|
+
return /* @__PURE__ */ React31.createElement(
|
|
2479
|
+
"div",
|
|
2480
|
+
{
|
|
2481
|
+
key: point.id,
|
|
2482
|
+
className: wrapperClasses,
|
|
2483
|
+
style: wrapperStyle,
|
|
2484
|
+
role: "listitem"
|
|
2485
|
+
},
|
|
2486
|
+
point.side === "left" ? /* @__PURE__ */ React31.createElement(React31.Fragment, null, /* @__PURE__ */ React31.createElement("div", { className: cardClasses }, body, resourceSection), connector) : /* @__PURE__ */ React31.createElement(React31.Fragment, null, connector, /* @__PURE__ */ React31.createElement("div", { className: cardClasses }, body, resourceSection))
|
|
2487
|
+
);
|
|
2488
|
+
}
|
|
2489
|
+
function renderGroupEntry(entry) {
|
|
2490
|
+
const wrapperClasses = [
|
|
2491
|
+
"canopy-timeline__point-wrapper",
|
|
2492
|
+
entry.side === "left" ? "canopy-timeline__point-wrapper--left" : "canopy-timeline__point-wrapper--right"
|
|
2493
|
+
].filter(Boolean).join(" ");
|
|
2494
|
+
const wrapperStyle = { top: `calc(${entry.progress * 100}% - 1rem)` };
|
|
2495
|
+
const isExpanded = expandedGroupIds.has(entry.id);
|
|
2496
|
+
const hasActivePoint = entry.points.some((point) => point.id === activeId);
|
|
2497
|
+
const connector = /* @__PURE__ */ React31.createElement(
|
|
2498
|
+
TimelineConnector,
|
|
2499
|
+
{
|
|
2500
|
+
side: entry.side,
|
|
2501
|
+
isActive: hasActivePoint,
|
|
2502
|
+
highlight: hasActivePoint
|
|
2503
|
+
}
|
|
2504
|
+
);
|
|
2505
|
+
const groupClasses = [
|
|
2506
|
+
"canopy-timeline__group",
|
|
2507
|
+
isExpanded ? "is-expanded" : "",
|
|
2508
|
+
hasActivePoint ? "is-active" : ""
|
|
2509
|
+
].filter(Boolean).join(" ");
|
|
2510
|
+
const countLabel = `${entry.count} event${entry.count > 1 ? "s" : ""}`;
|
|
2511
|
+
const header = /* @__PURE__ */ React31.createElement("div", { className: "canopy-timeline__group-header" }, /* @__PURE__ */ React31.createElement("div", { className: "canopy-timeline__group-summary" }, /* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__group-count" }, countLabel), /* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__group-range" }, entry.label)), /* @__PURE__ */ React31.createElement(
|
|
2512
|
+
"button",
|
|
2513
|
+
{
|
|
2514
|
+
type: "button",
|
|
2515
|
+
className: "canopy-timeline__group-toggle",
|
|
2516
|
+
"aria-expanded": isExpanded ? "true" : "false",
|
|
2517
|
+
onClick: () => toggleGroup(entry.id)
|
|
2518
|
+
},
|
|
2519
|
+
isExpanded ? "Hide details" : "Show details"
|
|
2520
|
+
));
|
|
2521
|
+
const groupPoints = isExpanded ? /* @__PURE__ */ React31.createElement("div", { className: "canopy-timeline__group-points" }, entry.points.map((point) => /* @__PURE__ */ React31.createElement(
|
|
2522
|
+
"button",
|
|
2523
|
+
{
|
|
2524
|
+
key: point.id,
|
|
2525
|
+
type: "button",
|
|
2526
|
+
className: [
|
|
2527
|
+
"canopy-timeline__group-point",
|
|
2528
|
+
point.id === activeId ? "is-active" : ""
|
|
2529
|
+
].filter(Boolean).join(" "),
|
|
2530
|
+
onClick: () => setActiveId(point.id)
|
|
2531
|
+
},
|
|
2532
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__group-point-date" }, point.meta.label),
|
|
2533
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__group-point-title" }, point.title)
|
|
2534
|
+
))) : null;
|
|
2535
|
+
const groupCard = /* @__PURE__ */ React31.createElement("div", { className: groupClasses }, header, groupPoints);
|
|
2536
|
+
return /* @__PURE__ */ React31.createElement(
|
|
2537
|
+
"div",
|
|
2538
|
+
{
|
|
2539
|
+
key: entry.id,
|
|
2540
|
+
className: wrapperClasses,
|
|
2541
|
+
style: wrapperStyle,
|
|
2542
|
+
role: "listitem"
|
|
2543
|
+
},
|
|
2544
|
+
entry.side === "left" ? /* @__PURE__ */ React31.createElement(React31.Fragment, null, groupCard, connector) : /* @__PURE__ */ React31.createElement(React31.Fragment, null, connector, groupCard)
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
return /* @__PURE__ */ React31.createElement("section", { className: containerClasses, ...rest }, title ? /* @__PURE__ */ React31.createElement("h2", { className: "canopy-timeline__title" }, title) : null, description ? /* @__PURE__ */ React31.createElement("p", { className: "canopy-timeline__description" }, description) : null, rangeLabel ? /* @__PURE__ */ React31.createElement("p", { className: "canopy-timeline__range", "aria-live": "polite" }, rangeLabel) : null, /* @__PURE__ */ React31.createElement("div", { className: "canopy-timeline__body" }, /* @__PURE__ */ React31.createElement(
|
|
2548
|
+
"div",
|
|
2549
|
+
{
|
|
2550
|
+
className: "canopy-timeline__list",
|
|
2551
|
+
role: "list",
|
|
2552
|
+
style: { minHeight: `${trackHeight}px` }
|
|
2553
|
+
},
|
|
2554
|
+
/* @__PURE__ */ React31.createElement("div", { className: "canopy-timeline__spine", "aria-hidden": "true" }),
|
|
2555
|
+
renderSteps(stepsValue, effectiveRange),
|
|
2556
|
+
groupedEntries.map((entry) => {
|
|
2557
|
+
if (entry.type === "group") return renderGroupEntry(entry);
|
|
2558
|
+
return renderPointEntry(entry.point);
|
|
2559
|
+
})
|
|
2560
|
+
)));
|
|
2561
|
+
}
|
|
2562
|
+
function renderSteps(stepSize, range) {
|
|
2563
|
+
if (!Number.isFinite(stepSize) || stepSize <= 0 || !range) return null;
|
|
2564
|
+
const startYear = range.startDate.getUTCFullYear();
|
|
2565
|
+
const endYear = range.endDate.getUTCFullYear();
|
|
2566
|
+
const markers = [];
|
|
2567
|
+
if (startYear < endYear) {
|
|
2568
|
+
markers.push(
|
|
2569
|
+
/* @__PURE__ */ React31.createElement(
|
|
2570
|
+
"span",
|
|
2571
|
+
{
|
|
2572
|
+
key: "timeline-step-start",
|
|
2573
|
+
className: "canopy-timeline__step canopy-timeline__step--edge",
|
|
2574
|
+
style: { top: "0%" },
|
|
2575
|
+
"aria-hidden": "true"
|
|
2576
|
+
},
|
|
2577
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__step-line" }),
|
|
2578
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__step-label" }, startYear)
|
|
2579
|
+
)
|
|
2580
|
+
);
|
|
2581
|
+
markers.push(
|
|
2582
|
+
/* @__PURE__ */ React31.createElement(
|
|
2583
|
+
"span",
|
|
2584
|
+
{
|
|
2585
|
+
key: "timeline-step-end",
|
|
2586
|
+
className: "canopy-timeline__step canopy-timeline__step--edge",
|
|
2587
|
+
style: { top: "calc(100% - 1px)" },
|
|
2588
|
+
"aria-hidden": "true"
|
|
2589
|
+
},
|
|
2590
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__step-line" }),
|
|
2591
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__step-label" }, endYear)
|
|
2592
|
+
)
|
|
2593
|
+
);
|
|
2594
|
+
}
|
|
2595
|
+
const baseYear = Math.ceil(startYear / stepSize) * stepSize;
|
|
2596
|
+
for (let year = baseYear; year <= endYear; year += stepSize) {
|
|
2597
|
+
const timestamp = Date.UTC(year, 0, 1);
|
|
2598
|
+
const progress = (timestamp - range.startDate.getTime()) / range.span;
|
|
2599
|
+
if (progress <= 0 || progress >= 1) continue;
|
|
2600
|
+
markers.push(
|
|
2601
|
+
/* @__PURE__ */ React31.createElement(
|
|
2602
|
+
"span",
|
|
2603
|
+
{
|
|
2604
|
+
key: `timeline-step-${year}`,
|
|
2605
|
+
className: "canopy-timeline__step",
|
|
2606
|
+
style: { top: `calc(${progress * 100}% - 0.5px)` },
|
|
2607
|
+
"aria-hidden": "true"
|
|
2608
|
+
},
|
|
2609
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__step-line" }),
|
|
2610
|
+
/* @__PURE__ */ React31.createElement("span", { className: "canopy-timeline__step-label" }, year)
|
|
2611
|
+
)
|
|
2612
|
+
);
|
|
2613
|
+
}
|
|
2614
|
+
return markers.length ? markers : null;
|
|
2615
|
+
}
|
|
2616
|
+
|
|
2617
|
+
// ui/src/content/timeline/TimelinePoint.jsx
|
|
2618
|
+
function TimelinePoint() {
|
|
2619
|
+
return null;
|
|
1990
2620
|
}
|
|
2621
|
+
TimelinePoint.displayName = "TimelinePoint";
|
|
1991
2622
|
export {
|
|
1992
2623
|
ArticleCard,
|
|
1993
2624
|
Button,
|
|
@@ -2017,6 +2648,9 @@ export {
|
|
|
2017
2648
|
MdxSearchTabs as SearchTabs,
|
|
2018
2649
|
SearchTabs as SearchTabsUI,
|
|
2019
2650
|
Slider,
|
|
2651
|
+
TeaserCard,
|
|
2652
|
+
Timeline,
|
|
2653
|
+
TimelinePoint,
|
|
2020
2654
|
Viewer
|
|
2021
2655
|
};
|
|
2022
2656
|
//# sourceMappingURL=index.mjs.map
|