@morphika/andami 0.1.2

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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/admin/assets.ts +4 -0
  4. package/admin/database.ts +4 -0
  5. package/admin/index.ts +6 -0
  6. package/admin/login.ts +4 -0
  7. package/admin/navigation.ts +4 -0
  8. package/admin/pages-editor.ts +4 -0
  9. package/admin/pages.ts +4 -0
  10. package/admin/projects-editor.ts +4 -0
  11. package/admin/projects.ts +4 -0
  12. package/admin/settings.ts +4 -0
  13. package/admin/setup.ts +4 -0
  14. package/admin/storage.ts +4 -0
  15. package/admin/styles.ts +4 -0
  16. package/app/(site)/[slug]/loading.tsx +20 -0
  17. package/app/(site)/[slug]/page.tsx +83 -0
  18. package/app/(site)/error.tsx +32 -0
  19. package/app/(site)/layout.tsx +53 -0
  20. package/app/(site)/loading.tsx +20 -0
  21. package/app/(site)/not-found.tsx +41 -0
  22. package/app/(site)/page.tsx +43 -0
  23. package/app/(site)/preview/page.tsx +99 -0
  24. package/app/(site)/work/[slug]/loading.tsx +23 -0
  25. package/app/(site)/work/[slug]/page.tsx +84 -0
  26. package/app/admin/assets/page.tsx +573 -0
  27. package/app/admin/database/page.tsx +302 -0
  28. package/app/admin/error.tsx +53 -0
  29. package/app/admin/layout.tsx +273 -0
  30. package/app/admin/login/page.tsx +88 -0
  31. package/app/admin/navigation/page.tsx +157 -0
  32. package/app/admin/page.tsx +17 -0
  33. package/app/admin/pages/[slug]/page.tsx +849 -0
  34. package/app/admin/pages/page.tsx +588 -0
  35. package/app/admin/projects/[slug]/page.tsx +3 -0
  36. package/app/admin/projects/page.tsx +669 -0
  37. package/app/admin/settings/page.tsx +132 -0
  38. package/app/admin/setup/page.tsx +64 -0
  39. package/app/admin/storage/page.tsx +518 -0
  40. package/app/admin/styles/page.tsx +243 -0
  41. package/app/api/admin/assets/file/route.ts +81 -0
  42. package/app/api/admin/assets/health/route.ts +170 -0
  43. package/app/api/admin/assets/register/route.ts +163 -0
  44. package/app/api/admin/assets/registry/route.ts +98 -0
  45. package/app/api/admin/assets/relink/confirm/route.ts +242 -0
  46. package/app/api/admin/assets/relink/route.ts +202 -0
  47. package/app/api/admin/assets/scan/route.ts +271 -0
  48. package/app/api/admin/auth/route.ts +160 -0
  49. package/app/api/admin/custom-sections/[slug]/route.ts +159 -0
  50. package/app/api/admin/custom-sections/route.ts +127 -0
  51. package/app/api/admin/database/route.ts +53 -0
  52. package/app/api/admin/pages/[slug]/duplicate/route.ts +91 -0
  53. package/app/api/admin/pages/[slug]/route.ts +617 -0
  54. package/app/api/admin/pages/[slug]/set-home/route.ts +76 -0
  55. package/app/api/admin/pages/route.ts +129 -0
  56. package/app/api/admin/preview/route.ts +53 -0
  57. package/app/api/admin/r2/connect/route.ts +181 -0
  58. package/app/api/admin/r2/delete/route.ts +198 -0
  59. package/app/api/admin/r2/disconnect/route.ts +42 -0
  60. package/app/api/admin/r2/rename/route.ts +265 -0
  61. package/app/api/admin/r2/status/route.ts +106 -0
  62. package/app/api/admin/r2/upload-url/route.ts +148 -0
  63. package/app/api/admin/revalidate/route.ts +55 -0
  64. package/app/api/admin/settings/route.ts +279 -0
  65. package/app/api/admin/setup/complete/route.ts +51 -0
  66. package/app/api/admin/setup/route.ts +118 -0
  67. package/app/api/admin/storage/switch/route.ts +117 -0
  68. package/app/api/admin/styles/fonts/route.ts +97 -0
  69. package/app/api/admin/styles/route.ts +304 -0
  70. package/app/api/assets/[...path]/route.ts +98 -0
  71. package/app/api/custom-sections/[id]/route.ts +43 -0
  72. package/app/api/draft-mode/disable/route.ts +10 -0
  73. package/app/api/draft-mode/enable/route.ts +26 -0
  74. package/app/api/projects/route.ts +42 -0
  75. package/app/api/styles/route.ts +88 -0
  76. package/app/favicon.ico +0 -0
  77. package/app/globals.css +7 -0
  78. package/app/layout.tsx +53 -0
  79. package/app/robots.ts +17 -0
  80. package/app/sitemap.ts +48 -0
  81. package/app/studio/[[...index]]/page.tsx +8 -0
  82. package/components/admin/MetadataEditor.tsx +173 -0
  83. package/components/admin/PublishToggle.tsx +130 -0
  84. package/components/admin/icons.tsx +40 -0
  85. package/components/admin/nav-builder/NavBuilder.tsx +182 -0
  86. package/components/admin/nav-builder/NavBuilderGrid.tsx +326 -0
  87. package/components/admin/nav-builder/NavGeneralSettings.tsx +275 -0
  88. package/components/admin/nav-builder/NavGridCell.tsx +48 -0
  89. package/components/admin/nav-builder/NavGridItem.tsx +189 -0
  90. package/components/admin/nav-builder/NavItemSettings.tsx +288 -0
  91. package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -0
  92. package/components/admin/nav-builder/NavLivePreview.tsx +125 -0
  93. package/components/admin/nav-builder/NavSettingsFields.tsx +248 -0
  94. package/components/admin/nav-builder/NavSettingsPanel.tsx +127 -0
  95. package/components/admin/nav-builder/index.ts +10 -0
  96. package/components/admin/nav-builder/nav-builder-utils.ts +238 -0
  97. package/components/admin/setup-wizard/BrandingStep.tsx +218 -0
  98. package/components/admin/setup-wizard/DatabaseStep.tsx +331 -0
  99. package/components/admin/setup-wizard/DoneStep.tsx +187 -0
  100. package/components/admin/setup-wizard/SetupWizard.tsx +166 -0
  101. package/components/admin/setup-wizard/StorageStep.tsx +308 -0
  102. package/components/admin/setup-wizard/WelcomeStep.tsx +96 -0
  103. package/components/admin/setup-wizard/index.ts +9 -0
  104. package/components/admin/styles/ColorsEditor.tsx +214 -0
  105. package/components/admin/styles/FontsEditor.tsx +258 -0
  106. package/components/admin/styles/GridLayoutEditor.tsx +292 -0
  107. package/components/admin/styles/LinksButtonsEditor.tsx +120 -0
  108. package/components/admin/styles/TypographyEditor.tsx +266 -0
  109. package/components/admin/styles/index.ts +9 -0
  110. package/components/admin/styles/shared.tsx +68 -0
  111. package/components/blocks/BlockRenderer.tsx +404 -0
  112. package/components/blocks/ButtonBlockRenderer.tsx +52 -0
  113. package/components/blocks/CoverBlockRenderer.tsx +239 -0
  114. package/components/blocks/CustomSectionInstanceRenderer.tsx +82 -0
  115. package/components/blocks/EnterAnimationWrapper.tsx +140 -0
  116. package/components/blocks/HoverAnimationWrapper.tsx +308 -0
  117. package/components/blocks/ImageBlockRenderer.tsx +61 -0
  118. package/components/blocks/ImageGridBlockRenderer.tsx +545 -0
  119. package/components/blocks/PageBackground.tsx +28 -0
  120. package/components/blocks/PageNavAnimation.tsx +35 -0
  121. package/components/blocks/PageNavColor.tsx +24 -0
  122. package/components/blocks/PageRenderer.tsx +142 -0
  123. package/components/blocks/ParallaxGroupRenderer.tsx +448 -0
  124. package/components/blocks/ParallaxSlideRenderer.tsx +175 -0
  125. package/components/blocks/ProjectGridBlockRenderer.tsx +556 -0
  126. package/components/blocks/SectionRenderer.tsx +170 -0
  127. package/components/blocks/SectionV2Renderer.tsx +330 -0
  128. package/components/blocks/ShaderCanvas.tsx +392 -0
  129. package/components/blocks/SpacerBlockRenderer.tsx +17 -0
  130. package/components/blocks/TextBlockRenderer.tsx +87 -0
  131. package/components/blocks/TypewriterRichText.tsx +464 -0
  132. package/components/blocks/TypewriterWrapper.tsx +149 -0
  133. package/components/blocks/VideoBlockRenderer.tsx +304 -0
  134. package/components/blocks/index.ts +2 -0
  135. package/components/builder/AssetBrowser.tsx +2 -0
  136. package/components/builder/BlockLivePreview.tsx +101 -0
  137. package/components/builder/BlockTypePicker.tsx +178 -0
  138. package/components/builder/BuilderCanvas.tsx +354 -0
  139. package/components/builder/CanvasMinimap.tsx +200 -0
  140. package/components/builder/CanvasToolbar.tsx +202 -0
  141. package/components/builder/ColorPicker.tsx +243 -0
  142. package/components/builder/ColorSwatchPicker.tsx +274 -0
  143. package/components/builder/ColumnDragContext.tsx +51 -0
  144. package/components/builder/ColumnDragOverlay.tsx +110 -0
  145. package/components/builder/CustomSectionInstanceCard.tsx +97 -0
  146. package/components/builder/DeviceFrame.tsx +123 -0
  147. package/components/builder/DndWrapper.tsx +337 -0
  148. package/components/builder/InsertionLines.tsx +186 -0
  149. package/components/builder/ParallaxGroupCanvas.tsx +228 -0
  150. package/components/builder/ParallaxSlideHeader.tsx +113 -0
  151. package/components/builder/ReadOnlyFrame.tsx +417 -0
  152. package/components/builder/SectionEditorBar.tsx +288 -0
  153. package/components/builder/SectionTypePicker.tsx +422 -0
  154. package/components/builder/SectionV2Canvas.tsx +297 -0
  155. package/components/builder/SectionV2Column.tsx +488 -0
  156. package/components/builder/SettingsPanel.tsx +911 -0
  157. package/components/builder/SortableBlock.tsx +230 -0
  158. package/components/builder/SortableRow.tsx +362 -0
  159. package/components/builder/VirtualAssetGrid.tsx +397 -0
  160. package/components/builder/asset-browser/AssetBrowser.tsx +178 -0
  161. package/components/builder/asset-browser/FileLightbox.tsx +116 -0
  162. package/components/builder/asset-browser/FolderTreeItem.tsx +55 -0
  163. package/components/builder/asset-browser/R2BrowserContent.tsx +436 -0
  164. package/components/builder/asset-browser/R2ContextMenu.tsx +98 -0
  165. package/components/builder/asset-browser/VideoThumbnail.tsx +63 -0
  166. package/components/builder/asset-browser/helpers.ts +88 -0
  167. package/components/builder/asset-browser/index.ts +1 -0
  168. package/components/builder/asset-browser/types.ts +49 -0
  169. package/components/builder/asset-browser/useAssetBrowser.ts +344 -0
  170. package/components/builder/asset-browser/useR2DragDrop.ts +116 -0
  171. package/components/builder/asset-browser/useR2Operations.ts +189 -0
  172. package/components/builder/blockStyles.tsx +295 -0
  173. package/components/builder/editors/ButtonBlockEditor.tsx +184 -0
  174. package/components/builder/editors/CoverBlockEditor.tsx +488 -0
  175. package/components/builder/editors/EnterAnimationPicker.tsx +297 -0
  176. package/components/builder/editors/HoverEffectPicker.tsx +209 -0
  177. package/components/builder/editors/ImageBlockEditor.tsx +206 -0
  178. package/components/builder/editors/ImageGridBlockEditor.tsx +386 -0
  179. package/components/builder/editors/ProjectGridEditor.tsx +648 -0
  180. package/components/builder/editors/SpacerBlockEditor.tsx +167 -0
  181. package/components/builder/editors/StaggerSettings.tsx +108 -0
  182. package/components/builder/editors/TextAlignmentIcons.tsx +39 -0
  183. package/components/builder/editors/TextBlockEditor.tsx +462 -0
  184. package/components/builder/editors/TextStylePicker.tsx +183 -0
  185. package/components/builder/editors/VideoBlockEditor.tsx +278 -0
  186. package/components/builder/editors/index.ts +10 -0
  187. package/components/builder/editors/shared.tsx +345 -0
  188. package/components/builder/hooks/useColumnDrag.ts +472 -0
  189. package/components/builder/hooks/useColumnResize.ts +221 -0
  190. package/components/builder/index.ts +12 -0
  191. package/components/builder/live-preview/LiveButtonPreview.tsx +38 -0
  192. package/components/builder/live-preview/LiveCoverPreview.tsx +146 -0
  193. package/components/builder/live-preview/LiveImageGridPreview.tsx +123 -0
  194. package/components/builder/live-preview/LiveImagePreview.tsx +107 -0
  195. package/components/builder/live-preview/LiveProjectGridPreview.tsx +1010 -0
  196. package/components/builder/live-preview/LiveSpacerPreview.tsx +9 -0
  197. package/components/builder/live-preview/LiveTextEditor.tsx +198 -0
  198. package/components/builder/live-preview/LiveVideoPreview.tsx +98 -0
  199. package/components/builder/live-preview/index.ts +10 -0
  200. package/components/builder/live-preview/shared.tsx +153 -0
  201. package/components/builder/settings-panel/BlockLayoutTab.tsx +532 -0
  202. package/components/builder/settings-panel/BlockSettings.tsx +94 -0
  203. package/components/builder/settings-panel/ColumnV2Settings.tsx +160 -0
  204. package/components/builder/settings-panel/LayoutTab.tsx +310 -0
  205. package/components/builder/settings-panel/PageSettings.tsx +200 -0
  206. package/components/builder/settings-panel/ParallaxGroupSettings.tsx +118 -0
  207. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +178 -0
  208. package/components/builder/settings-panel/SectionV2AnimationTab.tsx +103 -0
  209. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +312 -0
  210. package/components/builder/settings-panel/SectionV2Settings.tsx +323 -0
  211. package/components/builder/settings-panel/TRBLInputs.tsx +51 -0
  212. package/components/builder/settings-panel/index.ts +19 -0
  213. package/components/builder/settings-panel/responsive-helpers.ts +524 -0
  214. package/components/ui/CustomCursor.tsx +118 -0
  215. package/components/ui/NavContentLightbox.tsx +152 -0
  216. package/components/ui/Navbar.tsx +582 -0
  217. package/components/ui/PortfolioTracker.tsx +87 -0
  218. package/components/ui/ScrollToTop.tsx +47 -0
  219. package/lib/animation/enter-presets.ts +147 -0
  220. package/lib/animation/enter-resolve.ts +90 -0
  221. package/lib/animation/enter-types.ts +128 -0
  222. package/lib/animation/hover-effect-presets.ts +210 -0
  223. package/lib/animation/hover-effect-types.ts +126 -0
  224. package/lib/asset-retry.ts +111 -0
  225. package/lib/assets.ts +92 -0
  226. package/lib/audit.ts +35 -0
  227. package/lib/auth-token.ts +94 -0
  228. package/lib/auth.ts +13 -0
  229. package/lib/builder/cascade-helpers.ts +51 -0
  230. package/lib/builder/cascade.ts +533 -0
  231. package/lib/builder/constants.ts +103 -0
  232. package/lib/builder/defaults.ts +182 -0
  233. package/lib/builder/history.ts +48 -0
  234. package/lib/builder/index.ts +21 -0
  235. package/lib/builder/layout-styles.ts +344 -0
  236. package/lib/builder/masonry.ts +166 -0
  237. package/lib/builder/responsive.ts +156 -0
  238. package/lib/builder/serializer.ts +845 -0
  239. package/lib/builder/store-blocks.ts +193 -0
  240. package/lib/builder/store-canvas.ts +319 -0
  241. package/lib/builder/store-helpers.ts +490 -0
  242. package/lib/builder/store-sections.ts +709 -0
  243. package/lib/builder/store.ts +333 -0
  244. package/lib/builder/templates.ts +297 -0
  245. package/lib/builder/types.ts +374 -0
  246. package/lib/builder/utils.ts +37 -0
  247. package/lib/color-utils.ts +116 -0
  248. package/lib/config/index.ts +57 -0
  249. package/lib/config/types.ts +122 -0
  250. package/lib/contexts/AssetContext.tsx +79 -0
  251. package/lib/contexts/NavAnimationContext.tsx +44 -0
  252. package/lib/contexts/NavColorContext.tsx +38 -0
  253. package/lib/contexts/PageExitContext.tsx +194 -0
  254. package/lib/contexts/ThumbStatusContext.tsx +83 -0
  255. package/lib/csrf-client.ts +34 -0
  256. package/lib/csrf.ts +68 -0
  257. package/lib/format-utils.ts +24 -0
  258. package/lib/hooks/useViewport.ts +42 -0
  259. package/lib/logger.ts +81 -0
  260. package/lib/revalidate.ts +23 -0
  261. package/lib/sanitize.ts +91 -0
  262. package/lib/sanity/client.ts +8 -0
  263. package/lib/sanity/queries.ts +486 -0
  264. package/lib/sanity/types.ts +869 -0
  265. package/lib/sanity/writeClient.ts +24 -0
  266. package/lib/security.ts +402 -0
  267. package/lib/setup/detect.ts +156 -0
  268. package/lib/shader/glsl/index.ts +27 -0
  269. package/lib/shader/glsl/pixelate.ts +51 -0
  270. package/lib/shader/glsl/rgb-shift.ts +45 -0
  271. package/lib/shader/glsl/ripple.ts +46 -0
  272. package/lib/shader/glsl/vertex.ts +14 -0
  273. package/lib/storage/index.ts +211 -0
  274. package/lib/storage/r2-adapter.ts +286 -0
  275. package/lib/storage/types.ts +125 -0
  276. package/lib/styles/provider.tsx +267 -0
  277. package/lib/thumbnails/generate.ts +151 -0
  278. package/lib/utils.ts +6 -0
  279. package/package.json +212 -0
  280. package/sanity/compose.ts +65 -0
  281. package/sanity/sanity.config.ts +126 -0
  282. package/sanity/schemas/assetRegistry.ts +301 -0
  283. package/sanity/schemas/blocks/blockLayout.ts +90 -0
  284. package/sanity/schemas/blocks/buttonBlock.ts +82 -0
  285. package/sanity/schemas/blocks/coverBlock.ts +229 -0
  286. package/sanity/schemas/blocks/imageBlock.ts +58 -0
  287. package/sanity/schemas/blocks/imageGridBlock.ts +112 -0
  288. package/sanity/schemas/blocks/index.ts +9 -0
  289. package/sanity/schemas/blocks/projectGridBlock.ts +251 -0
  290. package/sanity/schemas/blocks/spacerBlock.ts +41 -0
  291. package/sanity/schemas/blocks/textBlock.ts +139 -0
  292. package/sanity/schemas/blocks/videoBlock.ts +80 -0
  293. package/sanity/schemas/customSection.ts +69 -0
  294. package/sanity/schemas/customSectionInstance.ts +163 -0
  295. package/sanity/schemas/index.ts +111 -0
  296. package/sanity/schemas/objects/enterAnimationConfig.ts +72 -0
  297. package/sanity/schemas/objects/hoverEffectConfig.ts +90 -0
  298. package/sanity/schemas/objects/parallaxGroup.ts +66 -0
  299. package/sanity/schemas/objects/parallaxSlide.ts +217 -0
  300. package/sanity/schemas/objects/typewriterConfig.ts +38 -0
  301. package/sanity/schemas/page.ts +162 -0
  302. package/sanity/schemas/pageSection.ts +157 -0
  303. package/sanity/schemas/pageSectionV2.ts +269 -0
  304. package/sanity/schemas/siteSettings.ts +256 -0
  305. package/sanity/schemas/siteStyles.ts +212 -0
  306. package/site/error.ts +4 -0
  307. package/site/index.ts +8 -0
  308. package/site/not-found.ts +4 -0
  309. package/site/page.ts +4 -0
  310. package/site/preview.ts +4 -0
  311. package/site/robots.ts +4 -0
  312. package/site/sitemap.ts +4 -0
  313. package/site/work.ts +4 -0
  314. package/studio/index.ts +4 -0
  315. package/styles/admin.css +85 -0
  316. package/styles/animations.css +237 -0
  317. package/styles/base.css +148 -0
  318. package/styles/globals.css +10 -0
  319. package/tsconfig.json +25 -0
