@riverbankcms/sdk 0.8.0 → 0.8.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.
Files changed (168) hide show
  1. package/dist/cli/index.js +583 -147
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/client/client.d.mts +2 -2
  4. package/dist/client/client.d.ts +2 -2
  5. package/dist/client/client.js +126 -30
  6. package/dist/client/client.js.map +1 -1
  7. package/dist/client/client.mjs +126 -30
  8. package/dist/client/client.mjs.map +1 -1
  9. package/dist/client/hooks.d.mts +2 -2
  10. package/dist/client/hooks.d.ts +2 -2
  11. package/dist/client/hooks.js +34 -20
  12. package/dist/client/hooks.js.map +1 -1
  13. package/dist/client/hooks.mjs +34 -20
  14. package/dist/client/hooks.mjs.map +1 -1
  15. package/dist/client/rendering/client.js +182 -123
  16. package/dist/client/rendering/client.js.map +1 -1
  17. package/dist/client/rendering/client.mjs +174 -109
  18. package/dist/client/rendering/client.mjs.map +1 -1
  19. package/dist/client/usePage-Dsi39Exp.d.ts +6915 -0
  20. package/dist/client/usePage-Im82JRRe.d.mts +6915 -0
  21. package/dist/server/{Layout-l2v4Qa6E.d.ts → Layout-CZ-kxKfl.d.ts} +1 -1
  22. package/dist/server/{Layout-D4J009eS.d.mts → Layout-ESG8zvrk.d.mts} +1 -1
  23. package/dist/server/{chunk-4YQJUL5W.mjs → chunk-5GCSRTIU.mjs} +8 -4
  24. package/dist/server/chunk-5GCSRTIU.mjs.map +1 -0
  25. package/dist/server/{chunk-YYO3RIFO.js → chunk-6ERSDFTY.js} +35 -21
  26. package/dist/server/chunk-6ERSDFTY.js.map +1 -0
  27. package/dist/server/{chunk-YXA4GAAQ.mjs → chunk-6VTKALLN.mjs} +2 -6
  28. package/dist/server/{chunk-YXA4GAAQ.mjs.map → chunk-6VTKALLN.mjs.map} +1 -1
  29. package/dist/server/{chunk-BYBJA6SP.mjs → chunk-A3UZ2LDH.mjs} +35 -21
  30. package/dist/server/chunk-A3UZ2LDH.mjs.map +1 -0
  31. package/dist/server/{chunk-OSF34JTQ.mjs → chunk-ADD3O2QO.mjs} +4 -4
  32. package/dist/server/{chunk-C6FIJC7T.mjs → chunk-BNHK7YOC.mjs} +2 -2
  33. package/dist/server/{chunk-TT5JWA4X.js → chunk-DAXWU3S3.js} +9 -9
  34. package/dist/server/{chunk-TT5JWA4X.js.map → chunk-DAXWU3S3.js.map} +1 -1
  35. package/dist/server/{chunk-7UPVCT3K.js → chunk-F2NDLDDA.js} +239 -154
  36. package/dist/server/chunk-F2NDLDDA.js.map +1 -0
  37. package/dist/server/{chunk-LNOUXALA.mjs → chunk-FUFPKTSI.mjs} +96 -11
  38. package/dist/server/chunk-FUFPKTSI.mjs.map +1 -0
  39. package/dist/server/{chunk-65A5HAUZ.mjs → chunk-GRFFJUCO.mjs} +3 -3
  40. package/dist/server/{chunk-65A5HAUZ.mjs.map → chunk-GRFFJUCO.mjs.map} +1 -1
  41. package/dist/server/{chunk-AEFWG657.mjs → chunk-HDHY4236.mjs} +2 -2
  42. package/dist/server/{chunk-2KCF2DNK.js → chunk-HE3RTUDX.js} +8 -8
  43. package/dist/server/{chunk-2KCF2DNK.js.map → chunk-HE3RTUDX.js.map} +1 -1
  44. package/dist/server/{chunk-RVDS7VSP.js → chunk-IJTJH4J3.js} +4 -4
  45. package/dist/server/{chunk-RVDS7VSP.js.map → chunk-IJTJH4J3.js.map} +1 -1
  46. package/dist/server/{chunk-P3NNN73G.js → chunk-K44OPKLA.js} +3 -3
  47. package/dist/server/{chunk-P3NNN73G.js.map → chunk-K44OPKLA.js.map} +1 -1
  48. package/dist/server/{chunk-EIJ27EZQ.js → chunk-KDCVCDW6.js} +10 -6
  49. package/dist/server/chunk-KDCVCDW6.js.map +1 -0
  50. package/dist/server/{chunk-7BVRA5MY.js → chunk-KGORQCHF.js} +9 -9
  51. package/dist/server/{chunk-7BVRA5MY.js.map → chunk-KGORQCHF.js.map} +1 -1
  52. package/dist/server/{chunk-WM646WI3.js → chunk-MFNWLB5G.js} +7 -7
  53. package/dist/server/{chunk-WM646WI3.js.map → chunk-MFNWLB5G.js.map} +1 -1
  54. package/dist/server/{chunk-EIVISR62.js → chunk-P4O3WSAR.js} +2 -6
  55. package/dist/server/chunk-P4O3WSAR.js.map +1 -0
  56. package/dist/server/{chunk-RBJFXNDM.mjs → chunk-PGZJUNCY.mjs} +4 -4
  57. package/dist/server/{chunk-ARNCLSQT.mjs → chunk-T5PAA22U.mjs} +2 -2
  58. package/dist/server/{chunk-T26N3P26.js → chunk-TLZHVGTL.js} +4 -4
  59. package/dist/server/{chunk-T26N3P26.js.map → chunk-TLZHVGTL.js.map} +1 -1
  60. package/dist/server/{chunk-P4K63SBZ.mjs → chunk-TR7MSLWL.mjs} +3 -3
  61. package/dist/server/{chunk-NFEGQTCC.mjs → chunk-WMJKH4XE.mjs} +8 -1
  62. package/dist/server/{chunk-4CV4JOE5.js → chunk-Z6ZWNWWR.js} +9 -2
  63. package/dist/server/chunk-Z6ZWNWWR.js.map +1 -0
  64. package/dist/server/{components-D2uCKCj7.d.ts → components-CE48wJM1.d.ts} +4 -4
  65. package/dist/server/{components-vtYEmmPF.d.mts → components-iEDvl2Yw.d.mts} +4 -4
  66. package/dist/server/components.d.mts +6 -6
  67. package/dist/server/components.d.ts +6 -6
  68. package/dist/server/components.js +7 -7
  69. package/dist/server/components.mjs +6 -6
  70. package/dist/server/config-validation.d.mts +3 -3
  71. package/dist/server/config-validation.d.ts +3 -3
  72. package/dist/server/config-validation.js +6 -6
  73. package/dist/server/config-validation.mjs +5 -5
  74. package/dist/server/config.d.mts +5 -5
  75. package/dist/server/config.d.ts +5 -5
  76. package/dist/server/config.js +6 -6
  77. package/dist/server/config.mjs +5 -5
  78. package/dist/server/data.d.mts +3 -3
  79. package/dist/server/data.d.ts +3 -3
  80. package/dist/server/data.js +4 -4
  81. package/dist/server/data.mjs +3 -3
  82. package/dist/server/env.js +1 -1
  83. package/dist/server/env.mjs +1 -1
  84. package/dist/server/{index-BxrAuL9K.d.ts → index-BHLK2mgQ.d.ts} +2 -2
  85. package/dist/server/{index-DfWg1Qle.d.mts → index-BrH_NIRO.d.mts} +2 -2
  86. package/dist/server/{index-2qnY7VH_.d.mts → index-Cgvb5fVQ.d.mts} +2 -2
  87. package/dist/server/{index-CH_dvF6n.d.ts → index-DTBg8eXj.d.ts} +2 -2
  88. package/dist/server/index.d.mts +6 -6
  89. package/dist/server/index.d.ts +6 -6
  90. package/dist/server/index.js +11 -11
  91. package/dist/server/index.mjs +2 -2
  92. package/dist/server/{loadContent-DECnsp4k.d.ts → loadContent-BUK6IVJf.d.ts} +26 -4
  93. package/dist/server/{loadContent-Du5kS8UM.d.mts → loadContent-au9Weoy0.d.mts} +26 -4
  94. package/dist/server/loadPage-AWYZ2QA2.mjs +11 -0
  95. package/dist/server/loadPage-CMHYAW2J.js +11 -0
  96. package/dist/server/{loadPage-AXNAERDS.js.map → loadPage-CMHYAW2J.js.map} +1 -1
  97. package/dist/server/{loadPage-VBorKlWv.d.mts → loadPage-DiHEl8BA.d.mts} +3 -3
  98. package/dist/server/{loadPage-BZohBxxf.d.ts → loadPage-JOIbF7ih.d.ts} +3 -3
  99. package/dist/server/metadata.d.mts +5 -5
  100. package/dist/server/metadata.d.ts +5 -5
  101. package/dist/server/metadata.js +1 -1
  102. package/dist/server/metadata.mjs +1 -1
  103. package/dist/server/navigation.d.mts +4 -8
  104. package/dist/server/navigation.d.ts +4 -8
  105. package/dist/server/navigation.js +3 -7
  106. package/dist/server/navigation.js.map +1 -1
  107. package/dist/server/navigation.mjs +2 -6
  108. package/dist/server/next/revalidate.js +1 -1
  109. package/dist/server/next/revalidate.mjs +1 -1
  110. package/dist/server/next/tags.js +1 -1
  111. package/dist/server/next/tags.mjs +1 -1
  112. package/dist/server/next.d.mts +7 -7
  113. package/dist/server/next.d.ts +7 -7
  114. package/dist/server/next.js +20 -16
  115. package/dist/server/next.js.map +1 -1
  116. package/dist/server/next.mjs +12 -8
  117. package/dist/server/next.mjs.map +1 -1
  118. package/dist/server/rendering/server.d.mts +5 -5
  119. package/dist/server/rendering/server.d.ts +5 -5
  120. package/dist/server/rendering/server.js +9 -9
  121. package/dist/server/rendering/server.mjs +8 -8
  122. package/dist/server/rendering.d.mts +8 -8
  123. package/dist/server/rendering.d.ts +8 -8
  124. package/dist/server/rendering.js +11 -11
  125. package/dist/server/rendering.mjs +10 -10
  126. package/dist/server/routing.d.mts +5 -5
  127. package/dist/server/routing.d.ts +5 -5
  128. package/dist/server/routing.js +2 -2
  129. package/dist/server/routing.mjs +2 -2
  130. package/dist/server/{schema-Z6-afHJG.d.mts → schema-DYtW0zEu.d.mts} +40 -0
  131. package/dist/server/{schema-Z6-afHJG.d.ts → schema-DYtW0zEu.d.ts} +40 -0
  132. package/dist/server/server.d.mts +6 -6
  133. package/dist/server/server.d.ts +6 -6
  134. package/dist/server/server.js +7 -7
  135. package/dist/server/server.mjs +6 -6
  136. package/dist/server/theme-bridge.js +9 -9
  137. package/dist/server/theme-bridge.mjs +3 -3
  138. package/dist/server/theme.js +1 -1
  139. package/dist/server/theme.mjs +1 -1
  140. package/dist/server/{types-DT30Qy7x.d.mts → types-BAM1kcGA.d.mts} +1 -1
  141. package/dist/server/{types-D0rPF8l5.d.ts → types-CmBB0Osp.d.ts} +2 -2
  142. package/dist/server/{types-BRQ_6yOc.d.mts → types-DDNKxQXw.d.mts} +2 -2
  143. package/dist/server/{types-D8XqwoVd.d.ts → types-DVesWaB7.d.ts} +1 -1
  144. package/dist/server/{types-CJfJwcuL.d.mts → types-M0CviVW2.d.mts} +1 -1
  145. package/dist/server/{types-CgSO0yxg.d.ts → types-_SNCu2ZZ.d.ts} +1 -1
  146. package/dist/server/{validation-Pv3Zs6dP.d.mts → validation-BA1TKthZ.d.mts} +2 -2
  147. package/dist/server/{validation-D1LaY1kQ.d.ts → validation-js7BCPN8.d.ts} +2 -2
  148. package/dist/server/webhooks.js +1 -1
  149. package/dist/server/webhooks.mjs +1 -1
  150. package/package.json +1 -1
  151. package/dist/server/chunk-4CV4JOE5.js.map +0 -1
  152. package/dist/server/chunk-4YQJUL5W.mjs.map +0 -1
  153. package/dist/server/chunk-7UPVCT3K.js.map +0 -1
  154. package/dist/server/chunk-BYBJA6SP.mjs.map +0 -1
  155. package/dist/server/chunk-EIJ27EZQ.js.map +0 -1
  156. package/dist/server/chunk-EIVISR62.js.map +0 -1
  157. package/dist/server/chunk-LNOUXALA.mjs.map +0 -1
  158. package/dist/server/chunk-YYO3RIFO.js.map +0 -1
  159. package/dist/server/loadPage-AXNAERDS.js +0 -11
  160. package/dist/server/loadPage-XR7ORQ2E.mjs +0 -11
  161. /package/dist/server/{chunk-OSF34JTQ.mjs.map → chunk-ADD3O2QO.mjs.map} +0 -0
  162. /package/dist/server/{chunk-C6FIJC7T.mjs.map → chunk-BNHK7YOC.mjs.map} +0 -0
  163. /package/dist/server/{chunk-AEFWG657.mjs.map → chunk-HDHY4236.mjs.map} +0 -0
  164. /package/dist/server/{chunk-RBJFXNDM.mjs.map → chunk-PGZJUNCY.mjs.map} +0 -0
  165. /package/dist/server/{chunk-ARNCLSQT.mjs.map → chunk-T5PAA22U.mjs.map} +0 -0
  166. /package/dist/server/{chunk-P4K63SBZ.mjs.map → chunk-TR7MSLWL.mjs.map} +0 -0
  167. /package/dist/server/{chunk-NFEGQTCC.mjs.map → chunk-WMJKH4XE.mjs.map} +0 -0
  168. /package/dist/server/{loadPage-XR7ORQ2E.mjs.map → loadPage-AWYZ2QA2.mjs.map} +0 -0
