domma-cms 0.41.1 → 0.43.1

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.
@@ -8,7 +8,8 @@ import {fileURLToPath} from 'url';
8
8
  import {getConfig} from '../config.js';
9
9
  import {getInjectionSnippets} from './plugins.js';
10
10
  import {applyTransforms} from './hooks.js';
11
- import {resolveLocation, resolveMenuDecorations} from './menus.js';
11
+ import {resolveLocation, resolveMenuDecorations, resolveOverlaysForPage} from './menus.js';
12
+ import {buildMenuNav} from './menuRender.js';
12
13
  import {getProjectForPage} from './projects.js';
13
14
 
14
15
  const VALID_LAYOUT_WIDTHS = new Set(['narrow', 'normal', 'wide', 'full']);
@@ -53,6 +54,26 @@ function filterHiddenNavItems(nav) {
53
54
  return {...nav, items};
54
55
  }
55
56
 
57
+ /**
58
+ * Build the overlay menus for a page: every menu bound to the `overlay` slot
59
+ * whose binding matches, each drawn by the same builder the `[menu]` shortcode
60
+ * uses so an overlay and a shortcode of the same menu look identical.
61
+ *
62
+ * Emitted as bare <nav> elements with no wrapper — a wrapping element would
63
+ * risk becoming a containing block for their position:fixed pinning.
64
+ *
65
+ * @param {{urlPath?: string, project?: string|null}} menuCtx
66
+ * @param {object|null} user
67
+ * @returns {Promise<string>} concatenated HTML, '' when nothing applies
68
+ */
69
+ async function buildOverlayMenus(menuCtx, user) {
70
+ const overlays = await resolveOverlaysForPage(menuCtx, user);
71
+ if (!overlays.length) return '';
72
+ const navs = await Promise.all(overlays.map(async (menu) =>
73
+ buildMenuNav(menu, await resolveMenuDecorations(menu.items))));
74
+ return navs.join('');
75
+ }
76
+
56
77
  /**
57
78
  * Render a page to a full HTML string.
58
79
  *
@@ -89,10 +110,11 @@ export async function renderPage(page, opts = {}) {
89
110
 
90
111
  // Resolve live badge counts (countFrom) before the menus are injected as
91
112
  // globals. Static badges/colours/pills pass through unchanged.
92
- const [navItemsDecorated, footerPrimaryDecorated, footerLegalDecorated] = await Promise.all([
113
+ const [navItemsDecorated, footerPrimaryDecorated, footerLegalDecorated, overlayMenus] = await Promise.all([
93
114
  resolveMenuDecorations(navMenu ? navMenu.items : []),
94
115
  resolveMenuDecorations(footerPrimary ? footerPrimary.items : []),
95
- resolveMenuDecorations(footerLegal ? footerLegal.items : [])
116
+ resolveMenuDecorations(footerLegal ? footerLegal.items : []),
117
+ buildOverlayMenus(menuCtx, opts.user || null)
96
118
  ]);
97
119
 
98
120
  // Compose the navigation object the public site script expects:
@@ -220,6 +242,10 @@ export async function renderPage(page, opts = {}) {
220
242
  headInject: [injection.head, navbarFontLink].filter(Boolean).join('\n'),
221
243
  headInjectLate: [injection.headLate, customCssTag, navbarStyleTag].filter(Boolean).join('\n'),
222
244
  bodyEndInject: [
245
+ // Overlay menus — bound to the `overlay` slot and pinned by their own
246
+ // float config, so they belong at the end of the body, clear of the
247
+ // content flow.
248
+ overlayMenus,
223
249
  injection.bodyEnd,
224
250
  site.backToTop?.enabled ? '<script src="/public/js/btt.js"></script>' : '',
225
251
  site.cookieConsent?.enabled ? '<script src="/public/js/cookie-consent.js"></script>' : '',
@@ -516,10 +542,11 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
516
542
 
517
543
  // Resolve live badge counts (countFrom) before the menus are injected as
518
544
  // globals. Static badges/colours/pills pass through unchanged.
519
- const [navItemsDecorated, footerPrimaryDecorated, footerLegalDecorated] = await Promise.all([
545
+ const [navItemsDecorated, footerPrimaryDecorated, footerLegalDecorated, overlayMenus] = await Promise.all([
520
546
  resolveMenuDecorations(navMenu ? navMenu.items : []),
521
547
  resolveMenuDecorations(footerPrimary ? footerPrimary.items : []),
522
- resolveMenuDecorations(footerLegal ? footerLegal.items : [])
548
+ resolveMenuDecorations(footerLegal ? footerLegal.items : []),
549
+ buildOverlayMenus(menuCtx, user)
523
550
  ]);
524
551
 
525
552
  // Compose the navigation object the public site script expects:
@@ -623,6 +650,7 @@ export async function renderBlogPage(templatePath, data = {}, seoMeta = {}) {
623
650
  headInject: [injection.head, navbarFontLink].filter(Boolean).join('\n'),
624
651
  headInjectLate: [injection.headLate, customCssTag, navbarStyleTag].filter(Boolean).join('\n'),
625
652
  bodyEndInject: [
653
+ overlayMenus,
626
654
  injection.bodyEnd,
627
655
  (seoMeta.usedComponents || [])
628
656
  .map(n => `<script type="module" src="/api/components/${n}.js"></script>`)
@@ -117,7 +117,7 @@
117
117
 
118
118
  <!-- Site initialisation -->
119
119
  <script src="/public/js/site.js?v=20260817-menu-bindings" type="module"></script>
120
- <script src="/public/js/collection-context.js?v=20260722-ctx-cursor" defer></script>
120
+ <script src="/public/js/collection-context.js?v=20260819-export" type="module"></script>
121
121
 
122
122
  <!-- Interactive Collection Browser (used by [collection] shortcodes with
123
123
  searchable / filterable / sortable / paginate attributes) -->