@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.
- package/dist/cli/index.js +583 -147
- package/dist/cli/index.js.map +1 -1
- package/dist/client/client.d.mts +2 -2
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +126 -30
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.mjs +126 -30
- package/dist/client/client.mjs.map +1 -1
- package/dist/client/hooks.d.mts +2 -2
- package/dist/client/hooks.d.ts +2 -2
- package/dist/client/hooks.js +34 -20
- package/dist/client/hooks.js.map +1 -1
- package/dist/client/hooks.mjs +34 -20
- package/dist/client/hooks.mjs.map +1 -1
- package/dist/client/rendering/client.js +182 -123
- package/dist/client/rendering/client.js.map +1 -1
- package/dist/client/rendering/client.mjs +174 -109
- package/dist/client/rendering/client.mjs.map +1 -1
- package/dist/client/usePage-Dsi39Exp.d.ts +6915 -0
- package/dist/client/usePage-Im82JRRe.d.mts +6915 -0
- package/dist/server/{Layout-l2v4Qa6E.d.ts → Layout-CZ-kxKfl.d.ts} +1 -1
- package/dist/server/{Layout-D4J009eS.d.mts → Layout-ESG8zvrk.d.mts} +1 -1
- package/dist/server/{chunk-4YQJUL5W.mjs → chunk-5GCSRTIU.mjs} +8 -4
- package/dist/server/chunk-5GCSRTIU.mjs.map +1 -0
- package/dist/server/{chunk-YYO3RIFO.js → chunk-6ERSDFTY.js} +35 -21
- package/dist/server/chunk-6ERSDFTY.js.map +1 -0
- package/dist/server/{chunk-YXA4GAAQ.mjs → chunk-6VTKALLN.mjs} +2 -6
- package/dist/server/{chunk-YXA4GAAQ.mjs.map → chunk-6VTKALLN.mjs.map} +1 -1
- package/dist/server/{chunk-BYBJA6SP.mjs → chunk-A3UZ2LDH.mjs} +35 -21
- package/dist/server/chunk-A3UZ2LDH.mjs.map +1 -0
- package/dist/server/{chunk-OSF34JTQ.mjs → chunk-ADD3O2QO.mjs} +4 -4
- package/dist/server/{chunk-C6FIJC7T.mjs → chunk-BNHK7YOC.mjs} +2 -2
- package/dist/server/{chunk-TT5JWA4X.js → chunk-DAXWU3S3.js} +9 -9
- package/dist/server/{chunk-TT5JWA4X.js.map → chunk-DAXWU3S3.js.map} +1 -1
- package/dist/server/{chunk-7UPVCT3K.js → chunk-F2NDLDDA.js} +239 -154
- package/dist/server/chunk-F2NDLDDA.js.map +1 -0
- package/dist/server/{chunk-LNOUXALA.mjs → chunk-FUFPKTSI.mjs} +96 -11
- package/dist/server/chunk-FUFPKTSI.mjs.map +1 -0
- package/dist/server/{chunk-65A5HAUZ.mjs → chunk-GRFFJUCO.mjs} +3 -3
- package/dist/server/{chunk-65A5HAUZ.mjs.map → chunk-GRFFJUCO.mjs.map} +1 -1
- package/dist/server/{chunk-AEFWG657.mjs → chunk-HDHY4236.mjs} +2 -2
- package/dist/server/{chunk-2KCF2DNK.js → chunk-HE3RTUDX.js} +8 -8
- package/dist/server/{chunk-2KCF2DNK.js.map → chunk-HE3RTUDX.js.map} +1 -1
- package/dist/server/{chunk-RVDS7VSP.js → chunk-IJTJH4J3.js} +4 -4
- package/dist/server/{chunk-RVDS7VSP.js.map → chunk-IJTJH4J3.js.map} +1 -1
- package/dist/server/{chunk-P3NNN73G.js → chunk-K44OPKLA.js} +3 -3
- package/dist/server/{chunk-P3NNN73G.js.map → chunk-K44OPKLA.js.map} +1 -1
- package/dist/server/{chunk-EIJ27EZQ.js → chunk-KDCVCDW6.js} +10 -6
- package/dist/server/chunk-KDCVCDW6.js.map +1 -0
- package/dist/server/{chunk-7BVRA5MY.js → chunk-KGORQCHF.js} +9 -9
- package/dist/server/{chunk-7BVRA5MY.js.map → chunk-KGORQCHF.js.map} +1 -1
- package/dist/server/{chunk-WM646WI3.js → chunk-MFNWLB5G.js} +7 -7
- package/dist/server/{chunk-WM646WI3.js.map → chunk-MFNWLB5G.js.map} +1 -1
- package/dist/server/{chunk-EIVISR62.js → chunk-P4O3WSAR.js} +2 -6
- package/dist/server/chunk-P4O3WSAR.js.map +1 -0
- package/dist/server/{chunk-RBJFXNDM.mjs → chunk-PGZJUNCY.mjs} +4 -4
- package/dist/server/{chunk-ARNCLSQT.mjs → chunk-T5PAA22U.mjs} +2 -2
- package/dist/server/{chunk-T26N3P26.js → chunk-TLZHVGTL.js} +4 -4
- package/dist/server/{chunk-T26N3P26.js.map → chunk-TLZHVGTL.js.map} +1 -1
- package/dist/server/{chunk-P4K63SBZ.mjs → chunk-TR7MSLWL.mjs} +3 -3
- package/dist/server/{chunk-NFEGQTCC.mjs → chunk-WMJKH4XE.mjs} +8 -1
- package/dist/server/{chunk-4CV4JOE5.js → chunk-Z6ZWNWWR.js} +9 -2
- package/dist/server/chunk-Z6ZWNWWR.js.map +1 -0
- package/dist/server/{components-D2uCKCj7.d.ts → components-CE48wJM1.d.ts} +4 -4
- package/dist/server/{components-vtYEmmPF.d.mts → components-iEDvl2Yw.d.mts} +4 -4
- package/dist/server/components.d.mts +6 -6
- package/dist/server/components.d.ts +6 -6
- package/dist/server/components.js +7 -7
- package/dist/server/components.mjs +6 -6
- package/dist/server/config-validation.d.mts +3 -3
- package/dist/server/config-validation.d.ts +3 -3
- package/dist/server/config-validation.js +6 -6
- package/dist/server/config-validation.mjs +5 -5
- package/dist/server/config.d.mts +5 -5
- package/dist/server/config.d.ts +5 -5
- package/dist/server/config.js +6 -6
- package/dist/server/config.mjs +5 -5
- package/dist/server/data.d.mts +3 -3
- package/dist/server/data.d.ts +3 -3
- package/dist/server/data.js +4 -4
- package/dist/server/data.mjs +3 -3
- package/dist/server/env.js +1 -1
- package/dist/server/env.mjs +1 -1
- package/dist/server/{index-BxrAuL9K.d.ts → index-BHLK2mgQ.d.ts} +2 -2
- package/dist/server/{index-DfWg1Qle.d.mts → index-BrH_NIRO.d.mts} +2 -2
- package/dist/server/{index-2qnY7VH_.d.mts → index-Cgvb5fVQ.d.mts} +2 -2
- package/dist/server/{index-CH_dvF6n.d.ts → index-DTBg8eXj.d.ts} +2 -2
- package/dist/server/index.d.mts +6 -6
- package/dist/server/index.d.ts +6 -6
- package/dist/server/index.js +11 -11
- package/dist/server/index.mjs +2 -2
- package/dist/server/{loadContent-DECnsp4k.d.ts → loadContent-BUK6IVJf.d.ts} +26 -4
- package/dist/server/{loadContent-Du5kS8UM.d.mts → loadContent-au9Weoy0.d.mts} +26 -4
- package/dist/server/loadPage-AWYZ2QA2.mjs +11 -0
- package/dist/server/loadPage-CMHYAW2J.js +11 -0
- package/dist/server/{loadPage-AXNAERDS.js.map → loadPage-CMHYAW2J.js.map} +1 -1
- package/dist/server/{loadPage-VBorKlWv.d.mts → loadPage-DiHEl8BA.d.mts} +3 -3
- package/dist/server/{loadPage-BZohBxxf.d.ts → loadPage-JOIbF7ih.d.ts} +3 -3
- package/dist/server/metadata.d.mts +5 -5
- package/dist/server/metadata.d.ts +5 -5
- package/dist/server/metadata.js +1 -1
- package/dist/server/metadata.mjs +1 -1
- package/dist/server/navigation.d.mts +4 -8
- package/dist/server/navigation.d.ts +4 -8
- package/dist/server/navigation.js +3 -7
- package/dist/server/navigation.js.map +1 -1
- package/dist/server/navigation.mjs +2 -6
- package/dist/server/next/revalidate.js +1 -1
- package/dist/server/next/revalidate.mjs +1 -1
- package/dist/server/next/tags.js +1 -1
- package/dist/server/next/tags.mjs +1 -1
- package/dist/server/next.d.mts +7 -7
- package/dist/server/next.d.ts +7 -7
- package/dist/server/next.js +20 -16
- package/dist/server/next.js.map +1 -1
- package/dist/server/next.mjs +12 -8
- package/dist/server/next.mjs.map +1 -1
- package/dist/server/rendering/server.d.mts +5 -5
- package/dist/server/rendering/server.d.ts +5 -5
- package/dist/server/rendering/server.js +9 -9
- package/dist/server/rendering/server.mjs +8 -8
- package/dist/server/rendering.d.mts +8 -8
- package/dist/server/rendering.d.ts +8 -8
- package/dist/server/rendering.js +11 -11
- package/dist/server/rendering.mjs +10 -10
- package/dist/server/routing.d.mts +5 -5
- package/dist/server/routing.d.ts +5 -5
- package/dist/server/routing.js +2 -2
- package/dist/server/routing.mjs +2 -2
- package/dist/server/{schema-Z6-afHJG.d.mts → schema-DYtW0zEu.d.mts} +40 -0
- package/dist/server/{schema-Z6-afHJG.d.ts → schema-DYtW0zEu.d.ts} +40 -0
- package/dist/server/server.d.mts +6 -6
- package/dist/server/server.d.ts +6 -6
- package/dist/server/server.js +7 -7
- package/dist/server/server.mjs +6 -6
- package/dist/server/theme-bridge.js +9 -9
- package/dist/server/theme-bridge.mjs +3 -3
- package/dist/server/theme.js +1 -1
- package/dist/server/theme.mjs +1 -1
- package/dist/server/{types-DT30Qy7x.d.mts → types-BAM1kcGA.d.mts} +1 -1
- package/dist/server/{types-D0rPF8l5.d.ts → types-CmBB0Osp.d.ts} +2 -2
- package/dist/server/{types-BRQ_6yOc.d.mts → types-DDNKxQXw.d.mts} +2 -2
- package/dist/server/{types-D8XqwoVd.d.ts → types-DVesWaB7.d.ts} +1 -1
- package/dist/server/{types-CJfJwcuL.d.mts → types-M0CviVW2.d.mts} +1 -1
- package/dist/server/{types-CgSO0yxg.d.ts → types-_SNCu2ZZ.d.ts} +1 -1
- package/dist/server/{validation-Pv3Zs6dP.d.mts → validation-BA1TKthZ.d.mts} +2 -2
- package/dist/server/{validation-D1LaY1kQ.d.ts → validation-js7BCPN8.d.ts} +2 -2
- package/dist/server/webhooks.js +1 -1
- package/dist/server/webhooks.mjs +1 -1
- package/package.json +1 -1
- package/dist/server/chunk-4CV4JOE5.js.map +0 -1
- package/dist/server/chunk-4YQJUL5W.mjs.map +0 -1
- package/dist/server/chunk-7UPVCT3K.js.map +0 -1
- package/dist/server/chunk-BYBJA6SP.mjs.map +0 -1
- package/dist/server/chunk-EIJ27EZQ.js.map +0 -1
- package/dist/server/chunk-EIVISR62.js.map +0 -1
- package/dist/server/chunk-LNOUXALA.mjs.map +0 -1
- package/dist/server/chunk-YYO3RIFO.js.map +0 -1
- package/dist/server/loadPage-AXNAERDS.js +0 -11
- package/dist/server/loadPage-XR7ORQ2E.mjs +0 -11
- /package/dist/server/{chunk-OSF34JTQ.mjs.map → chunk-ADD3O2QO.mjs.map} +0 -0
- /package/dist/server/{chunk-C6FIJC7T.mjs.map → chunk-BNHK7YOC.mjs.map} +0 -0
- /package/dist/server/{chunk-AEFWG657.mjs.map → chunk-HDHY4236.mjs.map} +0 -0
- /package/dist/server/{chunk-RBJFXNDM.mjs.map → chunk-PGZJUNCY.mjs.map} +0 -0
- /package/dist/server/{chunk-ARNCLSQT.mjs.map → chunk-T5PAA22U.mjs.map} +0 -0
- /package/dist/server/{chunk-P4K63SBZ.mjs.map → chunk-TR7MSLWL.mjs.map} +0 -0
- /package/dist/server/{chunk-NFEGQTCC.mjs.map → chunk-WMJKH4XE.mjs.map} +0 -0
- /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
|
-
|
|
271
|
-
buildLogo,
|
|
272
|
-
buildSimpleLogo
|
|
268
|
+
buildLogo
|
|
273
269
|
};
|
|
274
|
-
//# sourceMappingURL=chunk-
|
|
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
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
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,
|
|
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
|
-
|
|
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: "
|
|
2763
|
+
description: "Select a gradient from theme presets.",
|
|
2752
2764
|
required: false,
|
|
2753
|
-
multiline:
|
|
2765
|
+
multiline: false,
|
|
2754
2766
|
ui: {
|
|
2755
|
-
|
|
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
|
|
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-
|
|
5490
|
+
//# sourceMappingURL=chunk-A3UZ2LDH.mjs.map
|