bsmnt 0.10.0 → 0.12.0

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 (343) hide show
  1. package/dist/application/add-integration/index.js.map +1 -1
  2. package/dist/application/shared/node-version.js.map +1 -1
  3. package/dist/core/create/internal-ids.d.ts +3 -0
  4. package/dist/core/create/internal-ids.d.ts.map +1 -1
  5. package/dist/core/create/internal-ids.js +4 -0
  6. package/dist/core/create/internal-ids.js.map +1 -1
  7. package/dist/domain/agents.d.ts.map +1 -1
  8. package/dist/domain/animations.d.ts.map +1 -1
  9. package/dist/domain/cms.d.ts.map +1 -1
  10. package/dist/domain/forms.d.ts.map +1 -1
  11. package/dist/domain/hooks.d.ts.map +1 -1
  12. package/dist/domain/skills.d.ts.map +1 -1
  13. package/dist/domain/templates.d.ts +10 -0
  14. package/dist/domain/templates.d.ts.map +1 -1
  15. package/dist/domain/templates.js +5 -0
  16. package/dist/domain/templates.js.map +1 -1
  17. package/dist/index.js +18 -5
  18. package/dist/index.js.map +1 -1
  19. package/dist/infrastructure/agents/register-sanity-mcp.js +3 -8
  20. package/dist/infrastructure/agents/register-sanity-mcp.js.map +1 -1
  21. package/dist/infrastructure/agents/register-storybook-mcp.d.ts +18 -0
  22. package/dist/infrastructure/agents/register-storybook-mcp.d.ts.map +1 -0
  23. package/dist/infrastructure/agents/register-storybook-mcp.js +157 -0
  24. package/dist/infrastructure/agents/register-storybook-mcp.js.map +1 -0
  25. package/dist/infrastructure/create/lifecycle-registry.d.ts.map +1 -1
  26. package/dist/infrastructure/create/lifecycle-registry.js +39 -1
  27. package/dist/infrastructure/create/lifecycle-registry.js.map +1 -1
  28. package/dist/infrastructure/create/merge-registry.d.ts.map +1 -1
  29. package/dist/infrastructure/create/merge-registry.js +4 -0
  30. package/dist/infrastructure/create/merge-registry.js.map +1 -1
  31. package/dist/infrastructure/create/sanity-auth.js.map +1 -1
  32. package/dist/infrastructure/create/setup-agent.d.ts +6 -1
  33. package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
  34. package/dist/infrastructure/create/setup-agent.js +23 -21
  35. package/dist/infrastructure/create/setup-agent.js.map +1 -1
  36. package/dist/infrastructure/create/setup-dotenvx.js.map +1 -1
  37. package/dist/infrastructure/create/setup-remote.js.map +1 -1
  38. package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
  39. package/dist/infrastructure/create/setup-sanity.js +12 -1
  40. package/dist/infrastructure/create/setup-sanity.js.map +1 -1
  41. package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
  42. package/dist/infrastructure/integrations/inject-integration.js +2 -0
  43. package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
  44. package/dist/infrastructure/update/notifier.js.map +1 -1
  45. package/dist/modules/features/cms/index.d.ts.map +1 -1
  46. package/dist/modules/features/cms/index.js +2 -0
  47. package/dist/modules/features/cms/index.js.map +1 -1
  48. package/dist/modules/features/cms/sanity/config.d.ts.map +1 -1
  49. package/dist/modules/features/cms/sanity/config.js +1 -0
  50. package/dist/modules/features/cms/sanity/config.js.map +1 -1
  51. package/dist/modules/features/cms/sanity/index.d.ts.map +1 -1
  52. package/dist/modules/features/cms/sanity/index.js +10 -1
  53. package/dist/modules/features/cms/sanity/index.js.map +1 -1
  54. package/dist/modules/features/cms/sanity/mergers/check-integration.d.ts.map +1 -1
  55. package/dist/modules/features/cms/sanity/mergers/check-integration.js +15 -6
  56. package/dist/modules/features/cms/sanity/mergers/check-integration.js.map +1 -1
  57. package/dist/modules/features/cms/sanity/mergers/knip.d.ts.map +1 -1
  58. package/dist/modules/features/cms/sanity/mergers/knip.js +1 -0
  59. package/dist/modules/features/cms/sanity/mergers/knip.js.map +1 -1
  60. package/dist/modules/features/cms/sanity/mergers/layout.js.map +1 -1
  61. package/dist/modules/features/cms/sanity/mergers/package-json.d.ts +12 -0
  62. package/dist/modules/features/cms/sanity/mergers/package-json.d.ts.map +1 -0
  63. package/dist/modules/features/cms/sanity/mergers/package-json.js +19 -0
  64. package/dist/modules/features/cms/sanity/mergers/package-json.js.map +1 -0
  65. package/dist/modules/features/cms/sanity-pagebuilder/index.d.ts.map +1 -1
  66. package/dist/modules/features/cms/sanity-pagebuilder/index.js +7 -1
  67. package/dist/modules/features/cms/sanity-pagebuilder/index.js.map +1 -1
  68. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts +14 -0
  69. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.d.ts.map +1 -0
  70. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js +50 -0
  71. package/dist/modules/features/cms/sanity-pagebuilder-cache/config.js.map +1 -0
  72. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts +3 -0
  73. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.d.ts.map +1 -0
  74. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js +55 -0
  75. package/dist/modules/features/cms/sanity-pagebuilder-cache/index.js.map +1 -0
  76. package/dist/modules/presets/index.d.ts.map +1 -1
  77. package/dist/modules/presets/index.js +2 -0
  78. package/dist/modules/presets/index.js.map +1 -1
  79. package/dist/modules/presets/next-pagebuilder-cache.d.ts +3 -0
  80. package/dist/modules/presets/next-pagebuilder-cache.d.ts.map +1 -0
  81. package/dist/modules/presets/next-pagebuilder-cache.js +10 -0
  82. package/dist/modules/presets/next-pagebuilder-cache.js.map +1 -0
  83. package/dist/paths.d.ts +6 -0
  84. package/dist/paths.d.ts.map +1 -1
  85. package/dist/paths.js +6 -0
  86. package/dist/paths.js.map +1 -1
  87. package/package.json +5 -2
  88. package/src/modules/features/cms/sanity/files/components/ui/sanity-image/index.tsx +36 -4
  89. package/src/modules/features/cms/sanity/files/lib/integrations/check-integration.ts +7 -6
  90. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/README.md +1 -1
  91. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/rich-text.tsx +8 -1
  92. package/src/modules/features/cms/sanity/files/lib/integrations/sanity/env.ts +2 -2
  93. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/[[...slug]]/route.ts +4 -0
  94. package/src/modules/features/cms/sanity-pagebuilder/files/app/api/revalidate/route.ts +172 -24
  95. package/src/modules/features/cms/sanity-pagebuilder/files/app/feed.xml/route.ts +14 -7
  96. package/src/modules/features/cms/sanity-pagebuilder/files/app/sitemap.md/route.ts +2 -2
  97. package/src/modules/features/cms/sanity-pagebuilder/files/app/studio/[[...tool]]/page.tsx +16 -1
  98. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +8 -1
  99. package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/visual-editing.tsx +2 -2
  100. package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +36 -4
  101. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/check-integration.ts +6 -5
  102. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/client.ts +8 -0
  103. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-input.tsx +26 -7
  104. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-insert-menu.tsx +242 -0
  105. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/slug-notices.tsx +6 -16
  106. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/use-studio-preview-urls.ts +62 -0
  107. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/env.ts +2 -2
  108. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +22 -52
  109. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +2 -2
  110. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/live/index.tsx +3 -1
  111. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +78 -33
  112. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-tree-pane.tsx +70 -57
  113. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +119 -29
  114. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  115. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +2 -0
  116. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  117. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/studio-preview.ts +37 -0
  118. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +5 -0
  119. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/page.ts +12 -0
  120. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/singletons.ts +2 -0
  121. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +5 -0
  122. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/tags.ts +56 -0
  123. package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/utils/page-builder-markdown.ts +12 -0
  124. package/src/modules/features/cms/sanity-pagebuilder/files/lib/utils/sitemap.ts +11 -8
  125. package/src/modules/features/cms/sanity-pagebuilder-cache/files/.knip.json +8 -0
  126. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/[[...slug]]/route.ts +112 -0
  127. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/disable/route.ts +7 -0
  128. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/draft-mode/enable/route.ts +20 -0
  129. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/api/revalidate/route.ts +292 -0
  130. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/feed.xml/route.ts +66 -0
  131. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/sitemap.md/route.ts +132 -0
  132. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/layout.tsx +26 -0
  133. package/src/modules/features/cms/sanity-pagebuilder-cache/files/app/studio/[[...tool]]/page.tsx +23 -0
  134. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/draft-mode-toggle.tsx +27 -0
  135. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/rich-text.tsx +94 -0
  136. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/visual-editing.tsx +11 -0
  137. package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/ui/sanity-image/index.tsx +83 -0
  138. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/check-integration.ts +6 -0
  139. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/client.ts +37 -0
  140. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/block-actions-item.tsx +81 -0
  141. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/disable-draft-mode.tsx +23 -0
  142. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-input.tsx +58 -0
  143. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-insert-menu.tsx +242 -0
  144. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-category-input.tsx +50 -0
  145. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/same-as-input.tsx +66 -0
  146. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-field.tsx +24 -0
  147. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-input.tsx +14 -0
  148. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/slug-notices.tsx +125 -0
  149. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/use-studio-preview-urls.ts +62 -0
  150. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/confirm-publish-action.ts +40 -0
  151. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/env.ts +34 -0
  152. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/layout.ts +39 -0
  153. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/redirects.ts +22 -0
  154. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/icons.ts +81 -0
  155. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/live/index.tsx +140 -0
  156. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/markdown-proxy.config.ts +50 -0
  157. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-builder-config.ts +182 -0
  158. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-category.ts +28 -0
  159. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-tree-pane.tsx +431 -0
  160. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/pinned-pages.ts +54 -0
  161. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/presentation.ts +119 -0
  162. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts +548 -0
  163. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/revalidate-paths.ts +70 -0
  164. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.cli.ts +29 -0
  165. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/sanity.config.ts +116 -0
  166. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/index.ts +7 -0
  167. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +89 -0
  168. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/description.ts +29 -0
  169. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/gated-content.ts +178 -0
  170. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +28 -0
  171. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +39 -0
  172. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +45 -0
  173. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/studio-preview.ts +37 -0
  174. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/author.ts +70 -0
  175. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/content/blog-category.ts +55 -0
  176. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/index.ts +137 -0
  177. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/company-data.ts +70 -0
  178. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/footer.ts +79 -0
  179. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/navbar.ts +75 -0
  180. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/redirect.ts +152 -0
  181. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/structured-data.ts +124 -0
  182. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/link.ts +125 -0
  183. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/logo-field.ts +9 -0
  184. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/metadata.ts +132 -0
  185. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/nav-objects.ts +192 -0
  186. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-builder.ts +43 -0
  187. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page-folder.ts +124 -0
  188. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/page.ts +265 -0
  189. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/richText.ts +63 -0
  190. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/structured-data-blocks.ts +436 -0
  191. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/singletons.ts +53 -0
  192. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/structure.ts +339 -0
  193. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/tags.ts +89 -0
  194. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/image.ts +8 -0
  195. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/link.ts +140 -0
  196. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-builder-markdown.ts +113 -0
  197. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/utils/page-json-ld.ts +208 -0
  198. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/scripts/sanity-typegen.ts +36 -0
  199. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/check-redirect.ts +30 -0
  200. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/json-ld.tsx +522 -0
  201. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/metadata.ts +189 -0
  202. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/rss.ts +96 -0
  203. package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/utils/sitemap.ts +58 -0
  204. package/src/modules/features/cms/sanity-pagebuilder-cache/files/proxy.ts +90 -0
  205. package/src/modules/features/env/dotenvx/files/vercelignore +5 -0
  206. package/src/templates/next-default/.biome/plugins/no-fill-without-sizes.grit +11 -0
  207. package/src/templates/next-default/.env.example +2 -2
  208. package/src/templates/next-default/.github/workflows/ci.yml +1 -1
  209. package/src/templates/next-default/.vscode/settings.json +0 -2
  210. package/src/templates/next-default/biome.json +2 -1
  211. package/src/templates/next-default/components/ui/image/index.tsx +21 -15
  212. package/src/templates/next-default/knip.json +1 -1
  213. package/src/templates/next-default/lefthook.yml +1 -1
  214. package/src/templates/next-default/lib/utils/README.md +18 -5
  215. package/src/templates/next-default/lib/utils/image-sizes.ts +72 -0
  216. package/src/templates/next-default/lib/utils/merge-refs.ts +56 -0
  217. package/src/templates/next-default/lib/utils/strings.ts +1 -51
  218. package/src/templates/next-default/next.config.ts +12 -7
  219. package/src/templates/next-default/package.json +2 -5
  220. package/src/templates/next-experiments/.biome/plugins/no-fill-without-sizes.grit +11 -0
  221. package/src/templates/next-experiments/.env.example +2 -2
  222. package/src/templates/next-experiments/.github/workflows/ci.yml +1 -1
  223. package/src/templates/next-experiments/.vscode/settings.json +0 -2
  224. package/src/templates/next-experiments/app/page.tsx +1 -1
  225. package/src/templates/next-experiments/biome.json +2 -1
  226. package/src/templates/next-experiments/components/ui/image/index.tsx +21 -15
  227. package/src/templates/next-experiments/knip.json +1 -1
  228. package/src/templates/next-experiments/lefthook.yml +1 -1
  229. package/src/templates/next-experiments/lib/utils/README.md +18 -5
  230. package/src/templates/next-experiments/lib/utils/image-sizes.ts +72 -0
  231. package/src/templates/next-experiments/lib/utils/merge-refs.ts +56 -0
  232. package/src/templates/next-experiments/lib/utils/strings.ts +1 -51
  233. package/src/templates/next-experiments/next.config.ts +12 -7
  234. package/src/templates/next-experiments/package.json +2 -5
  235. package/src/templates/next-pagebuilder/.biome/plugins/no-fill-without-sizes.grit +11 -0
  236. package/src/templates/next-pagebuilder/.env.example +1 -0
  237. package/src/templates/next-pagebuilder/.github/workflows/ci.yml +1 -1
  238. package/src/templates/next-pagebuilder/.vscode/settings.json +0 -2
  239. package/src/templates/next-pagebuilder/README.md +64 -0
  240. package/src/templates/next-pagebuilder/app/sitemap.xml/route.ts +3 -1
  241. package/src/templates/next-pagebuilder/biome.json +4 -2
  242. package/src/templates/next-pagebuilder/components/layout/footer/index.tsx +3 -6
  243. package/src/templates/next-pagebuilder/components/layout/header/index.tsx +3 -2
  244. package/src/templates/next-pagebuilder/components/layout/json-ld/index.tsx +3 -8
  245. package/src/templates/next-pagebuilder/components/page-builder/components/article-content/index.tsx +5 -0
  246. package/src/templates/next-pagebuilder/components/page-builder/components/article-content/related-post-item.tsx +1 -0
  247. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/content-filters.tsx +23 -40
  248. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +88 -58
  249. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/skeleton.tsx +4 -16
  250. package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/types.ts +14 -1
  251. package/src/templates/next-pagebuilder/components/page-builder/components/rich-text-block.tsx +16 -0
  252. package/src/templates/next-pagebuilder/components/page-builder/renderer.tsx +21 -7
  253. package/src/templates/next-pagebuilder/components/page-document/index.tsx +1 -0
  254. package/src/templates/next-pagebuilder/components/ui/image/index.tsx +21 -12
  255. package/src/templates/next-pagebuilder/knip.json +3 -2
  256. package/src/templates/next-pagebuilder/lefthook.yml +1 -1
  257. package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +1221 -977
  258. package/src/templates/next-pagebuilder/lib/utils/image-sizes.ts +72 -0
  259. package/src/templates/next-pagebuilder/lib/utils/merge-refs.ts +56 -0
  260. package/src/templates/next-pagebuilder/next.config.ts +6 -6
  261. package/src/templates/next-pagebuilder/package.json +4 -6
  262. package/src/templates/next-pagebuilder-cache/.biome/plugins/README.md +21 -0
  263. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-anchor-element.grit +12 -0
  264. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-fill-without-sizes.grit +11 -0
  265. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-relative-parent-imports.grit +10 -0
  266. package/src/templates/next-pagebuilder-cache/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  267. package/src/templates/next-pagebuilder-cache/.env.example +40 -0
  268. package/src/templates/next-pagebuilder-cache/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  269. package/src/templates/next-pagebuilder-cache/.github/workflows/ci.yml +105 -0
  270. package/src/templates/next-pagebuilder-cache/.vscode/extensions.json +19 -0
  271. package/src/templates/next-pagebuilder-cache/.vscode/settings.json +103 -0
  272. package/src/templates/next-pagebuilder-cache/.zed/settings.json +47 -0
  273. package/src/templates/next-pagebuilder-cache/README.md +212 -0
  274. package/src/templates/next-pagebuilder-cache/_gitignore +65 -0
  275. package/src/templates/next-pagebuilder-cache/app/(content)/[[...slug]]/page.tsx +124 -0
  276. package/src/templates/next-pagebuilder-cache/app/(content)/layout.tsx +28 -0
  277. package/src/templates/next-pagebuilder-cache/app/actions/unlock-gated-content.ts +53 -0
  278. package/src/templates/next-pagebuilder-cache/app/favicon.ico +0 -0
  279. package/src/templates/next-pagebuilder-cache/app/layout.tsx +83 -0
  280. package/src/templates/next-pagebuilder-cache/app/robots.ts +13 -0
  281. package/src/templates/next-pagebuilder-cache/app/sitemap.xml/route.ts +84 -0
  282. package/src/templates/next-pagebuilder-cache/biome.json +258 -0
  283. package/src/templates/next-pagebuilder-cache/components/layout/footer/index.tsx +127 -0
  284. package/src/templates/next-pagebuilder-cache/components/layout/header/components/cta-button.tsx +28 -0
  285. package/src/templates/next-pagebuilder-cache/components/layout/header/components/mega-menu-panel.tsx +90 -0
  286. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-item-renderer.tsx +98 -0
  287. package/src/templates/next-pagebuilder-cache/components/layout/header/components/nav-leaf-item.tsx +33 -0
  288. package/src/templates/next-pagebuilder-cache/components/layout/header/components/types.ts +7 -0
  289. package/src/templates/next-pagebuilder-cache/components/layout/header/header-client.tsx +110 -0
  290. package/src/templates/next-pagebuilder-cache/components/layout/header/index.tsx +32 -0
  291. package/src/templates/next-pagebuilder-cache/components/layout/json-ld/index.tsx +79 -0
  292. package/src/templates/next-pagebuilder-cache/components/layout/wrapper/index.tsx +33 -0
  293. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/index.tsx +83 -0
  294. package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/related-post-item.tsx +28 -0
  295. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-card.tsx +66 -0
  296. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-filters.tsx +76 -0
  297. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-grid.tsx +40 -0
  298. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/content-pagination-nav.tsx +71 -0
  299. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/index.tsx +306 -0
  300. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/skeleton.tsx +41 -0
  301. package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/types.ts +32 -0
  302. package/src/templates/next-pagebuilder-cache/components/page-builder/components/description.tsx +17 -0
  303. package/src/templates/next-pagebuilder-cache/components/page-builder/components/gated-content/index.tsx +169 -0
  304. package/src/templates/next-pagebuilder-cache/components/page-builder/components/hero.tsx +17 -0
  305. package/src/templates/next-pagebuilder-cache/components/page-builder/components/rich-text-block.tsx +16 -0
  306. package/src/templates/next-pagebuilder-cache/components/page-builder/renderer.tsx +71 -0
  307. package/src/templates/next-pagebuilder-cache/components/page-builder/types.ts +25 -0
  308. package/src/templates/next-pagebuilder-cache/components/page-document/index.tsx +147 -0
  309. package/src/templates/next-pagebuilder-cache/components/ui/image/index.tsx +225 -0
  310. package/src/templates/next-pagebuilder-cache/components/ui/link/index.tsx +152 -0
  311. package/src/templates/next-pagebuilder-cache/components.json +24 -0
  312. package/src/templates/next-pagebuilder-cache/knip.json +35 -0
  313. package/src/templates/next-pagebuilder-cache/lefthook.yml +25 -0
  314. package/src/templates/next-pagebuilder-cache/lib/integrations/sanity/sanity.types.ts +1588 -0
  315. package/src/templates/next-pagebuilder-cache/lib/scripts/setup-ci-secrets.ts +117 -0
  316. package/src/templates/next-pagebuilder-cache/lib/styles/cn.ts +5 -0
  317. package/src/templates/next-pagebuilder-cache/lib/styles/global.css +69 -0
  318. package/src/templates/next-pagebuilder-cache/lib/utils/base-url.ts +17 -0
  319. package/src/templates/next-pagebuilder-cache/lib/utils/format-date.ts +8 -0
  320. package/src/templates/next-pagebuilder-cache/lib/utils/image-sizes.ts +72 -0
  321. package/src/templates/next-pagebuilder-cache/lib/utils/slug-tag.ts +6 -0
  322. package/src/templates/next-pagebuilder-cache/next.config.ts +177 -0
  323. package/src/templates/next-pagebuilder-cache/package.json +62 -0
  324. package/src/templates/next-pagebuilder-cache/postcss.config.mjs +65 -0
  325. package/src/templates/next-pagebuilder-cache/svg.d.ts +6 -0
  326. package/src/templates/next-pagebuilder-cache/tsconfig.json +38 -0
  327. package/src/templates/next-webgl/.biome/plugins/no-fill-without-sizes.grit +11 -0
  328. package/src/templates/next-webgl/.env.example +2 -2
  329. package/src/templates/next-webgl/.github/workflows/ci.yml +1 -1
  330. package/src/templates/next-webgl/.vscode/settings.json +0 -2
  331. package/src/templates/next-webgl/biome.json +2 -1
  332. package/src/templates/next-webgl/components/ui/image/index.tsx +21 -15
  333. package/src/templates/next-webgl/knip.json +1 -1
  334. package/src/templates/next-webgl/lefthook.yml +1 -1
  335. package/src/templates/next-webgl/lib/utils/README.md +18 -5
  336. package/src/templates/next-webgl/lib/utils/image-sizes.ts +72 -0
  337. package/src/templates/next-webgl/lib/utils/merge-refs.ts +56 -0
  338. package/src/templates/next-webgl/lib/utils/strings.ts +1 -51
  339. package/src/templates/next-webgl/next.config.ts +12 -7
  340. package/src/templates/next-webgl/package.json +4 -7
  341. package/src/templates/next-experiments/tsconfig.tsbuildinfo +0 -1
  342. package/src/templates/next-pagebuilder/app/actions/refresh.ts +0 -5
  343. package/src/templates/next-webgl/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,258 @@
