@canopy-iiif/app 0.10.6 → 0.10.7
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/server.mjs +175 -31
- package/ui/dist/server.mjs.map +2 -2
- package/ui/styles/components/_footer.scss +2 -3
- package/ui/styles/components/_layout.scss +69 -0
- package/ui/styles/components/_sub-navigation.scss +62 -19
- package/ui/styles/components/header/_header.scss +7 -9
- package/ui/styles/components/index.scss +1 -0
- package/ui/styles/index.css +130 -30
package/package.json
CHANGED
package/ui/dist/server.mjs
CHANGED
|
@@ -612,9 +612,13 @@ import navigationHelpers2 from "@canopy-iiif/app/lib/components/navigation.js";
|
|
|
612
612
|
// ui/src/layout/ContentNavigation.jsx
|
|
613
613
|
import React11 from "react";
|
|
614
614
|
var SCROLL_OFFSET_REM = 1.618;
|
|
615
|
+
var MAX_HEADING_DEPTH = 3;
|
|
615
616
|
function depthIndex(depth) {
|
|
616
617
|
return Math.max(0, Math.min(5, (depth || 1) - 1));
|
|
617
618
|
}
|
|
619
|
+
function resolveDepth(value, fallback = 1) {
|
|
620
|
+
return Math.max(1, typeof value === "number" ? value : fallback);
|
|
621
|
+
}
|
|
618
622
|
function ContentNavigation({
|
|
619
623
|
items = [],
|
|
620
624
|
className = "",
|
|
@@ -627,7 +631,11 @@ function ContentNavigation({
|
|
|
627
631
|
const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
|
|
628
632
|
const savedDepthsRef = React11.useRef(null);
|
|
629
633
|
if ((!items || !items.length) && !headingId) return null;
|
|
630
|
-
const combinedClassName = [
|
|
634
|
+
const combinedClassName = [
|
|
635
|
+
"canopy-sub-navigation canopy-content-navigation",
|
|
636
|
+
"canopy-content-navigation--collapsed",
|
|
637
|
+
className
|
|
638
|
+
].filter(Boolean).join(" ");
|
|
631
639
|
const effectiveHeading = heading || pageTitle || null;
|
|
632
640
|
const navLabel = ariaLabel || (effectiveHeading ? `${effectiveHeading} navigation` : "Section navigation");
|
|
633
641
|
const getSavedDepth = React11.useCallback(
|
|
@@ -656,7 +664,11 @@ function ContentNavigation({
|
|
|
656
664
|
const id = String(node.id);
|
|
657
665
|
if (seen.has(id)) return;
|
|
658
666
|
seen.add(id);
|
|
659
|
-
const depth =
|
|
667
|
+
const depth = resolveDepth(
|
|
668
|
+
typeof node.depth === "number" ? node.depth : node.level,
|
|
669
|
+
getSavedDepth(id, 2)
|
|
670
|
+
);
|
|
671
|
+
if (depth > MAX_HEADING_DEPTH) return;
|
|
660
672
|
entries.push({ id, depth });
|
|
661
673
|
if (node.children && node.children.length) pushNodes(node.children);
|
|
662
674
|
});
|
|
@@ -772,9 +784,14 @@ function ContentNavigation({
|
|
|
772
784
|
return nodes.map((node) => {
|
|
773
785
|
if (!node) return null;
|
|
774
786
|
const id = node.id ? String(node.id) : "";
|
|
775
|
-
const depth =
|
|
787
|
+
const depth = resolveDepth(
|
|
788
|
+
typeof node.depth === "number" ? node.depth : node.level,
|
|
789
|
+
getSavedDepth(id, 2)
|
|
790
|
+
);
|
|
791
|
+
if (depth > MAX_HEADING_DEPTH) return null;
|
|
776
792
|
const idx = depthIndex(depth);
|
|
777
793
|
const isActive = id && activeId === id;
|
|
794
|
+
const childNodes = depth < MAX_HEADING_DEPTH ? renderNodes2(node.children) : null;
|
|
778
795
|
return /* @__PURE__ */ React11.createElement("li", { key: id || node.title, className: "canopy-sub-navigation__item", "data-depth": idx }, /* @__PURE__ */ React11.createElement(
|
|
779
796
|
"a",
|
|
780
797
|
{
|
|
@@ -784,19 +801,19 @@ function ContentNavigation({
|
|
|
784
801
|
"aria-current": isActive ? "location" : void 0
|
|
785
802
|
},
|
|
786
803
|
node.title
|
|
787
|
-
),
|
|
804
|
+
), childNodes ? /* @__PURE__ */ React11.createElement(
|
|
788
805
|
"ul",
|
|
789
806
|
{
|
|
790
807
|
className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested",
|
|
791
808
|
role: "list"
|
|
792
809
|
},
|
|
793
|
-
|
|
810
|
+
childNodes
|
|
794
811
|
) : null);
|
|
795
812
|
});
|
|
796
813
|
},
|
|
797
814
|
[handleAnchorClick, activeId, getSavedDepth]
|
|
798
815
|
);
|
|
799
|
-
const nestedItems = renderNodes2(items);
|
|
816
|
+
const nestedItems = React11.useMemo(() => renderNodes2(items), [items, renderNodes2]);
|
|
800
817
|
const topLink = headingId ? /* @__PURE__ */ React11.createElement("li", { className: "canopy-sub-navigation__item", "data-depth": 0 }, /* @__PURE__ */ React11.createElement(
|
|
801
818
|
"a",
|
|
802
819
|
{
|
|
@@ -814,7 +831,32 @@ function ContentNavigation({
|
|
|
814
831
|
},
|
|
815
832
|
nestedItems
|
|
816
833
|
) : null) : null;
|
|
817
|
-
return /* @__PURE__ */ React11.createElement(
|
|
834
|
+
return /* @__PURE__ */ React11.createElement(
|
|
835
|
+
"nav",
|
|
836
|
+
{
|
|
837
|
+
className: combinedClassName,
|
|
838
|
+
style,
|
|
839
|
+
"aria-label": navLabel,
|
|
840
|
+
"data-canopy-content-nav": "true"
|
|
841
|
+
},
|
|
842
|
+
/* @__PURE__ */ React11.createElement(
|
|
843
|
+
"button",
|
|
844
|
+
{
|
|
845
|
+
type: "button",
|
|
846
|
+
className: "canopy-content-navigation__toggle",
|
|
847
|
+
"aria-expanded": "false",
|
|
848
|
+
"aria-label": "Show section navigation",
|
|
849
|
+
title: "Show section navigation",
|
|
850
|
+
"data-canopy-content-nav-toggle": "true",
|
|
851
|
+
"data-show-label": "Show",
|
|
852
|
+
"data-hide-label": "Hide",
|
|
853
|
+
"data-show-full-label": "Show section navigation",
|
|
854
|
+
"data-hide-full-label": "Hide section navigation"
|
|
855
|
+
},
|
|
856
|
+
"Show"
|
|
857
|
+
),
|
|
858
|
+
/* @__PURE__ */ React11.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, topLink || nestedItems)
|
|
859
|
+
);
|
|
818
860
|
}
|
|
819
861
|
|
|
820
862
|
// ui/src/layout/Layout.jsx
|
|
@@ -853,6 +895,106 @@ function buildNavigationAside(sidebar, className) {
|
|
|
853
895
|
}
|
|
854
896
|
return sidebar;
|
|
855
897
|
}
|
|
898
|
+
function ContentNavigationScript() {
|
|
899
|
+
const code = `
|
|
900
|
+
(function () {
|
|
901
|
+
if (typeof window === 'undefined') return;
|
|
902
|
+
if (window.__CANOPY_CONTENT_NAV_READY__) return;
|
|
903
|
+
window.__CANOPY_CONTENT_NAV_READY__ = true;
|
|
904
|
+
var STORAGE_KEY = 'canopy_content_nav_collapsed';
|
|
905
|
+
var storage = null;
|
|
906
|
+
try {
|
|
907
|
+
storage = window.localStorage;
|
|
908
|
+
} catch (error) {
|
|
909
|
+
storage = null;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function setStored(value) {
|
|
913
|
+
if (!storage) return;
|
|
914
|
+
try {
|
|
915
|
+
if (value == null) {
|
|
916
|
+
storage.removeItem(STORAGE_KEY);
|
|
917
|
+
} else {
|
|
918
|
+
storage.setItem(STORAGE_KEY, value);
|
|
919
|
+
}
|
|
920
|
+
} catch (error) {}
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
function getStored() {
|
|
924
|
+
if (!storage) return null;
|
|
925
|
+
try {
|
|
926
|
+
return storage.getItem(STORAGE_KEY);
|
|
927
|
+
} catch (error) {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
function ready(fn) {
|
|
933
|
+
if (!fn) return;
|
|
934
|
+
if (document.readyState === 'loading') {
|
|
935
|
+
document.addEventListener('DOMContentLoaded', fn, { once: true });
|
|
936
|
+
} else {
|
|
937
|
+
fn();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
function applyState(root, collapsed) {
|
|
942
|
+
if (!root) return;
|
|
943
|
+
var isCollapsed = !!collapsed;
|
|
944
|
+
root.classList.toggle('is-collapsed', isCollapsed);
|
|
945
|
+
var layout = root.closest('.canopy-layout');
|
|
946
|
+
if (layout) layout.classList.toggle('canopy-layout--content-nav-collapsed', isCollapsed);
|
|
947
|
+
var nav = root.querySelector('[data-canopy-content-nav]');
|
|
948
|
+
if (nav) nav.classList.toggle('canopy-content-navigation--collapsed', isCollapsed);
|
|
949
|
+
var toggle = root.querySelector('[data-canopy-content-nav-toggle]');
|
|
950
|
+
if (toggle) {
|
|
951
|
+
var showLabel = toggle.getAttribute('data-show-label') || 'Show';
|
|
952
|
+
var hideLabel = toggle.getAttribute('data-hide-label') || 'Hide';
|
|
953
|
+
var showFull = toggle.getAttribute('data-show-full-label') || 'Show section navigation';
|
|
954
|
+
var hideFull = toggle.getAttribute('data-hide-full-label') || 'Hide section navigation';
|
|
955
|
+
toggle.textContent = isCollapsed ? showLabel : hideLabel;
|
|
956
|
+
toggle.setAttribute('aria-expanded', isCollapsed ? 'false' : 'true');
|
|
957
|
+
toggle.setAttribute('aria-label', isCollapsed ? showFull : hideFull);
|
|
958
|
+
toggle.setAttribute('title', isCollapsed ? showFull : hideFull);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
ready(function () {
|
|
963
|
+
var roots = Array.prototype.slice.call(
|
|
964
|
+
document.querySelectorAll('[data-canopy-content-nav-root]')
|
|
965
|
+
);
|
|
966
|
+
if (!roots.length) return;
|
|
967
|
+
var stored = getStored();
|
|
968
|
+
var collapsed = true;
|
|
969
|
+
if (stored === '0' || stored === 'false') {
|
|
970
|
+
collapsed = false;
|
|
971
|
+
} else if (stored === '1' || stored === 'true') {
|
|
972
|
+
collapsed = true;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
function sync(next) {
|
|
976
|
+
collapsed = !!next;
|
|
977
|
+
roots.forEach(function (root) {
|
|
978
|
+
applyState(root, collapsed);
|
|
979
|
+
});
|
|
980
|
+
setStored(collapsed ? '1' : '0');
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
sync(collapsed);
|
|
984
|
+
|
|
985
|
+
roots.forEach(function (root) {
|
|
986
|
+
var toggle = root.querySelector('[data-canopy-content-nav-toggle]');
|
|
987
|
+
if (!toggle) return;
|
|
988
|
+
toggle.addEventListener('click', function (event) {
|
|
989
|
+
event.preventDefault();
|
|
990
|
+
sync(!collapsed);
|
|
991
|
+
});
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
})();
|
|
995
|
+
`;
|
|
996
|
+
return /* @__PURE__ */ React12.createElement("script", { dangerouslySetInnerHTML: { __html: code } });
|
|
997
|
+
}
|
|
856
998
|
function Layout({
|
|
857
999
|
children,
|
|
858
1000
|
sidebar,
|
|
@@ -892,38 +1034,40 @@ function Layout({
|
|
|
892
1034
|
const showLeftColumn = navigation !== false;
|
|
893
1035
|
const hasContentNavigation = navigation !== false && contentNavigation !== false && headingTree.length > 0;
|
|
894
1036
|
const containerClassName = (() => {
|
|
895
|
-
const classes = ["
|
|
896
|
-
classes.push(
|
|
897
|
-
|
|
898
|
-
)
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
1037
|
+
const classes = ["canopy-layout"];
|
|
1038
|
+
classes.push(fluid ? "canopy-layout--fluid" : "canopy-layout--fixed");
|
|
1039
|
+
if (showLeftColumn) classes.push("canopy-layout--with-sidebar");
|
|
1040
|
+
if (hasContentNavigation) {
|
|
1041
|
+
classes.push("canopy-layout--with-content-nav");
|
|
1042
|
+
classes.push("canopy-layout--content-nav-collapsed");
|
|
1043
|
+
}
|
|
902
1044
|
if (className) classes.push(className);
|
|
903
1045
|
return classes.join(" ");
|
|
904
1046
|
})();
|
|
905
|
-
const leftAsideClassName = [
|
|
906
|
-
|
|
907
|
-
sidebarClassName
|
|
908
|
-
].filter(Boolean).join(" ");
|
|
909
|
-
const contentClassNames = [
|
|
910
|
-
"getting-started-layout__content",
|
|
911
|
-
contentClassName
|
|
912
|
-
].filter(Boolean).join(" ");
|
|
1047
|
+
const leftAsideClassName = ["canopy-layout__sidebar", sidebarClassName].filter(Boolean).join(" ");
|
|
1048
|
+
const contentClassNames = ["canopy-layout__content", contentClassName].filter(Boolean).join(" ");
|
|
913
1049
|
const contentNavigationAsideClassName = [
|
|
914
|
-
"
|
|
1050
|
+
"canopy-layout__content-nav",
|
|
1051
|
+
"is-collapsed",
|
|
915
1052
|
contentNavigationClassName
|
|
916
1053
|
].filter(Boolean).join(" ");
|
|
917
1054
|
const sidebarNode = showLeftColumn ? buildNavigationAside(sidebar, sidebarClassName) : null;
|
|
918
|
-
return /* @__PURE__ */ React12.createElement("div", { className: containerClassName, ...rest }, showLeftColumn ? /* @__PURE__ */ React12.createElement("aside", { className: leftAsideClassName }, sidebarNode) : null, /* @__PURE__ */ React12.createElement("div", { className: contentClassNames }, children), hasContentNavigation ? /* @__PURE__ */ React12.createElement(
|
|
919
|
-
|
|
1055
|
+
return /* @__PURE__ */ React12.createElement("div", { className: containerClassName, ...rest }, showLeftColumn ? /* @__PURE__ */ React12.createElement("aside", { className: leftAsideClassName }, sidebarNode) : null, /* @__PURE__ */ React12.createElement("div", { className: contentClassNames }, children), hasContentNavigation ? /* @__PURE__ */ React12.createElement(React12.Fragment, null, /* @__PURE__ */ React12.createElement(
|
|
1056
|
+
"aside",
|
|
920
1057
|
{
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
1058
|
+
className: contentNavigationAsideClassName,
|
|
1059
|
+
"data-canopy-content-nav-root": "true"
|
|
1060
|
+
},
|
|
1061
|
+
/* @__PURE__ */ React12.createElement(
|
|
1062
|
+
ContentNavigation,
|
|
1063
|
+
{
|
|
1064
|
+
items: headingTree,
|
|
1065
|
+
heading: contentHeading || void 0,
|
|
1066
|
+
headingId: headingAnchorId || void 0,
|
|
1067
|
+
pageTitle: context && context.page ? context.page.title : void 0
|
|
1068
|
+
}
|
|
1069
|
+
)
|
|
1070
|
+
), /* @__PURE__ */ React12.createElement(ContentNavigationScript, null)) : null);
|
|
927
1071
|
}
|
|
928
1072
|
|
|
929
1073
|
// ui/src/layout/CanopyHeader.jsx
|