@canopy-iiif/app 0.10.5 → 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.
@@ -179,13 +179,38 @@ var Image = (props) => {
179
179
  } catch (_) {
180
180
  json = "{}";
181
181
  }
182
- return /* @__PURE__ */ React5.createElement("div", { "data-canopy-image": "1", className: "not-prose" }, /* @__PURE__ */ React5.createElement(
183
- "script",
182
+ const {
183
+ height = `600px`,
184
+ backgroundColor = `var(--color-gray-200)`,
185
+ caption
186
+ } = props || {};
187
+ return /* @__PURE__ */ React5.createElement(
188
+ "figure",
184
189
  {
185
- type: "application/json",
186
- dangerouslySetInnerHTML: { __html: json }
187
- }
188
- ));
190
+ style: {
191
+ margin: `1.618rem 0 2.618rem`
192
+ }
193
+ },
194
+ /* @__PURE__ */ React5.createElement(
195
+ "div",
196
+ {
197
+ "data-canopy-image": "1",
198
+ style: {
199
+ height,
200
+ backgroundColor,
201
+ borderRadius: `0.25rem`
202
+ }
203
+ },
204
+ /* @__PURE__ */ React5.createElement(
205
+ "script",
206
+ {
207
+ type: "application/json",
208
+ dangerouslySetInnerHTML: { __html: json }
209
+ }
210
+ )
211
+ ),
212
+ caption && /* @__PURE__ */ React5.createElement("figcaption", null, caption)
213
+ );
189
214
  }
190
215
  return /* @__PURE__ */ React5.createElement(CloverImage, { ...props });
191
216
  };
@@ -587,9 +612,13 @@ import navigationHelpers2 from "@canopy-iiif/app/lib/components/navigation.js";
587
612
  // ui/src/layout/ContentNavigation.jsx
588
613
  import React11 from "react";
589
614
  var SCROLL_OFFSET_REM = 1.618;
615
+ var MAX_HEADING_DEPTH = 3;
590
616
  function depthIndex(depth) {
591
617
  return Math.max(0, Math.min(5, (depth || 1) - 1));
592
618
  }