1
+ {
2
+ "root": true,
3
+ "$schema": "node_modules/@biomejs/biome/configuration_schema.json",
4
+ "assist": {
5
+ "actions": {
6
+ "source": {
7
+ "organizeImports": "on"
8
+ }
9
+ }
10
+ },
11
+ "css": {
12
+ "formatter": {
13
+ "enabled": true
14
+ },
15
+ "linter": {
16
+ "enabled": true
17
+ },
18
+ "parser": {
19
+ "cssModules": true,
20
+ "tailwindDirectives": true
21
+ }
22
+ },
23
+ "files": {
24
+ "ignoreUnknown": true,
25
+ "includes": [
26
+ "**",
27
+ "!node_modules",
28
+ "!**/.next",
29
+ "!**/dist",
30
+ "!**/public",
31
+ "!.github",
32
+ "!.vercel",
33
+ "!pnpm-lock.yaml",
34
+ "!bun.lock",
35
+ "!**/*.md",
36
+ "!**/*.mdx",
37
+ "!**/tailwind.css",
38
+ "!**/*.grit",
39
+ "!**/sanity.types.ts"
40
+ ]
41
+ },
42
+ "formatter": {
43
+ "enabled": true,
44
+ "indentStyle": "space",
45
+ "indentWidth": 2,
46
+ "lineEnding": "lf",
47
+ "lineWidth": 80
48
+ },
49
+ "javascript": {
50
+ "formatter": {
51
+ "enabled": true,
52
+ "quoteStyle": "double",
53
+ "semicolons": "asNeeded",
54
+ "trailingCommas": "es5"
55
+ }
56
+ },
57
+ "json": {
58
+ "parser": {
59
+ "allowComments": true
60
+ }
61
+ },
62
+ "linter": {
63
+ "domains": {
64
+ "next": "recommended",
65
+ "react": "recommended"
66
+ },
67
+ "enabled": true,
68
+ "rules": {
69
+ "a11y": {
70
+ "noAriaUnsupportedElements": "error",
71
+ "noAutofocus": "warn",
72
+ "noDistractingElements": "error",
73
+ "noRedundantAlt": "error",
74
+ "useAltText": "error",
75
+ "useButtonType": "error",
76
+ "useKeyWithClickEvents": "warn",
77
+ "useSemanticElements": "warn",
78
+ "useValidAnchor": "warn",
79
+ "useValidAriaProps": "error",
80
+ "useValidAriaRole": "error",
81
+ "useValidAriaValues": "error"
82
+ },
83
+ "complexity": {
84
+ "noForEach": "off",
85
+ "useFlatMap": "warn",
86
+ "useSimplifiedLogicExpression": "off"
87
+ },
88
+ "correctness": {
89
+ "noInvalidUseBeforeDeclaration": "error",
90
+ "noUnknownMediaFeatureName": "off",
91
+ "noUnusedFunctionParameters": "warn",
92
+ "noUnusedImports": "error",
93
+ "noUnusedVariables": "error",
94
+ "useExhaustiveDependencies": "warn"
95
+ },
96
+ "nursery": {
97
+ "useSortedClasses": {
98
+ "fix": "safe",
99
+ "level": "warn",
100
+ "options": {
101
+ "attributes": ["class", "className"],
102
+ "functions": ["cn", "cva"]
103
+ }
104
+ }
105
+ },
106
+ "performance": {
107
+ "noImgElement": "error"
108
+ },
109
+ "recommended": true,
110
+ "security": {
111
+ "noDangerouslySetInnerHtml": "warn",
112
+ "noDangerouslySetInnerHtmlWithChildren": "error",
113
+ "noGlobalEval": "error"
114
+ },
115
+ "style": {
116
+ "noInferrableTypes": "error",
117
+ "noNestedTernary": "error",
118
+ "noNonNullAssertion": "off",
119
+ "noParameterAssign": "error",
120
+ "noUnusedTemplateLiteral": "off",
121
+ "noUselessElse": "error",
122
+ "useAsConstAssertion": "error",
123
+ "useCollapsedElseIf": "warn",
124
+ "useConsistentArrayType": "error",
125
+ "useConsistentBuiltinInstantiation": "error",
126
+ "useDefaultParameterLast": "error",
127
+ "useEnumInitializers": "error",
128
+ "useExponentiationOperator": "error",
129
+ "useFilenamingConvention": {
130
+ "level": "warn",
131
+ "options": {
132
+ "filenameCases": ["kebab-case", "camelCase"],
133
+ "strictCase": false
134
+ }
135
+ },
136
+ "useForOf": "warn",
137
+ "useNumberNamespace": "error",
138
+ "useSelfClosingElements": "error",
139
+ "useShorthandAssign": "error",
140
+ "useSingleVarDeclarator": "error",
141
+ "useTemplate": "warn"
142
+ },
143
+ "suspicious": {
144
+ "noConsole": {
145
+ "fix": "none",
146
+ "level": "warn",
147
+ "options": { "allow": ["warn", "error", "info"] }
148
+ },
149
+ "noDebugger": "warn",
150
+ "noDoubleEquals": "error",
151
+ "noEmptyBlockStatements": "warn",
152
+ "noExplicitAny": "error",
153
+ "noGlobalIsFinite": "error",
154
+ "noGlobalIsNan": "error",
155
+ "noMisleadingCharacterClass": "error",
156
+ "noPrototypeBuiltins": "warn",
157
+ "noSelfCompare": "error",
158
+ "noSparseArray": "error",
159
+ "useAwait": "off"
160
+ }
161
+ }
162
+ },
163
+ "overrides": [
164
+ {
165
+ "includes": ["**/*.css"],
166
+ "linter": {
167
+ "rules": {
168
+ "correctness": {
169
+ "noUnknownFunction": "off"
170
+ }
171
+ }
172
+ }
173
+ },
174
+ {
175
+ "includes": ["**/*.tsx", "**/*.jsx"],
176
+ "linter": {
177
+ "rules": {
178
+ "a11y": {
179
+ "useKeyWithClickEvents": "error",
180
+ "useKeyWithMouseEvents": "error",
181
+ "useValidAnchor": "error"
182
+ },
183
+ "correctness": {
184
+ "useJsxKeyInIterable": "error"
185
+ }
186
+ }
187
+ }
188
+ },
189
+ {
190
+ "includes": ["**/*.ts", "**/*.tsx"],
191
+ "linter": {
192
+ "rules": {
193
+ "correctness": {
194
+ "noUndeclaredVariables": "off"
195
+ },
196
+ "style": {
197
+ "useConsistentArrayType": "error",
198
+ "useExportType": "error",
199
+ "useImportType": "error"
200
+ }
201
+ }
202
+ }
203
+ },
204
+ {
205
+ "includes": [
206
+ "app/**/*.tsx",
207
+ "app/**/*.ts",
208
+ "app/**/*.jsx",
209
+ "app/**/*.js"
210
+ ],
211
+ "linter": {
212
+ "rules": {
213
+ "style": {
214
+ "noDefaultExport": "off"
215
+ },
216
+ "suspicious": {
217
+ "useAwait": "off"
218
+ }
219
+ }
220
+ }
221
+ },
222
+ {
223
+ "includes": ["**/*.module.css"],
224
+ "linter": {
225
+ "rules": {
226
+ "correctness": {
227
+ "noUnknownProperty": "off"
228
+ },
229
+ "style": {
230
+ "noDescendingSpecificity": "off"
231
+ }
232
+ }
233
+ }
234
+ },
235
+ {
236
+ "includes": ["lib/styles/tokens.css"],
237
+ "linter": {
238
+ "rules": {
239
+ "suspicious": {
240
+ "noDuplicateCustomProperties": "off",
241
+ "noUnknownAtRules": "off"
242
+ }
243
+ }
244
+ }
245
+ }
246
+ ],
247
+ "plugins": [
248
+ "./.biome/plugins/no-anchor-element.grit",
249
+ "./.biome/plugins/no-unnecessary-forwardref.grit",
250
+ "./.biome/plugins/no-relative-parent-imports.grit",
251
+ "./.biome/plugins/no-fill-without-sizes.grit"
252
+ ],
253
+ "vcs": {
254
+ "clientKind": "git",
255
+ "enabled": true,
256
+ "useIgnoreFile": false
257
+ }
258
+ }
@@ -0,0 +1,127 @@
1
+ import { cacheLife } from "next/cache"
2
+ import { Suspense } from "react"
3
+ import { Link } from "@/components/ui/link"
4
+ import { SanityImage } from "@/components/ui/sanity-image"
5
+ import {
6
+ type CompanyData,
7
+ type FooterData,
8
+ getLayoutData,
9
+ } from "@/lib/integrations/sanity/fetchers/layout"
10
+ import {
11
+ type DynamicFetchOptions,
12
+ getDynamicFetchOptions,
13
+ PUBLISHED_FETCH_OPTIONS,
14
+ } from "@/lib/integrations/sanity/live"
15
+
16
+ type FooterLinkGroup = NonNullable<FooterData["links"]>[number]
17
+ type FooterLinkItem = NonNullable<FooterLinkGroup["items"]>[number]
18
+ type SocialLink = NonNullable<CompanyData["socialLinks"]>[number]
19
+
20
+ // Published chrome caches into the static shell; draft mode streams through a
21
+ // Suspense boundary so the shell still prerenders.
22
+ export const Footer = ({ isDraftMode }: { isDraftMode: boolean }) =>
23
+ isDraftMode ? (
24
+ <Suspense fallback={<FooterFallback />}>
25
+ <DynamicFooter />
26
+ </Suspense>
27
+ ) : (
28
+ <CachedFooter {...PUBLISHED_FETCH_OPTIONS} />
29
+ )
30
+
31
+ // Resolves the draft fetch options outside the cache boundary.
32
+ async function DynamicFooter() {
33
+ const fetchOptions = await getDynamicFetchOptions()
34
+ return <CachedFooter {...fetchOptions} />
35
+ }
36
+
37
+ // The year lives here, not in a leaf: a nested boundary would propagate the same
38
+ // clamp upwards and only add an entry.
39
+ async function CachedFooter(fetchOptions: DynamicFetchOptions) {
40
+ "use cache"
41
+ // Expire at the year boundary, or the ~1y default freezes this year's value.
42
+ const now = new Date()
43
+ const year = now.getFullYear()
44
+ const untilNextYear = Math.max(
45
+ 300,
46
+ Math.ceil((new Date(year + 1, 0, 1).getTime() - now.getTime()) / 1000)
47
+ )
48
+ cacheLife({ revalidate: untilNextYear, expire: untilNextYear })
49
+
50
+ // One entry, shared with the header and json-ld. The clamp above applies here,
51
+ // not to that entry — nested lifetimes propagate upwards, never inwards.
52
+ const { footer, companyData } = await getLayoutData(fetchOptions)
53
+
54
+ const links = footer?.links ?? []
55
+
56
+ return (
57
+ <footer className="border-black/10 border-t">
58
+ <div className="mx-auto flex max-w-7xl flex-col gap-8 px-6 py-6">
59
+ {links.length > 0 ? (
60
+ <div className="grid grid-cols-5 gap-x-8 gap-y-6 md:grid-cols-3 lg:grid-cols-5">
61
+ {links.map((linkGroup: FooterLinkGroup) => (
62
+ <div key={linkGroup._key} className="flex flex-col gap-2">
63
+ <h3>{linkGroup.title}</h3>
64
+ {linkGroup.items && linkGroup.items.length > 0 ? (
65
+ <ul className="space-y-1">
66
+ {linkGroup.items
67
+ .filter(
68
+ (item: FooterLinkItem) => item.href && item.href !== "#"
69
+ )
70
+ .map((item: FooterLinkItem) => (
71
+ <li key={item._key}>
72
+ <Link href={item.href!}>{item.label}</Link>
73
+ </li>
74
+ ))}
75
+ </ul>
76
+ ) : null}
77
+ </div>
78
+ ))}
79
+ </div>
80
+ ) : (
81
+ <Link href="/">Basement</Link>
82
+ )}
83
+
84
+ <div className="flex flex-row items-center justify-between gap-3 border-black/10 border-t pt-4">
85
+ <p className="w-fit">
86
+ &copy; <span>{year}</span> Basement
87
+ </p>
88
+
89
+ {companyData?.socialLinks && companyData.socialLinks.length > 0 ? (
90
+ <div className="flex w-fit flex-wrap items-center gap-x-4 gap-y-2">
91
+ {companyData.socialLinks
92
+ .filter((social: SocialLink) => social.url)
93
+ .map((social: SocialLink) => (
94
+ <Link
95
+ key={social._key}
96
+ href={social.url!}
97
+ aria-label={
98
+ social.label ||
99
+ `Follow us on ${social.name || "social media"}`
100
+ }
101
+ >
102
+ {social.icon ? (
103
+ <SanityImage
104
+ image={social.icon}
105
+ alt={social.name || ""}
106
+ width={20}
107
+ height={20}
108
+ maxWidth={40}
109
+ />
110
+ ) : (
111
+ <span>{social.name}</span>
112
+ )}
113
+ </Link>
114
+ ))}
115
+ </div>
116
+ ) : null}
117
+ </div>
118
+ </div>
119
+ </footer>
120
+ )
121
+ }
122
+
123
+ const FooterFallback = () => (
124
+ <footer className="border-black/10 border-t">
125
+ <div className="mx-auto min-h-[100px] max-w-7xl px-6 py-6" />
126
+ </footer>
127
+ )
@@ -0,0 +1,28 @@
1
+ import { Link } from "@/components/ui/link"
2
+ import { getLinkAttributes } from "@/lib/integrations/sanity/utils/link"
3
+ import { cn } from "@/lib/styles/cn"
4
+
5
+ import type { CtaButton } from "./types"
6
+
7
+ export function CtaButtonRenderer({ cta }: { cta: CtaButton }) {
8
+ const linkAttributes = cta.link ? getLinkAttributes(cta.link) : undefined
9
+ const href = linkAttributes?.href
10
+
11
+ if (!(cta.label && href && href !== "#")) return null
12
+
13
+ return (
14
+ <Link
15
+ href={href}
16
+ target={linkAttributes?.target}
17
+ rel={linkAttributes?.rel}
18
+ className={cn(
19
+ "rounded-lg px-4 py-2 font-medium text-sm transition-opacity hover:opacity-80",
20
+ cta.variant === "primary"
21
+ ? "bg-black text-white"
22
+ : "border border-black/20 text-black"
23
+ )}
24
+ >
25
+ {cta.label}
26
+ </Link>
27
+ )
28
+ }
@@ -0,0 +1,90 @@
1
+ import { Link } from "@/components/ui/link"
2
+ import { getLinkAttributes } from "@/lib/integrations/sanity/utils/link"
3
+ import { cn } from "@/lib/styles/cn"
4
+
5
+ import { NavLeafItem } from "./nav-leaf-item"
6
+ import type { MegaMenu, MegaMenuColumn as MegaMenuColumnType } from "./types"
7
+
8
+ function ColumnHeader({
9
+ title,
10
+ href,
11
+ target,
12
+ rel,
13
+ }: {
14
+ title: string
15
+ href: string | null
16
+ target?: string | undefined
17
+ rel?: string | undefined
18
+ }) {
19
+ const className =
20
+ "mb-1 block px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-400"
21
+
22
+ if (href) {
23
+ return (
24
+ <Link
25
+ href={href}
26
+ target={target}
27
+ rel={rel}
28
+ className={cn(className, "transition-colors hover:text-black")}
29
+ >
30
+ {title}
31
+ </Link>
32
+ )
33
+ }
34
+
35
+ return <span className={className}>{title}</span>
36
+ }
37
+
38
+ function MegaMenuColumn({ column }: { column: MegaMenuColumnType }) {
39
+ const linkAttributes = column.link
40
+ ? getLinkAttributes(column.link)
41
+ : undefined
42
+ const href = linkAttributes?.href
43
+ const items = column.items
44
+
45
+ return (
46
+ <div className="flex min-w-48 flex-col">
47
+ {column.title ? (
48
+ <ColumnHeader
49
+ title={column.title}
50
+ href={href && href !== "#" ? href : null}
51
+ target={linkAttributes?.target}
52
+ rel={linkAttributes?.rel}
53
+ />
54
+ ) : null}
55
+
56
+ {items?.length ? (
57
+ <ul className="flex flex-col">
58
+ {items.map((item) => (
59
+ <li key={item._key}>
60
+ <NavLeafItem item={item} />
61
+ </li>
62
+ ))}
63
+ </ul>
64
+ ) : null}
65
+ </div>
66
+ )
67
+ }
68
+
69
+ export function MegaMenuPanel({
70
+ megaMenu,
71
+ id,
72
+ }: {
73
+ megaMenu: MegaMenu
74
+ id: string
75
+ }) {
76
+ return (
77
+ <div
78
+ id={id}
79
+ className="absolute top-full left-1/2 z-50 w-max max-w-[90vw] -translate-x-1/2 pt-2"
80
+ >
81
+ <div className="rounded-xl border border-black/10 bg-white p-6 shadow-black/5 shadow-xl">
82
+ <div className="flex gap-8">
83
+ {megaMenu.columns?.map((col) => (
84
+ <MegaMenuColumn key={col._key} column={col} />
85
+ ))}
86
+ </div>
87
+ </div>
88
+ </div>
89
+ )
90
+ }
@@ -0,0 +1,98 @@
1
+ import { CaretDownIcon } from "@phosphor-icons/react/dist/ssr"
2
+
3
+ import { Link } from "@/components/ui/link"
4
+ import { getLinkAttributes } from "@/lib/integrations/sanity/utils/link"
5
+ import { cn } from "@/lib/styles/cn"
6
+
7
+ import { MegaMenuPanel } from "./mega-menu-panel"
8
+ import type { NavItem } from "./types"
9
+
10
+ type NavItemRendererProps = {
11
+ item: NavItem
12
+ isOpen: boolean
13
+ onToggle: () => void
14
+ triggerRef: (el: HTMLButtonElement | null) => void
15
+ }
16
+
17
+ const baseLabelClassName = "text-sm transition-colors"
18
+ const interactiveLabelClassName = cn(
19
+ baseLabelClassName,
20
+ "text-gray-600 hover:text-black"
21
+ )
22
+
23
+ function getValidHref(item: NavItem) {
24
+ const linkAttributes = item.link ? getLinkAttributes(item.link) : undefined
25
+ const href =
26
+ linkAttributes?.href && linkAttributes.href !== "#"
27
+ ? linkAttributes.href
28
+ : null
29
+
30
+ return { href, linkAttributes }
31
+ }
32
+
33
+ export function NavItemRenderer({
34
+ item,
35
+ isOpen,
36
+ onToggle,
37
+ triggerRef,
38
+ }: NavItemRendererProps) {
39
+ const panelId = `mega-menu-${item._key}`
40
+
41
+ if (item.itemType === "mega-menu" && item.megaMenu) {
42
+ const megaMenu = item.megaMenu
43
+
44
+ return (
45
+ <li className="relative">
46
+ <button
47
+ ref={triggerRef}
48
+ type="button"
49
+ onClick={onToggle}
50
+ className={cn(
51
+ "flex items-center gap-1",
52
+ baseLabelClassName,
53
+ isOpen ? "text-black" : interactiveLabelClassName
54
+ )}
55
+ aria-expanded={isOpen}
56
+ aria-haspopup="true"
57
+ aria-controls={panelId}
58
+ >
59
+ {item.title}
60
+ <CaretDownIcon
61
+ size={14}
62
+ weight="bold"
63
+ className={cn(
64
+ "transition-transform duration-200",
65
+ isOpen && "rotate-180"
66
+ )}
67
+ />
68
+ </button>
69
+ {isOpen ? <MegaMenuPanel megaMenu={megaMenu} id={panelId} /> : null}
70
+ </li>
71
+ )
72
+ }
73
+
74
+ const { href, linkAttributes } = getValidHref(item)
75
+
76
+ if (href) {
77
+ return (
78
+ <li>
79
+ <Link
80
+ href={href}
81
+ target={linkAttributes?.target}
82
+ rel={linkAttributes?.rel}
83
+ className={interactiveLabelClassName}
84
+ >
85
+ {item.title}
86
+ </Link>
87
+ </li>
88
+ )
89
+ }
90
+
91
+ return (
92
+ <li>
93
+ <span className={cn(baseLabelClassName, "cursor-default text-gray-400")}>
94
+ {item.title}
95
+ </span>
96
+ </li>
97
+ )
98
+ }
@@ -0,0 +1,33 @@
1
+ import { Link } from "@/components/ui/link"
2
+ import { getLinkAttributes } from "@/lib/integrations/sanity/utils/link"
3
+
4
+ import type { LeafItem } from "./types"
5
+
6
+ export function NavLeafItem({ item }: { item: LeafItem }) {
7
+ const linkAttributes = item.link ? getLinkAttributes(item.link) : undefined
8
+ const href = linkAttributes?.href
9
+ const label = (
10
+ <span className="text-gray-700 text-sm transition-colors group-hover/leaf:text-black">
11
+ {item.title}
12
+ </span>
13
+ )
14
+
15
+ if (href && href !== "#") {
16
+ return (
17
+ <Link
18
+ href={href}
19
+ target={linkAttributes?.target}
20
+ rel={linkAttributes?.rel}
21
+ className="block rounded-md px-3 py-2 transition-colors hover:bg-gray-50"
22
+ >
23
+ {label}
24
+ </Link>
25
+ )
26
+ }
27
+
28
+ return (
29
+ <span className="block cursor-default rounded-md px-3 py-2 opacity-50">
30
+ {label}
31
+ </span>
32
+ )
33
+ }
@@ -0,0 +1,7 @@
1
+ import type { NavbarData } from "@/lib/integrations/sanity/fetchers/layout"
2
+
3
+ export type NavItem = NonNullable<NavbarData["navigationItems"]>[number]
4
+ export type MegaMenu = NonNullable<NavItem["megaMenu"]>
5
+ export type MegaMenuColumn = NonNullable<MegaMenu["columns"]>[number]
6
+ export type LeafItem = NonNullable<MegaMenuColumn["items"]>[number]
7
+ export type CtaButton = NonNullable<NavbarData["ctaButtons"]>[number]