@@ -0,0 +1,125 @@
1
+ "use client";
2
+
3
+ import type { NavItem, NavDesign, NavColorVariant } from "../../../lib/sanity/types";
4
+ import { hexToRgba } from "./nav-builder-utils";
5
+ import { getSiteConfig } from "../../../lib/config";
6
+
7
+ const _cfg = getSiteConfig();
8
+
9
+ const NAV_COLOR_HEX: Record<NavColorVariant, string> = {
10
+ "yellow-lime": _cfg.palette.accent,
11
+ yellow: _cfg.palette.accent,
12
+ "red-coral": _cfg.palette.secondary,
13
+ blue: "#076bff",
14
+ green: _cfg.palette.accent,
15
+ white: _cfg.palette.text,
16
+ };
17
+
18
+ interface NavLivePreviewProps {
19
+ items: NavItem[];
20
+ design: NavDesign;
21
+ }
22
+
23
+ export default function NavLivePreview({ items, design }: NavLivePreviewProps) {
24
+ const bgColor = design.background_color || "";
25
+ const bgOpacity = design.background_opacity ?? 0;
26
+ const isTransparent = !bgColor || bgOpacity <= 0;
27
+ const previewBg = isTransparent ? "transparent" : hexToRgba(bgColor, bgOpacity / 100);
28
+
29
+ const paddingH = design.padding_h ?? 24;
30
+ const paddingV = design.padding_v ?? 27;
31
+ const marginH = design.margin_h ?? 0;
32
+ const marginV = design.margin_v ?? 0;
33
+ const fontSize = design.font_size ?? 14;
34
+ const fontFamily =
35
+ design.font_family || `var(--font-family, '${_cfg.typography.defaultFont}', ${_cfg.typography.monoFallback})`;
36
+ const textTransform = (design.text_transform || "uppercase") as React.CSSProperties["textTransform"];
37
+ const textAlign = design.text_align || "left";
38
+ const itemJustify =
39
+ textAlign === "center"
40
+ ? "center"
41
+ : textAlign === "right"
42
+ ? "flex-end"
43
+ : "flex-start";
44
+ const color = NAV_COLOR_HEX[design.color || "yellow-lime"];
45
+ const verticalAlign = design.vertical_align || "top";
46
+ const gridAlignItems = verticalAlign === "bottom" ? "end" : verticalAlign === "middle" ? "center" : "start";
47
+ const hasMargin = marginH > 0 || marginV > 0;
48
+
49
+ const visibleItems = items.filter((i) => i.visible);
50
+
51
+ return (
52
+ <div
53
+ style={{
54
+ background: "#020202",
55
+ padding: `${marginV}px ${marginH}px`,
56
+ borderBottom: "1px solid rgba(51,51,51,0.3)",
57
+ }}
58
+ >
59
+ <div
60
+ style={{
61
+ paddingLeft: `${paddingH}px`,
62
+ paddingRight: `${paddingH}px`,
63
+ paddingTop: `${paddingV}px`,
64
+ paddingBottom: `${paddingV}px`,
65
+ background: isTransparent ? "transparent" : previewBg,
66
+ backdropFilter: design.backdrop_blur ? "blur(12px)" : "none",
67
+ borderRadius: hasMargin ? 8 : 0,
68
+ ...(isTransparent
69
+ ? {
70
+ backgroundImage:
71
+ "linear-gradient(45deg, #1a1a1a 25%, transparent 25%), linear-gradient(-45deg, #1a1a1a 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #1a1a1a 75%), linear-gradient(-45deg, transparent 75%, #1a1a1a 75%)",
72
+ backgroundSize: "16px 16px",
73
+ backgroundPosition: "0 0, 0 8px, 8px -8px, -8px 0px",
74
+ backgroundColor: "#111",
75
+ }
76
+ : {}),
77
+ display: "grid",
78
+ gridTemplateColumns: "repeat(12, 1fr)",
79
+ alignItems: gridAlignItems,
80
+ columnGap: `${design.items_gap ?? 32}px`,
81
+ minHeight: 20,
82
+ fontFamily,
83
+ fontSize,
84
+ textTransform,
85
+ letterSpacing: "0.05em",
86
+ }}
87
+ >
88
+ {visibleItems.map((item) => {
89
+ const overrides = item.style_overrides || {};
90
+ const itemVAlign = overrides.vertical_align;
91
+ return (
92
+ <div
93
+ key={item._key}
94
+ style={{
95
+ gridColumn: `${item.grid_column} / span ${item.column_span}`,
96
+ gridRow: 1,
97
+ color: overrides.color || color,
98
+ fontSize: overrides.font_size || "inherit",
99
+ fontFamily: overrides.font_family || "inherit",
100
+ textTransform: (overrides.text_transform || "inherit") as React.CSSProperties["textTransform"],
101
+ fontWeight: overrides.font_weight || design.font_weight || "400",
102
+ display: "flex",
103
+ justifyContent: item.type === "logo" ? "flex-start" : itemJustify,
104
+ alignItems: "center",
105
+ minWidth: 0,
106
+ whiteSpace: "nowrap",
107
+ overflow: "hidden",
108
+ textOverflow: "ellipsis",
109
+ ...(itemVAlign ? {
110
+ alignSelf: itemVAlign === "bottom" ? "end" : itemVAlign === "middle" ? "center" : "start",
111
+ } : {}),
112
+ }}
113
+ >
114
+ {item.label || "Untitled"}
115
+ </div>
116
+ );
117
+ })}
118
+ </div>
119
+ <div className="flex items-center justify-between px-3 py-1 text-[9px] text-neutral-600">
120
+ <span>Live Preview</span>
121
+ <span>{visibleItems.length} visible items</span>
122
+ </div>
123
+ </div>
124
+ );
125
+ }
@@ -0,0 +1,248 @@
1
+ "use client";
2
+
3
+ import { useState, type ReactNode } from "react";
4
+
5
+ // ── Shared field components for NavBuilder settings panel ──
6
+ // Matches admin page style: light theme, white cards, neutral borders
7
+
8
+ // ── Field wrapper ──
9
+
10
+ export function Field({ label, children }: { label: string; children: ReactNode }) {
11
+ return (
12
+ <div className="flex items-center gap-3 py-1.5">
13
+ <label className="text-xs text-neutral-500 w-[72px] shrink-0 text-right">
14
+ {label}
15
+ </label>
16
+ <div className="flex-1">{children}</div>
17
+ </div>
18
+ );
19
+ }
20
+
21
+ // ── Text input ──
22
+
23
+ export function TextInput({
24
+ value,
25
+ onChange,
26
+ placeholder,
27
+ type = "text",
28
+ }: {
29
+ value: string | number;
30
+ onChange: (value: string) => void;
31
+ placeholder?: string;
32
+ type?: "text" | "number";
33
+ }) {
34
+ return (
35
+ <input
36
+ type={type}
37
+ value={value}
38
+ onChange={(e) => onChange(e.target.value)}
39
+ placeholder={placeholder}
40
+ className="w-full px-2.5 py-1.5 text-xs bg-white border border-neutral-200 rounded-lg text-neutral-900 outline-none focus:border-[#076bff] focus:ring-2 focus:ring-[#076bff]/10 transition-colors font-[inherit] placeholder:text-neutral-400"
41
+ />
42
+ );
43
+ }
44
+
45
+ // ── Select input ──
46
+
47
+ export function SelectInput({
48
+ value,
49
+ onChange,
50
+ options,
51
+ }: {
52
+ value: string;
53
+ onChange: (value: string) => void;
54
+ options: { value: string; label: string }[];
55
+ }) {
56
+ return (
57
+ <div className="relative">
58
+ <select
59
+ value={value}
60
+ onChange={(e) => onChange(e.target.value)}
61
+ className="w-full px-2.5 py-1.5 text-xs bg-white border border-neutral-200 rounded-lg text-neutral-900 outline-none appearance-none cursor-pointer pr-7 focus:border-[#076bff] focus:ring-2 focus:ring-[#076bff]/10 transition-colors font-[inherit]"
62
+ >
63
+ {options.map((opt) => (
64
+ <option key={opt.value} value={opt.value}>
65
+ {opt.label}
66
+ </option>
67
+ ))}
68
+ </select>
69
+ <div className="absolute right-2 top-1/2 -translate-y-1/2 pointer-events-none text-neutral-400">
70
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
71
+ <path
72
+ d="M3 4.5l3 3 3-3"
73
+ stroke="currentColor"
74
+ strokeWidth="1.5"
75
+ strokeLinecap="round"
76
+ strokeLinejoin="round"
77
+ />
78
+ </svg>
79
+ </div>
80
+ </div>
81
+ );
82
+ }
83
+
84
+ // ── Segmented control ──
85
+
86
+ export function SegmentedControl({
87
+ value,
88
+ onChange,
89
+ options,
90
+ }: {
91
+ value: string;
92
+ onChange: (value: string) => void;
93
+ options: { value: string; label: string }[];
94
+ }) {
95
+ return (
96
+ <div className="flex bg-neutral-100 rounded-lg p-0.5">
97
+ {options.map((opt) => (
98
+ <button
99
+ key={opt.value}
100
+ onClick={() => onChange(opt.value)}
101
+ className={`flex-1 px-2 py-1.5 text-[11px] font-medium rounded-md transition-all ${
102
+ value === opt.value
103
+ ? "text-neutral-900 bg-white shadow-sm"
104
+ : "text-neutral-500 hover:text-neutral-700"
105
+ }`}
106
+ >
107
+ {opt.label}
108
+ </button>
109
+ ))}
110
+ </div>
111
+ );
112
+ }
113
+
114
+ // ── Toggle switch ──
115
+
116
+ export function Toggle({
117
+ value,
118
+ onChange,
119
+ }: {
120
+ value: boolean;
121
+ onChange: (value: boolean) => void;
122
+ }) {
123
+ return (
124
+ <button
125
+ onClick={() => onChange(!value)}
126
+ className="w-9 h-5 rounded-full relative transition-all cursor-pointer"
127
+ style={{
128
+ background: value ? "#076bff" : "#d4d4d4",
129
+ }}
130
+ >
131
+ <div
132
+ className="w-3.5 h-3.5 rounded-full bg-white absolute top-[2.5px] transition-all shadow-sm"
133
+ style={{ left: value ? 19 : 2 }}
134
+ />
135
+ </button>
136
+ );
137
+ }
138
+
139
+ // ── Range slider ──
140
+
141
+ export function RangeSlider({
142
+ value,
143
+ onChange,
144
+ min = 0,
145
+ max = 100,
146
+ suffix,
147
+ }: {
148
+ value: number;
149
+ onChange: (value: number) => void;
150
+ min?: number;
151
+ max?: number;
152
+ suffix?: string;
153
+ }) {
154
+ return (
155
+ <div className="flex items-center gap-2">
156
+ <input
157
+ type="range"
158
+ min={min}
159
+ max={max}
160
+ value={value}
161
+ onChange={(e) => onChange(Number(e.target.value))}
162
+ className="flex-1"
163
+ style={{ accentColor: "#076bff" }}
164
+ />
165
+ <span className="text-[10px] text-neutral-500 w-8 text-right tabular-nums">
166
+ {value}{suffix || ""}
167
+ </span>
168
+ </div>
169
+ );
170
+ }
171
+
172
+ // ── Collapsible section ──
173
+
174
+ export function Section({
175
+ title,
176
+ defaultOpen = true,
177
+ children,
178
+ }: {
179
+ title: string;
180
+ defaultOpen?: boolean;
181
+ children: ReactNode;
182
+ }) {
183
+ const [open, setOpen] = useState(defaultOpen);
184
+
185
+ return (
186
+ <div>
187
+ <button
188
+ onClick={() => setOpen(!open)}
189
+ className="flex items-center justify-between w-full py-2 border-t border-neutral-200 mt-1 text-[11px] font-semibold text-neutral-900 tracking-wide cursor-pointer uppercase"
190
+ >
191
+ {title}
192
+ <span
193
+ className="text-neutral-400 transition-transform"
194
+ style={{ transform: open ? "rotate(180deg)" : "rotate(0)" }}
195
+ >
196
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
197
+ <path
198
+ d="M3 4.5l3 3 3-3"
199
+ stroke="currentColor"
200
+ strokeWidth="1.5"
201
+ strokeLinecap="round"
202
+ strokeLinejoin="round"
203
+ />
204
+ </svg>
205
+ </span>
206
+ </button>
207
+ {open && <div className="pb-2">{children}</div>}
208
+ </div>
209
+ );
210
+ }
211
+
212
+ // ── Color input (hex) with swatch preview ──
213
+
214
+ export function ColorInput({
215
+ value,
216
+ onChange,
217
+ placeholder = "Transparent",
218
+ }: {
219
+ value: string;
220
+ onChange: (value: string) => void;
221
+ placeholder?: string;
222
+ }) {
223
+ return (
224
+ <div className="flex gap-1.5 items-center">
225
+ <div
226
+ className="w-7 h-7 rounded-lg border border-neutral-200 shrink-0 cursor-pointer"
227
+ style={{
228
+ background: value || "transparent",
229
+ backgroundImage: !value
230
+ ? "repeating-conic-gradient(#e5e5e5 0% 25%, transparent 0% 50%) 0 0 / 8px 8px"
231
+ : "none",
232
+ }}
233
+ />
234
+ <TextInput value={value} onChange={onChange} placeholder={placeholder} />
235
+ {value && (
236
+ <button
237
+ onClick={() => onChange("")}
238
+ className="text-neutral-400 hover:text-neutral-600 shrink-0"
239
+ title="Clear"
240
+ >
241
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
242
+ <path d="M3 3l6 6M9 3l-6 6" stroke="currentColor" strokeWidth="1.2" strokeLinecap="round" />
243
+ </svg>
244
+ </button>
245
+ )}
246
+ </div>
247
+ );
248
+ }
@@ -0,0 +1,127 @@
1
+ "use client";
2
+
3
+ import { useState, useRef, useEffect } from "react";
4
+ import type { NavItem, NavDesign, PageListItem } from "../../../lib/sanity/types";
5
+ import NavGeneralSettings from "./NavGeneralSettings";
6
+ import NavItemSettings from "./NavItemSettings";
7
+
8
+ type SettingsTab = "settings" | "layout";
9
+
10
+ interface NavSettingsPanelProps {
11
+ selectedItem: NavItem | null;
12
+ items: NavItem[];
13
+ design: NavDesign;
14
+ onDesignChange: (design: NavDesign) => void;
15
+ onUpdateItem: (updated: NavItem) => void;
16
+ pages: PageListItem[];
17
+ fonts: string[];
18
+ }
19
+
20
+ // ── Tab button ──
21
+
22
+ function TabButton({
23
+ active,
24
+ label,
25
+ onClick,
26
+ }: {
27
+ active: boolean;
28
+ label: string;
29
+ onClick: () => void;
30
+ }) {
31
+ return (
32
+ <button
33
+ onClick={onClick}
34
+ className={`px-4 py-2 text-xs font-medium transition-colors border-b-2 -mb-px ${
35
+ active
36
+ ? "text-neutral-900 border-[#076bff]"
37
+ : "text-neutral-400 border-transparent hover:text-neutral-600"
38
+ }`}
39
+ >
40
+ {label}
41
+ </button>
42
+ );
43
+ }
44
+
45
+ // ── Main panel — renders below the grid as a horizontal block ──
46
+
47
+ export default function NavSettingsPanel({
48
+ selectedItem,
49
+ items,
50
+ design,
51
+ onDesignChange,
52
+ onUpdateItem,
53
+ pages,
54
+ fonts,
55
+ }: NavSettingsPanelProps) {
56
+ const [activeTab, setActiveTab] = useState<SettingsTab>("settings");
57
+ const prevKeyRef = useRef(selectedItem?._key);
58
+
59
+ // Reset to settings tab when selection changes
60
+ useEffect(() => {
61
+ if (selectedItem?._key !== prevKeyRef.current) {
62
+ prevKeyRef.current = selectedItem?._key;
63
+ setActiveTab("settings");
64
+ }
65
+ }, [selectedItem?._key]);
66
+
67
+ const isItemMode = !!selectedItem;
68
+ const panelTitle = isItemMode
69
+ ? selectedItem.type === "logo"
70
+ ? "Logo"
71
+ : selectedItem.label || "Untitled"
72
+ : "Navigation";
73
+ const panelSubtitle = isItemMode
74
+ ? `${selectedItem.type === "logo" ? "Logo" : "Menu Item"} · Col ${selectedItem.grid_column}${
75
+ selectedItem.column_span > 1
76
+ ? `–${selectedItem.grid_column + selectedItem.column_span - 1}`
77
+ : ""
78
+ }`
79
+ : `${items.length} items · 12 columns`;
80
+
81
+ return (
82
+ <div className="bg-neutral-50/50">
83
+ {/* Header + Tabs row */}
84
+ <div className="flex items-center gap-6 px-5 border-b border-neutral-200">
85
+ <div className="py-3 pr-4 border-r border-neutral-200">
86
+ <div className="text-[13px] font-semibold text-neutral-900 truncate">
87
+ {panelTitle}
88
+ </div>
89
+ <div className="text-[10px] text-neutral-400 mt-0.5">{panelSubtitle}</div>
90
+ </div>
91
+ <div className="flex gap-0.5">
92
+ <TabButton
93
+ active={activeTab === "settings"}
94
+ label="Settings"
95
+ onClick={() => setActiveTab("settings")}
96
+ />
97
+ <TabButton
98
+ active={activeTab === "layout"}
99
+ label="Layout"
100
+ onClick={() => setActiveTab("layout")}
101
+ />
102
+ </div>
103
+ </div>
104
+
105
+ {/* Content — constrained width for readability */}
106
+ <div className="px-5 py-4 max-w-xl">
107
+ {isItemMode ? (
108
+ <NavItemSettings
109
+ item={selectedItem}
110
+ items={items}
111
+ activeTab={activeTab}
112
+ onUpdate={onUpdateItem}
113
+ pages={pages}
114
+ fonts={fonts}
115
+ />
116
+ ) : (
117
+ <NavGeneralSettings
118
+ design={design}
119
+ activeTab={activeTab}
120
+ onChange={onDesignChange}
121
+ fonts={fonts}
122
+ />
123
+ )}
124
+ </div>
125
+ </div>
126
+ );
127
+ }
@@ -0,0 +1,10 @@
1
+ export { default as NavBuilder } from "./NavBuilder";
2
+ export { default as NavBuilderGrid } from "./NavBuilderGrid";
3
+ export { default as NavGridCell } from "./NavGridCell";
4
+ export { default as NavGridItem } from "./NavGridItem";
5
+ export { default as NavItemTypePicker } from "./NavItemTypePicker";
6
+ export { default as NavLivePreview } from "./NavLivePreview";
7
+ export { default as NavSettingsPanel } from "./NavSettingsPanel";
8
+ export { default as NavGeneralSettings } from "./NavGeneralSettings";
9
+ export { default as NavItemSettings } from "./NavItemSettings";
10
+ export * from "./nav-builder-utils";