619
+ function resolveDepth(value, fallback = 1) {
620
+ return Math.max(1, typeof value === "number" ? value : fallback);
621
+ }
593
622
  function ContentNavigation({
594
623
  items = [],
595
624
  className = "",
@@ -602,7 +631,11 @@ function ContentNavigation({
602
631
  const isBrowser = typeof window !== "undefined" && typeof document !== "undefined";
603
632
  const savedDepthsRef = React11.useRef(null);
604
633
  if ((!items || !items.length) && !headingId) return null;
605
- const combinedClassName = ["canopy-sub-navigation canopy-content-navigation", className].filter(Boolean).join(" ");
634
+ const combinedClassName = [
635
+ "canopy-sub-navigation canopy-content-navigation",
636
+ "canopy-content-navigation--collapsed",
637
+ className
638
+ ].filter(Boolean).join(" ");
606
639
  const effectiveHeading = heading || pageTitle || null;
607
640
  const navLabel = ariaLabel || (effectiveHeading ? `${effectiveHeading} navigation` : "Section navigation");
608
641
  const getSavedDepth = React11.useCallback(
@@ -631,7 +664,11 @@ function ContentNavigation({
631
664
  const id = String(node.id);
632
665
  if (seen.has(id)) return;
633
666
  seen.add(id);
634
- const depth = node.depth || node.level || getSavedDepth(id, 2);
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;
635
672
  entries.push({ id, depth });
636
673
  if (node.children && node.children.length) pushNodes(node.children);
637
674
  });
@@ -747,9 +784,14 @@ function ContentNavigation({
747
784
  return nodes.map((node) => {
748
785
  if (!node) return null;
749
786
  const id = node.id ? String(node.id) : "";
750
- const depth = node.depth || node.level || getSavedDepth(id, 2);
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;
751
792
  const idx = depthIndex(depth);
752
793
  const isActive = id && activeId === id;
794
+ const childNodes = depth < MAX_HEADING_DEPTH ? renderNodes2(node.children) : null;
753
795
  return /* @__PURE__ */ React11.createElement("li", { key: id || node.title, className: "canopy-sub-navigation__item", "data-depth": idx }, /* @__PURE__ */ React11.createElement(
754
796
  "a",
755
797
  {
@@ -759,19 +801,19 @@ function ContentNavigation({
759
801
  "aria-current": isActive ? "location" : void 0
760
802
  },
761
803
  node.title
762
- ), node.children && node.children.length ? /* @__PURE__ */ React11.createElement(
804
+ ), childNodes ? /* @__PURE__ */ React11.createElement(
763
805
  "ul",
764
806
  {
765
807
  className: "canopy-sub-navigation__list canopy-sub-navigation__list--nested",
766
808
  role: "list"
767
809
  },
768
- renderNodes2(node.children)
810
+ childNodes
769
811
  ) : null);
770
812
  });
771
813
  },
772
814
  [handleAnchorClick, activeId, getSavedDepth]
773
815
  );
774
- const nestedItems = renderNodes2(items);
816
+ const nestedItems = React11.useMemo(() => renderNodes2(items), [items, renderNodes2]);
775
817
  const topLink = headingId ? /* @__PURE__ */ React11.createElement("li", { className: "canopy-sub-navigation__item", "data-depth": 0 }, /* @__PURE__ */ React11.createElement(
776
818
  "a",
777
819
  {
@@ -789,7 +831,32 @@ function ContentNavigation({
789
831
  },
790
832
  nestedItems
791
833
  ) : null) : null;
792
- return /* @__PURE__ */ React11.createElement("nav", { className: combinedClassName, style, "aria-label": navLabel }, /* @__PURE__ */ React11.createElement("ul", { className: "canopy-sub-navigation__list", role: "list" }, topLink || nestedItems));
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
+ );
793
860
  }
794
861
 
795
862
  // ui/src/layout/Layout.jsx
@@ -828,6 +895,106 @@ function buildNavigationAside(sidebar, className) {
828
895
  }
829
896
  return sidebar;
830
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
+ }
831
998
  function Layout({
832
999
  children,
833
1000
  sidebar,
@@ -866,44 +1033,41 @@ function Layout({
866
1033
  );
867
1034
  const showLeftColumn = navigation !== false;
868
1035
  const hasContentNavigation = navigation !== false && contentNavigation !== false && headingTree.length > 0;
869
- const gridClass = (() => {
870
- if (showLeftColumn && hasContentNavigation) {
871
- return "md:grid md:grid-cols-[17rem_minmax(0,1fr)_14rem] md:items-start md:gap-10";
872
- }
873
- if (showLeftColumn) {
874
- return "md:grid md:grid-cols-[17rem_minmax(0,1fr)] md:items-start md:gap-10";
875
- }
1036
+ const containerClassName = (() => {
1037
+ const classes = ["canopy-layout"];
1038
+ classes.push(fluid ? "canopy-layout--fluid" : "canopy-layout--fixed");
1039
+ if (showLeftColumn) classes.push("canopy-layout--with-sidebar");
876
1040
  if (hasContentNavigation) {
877
- return "md:grid md:grid-cols-[minmax(0,1fr)_14rem] md:items-start md:gap-10";
1041
+ classes.push("canopy-layout--with-content-nav");
1042
+ classes.push("canopy-layout--content-nav-collapsed");
878
1043
  }
879
- return "";
1044
+ if (className) classes.push(className);
1045
+ return classes.join(" ");
880
1046
  })();
881
- const containerClassName = [
882
- "w-full py-6 getting-started-layout",
883
- gridClass,
884
- fluid ? "px-4 md:px-8 lg:px-12" : "mx-auto max-w-content px-4",
885
- className
886
- ].filter(Boolean).join(" ");
887
- const leftAsideClassName = [
888
- "mt-8 md:mt-0 md:order-1 md:sticky md:top-24 md:max-h-[calc(100vh-6rem)] md:overflow-y-auto text-slate-600",
889
- sidebarClassName
890
- ].filter(Boolean).join(" ");
891
- const contentOrderClass = showLeftColumn ? "md:order-2" : hasContentNavigation ? "md:order-1" : "";
892
- const contentClassNames = ["space-y-6", contentOrderClass, contentClassName].filter(Boolean).join(" ");
1047
+ const leftAsideClassName = ["canopy-layout__sidebar", sidebarClassName].filter(Boolean).join(" ");
1048
+ const contentClassNames = ["canopy-layout__content", contentClassName].filter(Boolean).join(" ");
893
1049
  const contentNavigationAsideClassName = [
894
- "hidden md:block md:order-3 mt-8 md:mt-0 md:sticky md:top-24 md:max-h-[calc(100vh-6rem)] md:overflow-y-auto text-slate-600",
1050
+ "canopy-layout__content-nav",
1051
+ "is-collapsed",
895
1052
  contentNavigationClassName
896
1053
  ].filter(Boolean).join(" ");
897
1054
  const sidebarNode = showLeftColumn ? buildNavigationAside(sidebar, sidebarClassName) : null;
898
- 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("aside", { className: contentNavigationAsideClassName }, /* @__PURE__ */ React12.createElement(
899
- ContentNavigation,
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",
900
1057
  {
901
- items: headingTree,
902
- heading: contentHeading || void 0,
903
- headingId: headingAnchorId || void 0,
904
- pageTitle: context && context.page ? context.page.title : void 0
905
- }
906
- )) : null);
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);
907
1071
  }
908
1072
 
909
1073
  // ui/src/layout/CanopyHeader.jsx