@@ -218,7 +218,6 @@ function buildMenu(navigation, routes) {
218
218
  }
219
219
  );
220
220
  }
221
- var buildSimpleMenu = buildMenu;
222
221
  function resolveHref(link, routes) {
223
222
  if (!link) return null;
224
223
  if (link.kind === "external" || link.kind === "url") {
@@ -254,7 +253,6 @@ function buildLogo(logo, fallbackAlt) {
254
253
  }
255
254
  return result;
256
255
  }
257
- var buildSimpleLogo = buildLogo;
258
256
 
259
257
  export {
260
258
  isNavLink,
@@ -267,8 +265,6 @@ export {
267
265
  buildMenuViewModel,
268
266
  buildLogoViewModel,
269
267
  buildMenu,
270
- buildSimpleMenu,
271
- buildLogo,
272
- buildSimpleLogo
268
+ buildLogo
273
269
  };
274
- //# sourceMappingURL=chunk-YXA4GAAQ.mjs.map
270
+ //# sourceMappingURL=chunk-6VTKALLN.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["/**\n * Navigation helper utilities for SDK sites.\n *\n * Provides helpers to transform CMS navigation data into render-ready structures.\n * All navigation functions return nested structures supporting dropdowns.\n *\n * @example Getting nav items\n * ```ts\n * import { getPrimaryNavItems, isNavLink, isNavDropdown } from '@riverbankcms/sdk/navigation';\n *\n * const headerNav = getPrimaryNavItems(siteData.navigation);\n * headerNav.forEach(item => {\n * if (isNavLink(item)) {\n * console.log(item.href);\n * } else {\n * console.log(`${item.label} has ${item.children.length} children`);\n * }\n * });\n * ```\n *\n * @example Building menu and logo\n * ```ts\n * import { buildMenu, buildLogo } from '@riverbankcms/sdk/navigation';\n *\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { NavigationMenuWithItems, NavigationItemRecord, LinkPayload } from '@riverbankcms/api';\nimport type { RouteMap } from '@riverbankcms/blocks';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types for block rendering (full LinkValue data)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Link value types matching @riverbankcms/blocks\n */\nexport type InternalLinkValue = {\n kind: 'internal';\n routeId: string;\n entityId: string;\n entityType: 'page' | 'content';\n href: string;\n title: string;\n typeLabel: string;\n contentTypeKey?: string | null;\n contentTypeName?: string | null;\n updatedAt?: string | null;\n};\n\nexport type ExternalLinkValue = {\n kind: 'external';\n href: string;\n};\n\nexport type CustomLinkValue = {\n kind: 'url';\n href: string;\n};\n\nexport type LinkValue = InternalLinkValue | ExternalLinkValue | CustomLinkValue;\n\n/**\n * Menu link view model for block rendering\n */\nexport type MenuLinkViewModel = {\n id: string;\n label: string;\n link: LinkValue | null;\n target: string | null;\n rel: string | null;\n active?: boolean;\n};\n\n/**\n * CTA link view model (extends MenuLinkViewModel with variant)\n */\nexport type MenuCtaViewModel = MenuLinkViewModel & {\n variant: string;\n};\n\n/**\n * Complete menu view model for header/footer blocks\n */\nexport type MenuViewModel = {\n items: MenuLinkViewModel[];\n ctaItem: MenuCtaViewModel | null;\n};\n\n/**\n * Logo source data from site layout\n */\nexport type LogoSource = {\n url: string | null;\n alt: string | null;\n assetId?: string | null;\n width?: number | null;\n height?: number | null;\n storagePath?: string | null;\n storageBucket?: string | null;\n} | null;\n\n/**\n * Logo view model for block rendering\n */\nexport type LogoViewModel = {\n type: 'image';\n src: string;\n alt: string;\n assetId?: string;\n width?: number | null;\n height?: number | null;\n storagePath?: string;\n storageBucket?: string;\n} | null;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Navigation Item Types (always nested)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A navigation link item with a direct URL.\n * Use `kind` discriminator for type-safe narrowing.\n *\n * @example\n * ```ts\n * if (item.kind === 'link') {\n * console.log(item.href); // TypeScript knows href exists\n * }\n * ```\n */\nexport type NavLink = {\n /** Discriminator for type narrowing */\n kind: 'link';\n /** Unique identifier from CMS */\n id: string;\n /** Display text for the navigation link */\n label: string;\n /** The URL to navigate to */\n href: string;\n /** Whether link should open in new tab (external links) */\n isExternal: boolean;\n};\n\n/**\n * A dropdown container holding child navigation items.\n * Clicking reveals children instead of navigating.\n * Max 1 level of nesting (children are always NavLink, not NavDropdown).\n */\nexport type NavDropdown = {\n /** Discriminator for type narrowing */\n kind: 'dropdown';\n /** Unique identifier from CMS */\n id: string;\n /** Display text for the dropdown trigger */\n label: string;\n /** Child navigation links */\n children: NavLink[];\n};\n\n/**\n * Navigation item - either a link or a dropdown container.\n * Use `kind` property for type-safe discrimination.\n *\n * @example\n * ```ts\n * const items = getPrimaryNavItems(navigation);\n * items.forEach(item => {\n * if (item.kind === 'link') {\n * console.log(item.href);\n * } else {\n * console.log(item.children.length);\n * }\n * });\n * ```\n */\nexport type NavItem = NavLink | NavDropdown;\n\n/**\n * Type guard to check if a navigation item is a link.\n */\nexport function isNavLink(item: NavItem): item is NavLink {\n return item.kind === 'link';\n}\n\n/**\n * Type guard to check if a navigation item is a dropdown.\n */\nexport function isNavDropdown(item: NavItem): item is NavDropdown {\n return item.kind === 'dropdown';\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu and Logo types for SDK sites\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Navigation menu with items and optional CTA.\n * Built from CMS navigation data with pre-resolved hrefs.\n *\n * @example\n * ```ts\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * menu.items.forEach(item => {\n * if (item.kind === 'link') {\n * console.log(item.href);\n * } else {\n * console.log(item.children);\n * }\n * });\n * ```\n */\nexport type Menu = {\n items: NavItem[];\n ctaItem: NavLink | null;\n};\n\n/**\n * Logo data for site rendering.\n *\n * @example\n * ```ts\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * if (logo) {\n * <img src={logo.src} alt={logo.alt} />\n * }\n * ```\n */\nexport type Logo = {\n type: 'image';\n src: string;\n alt: string;\n width?: number;\n height?: number;\n} | null;\n\n// Legacy type aliases for backwards compatibility\n/** @deprecated Use `NavLink` instead */\nexport type SimpleNavLink = NavLink;\n/** @deprecated Use `NavDropdown` instead */\nexport type SimpleNavDropdown = NavDropdown;\n/** @deprecated Use `NavItem` instead */\nexport type SimpleNavItem = NavItem;\n/** @deprecated Use `Menu` instead */\nexport type SimpleMenuViewModel = Menu;\n/** @deprecated Use `Logo` instead */\nexport type SimpleLogo = Logo;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu selection helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get the primary navigation menu object.\n * Returns menu marked as isPrimary, or first menu if none marked.\n *\n * @example\n * ```ts\n * const menu = getPrimaryNavigation(siteData.navigation);\n * console.log(menu?.name); // \"main\"\n * ```\n */\nexport function getPrimaryNavigation(\n navigation: NavigationMenuWithItems[],\n): NavigationMenuWithItems | null {\n if (!navigation || navigation.length === 0) return null;\n return navigation.find((menu) => menu.isPrimary) ?? navigation[0] ?? null;\n}\n\n/**\n * Get a navigation menu by name/slug.\n *\n * @example\n * ```ts\n * const footerMenu = getNavigationBySlug(siteData.navigation, 'footer');\n * ```\n */\nexport function getNavigationBySlug(\n navigation: NavigationMenuWithItems[],\n slug: string,\n): NavigationMenuWithItems | null {\n if (!navigation || navigation.length === 0) return null;\n return navigation.find((menu) => menu.name === slug) ?? null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NavItem transformations\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get nav items from the primary menu (marked isPrimary, or first menu).\n * Returns nested structure supporting both links and dropdowns.\n *\n * @example\n * ```ts\n * const headerNav = getPrimaryNavItems(siteData.navigation);\n * headerNav.forEach(item => {\n * if (item.kind === 'dropdown') {\n * console.log(`${item.label} has ${item.children.length} children`);\n * }\n * });\n * ```\n */\nexport function getPrimaryNavItems(navigation: NavigationMenuWithItems[]): NavItem[] {\n return transformToNavItems(getPrimaryNavigation(navigation));\n}\n\n/**\n * Get nav items from a specific menu by slug.\n * Returns nested structure supporting both links and dropdowns.\n *\n * @example\n * ```ts\n * const footerNav = getNavItemsBySlug(siteData.navigation, 'footer');\n * ```\n */\nexport function getNavItemsBySlug(navigation: NavigationMenuWithItems[], slug: string): NavItem[] {\n return transformToNavItems(getNavigationBySlug(navigation, slug));\n}\n\n/**\n * Transform a menu into NavItem array.\n * Builds nested structure from flat items, supporting dropdowns with children.\n *\n * @example\n * ```ts\n * const nav = transformToNavItems(menu);\n * // [\n * // { kind: 'link', href: '/', label: 'Home', ... },\n * // { kind: 'dropdown', label: 'Services', children: [...] },\n * // ]\n * ```\n */\nexport function transformToNavItems(menu: NavigationMenuWithItems | null): NavItem[] {\n if (!menu?.items || menu.items.length === 0) return [];\n\n const toNavLink = (item: NavigationItemRecord): NavLink | null => {\n const href = extractHref(item);\n if (!href) return null;\n return {\n kind: 'link',\n id: item.id,\n label: item.label,\n href,\n isExternal: isExternalLink(item),\n };\n };\n\n const { items } = buildNestedStructure<NavLink, NavDropdown, NavLink>(\n menu.items,\n {\n toLink: toNavLink,\n createDropdown: (id, label, children) => ({\n kind: 'dropdown',\n id,\n label,\n children,\n }),\n extractCta: false,\n }\n );\n\n return items;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Full MenuViewModel for block rendering\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a MenuViewModel from navigation data for block rendering.\n * Extracts CTA item separately and preserves full link data.\n *\n * @example\n * ```ts\n * const menuViewModel = buildMenuViewModel(siteData.navigation);\n *\n * renderBlock(siteHeaderManifest, layout.header, {\n * viewModelOverrides: { menu: menuViewModel },\n * });\n * ```\n */\nexport function buildMenuViewModel(navigation: NavigationMenuWithItems[]): MenuViewModel {\n const menu = getPrimaryNavigation(navigation);\n\n if (!menu) {\n return { items: [], ctaItem: null };\n }\n\n const flatItems = menu.items\n .filter((item) => !item.parentId && item.urlType !== 'dropdown')\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));\n\n const items: MenuLinkViewModel[] = [];\n let ctaItem: MenuCtaViewModel | null = null;\n\n for (const item of flatItems) {\n const viewItem: MenuLinkViewModel = {\n id: item.id,\n label: item.label,\n link: convertToLinkValue(item),\n target: null,\n rel: null,\n active: false,\n };\n\n // Extract first CTA item separately\n if (!ctaItem && Boolean(item.isCta)) {\n ctaItem = { ...viewItem, variant: 'primary' };\n continue;\n }\n\n items.push(viewItem);\n }\n\n return { items, ctaItem };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Logo view model helper\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a LogoViewModel from site layout data.\n *\n * @param logo - Logo source from siteData.layout.logo\n * @param fallbackTitle - Site title to use as alt text fallback\n *\n * @example\n * ```ts\n * const logoViewModel = buildLogoViewModel(siteData.layout.logo, siteData.site.title);\n *\n * renderBlock(siteHeaderManifest, layout.header, {\n * viewModelOverrides: { content: { logo: logoViewModel } },\n * });\n * ```\n */\nexport function buildLogoViewModel(\n logo: LogoSource,\n fallbackTitle: string | null | undefined,\n): LogoViewModel {\n if (!logo) {\n return null;\n }\n\n // Logo must have storagePath (for direct Supabase URL) or explicit url\n if (!logo.url && !logo.storagePath) {\n return null;\n }\n\n return {\n type: 'image',\n src: logo.url ?? '', // Empty when using storagePath - MediaNode builds direct URL\n alt: resolveAltText(logo.alt, fallbackTitle),\n assetId: logo.assetId ?? undefined,\n width: logo.width ?? undefined,\n height: logo.height ?? undefined,\n storagePath: logo.storagePath ?? undefined,\n storageBucket: logo.storageBucket ?? undefined,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolve alt text with fallback.\n * Returns the provided alt if non-empty, otherwise falls back to the provided fallback or 'Site logo'.\n * @internal\n */\nfunction resolveAltText(alt: string | null | undefined, fallback: string | null | undefined): string {\n return (alt && alt.trim().length > 0) ? alt : (fallback ?? 'Site logo');\n}\n\n/**\n * Extracts the href from a navigation item's url field.\n */\nfunction extractHref(item: NavigationItemRecord): string {\n const link = item.url as LinkPayload | null | undefined;\n if (!link || typeof link !== 'object' || !('href' in link)) return '';\n return link.href;\n}\n\n/**\n * Determines if a navigation item's link is external.\n */\nfunction isExternalLink(item: NavigationItemRecord): boolean {\n const link = item.url as LinkPayload | null | undefined;\n if (!link || typeof link !== 'object' || !('kind' in link)) return false;\n return link.kind === 'external';\n}\n\n/**\n * Build a map of children items by parent ID.\n * Also warns about deeply nested items (grandchildren) which CMS shouldn't allow.\n * @internal\n */\nfunction buildChildrenByParentId(\n items: NavigationItemRecord[]\n): Map<string, NavigationItemRecord[]> {\n const childrenByParentId = new Map<string, NavigationItemRecord[]>();\n const itemsById = new Map<string, NavigationItemRecord>();\n\n // First pass: index items by ID\n for (const item of items) {\n itemsById.set(item.id, item);\n }\n\n // Second pass: build children map and warn about deep nesting\n for (const item of items) {\n if (item.parentId) {\n // Check if parent is itself a child (deep nesting)\n const parent = itemsById.get(item.parentId);\n if (parent?.parentId && typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') {\n console.warn(\n `[SDK Navigation] Deeply nested item detected: \"${item.label}\" (id: ${item.id}). ` +\n `Only 1 level of nesting is supported. This item will be ignored.`\n );\n continue; // Skip deeply nested items\n }\n\n const siblings = childrenByParentId.get(item.parentId) ?? [];\n siblings.push(item);\n childrenByParentId.set(item.parentId, siblings);\n }\n }\n\n return childrenByParentId;\n}\n\n/**\n * Generic nested structure builder.\n * @internal\n */\nfunction buildNestedStructure<TLink, TDropdown, TCta extends TLink>(\n items: NavigationItemRecord[],\n options: {\n /** Convert a navigation item record to a link type */\n toLink: (item: NavigationItemRecord) => TLink | null;\n /** Create a dropdown from id, label, and children */\n createDropdown: (id: string, label: string, children: TLink[]) => TDropdown;\n /** Extract CTA from items (return null to skip CTA extraction) */\n extractCta?: boolean;\n }\n): { items: (TLink | TDropdown)[]; ctaItem: TCta | null } {\n const childrenByParentId = buildChildrenByParentId(items);\n\n // Process root items (no parentId) sorted by orderIndex\n const rootItems = items\n .filter((item) => !item.parentId)\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));\n\n const result: (TLink | TDropdown)[] = [];\n let ctaItem: TCta | null = null;\n\n for (const item of rootItems) {\n // Handle CTA extraction (CTAs are always links, not dropdowns)\n if (options.extractCta && !ctaItem && Boolean(item.isCta) && item.urlType !== 'dropdown') {\n const link = options.toLink(item);\n if (link) {\n ctaItem = link as TCta;\n continue;\n }\n }\n\n if (item.urlType === 'dropdown') {\n // It's a dropdown - gather its children\n const childRecords = childrenByParentId.get(item.id) ?? [];\n const children = childRecords\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0))\n .map(options.toLink)\n .filter((child): child is TLink => child !== null);\n\n // Only include dropdown if it has children\n if (children.length > 0) {\n result.push(options.createDropdown(item.id, item.label, children));\n }\n } else {\n // It's a regular link\n const link = options.toLink(item);\n if (link) {\n result.push(link);\n }\n }\n }\n\n return { items: result, ctaItem };\n}\n\n/**\n * Convert navigation item to LinkValue for block rendering.\n */\nfunction convertToLinkValue(item: NavigationItemRecord): LinkValue | null {\n const payload = item.url as Record<string, unknown> | null;\n if (!payload) return null;\n\n const kind = typeof payload.kind === 'string' ? payload.kind : null;\n\n if (kind === 'external' || kind === 'url') {\n const href = typeof payload.href === 'string' ? payload.href : null;\n return href ? { kind, href } as ExternalLinkValue | CustomLinkValue : null;\n }\n\n if (kind === 'internal') {\n const routeId = typeof payload.routeId === 'string' ? payload.routeId : null;\n const entityId = typeof payload.entityId === 'string' ? payload.entityId : null;\n const entityType = payload.entityType === 'page' || payload.entityType === 'content'\n ? payload.entityType\n : null;\n const href = typeof payload.href === 'string' ? payload.href : null;\n const title = typeof payload.title === 'string' ? payload.title : null;\n const typeLabel = typeof payload.typeLabel === 'string' ? payload.typeLabel : null;\n\n if (!routeId || !entityId || !entityType || !href || !title || !typeLabel) {\n return null;\n }\n\n return {\n kind: 'internal',\n routeId,\n entityId,\n entityType,\n href,\n title,\n typeLabel,\n contentTypeKey: typeof payload.contentTypeKey === 'string' ? payload.contentTypeKey : null,\n contentTypeName: typeof payload.contentTypeName === 'string' ? payload.contentTypeName : null,\n updatedAt: typeof payload.updatedAt === 'string' ? payload.updatedAt : null,\n };\n }\n\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu and Logo builders\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a Menu from navigation data with pre-resolved hrefs.\n * Supports dropdown containers with nested children.\n *\n * @param navigation - Navigation menus from site data\n * @param routes - Route map for resolving internal links\n *\n * @example\n * ```ts\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * menu.items.forEach(item => {\n * if (item.kind === 'dropdown') {\n * console.log(item.children);\n * }\n * });\n * ```\n */\nexport function buildMenu(\n navigation: NavigationMenuWithItems[],\n routes: RouteMap,\n): Menu {\n const menu = getPrimaryNavigation(navigation);\n\n if (!menu) {\n return { items: [], ctaItem: null };\n }\n\n const toNavLink = (item: NavigationItemRecord): NavLink | null => {\n const link = item.url as LinkPayload | null;\n const href = resolveHref(link, routes);\n if (!href) return null;\n\n return {\n kind: 'link',\n id: item.id,\n label: item.label,\n href,\n isExternal: link?.kind === 'external',\n };\n };\n\n return buildNestedStructure<NavLink, NavDropdown, NavLink>(\n menu.items,\n {\n toLink: toNavLink,\n createDropdown: (id, label, children) => ({\n kind: 'dropdown',\n id,\n label,\n children,\n }),\n extractCta: true,\n }\n );\n}\n\n/** @deprecated Use `buildMenu` instead */\nexport const buildSimpleMenu = buildMenu;\n\n/**\n * Resolve href from a link payload using route map.\n * @internal\n */\nfunction resolveHref(link: LinkPayload | null, routes: RouteMap): string | null {\n if (!link) return null;\n\n // External or custom URL links - use href directly\n if (link.kind === 'external' || link.kind === 'url') {\n return link.href || null;\n }\n\n // Internal link - resolve from route map\n if (link.kind === 'internal' && link.routeId) {\n const route = routes[link.routeId];\n if (route) {\n if (typeof route === 'string') {\n return route;\n }\n // Try path first, then href, then draftPath\n return route.path ?? route.href ?? route.draftPath ?? link.href ?? null;\n }\n // Fall back to link.href if route not found\n return link.href ?? null;\n }\n\n return null;\n}\n\n/**\n * Build a Logo from site layout data.\n *\n * @param logo - Logo data from site layout\n * @param fallbackAlt - Fallback alt text (usually site title)\n *\n * @example\n * ```ts\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * // { src: 'https://...', alt: 'Site Name', width: 200, height: 50 }\n * ```\n */\nexport function buildLogo(\n logo: Partial<LogoSource> | null,\n fallbackAlt: string | null | undefined,\n): Logo {\n if (!logo || !logo.url) {\n return null;\n }\n\n const alt = resolveAltText(logo.alt, fallbackAlt);\n\n const result: NonNullable<Logo> = {\n type: 'image',\n src: logo.url,\n alt,\n };\n\n if (logo.width != null) {\n result.width = logo.width;\n }\n if (logo.height != null) {\n result.height = logo.height;\n }\n\n return result;\n}\n\n/** @deprecated Use `buildLogo` instead */\nexport const buildSimpleLogo = buildLogo;\n\n// Re-export types for convenience\nexport type { NavigationMenuWithItems, NavigationItemRecord, LinkPayload } from '@riverbankcms/api';\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/navigation/index.ts"],"sourcesContent":["/**\n * Navigation helper utilities for SDK sites.\n *\n * Provides helpers to transform CMS navigation data into render-ready structures.\n * All navigation functions return nested structures supporting dropdowns.\n *\n * @example Getting nav items\n * ```ts\n * import { getPrimaryNavItems, isNavLink, isNavDropdown } from '@riverbankcms/sdk/navigation';\n *\n * const headerNav = getPrimaryNavItems(siteData.navigation);\n * headerNav.forEach(item => {\n * if (isNavLink(item)) {\n * console.log(item.href);\n * } else {\n * console.log(`${item.label} has ${item.children.length} children`);\n * }\n * });\n * ```\n *\n * @example Building menu and logo\n * ```ts\n * import { buildMenu, buildLogo } from '@riverbankcms/sdk/navigation';\n *\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { NavigationMenuWithItems, NavigationItemRecord, LinkPayload } from '@riverbankcms/api';\nimport type { RouteMap } from '@riverbankcms/blocks';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types for block rendering (full LinkValue data)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Link value types matching @riverbankcms/blocks\n */\nexport type InternalLinkValue = {\n kind: 'internal';\n routeId: string;\n entityId: string;\n entityType: 'page' | 'content';\n href: string;\n title: string;\n typeLabel: string;\n contentTypeKey?: string | null;\n contentTypeName?: string | null;\n updatedAt?: string | null;\n};\n\nexport type ExternalLinkValue = {\n kind: 'external';\n href: string;\n};\n\nexport type CustomLinkValue = {\n kind: 'url';\n href: string;\n};\n\nexport type LinkValue = InternalLinkValue | ExternalLinkValue | CustomLinkValue;\n\n/**\n * Menu link view model for block rendering\n */\nexport type MenuLinkViewModel = {\n id: string;\n label: string;\n link: LinkValue | null;\n target: string | null;\n rel: string | null;\n active?: boolean;\n};\n\n/**\n * CTA link view model (extends MenuLinkViewModel with variant)\n */\nexport type MenuCtaViewModel = MenuLinkViewModel & {\n variant: string;\n};\n\n/**\n * Complete menu view model for header/footer blocks\n */\nexport type MenuViewModel = {\n items: MenuLinkViewModel[];\n ctaItem: MenuCtaViewModel | null;\n};\n\n/**\n * Logo source data from site layout\n */\nexport type LogoSource = {\n url: string | null;\n alt: string | null;\n assetId?: string | null;\n width?: number | null;\n height?: number | null;\n storagePath?: string | null;\n storageBucket?: string | null;\n} | null;\n\n/**\n * Logo view model for block rendering\n */\nexport type LogoViewModel = {\n type: 'image';\n src: string;\n alt: string;\n assetId?: string;\n width?: number | null;\n height?: number | null;\n storagePath?: string;\n storageBucket?: string;\n} | null;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Navigation Item Types (always nested)\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * A navigation link item with a direct URL.\n * Use `kind` discriminator for type-safe narrowing.\n *\n * @example\n * ```ts\n * if (item.kind === 'link') {\n * console.log(item.href); // TypeScript knows href exists\n * }\n * ```\n */\nexport type NavLink = {\n /** Discriminator for type narrowing */\n kind: 'link';\n /** Unique identifier from CMS */\n id: string;\n /** Display text for the navigation link */\n label: string;\n /** The URL to navigate to */\n href: string;\n /** Whether link should open in new tab (external links) */\n isExternal: boolean;\n};\n\n/**\n * A dropdown container holding child navigation items.\n * Clicking reveals children instead of navigating.\n * Max 1 level of nesting (children are always NavLink, not NavDropdown).\n */\nexport type NavDropdown = {\n /** Discriminator for type narrowing */\n kind: 'dropdown';\n /** Unique identifier from CMS */\n id: string;\n /** Display text for the dropdown trigger */\n label: string;\n /** Child navigation links */\n children: NavLink[];\n};\n\n/**\n * Navigation item - either a link or a dropdown container.\n * Use `kind` property for type-safe discrimination.\n *\n * @example\n * ```ts\n * const items = getPrimaryNavItems(navigation);\n * items.forEach(item => {\n * if (item.kind === 'link') {\n * console.log(item.href);\n * } else {\n * console.log(item.children.length);\n * }\n * });\n * ```\n */\nexport type NavItem = NavLink | NavDropdown;\n\n/**\n * Type guard to check if a navigation item is a link.\n */\nexport function isNavLink(item: NavItem): item is NavLink {\n return item.kind === 'link';\n}\n\n/**\n * Type guard to check if a navigation item is a dropdown.\n */\nexport function isNavDropdown(item: NavItem): item is NavDropdown {\n return item.kind === 'dropdown';\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu and Logo types for SDK sites\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Navigation menu with items and optional CTA.\n * Built from CMS navigation data with pre-resolved hrefs.\n *\n * @example\n * ```ts\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * menu.items.forEach(item => {\n * if (item.kind === 'link') {\n * console.log(item.href);\n * } else {\n * console.log(item.children);\n * }\n * });\n * ```\n */\nexport type Menu = {\n items: NavItem[];\n ctaItem: NavLink | null;\n};\n\n/**\n * Logo data for site rendering.\n *\n * @example\n * ```ts\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * if (logo) {\n * <img src={logo.src} alt={logo.alt} />\n * }\n * ```\n */\nexport type Logo = {\n type: 'image';\n src: string;\n alt: string;\n width?: number;\n height?: number;\n} | null;\n\n// Legacy type aliases for backwards compatibility\n/** @deprecated Use `NavLink` instead */\nexport type SimpleNavLink = NavLink;\n/** @deprecated Use `NavDropdown` instead */\nexport type SimpleNavDropdown = NavDropdown;\n/** @deprecated Use `NavItem` instead */\nexport type SimpleNavItem = NavItem;\n/** @deprecated Use `Menu` instead */\nexport type SimpleMenuViewModel = Menu;\n/** @deprecated Use `Logo` instead */\nexport type SimpleLogo = Logo;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu selection helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get the primary navigation menu object.\n * Returns menu marked as isPrimary, or first menu if none marked.\n *\n * @example\n * ```ts\n * const menu = getPrimaryNavigation(siteData.navigation);\n * console.log(menu?.name); // \"main\"\n * ```\n */\nexport function getPrimaryNavigation(\n navigation: NavigationMenuWithItems[],\n): NavigationMenuWithItems | null {\n if (!navigation || navigation.length === 0) return null;\n return navigation.find((menu) => menu.isPrimary) ?? navigation[0] ?? null;\n}\n\n/**\n * Get a navigation menu by name/slug.\n *\n * @example\n * ```ts\n * const footerMenu = getNavigationBySlug(siteData.navigation, 'footer');\n * ```\n */\nexport function getNavigationBySlug(\n navigation: NavigationMenuWithItems[],\n slug: string,\n): NavigationMenuWithItems | null {\n if (!navigation || navigation.length === 0) return null;\n return navigation.find((menu) => menu.name === slug) ?? null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// NavItem transformations\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Get nav items from the primary menu (marked isPrimary, or first menu).\n * Returns nested structure supporting both links and dropdowns.\n *\n * @example\n * ```ts\n * const headerNav = getPrimaryNavItems(siteData.navigation);\n * headerNav.forEach(item => {\n * if (item.kind === 'dropdown') {\n * console.log(`${item.label} has ${item.children.length} children`);\n * }\n * });\n * ```\n */\nexport function getPrimaryNavItems(navigation: NavigationMenuWithItems[]): NavItem[] {\n return transformToNavItems(getPrimaryNavigation(navigation));\n}\n\n/**\n * Get nav items from a specific menu by slug.\n * Returns nested structure supporting both links and dropdowns.\n *\n * @example\n * ```ts\n * const footerNav = getNavItemsBySlug(siteData.navigation, 'footer');\n * ```\n */\nexport function getNavItemsBySlug(navigation: NavigationMenuWithItems[], slug: string): NavItem[] {\n return transformToNavItems(getNavigationBySlug(navigation, slug));\n}\n\n/**\n * Transform a menu into NavItem array.\n * Builds nested structure from flat items, supporting dropdowns with children.\n *\n * @example\n * ```ts\n * const nav = transformToNavItems(menu);\n * // [\n * // { kind: 'link', href: '/', label: 'Home', ... },\n * // { kind: 'dropdown', label: 'Services', children: [...] },\n * // ]\n * ```\n */\nexport function transformToNavItems(menu: NavigationMenuWithItems | null): NavItem[] {\n if (!menu?.items || menu.items.length === 0) return [];\n\n const toNavLink = (item: NavigationItemRecord): NavLink | null => {\n const href = extractHref(item);\n if (!href) return null;\n return {\n kind: 'link',\n id: item.id,\n label: item.label,\n href,\n isExternal: isExternalLink(item),\n };\n };\n\n const { items } = buildNestedStructure<NavLink, NavDropdown, NavLink>(\n menu.items,\n {\n toLink: toNavLink,\n createDropdown: (id, label, children) => ({\n kind: 'dropdown',\n id,\n label,\n children,\n }),\n extractCta: false,\n }\n );\n\n return items;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Full MenuViewModel for block rendering\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a MenuViewModel from navigation data for block rendering.\n * Extracts CTA item separately and preserves full link data.\n *\n * @example\n * ```ts\n * const menuViewModel = buildMenuViewModel(siteData.navigation);\n *\n * renderBlock(siteHeaderManifest, layout.header, {\n * viewModelOverrides: { menu: menuViewModel },\n * });\n * ```\n */\nexport function buildMenuViewModel(navigation: NavigationMenuWithItems[]): MenuViewModel {\n const menu = getPrimaryNavigation(navigation);\n\n if (!menu) {\n return { items: [], ctaItem: null };\n }\n\n const flatItems = menu.items\n .filter((item) => !item.parentId && item.urlType !== 'dropdown')\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));\n\n const items: MenuLinkViewModel[] = [];\n let ctaItem: MenuCtaViewModel | null = null;\n\n for (const item of flatItems) {\n const viewItem: MenuLinkViewModel = {\n id: item.id,\n label: item.label,\n link: convertToLinkValue(item),\n target: null,\n rel: null,\n active: false,\n };\n\n // Extract first CTA item separately\n if (!ctaItem && Boolean(item.isCta)) {\n ctaItem = { ...viewItem, variant: 'primary' };\n continue;\n }\n\n items.push(viewItem);\n }\n\n return { items, ctaItem };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Logo view model helper\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a LogoViewModel from site layout data.\n *\n * @param logo - Logo source from siteData.layout.logo\n * @param fallbackTitle - Site title to use as alt text fallback\n *\n * @example\n * ```ts\n * const logoViewModel = buildLogoViewModel(siteData.layout.logo, siteData.site.title);\n *\n * renderBlock(siteHeaderManifest, layout.header, {\n * viewModelOverrides: { content: { logo: logoViewModel } },\n * });\n * ```\n */\nexport function buildLogoViewModel(\n logo: LogoSource,\n fallbackTitle: string | null | undefined,\n): LogoViewModel {\n if (!logo) {\n return null;\n }\n\n // Logo must have storagePath (for direct Supabase URL) or explicit url\n if (!logo.url && !logo.storagePath) {\n return null;\n }\n\n return {\n type: 'image',\n src: logo.url ?? '', // Empty when using storagePath - MediaNode builds direct URL\n alt: resolveAltText(logo.alt, fallbackTitle),\n assetId: logo.assetId ?? undefined,\n width: logo.width ?? undefined,\n height: logo.height ?? undefined,\n storagePath: logo.storagePath ?? undefined,\n storageBucket: logo.storageBucket ?? undefined,\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Internal helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Resolve alt text with fallback.\n * Returns the provided alt if non-empty, otherwise falls back to the provided fallback or 'Site logo'.\n * @internal\n */\nfunction resolveAltText(alt: string | null | undefined, fallback: string | null | undefined): string {\n return (alt && alt.trim().length > 0) ? alt : (fallback ?? 'Site logo');\n}\n\n/**\n * Extracts the href from a navigation item's url field.\n */\nfunction extractHref(item: NavigationItemRecord): string {\n const link = item.url as LinkPayload | null | undefined;\n if (!link || typeof link !== 'object' || !('href' in link)) return '';\n return link.href;\n}\n\n/**\n * Determines if a navigation item's link is external.\n */\nfunction isExternalLink(item: NavigationItemRecord): boolean {\n const link = item.url as LinkPayload | null | undefined;\n if (!link || typeof link !== 'object' || !('kind' in link)) return false;\n return link.kind === 'external';\n}\n\n/**\n * Build a map of children items by parent ID.\n * Also warns about deeply nested items (grandchildren) which CMS shouldn't allow.\n * @internal\n */\nfunction buildChildrenByParentId(\n items: NavigationItemRecord[]\n): Map<string, NavigationItemRecord[]> {\n const childrenByParentId = new Map<string, NavigationItemRecord[]>();\n const itemsById = new Map<string, NavigationItemRecord>();\n\n // First pass: index items by ID\n for (const item of items) {\n itemsById.set(item.id, item);\n }\n\n // Second pass: build children map and warn about deep nesting\n for (const item of items) {\n if (item.parentId) {\n // Check if parent is itself a child (deep nesting)\n const parent = itemsById.get(item.parentId);\n if (parent?.parentId && typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') {\n console.warn(\n `[SDK Navigation] Deeply nested item detected: \"${item.label}\" (id: ${item.id}). ` +\n `Only 1 level of nesting is supported. This item will be ignored.`\n );\n continue; // Skip deeply nested items\n }\n\n const siblings = childrenByParentId.get(item.parentId) ?? [];\n siblings.push(item);\n childrenByParentId.set(item.parentId, siblings);\n }\n }\n\n return childrenByParentId;\n}\n\n/**\n * Generic nested structure builder.\n * @internal\n */\nfunction buildNestedStructure<TLink, TDropdown, TCta extends TLink>(\n items: NavigationItemRecord[],\n options: {\n /** Convert a navigation item record to a link type */\n toLink: (item: NavigationItemRecord) => TLink | null;\n /** Create a dropdown from id, label, and children */\n createDropdown: (id: string, label: string, children: TLink[]) => TDropdown;\n /** Extract CTA from items (return null to skip CTA extraction) */\n extractCta?: boolean;\n }\n): { items: (TLink | TDropdown)[]; ctaItem: TCta | null } {\n const childrenByParentId = buildChildrenByParentId(items);\n\n // Process root items (no parentId) sorted by orderIndex\n const rootItems = items\n .filter((item) => !item.parentId)\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0));\n\n const result: (TLink | TDropdown)[] = [];\n let ctaItem: TCta | null = null;\n\n for (const item of rootItems) {\n // Handle CTA extraction (CTAs are always links, not dropdowns)\n if (options.extractCta && !ctaItem && Boolean(item.isCta) && item.urlType !== 'dropdown') {\n const link = options.toLink(item);\n if (link) {\n ctaItem = link as TCta;\n continue;\n }\n }\n\n if (item.urlType === 'dropdown') {\n // It's a dropdown - gather its children\n const childRecords = childrenByParentId.get(item.id) ?? [];\n const children = childRecords\n .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0))\n .map(options.toLink)\n .filter((child): child is TLink => child !== null);\n\n // Only include dropdown if it has children\n if (children.length > 0) {\n result.push(options.createDropdown(item.id, item.label, children));\n }\n } else {\n // It's a regular link\n const link = options.toLink(item);\n if (link) {\n result.push(link);\n }\n }\n }\n\n return { items: result, ctaItem };\n}\n\n/**\n * Convert navigation item to LinkValue for block rendering.\n */\nfunction convertToLinkValue(item: NavigationItemRecord): LinkValue | null {\n const payload = item.url as Record<string, unknown> | null;\n if (!payload) return null;\n\n const kind = typeof payload.kind === 'string' ? payload.kind : null;\n\n if (kind === 'external' || kind === 'url') {\n const href = typeof payload.href === 'string' ? payload.href : null;\n return href ? { kind, href } as ExternalLinkValue | CustomLinkValue : null;\n }\n\n if (kind === 'internal') {\n const routeId = typeof payload.routeId === 'string' ? payload.routeId : null;\n const entityId = typeof payload.entityId === 'string' ? payload.entityId : null;\n const entityType = payload.entityType === 'page' || payload.entityType === 'content'\n ? payload.entityType\n : null;\n const href = typeof payload.href === 'string' ? payload.href : null;\n const title = typeof payload.title === 'string' ? payload.title : null;\n const typeLabel = typeof payload.typeLabel === 'string' ? payload.typeLabel : null;\n\n if (!routeId || !entityId || !entityType || !href || !title || !typeLabel) {\n return null;\n }\n\n return {\n kind: 'internal',\n routeId,\n entityId,\n entityType,\n href,\n title,\n typeLabel,\n contentTypeKey: typeof payload.contentTypeKey === 'string' ? payload.contentTypeKey : null,\n contentTypeName: typeof payload.contentTypeName === 'string' ? payload.contentTypeName : null,\n updatedAt: typeof payload.updatedAt === 'string' ? payload.updatedAt : null,\n };\n }\n\n return null;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Menu and Logo builders\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Build a Menu from navigation data with pre-resolved hrefs.\n * Supports dropdown containers with nested children.\n *\n * @param navigation - Navigation menus from site data\n * @param routes - Route map for resolving internal links\n *\n * @example\n * ```ts\n * const menu = buildMenu(siteData.navigation, siteData.routes);\n * menu.items.forEach(item => {\n * if (item.kind === 'dropdown') {\n * console.log(item.children);\n * }\n * });\n * ```\n */\nexport function buildMenu(\n navigation: NavigationMenuWithItems[],\n routes: RouteMap,\n): Menu {\n const menu = getPrimaryNavigation(navigation);\n\n if (!menu) {\n return { items: [], ctaItem: null };\n }\n\n const toNavLink = (item: NavigationItemRecord): NavLink | null => {\n const link = item.url as LinkPayload | null;\n const href = resolveHref(link, routes);\n if (!href) return null;\n\n return {\n kind: 'link',\n id: item.id,\n label: item.label,\n href,\n isExternal: link?.kind === 'external',\n };\n };\n\n return buildNestedStructure<NavLink, NavDropdown, NavLink>(\n menu.items,\n {\n toLink: toNavLink,\n createDropdown: (id, label, children) => ({\n kind: 'dropdown',\n id,\n label,\n children,\n }),\n extractCta: true,\n }\n );\n}\n\n/**\n * Resolve href from a link payload using route map.\n * @internal\n */\nfunction resolveHref(link: LinkPayload | null, routes: RouteMap): string | null {\n if (!link) return null;\n\n // External or custom URL links - use href directly\n if (link.kind === 'external' || link.kind === 'url') {\n return link.href || null;\n }\n\n // Internal link - resolve from route map\n if (link.kind === 'internal' && link.routeId) {\n const route = routes[link.routeId];\n if (route) {\n if (typeof route === 'string') {\n return route;\n }\n // Try path first, then href, then draftPath\n return route.path ?? route.href ?? route.draftPath ?? link.href ?? null;\n }\n // Fall back to link.href if route not found\n return link.href ?? null;\n }\n\n return null;\n}\n\n/**\n * Build a Logo from site layout data.\n *\n * @param logo - Logo data from site layout\n * @param fallbackAlt - Fallback alt text (usually site title)\n *\n * @example\n * ```ts\n * const logo = buildLogo(siteData.layout.logo, siteData.site.title);\n * // { src: 'https://...', alt: 'Site Name', width: 200, height: 50 }\n * ```\n */\nexport function buildLogo(\n logo: Partial<LogoSource> | null,\n fallbackAlt: string | null | undefined,\n): Logo {\n if (!logo || !logo.url) {\n return null;\n }\n\n const alt = resolveAltText(logo.alt, fallbackAlt);\n\n const result: NonNullable<Logo> = {\n type: 'image',\n src: logo.url,\n alt,\n };\n\n if (logo.width != null) {\n result.width = logo.width;\n }\n if (logo.height != null) {\n result.height = logo.height;\n }\n\n return result;\n}\n\n// Re-export types for convenience\nexport type { NavigationMenuWithItems, NavigationItemRecord, LinkPayload } from '@riverbankcms/api';\n"],"mappings":"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","names":[]}
@@ -108,7 +108,11 @@ var uiSchema = z2.object({
108
108
  layout: z2.enum(["stack", "grid"]).optional(),
109
109
  columns: z2.number().int().min(2).max(4).optional(),
110
110
  // Entry picker configuration
111
- contentTypeField: z2.string().optional()
111
+ contentTypeField: z2.string().optional(),
112
+ // Extras pattern: fields marked as extras are hidden behind a modal toggle
113
+ extras: z2.boolean().optional(),
114
+ // Render in block header instead of form body (used for section styles)
115
+ renderInHeader: z2.boolean().optional()
112
116
  }).partial();
113
117
  var baseFieldSchema = z2.object({
114
118
  id: z2.string().min(1, "Field id is required"),
@@ -1366,25 +1370,30 @@ function createButtonGroup(options = {}) {
1366
1370
  ui: { colSpan: 2 }
1367
1371
  }
1368
1372
  ];
1369
- const iconsGroup = {
1370
- id: "icons",
1371
- type: "group",
1372
- label: "Icons",
1373
- required: false,
1374
- ui: { preset: "disclosure", colSpan: 2 },
1375
- schema: {
1376
- fields: [
1377
- { id: "iconLeft", type: "media", label: "Left icon", required: false, mediaKinds: ["image"] },
1378
- { id: "iconRight", type: "media", label: "Right icon", required: false, mediaKinds: ["image"] }
1379
- ]
1373
+ const iconFields = [
1374
+ {
1375
+ id: "iconLeft",
1376
+ type: "media",
1377
+ label: "Left icon",
1378
+ required: false,
1379
+ mediaKinds: ["image"],
1380
+ ui: { extras: true }
1381
+ },
1382
+ {
1383
+ id: "iconRight",
1384
+ type: "media",
1385
+ label: "Right icon",
1386
+ required: false,
1387
+ mediaKinds: ["image"],
1388
+ ui: { extras: true }
1380
1389
  }
1381
- };
1390
+ ];
1382
1391
  return {
1383
1392
  id: groupId,
1384
1393
  type: "group",
1385
1394
  label: groupLabel,
1386
1395
  ui: { layout: "grid", columns: 2, flattenInRepeater, hideLabel: !showGroupLabel },
1387
- schema: { fields: [...mainFields, iconsGroup] },
1396
+ schema: { fields: [...mainFields, ...iconFields] },
1388
1397
  required: false
1389
1398
  };
1390
1399
  }
@@ -2705,6 +2714,10 @@ function getAnchorClasses(position) {
2705
2714
  }
2706
2715
 
2707
2716
  // ../blocks/src/system/fields/background.ts
2717
+ var BACKGROUND_WIDGETS = {
2718
+ COLOR: "backgroundColor",
2719
+ GRADIENT: "backgroundGradient"
2720
+ };
2708
2721
  function createBackgroundField(options = {}) {
2709
2722
  const {
2710
2723
  id = "background",
@@ -2730,8 +2743,7 @@ function createBackgroundField(options = {}) {
2730
2743
  required: false,
2731
2744
  multiline: false,
2732
2745
  ui: {
2733
- // Use BackgroundColorWidget via widget override
2734
- widget: "backgroundColor"
2746
+ widget: BACKGROUND_WIDGETS.COLOR
2735
2747
  }
2736
2748
  }
2737
2749
  ]
@@ -2748,11 +2760,11 @@ function createBackgroundField(options = {}) {
2748
2760
  id: "gradient",
2749
2761
  type: "text",
2750
2762
  label: "Gradient",
2751
- description: "CSS gradient value (e.g., linear-gradient(to right, #ff0000, #00ff00)).",
2763
+ description: "Select a gradient from theme presets.",
2752
2764
  required: false,
2753
- multiline: true,
2765
+ multiline: false,
2754
2766
  ui: {
2755
- placeholder: "linear-gradient(to right, #ff0000, #00ff00)"
2767
+ widget: BACKGROUND_WIDGETS.GRADIENT
2756
2768
  }
2757
2769
  }
2758
2770
  ]
@@ -2811,7 +2823,7 @@ function createBackgroundField(options = {}) {
2811
2823
  id: "position",
2812
2824
  type: "presetOrCustom",
2813
2825
  label: "Position",
2814
- description: 'Anchor point for the image. Relevant for "Fill" and "Custom size" options.',
2826
+ description: 'Anchor point for scaled images. For "Fill" mode, the image focus point (if set) takes precedence.',
2815
2827
  required: false,
2816
2828
  presets: [...BACKGROUND_POSITION_PRESETS],
2817
2829
  customInput: {
@@ -2902,6 +2914,8 @@ function sectionStylesField(options = {}) {
2902
2914
  required: false,
2903
2915
  schema: { fields: fields4 },
2904
2916
  ui: {
2917
+ // Render in block header instead of form body
2918
+ renderInHeader: true,
2905
2919
  modalConfig: {
2906
2920
  buttonLabel: label,
2907
2921
  description: "Configure background and spacing for this section.",
@@ -5473,4 +5487,4 @@ export {
5473
5487
  siteFooterManifest,
5474
5488
  getBlockDefinition
5475
5489
  };
5476
- //# sourceMappingURL=chunk-BYBJA6SP.mjs.map
5490
+ //# sourceMappingURL=chunk-A3UZ2LDH.mjs.map