@canopy-iiif/app 0.10.24 → 0.10.26
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/mdx.js +366 -182
- package/lib/page-context.js +15 -5
- package/package.json +1 -1
- package/ui/dist/index.mjs +126 -7
- package/ui/dist/index.mjs.map +4 -4
- package/ui/dist/server.mjs +71 -4
- package/ui/dist/server.mjs.map +4 -4
- package/ui/styles/components/_card.scss +0 -1
- package/ui/styles/components/_diagram.scss +75 -0
- package/ui/styles/components/header/_navbar.scss +55 -0
- package/ui/styles/components/iiif/_image.scss +31 -1
- package/ui/styles/components/iiif/_slider.scss +91 -0
- package/ui/styles/components/iiif/index.scss +1 -0
- package/ui/styles/components/index.scss +1 -0
- package/ui/styles/components/search/_tabs.scss +36 -0
- package/ui/styles/components/search/index.scss +4 -3
- package/ui/styles/index.css +236 -1
package/lib/page-context.js
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
const React = require('react');
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const CONTEXT_KEY =
|
|
4
|
+
typeof Symbol === 'function' ? Symbol.for('__CANOPY_PAGE_CONTEXT__') : '__CANOPY_PAGE_CONTEXT__';
|
|
5
|
+
|
|
6
|
+
function getGlobalRoot() {
|
|
7
|
+
if (typeof globalThis !== 'undefined') return globalThis;
|
|
8
|
+
if (typeof global !== 'undefined') return global;
|
|
9
|
+
if (typeof window !== 'undefined') return window;
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
function getPageContext() {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
14
|
+
const root = getGlobalRoot();
|
|
15
|
+
if (root[CONTEXT_KEY]) return root[CONTEXT_KEY];
|
|
16
|
+
const ctx = React.createContext({ navigation: null, page: null });
|
|
17
|
+
root[CONTEXT_KEY] = ctx;
|
|
18
|
+
return ctx;
|
|
10
19
|
}
|
|
11
20
|
|
|
12
21
|
module.exports = {
|
|
13
22
|
getPageContext,
|
|
23
|
+
CONTEXT_KEY,
|
|
14
24
|
};
|
package/package.json
CHANGED
package/ui/dist/index.mjs
CHANGED
|
@@ -958,12 +958,50 @@ function HeaderScript() {
|
|
|
958
958
|
}
|
|
959
959
|
);
|
|
960
960
|
}
|
|
961
|
+
var CONTEXT_KEY = typeof Symbol === "function" ? Symbol.for("__CANOPY_PAGE_CONTEXT__") : "__CANOPY_PAGE_CONTEXT__";
|
|
962
|
+
function getSharedRoot() {
|
|
963
|
+
if (typeof globalThis !== "undefined") return globalThis;
|
|
964
|
+
if (typeof window !== "undefined") return window;
|
|
965
|
+
if (typeof global !== "undefined") return global;
|
|
966
|
+
return null;
|
|
967
|
+
}
|
|
968
|
+
function getSafePageContext() {
|
|
969
|
+
const root = getSharedRoot();
|
|
970
|
+
if (root && root[CONTEXT_KEY]) return root[CONTEXT_KEY];
|
|
971
|
+
const ctx = React14.createContext({ navigation: null, page: null });
|
|
972
|
+
if (root) root[CONTEXT_KEY] = ctx;
|
|
973
|
+
return ctx;
|
|
974
|
+
}
|
|
961
975
|
function ensureArray(navLinks) {
|
|
962
976
|
if (!Array.isArray(navLinks)) return [];
|
|
963
977
|
return navLinks.filter(
|
|
964
978
|
(link) => link && typeof link === "object" && typeof link.href === "string"
|
|
965
979
|
);
|
|
966
980
|
}
|
|
981
|
+
function SectionNavList({ root }) {
|
|
982
|
+
if (!root || !Array.isArray(root.children) || !root.children.length) return null;
|
|
983
|
+
return /* @__PURE__ */ React14.createElement("ul", { className: "canopy-modal__section-list", role: "list" }, root.children.map((node) => /* @__PURE__ */ React14.createElement(SectionNavItem, { key: node.slug || node.href || node.title, node, depth: 0 })));
|
|
984
|
+
}
|
|
985
|
+
function SectionNavItem({ node, depth }) {
|
|
986
|
+
if (!node) return null;
|
|
987
|
+
const hasChildren = Array.isArray(node.children) && node.children.length > 0;
|
|
988
|
+
const Tag = node.href ? "a" : "span";
|
|
989
|
+
const classes = [
|
|
990
|
+
"canopy-modal__section-link",
|
|
991
|
+
`depth-${Math.min(5, Math.max(0, depth + 1))}`
|
|
992
|
+
];
|
|
993
|
+
if (!node.href) classes.push("is-label");
|
|
994
|
+
if (node.isActive) classes.push("is-active");
|
|
995
|
+
return /* @__PURE__ */ React14.createElement("li", { className: "canopy-modal__section-item", "data-depth": depth }, /* @__PURE__ */ React14.createElement(
|
|
996
|
+
Tag,
|
|
997
|
+
{
|
|
998
|
+
className: classes.join(" "),
|
|
999
|
+
href: node.href || void 0,
|
|
1000
|
+
"aria-current": node.isActive ? "page" : void 0
|
|
1001
|
+
},
|
|
1002
|
+
node.title || node.slug
|
|
1003
|
+
), hasChildren ? /* @__PURE__ */ React14.createElement("ul", { className: "canopy-modal__section-list canopy-modal__section-list--nested", role: "list" }, node.children.map((child) => /* @__PURE__ */ React14.createElement(SectionNavItem, { key: child.slug || child.href || child.title, node: child, depth: depth + 1 }))) : null);
|
|
1004
|
+
}
|
|
967
1005
|
function CanopyHeader(props = {}) {
|
|
968
1006
|
const {
|
|
969
1007
|
navigation: navLinksProp,
|
|
@@ -975,6 +1013,13 @@ function CanopyHeader(props = {}) {
|
|
|
975
1013
|
logo: SiteLogo
|
|
976
1014
|
} = props;
|
|
977
1015
|
const navLinks = ensureArray(navLinksProp);
|
|
1016
|
+
const PageContext = getSafePageContext();
|
|
1017
|
+
const context = React14.useContext(PageContext);
|
|
1018
|
+
const sectionNavigation = context && context.navigation && context.navigation.root ? context.navigation : null;
|
|
1019
|
+
const sectionHeading = sectionNavigation && sectionNavigation.title || (sectionNavigation && sectionNavigation.root ? sectionNavigation.root.title : "");
|
|
1020
|
+
const hasSectionNav = !!(sectionNavigation && sectionNavigation.root && Array.isArray(sectionNavigation.root.children) && sectionNavigation.root.children.length);
|
|
1021
|
+
const sectionLabel = sectionHeading ? `More in ${sectionHeading}` : "More in this section";
|
|
1022
|
+
const sectionAriaLabel = sectionHeading ? `${sectionHeading} section navigation` : "Section navigation";
|
|
978
1023
|
return /* @__PURE__ */ React14.createElement(React14.Fragment, null, /* @__PURE__ */ React14.createElement(
|
|
979
1024
|
"header",
|
|
980
1025
|
{
|
|
@@ -1085,7 +1130,16 @@ function CanopyHeader(props = {}) {
|
|
|
1085
1130
|
"aria-label": "Primary navigation"
|
|
1086
1131
|
},
|
|
1087
1132
|
navLinks.map((link) => /* @__PURE__ */ React14.createElement("a", { key: link.href, href: link.href }, link.label || link.href))
|
|
1088
|
-
)
|
|
1133
|
+
),
|
|
1134
|
+
hasSectionNav ? /* @__PURE__ */ React14.createElement(
|
|
1135
|
+
"nav",
|
|
1136
|
+
{
|
|
1137
|
+
className: "canopy-modal__section-nav",
|
|
1138
|
+
"aria-label": sectionAriaLabel
|
|
1139
|
+
},
|
|
1140
|
+
/* @__PURE__ */ React14.createElement("div", { className: "canopy-modal__section-label" }, sectionLabel),
|
|
1141
|
+
/* @__PURE__ */ React14.createElement(SectionNavList, { root: sectionNavigation.root })
|
|
1142
|
+
) : null
|
|
1089
1143
|
), /* @__PURE__ */ React14.createElement(
|
|
1090
1144
|
CanopyModal,
|
|
1091
1145
|
{
|
|
@@ -1212,7 +1266,7 @@ var Slider = (props) => {
|
|
|
1212
1266
|
} catch (_) {
|
|
1213
1267
|
json = "{}";
|
|
1214
1268
|
}
|
|
1215
|
-
return /* @__PURE__ */ React17.createElement("div", { "data-canopy-slider": "1"
|
|
1269
|
+
return /* @__PURE__ */ React17.createElement("div", { className: "canopy-slider", "data-canopy-slider": "1" }, /* @__PURE__ */ React17.createElement(
|
|
1216
1270
|
"script",
|
|
1217
1271
|
{
|
|
1218
1272
|
type: "application/json",
|
|
@@ -1220,7 +1274,7 @@ var Slider = (props) => {
|
|
|
1220
1274
|
}
|
|
1221
1275
|
));
|
|
1222
1276
|
}
|
|
1223
|
-
return /* @__PURE__ */ React17.createElement(CloverSlider, { ...props });
|
|
1277
|
+
return /* @__PURE__ */ React17.createElement(CloverSlider, { ...props, className: "canopy-slider" });
|
|
1224
1278
|
};
|
|
1225
1279
|
|
|
1226
1280
|
// ui/src/iiif/Scroll.jsx
|
|
@@ -1325,7 +1379,13 @@ function MdxRelatedItems(props) {
|
|
|
1325
1379
|
} catch (_) {
|
|
1326
1380
|
json = "{}";
|
|
1327
1381
|
}
|
|
1328
|
-
return /* @__PURE__ */ React20.createElement("div", { "data-canopy-related-items": "1"
|
|
1382
|
+
return /* @__PURE__ */ React20.createElement("div", { "data-canopy-related-items": "1" }, /* @__PURE__ */ React20.createElement(
|
|
1383
|
+
"script",
|
|
1384
|
+
{
|
|
1385
|
+
type: "application/json",
|
|
1386
|
+
dangerouslySetInnerHTML: { __html: json }
|
|
1387
|
+
}
|
|
1388
|
+
));
|
|
1329
1389
|
}
|
|
1330
1390
|
|
|
1331
1391
|
// ui/src/search/MdxSearchResults.jsx
|
|
@@ -1488,7 +1548,7 @@ function SearchResults({
|
|
|
1488
1548
|
}
|
|
1489
1549
|
|
|
1490
1550
|
// ui/src/search/SearchTabs.jsx
|
|
1491
|
-
import React25 from "react";
|
|
1551
|
+
import React25, { useRef as useRef2, useState as useState6, useEffect as useEffect6 } from "react";
|
|
1492
1552
|
function SearchTabs({
|
|
1493
1553
|
type = "all",
|
|
1494
1554
|
onTypeChange,
|
|
@@ -1503,13 +1563,62 @@ function SearchTabs({
|
|
|
1503
1563
|
const toLabel = (t) => t && t.length ? t.charAt(0).toUpperCase() + t.slice(1) : "";
|
|
1504
1564
|
const hasFilters = typeof onOpenFilters === "function";
|
|
1505
1565
|
const filterBadge = activeFilterCount > 0 ? ` (${activeFilterCount})` : "";
|
|
1566
|
+
const [itemBoundingBox, setItemBoundingBox] = useState6(null);
|
|
1567
|
+
const [wrapperBoundingBox, setWrapperBoundingBox] = useState6(null);
|
|
1568
|
+
const [highlightedTab, setHighlightedTab] = useState6(null);
|
|
1569
|
+
const [isHoveredFromNull, setIsHoveredFromNull] = useState6(true);
|
|
1570
|
+
const wrapperRef = useRef2(null);
|
|
1571
|
+
const highlightRef = useRef2(null);
|
|
1572
|
+
const repositionHighlight = (e, tabKey) => {
|
|
1573
|
+
if (!tabKey || !wrapperRef.current) return;
|
|
1574
|
+
const item = e.currentTarget;
|
|
1575
|
+
setItemBoundingBox(item.getBoundingClientRect());
|
|
1576
|
+
setWrapperBoundingBox(wrapperRef.current.getBoundingClientRect());
|
|
1577
|
+
setIsHoveredFromNull(!highlightedTab);
|
|
1578
|
+
setHighlightedTab(tabKey);
|
|
1579
|
+
};
|
|
1580
|
+
const resetHighlight = () => {
|
|
1581
|
+
setHighlightedTab(null);
|
|
1582
|
+
};
|
|
1583
|
+
useEffect6(() => {
|
|
1584
|
+
if (!wrapperRef.current) return;
|
|
1585
|
+
const activeButton = wrapperRef.current.querySelector(
|
|
1586
|
+
`button[data-tab-value="${type}"]`
|
|
1587
|
+
);
|
|
1588
|
+
if (!activeButton) return;
|
|
1589
|
+
const itemBox = activeButton.getBoundingClientRect();
|
|
1590
|
+
const wrapperBox = wrapperRef.current.getBoundingClientRect();
|
|
1591
|
+
setItemBoundingBox(itemBox);
|
|
1592
|
+
setWrapperBoundingBox(wrapperBox);
|
|
1593
|
+
setIsHoveredFromNull(true);
|
|
1594
|
+
setHighlightedTab(type);
|
|
1595
|
+
}, [type]);
|
|
1596
|
+
let highlightStyles = {};
|
|
1597
|
+
if (itemBoundingBox && wrapperBoundingBox) {
|
|
1598
|
+
highlightStyles = {
|
|
1599
|
+
opacity: highlightedTab ? 1 : 0,
|
|
1600
|
+
transform: `translateX(${itemBoundingBox.left - wrapperBoundingBox.left}px)`,
|
|
1601
|
+
transitionDuration: isHoveredFromNull ? "0ms" : "200ms",
|
|
1602
|
+
width: `${itemBoundingBox.width}px`
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1506
1605
|
return /* @__PURE__ */ React25.createElement("div", { className: "canopy-search-tabs-wrapper" }, /* @__PURE__ */ React25.createElement(
|
|
1507
1606
|
"div",
|
|
1508
1607
|
{
|
|
1509
1608
|
role: "tablist",
|
|
1510
1609
|
"aria-label": "Search types",
|
|
1511
|
-
className: "canopy-search-tabs"
|
|
1610
|
+
className: "canopy-search-tabs",
|
|
1611
|
+
ref: wrapperRef,
|
|
1612
|
+
onMouseLeave: resetHighlight
|
|
1512
1613
|
},
|
|
1614
|
+
/* @__PURE__ */ React25.createElement(
|
|
1615
|
+
"div",
|
|
1616
|
+
{
|
|
1617
|
+
ref: highlightRef,
|
|
1618
|
+
className: "canopy-search-tabs__highlight",
|
|
1619
|
+
style: highlightStyles
|
|
1620
|
+
}
|
|
1621
|
+
),
|
|
1513
1622
|
orderedTypes.map((t) => {
|
|
1514
1623
|
const active = String(type).toLowerCase() === String(t).toLowerCase();
|
|
1515
1624
|
const cRaw = counts && Object.prototype.hasOwnProperty.call(counts, t) ? counts[t] : void 0;
|
|
@@ -1521,7 +1630,10 @@ function SearchTabs({
|
|
|
1521
1630
|
role: "tab",
|
|
1522
1631
|
"aria-selected": active,
|
|
1523
1632
|
type: "button",
|
|
1524
|
-
|
|
1633
|
+
"data-tab-value": t,
|
|
1634
|
+
onClick: () => onTypeChange && onTypeChange(t),
|
|
1635
|
+
onMouseOver: (e) => repositionHighlight(e, t),
|
|
1636
|
+
className: `canopy-search-tab ${active ? "is-active" : ""}`
|
|
1525
1637
|
},
|
|
1526
1638
|
toLabel(t),
|
|
1527
1639
|
" (",
|
|
@@ -1749,11 +1861,18 @@ function MarkdownTable({ className = "", ...rest }) {
|
|
|
1749
1861
|
const merged = ["markdown-table", className].filter(Boolean).join(" ");
|
|
1750
1862
|
return /* @__PURE__ */ React28.createElement("div", { className: "markdown-table__frame" }, /* @__PURE__ */ React28.createElement("table", { className: merged, ...rest }));
|
|
1751
1863
|
}
|
|
1864
|
+
|
|
1865
|
+
// ui/src/docs/Diagram.jsx
|
|
1866
|
+
import React29 from "react";
|
|
1867
|
+
function CanopyDiagram() {
|
|
1868
|
+
return /* @__PURE__ */ React29.createElement("div", { className: "canopy-diagram" }, /* @__PURE__ */ React29.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--collections" }, /* @__PURE__ */ React29.createElement("h3", null, "IIIF Collection(s)"), /* @__PURE__ */ React29.createElement("span", { className: "canopy-diagram__section-summary" }, "Source collections contribute 170 total manifests that Canopy retrieves as-is via IIIF endpoints."), /* @__PURE__ */ React29.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Collection A"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "70 Manifests"), /* @__PURE__ */ React29.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React29.createElement("li", null, "Textual Annotations"))), /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Collection B"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "35 Manifests"), /* @__PURE__ */ React29.createElement("li", null, "IIIF Images + A/V"), /* @__PURE__ */ React29.createElement("li", null, "Textual Annotations"))))), /* @__PURE__ */ React29.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React29.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React29.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React29.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--build" }, /* @__PURE__ */ React29.createElement("h3", null, "Canopy Build Process"), /* @__PURE__ */ React29.createElement("span", { className: "canopy-diagram__section-summary" }, "Canopy syncs manifests, authored pages, and annotations before bundling data for the static site."), /* @__PURE__ */ React29.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Automated content"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "105 manifests \u2192 105 work pages"), /* @__PURE__ */ React29.createElement("li", null, "One page per manifest"), /* @__PURE__ */ React29.createElement("li", null, "Customize layout per work"))), /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Contextual content"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "Markdown & MDX pages"), /* @__PURE__ */ React29.createElement("li", null, "Author narratives & tours"), /* @__PURE__ */ React29.createElement("li", null, "Reference manifests inline"))), /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Search index"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "Combines works + pages"), /* @__PURE__ */ React29.createElement("li", null, "FlexSearch powered"), /* @__PURE__ */ React29.createElement("li", null, "Optional annotations"))))), /* @__PURE__ */ React29.createElement("div", { className: "canopy-diagram__arrow", "aria-hidden": "true" }, /* @__PURE__ */ React29.createElement("span", { className: "canopy-diagram__arrow-line" }), /* @__PURE__ */ React29.createElement("span", { className: "canopy-diagram__arrow-head" })), /* @__PURE__ */ React29.createElement("section", { className: "canopy-diagram__section canopy-diagram__section--output" }, /* @__PURE__ */ React29.createElement("h3", null, "Static Digital Project"), /* @__PURE__ */ React29.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__ */ React29.createElement("div", { className: "canopy-diagram__grid" }, /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Work pages"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "105 generated HTML pages"), /* @__PURE__ */ React29.createElement("li", null, "Each links back to source manifests"), /* @__PURE__ */ React29.createElement("li", null, "Styled with Canopy components"))), /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Custom pages"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "Markdown & MDX-authored content"), /* @__PURE__ */ React29.createElement("li", null, "Reusable layouts for narratives"), /* @__PURE__ */ React29.createElement("li", null, "Embed IIIF media & interstitials"))), /* @__PURE__ */ React29.createElement("article", null, /* @__PURE__ */ React29.createElement("h4", null, "Search bundle"), /* @__PURE__ */ React29.createElement("ul", null, /* @__PURE__ */ React29.createElement("li", null, "Static FlexSearch index"), /* @__PURE__ */ React29.createElement("li", null, "Works + pages share records"), /* @__PURE__ */ React29.createElement("li", null, "Optional annotation dataset"))))));
|
|
1869
|
+
}
|
|
1752
1870
|
export {
|
|
1753
1871
|
ArticleCard,
|
|
1754
1872
|
Button,
|
|
1755
1873
|
ButtonWrapper,
|
|
1756
1874
|
CanopyBrand,
|
|
1875
|
+
CanopyDiagram,
|
|
1757
1876
|
CanopyFooter,
|
|
1758
1877
|
CanopyHeader,
|
|
1759
1878
|
CanopyModal,
|