@morphika/andami 0.1.9 → 0.2.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 (68) hide show
  1. package/app/admin/pages/[slug]/page.tsx +3 -7
  2. package/app/api/admin/pages/[slug]/route.ts +2 -28
  3. package/app/api/admin/settings/route.ts +30 -0
  4. package/components/admin/nav-builder/NavBuilder.tsx +90 -14
  5. package/components/admin/nav-builder/NavGeneralSettings.tsx +521 -271
  6. package/components/admin/nav-builder/NavItemSettings.tsx +331 -312
  7. package/components/admin/nav-builder/NavMobileSettings.tsx +159 -140
  8. package/components/admin/nav-builder/NavSettingsFields.tsx +287 -21
  9. package/components/admin/nav-builder/NavSettingsPanel.tsx +137 -127
  10. package/components/blocks/EnterAnimationWrapper.tsx +19 -4
  11. package/components/blocks/PageRenderer.tsx +2 -15
  12. package/components/blocks/ProjectGridBlockRenderer.tsx +34 -36
  13. package/components/blocks/TextBlockRenderer.tsx +1 -1
  14. package/components/builder/DndWrapper.tsx +2 -24
  15. package/components/builder/InsertionLines.tsx +5 -5
  16. package/components/builder/ReadOnlyFrame.tsx +5 -49
  17. package/components/builder/SectionV2Canvas.tsx +2 -2
  18. package/components/builder/SectionV2Column.tsx +5 -5
  19. package/components/builder/SettingsPanel.tsx +0 -12
  20. package/components/builder/SortableBlock.tsx +3 -3
  21. package/components/builder/SortableRow.tsx +6 -27
  22. package/components/builder/editors/ButtonBlockEditor.tsx +8 -3
  23. package/components/builder/editors/CoverBlockEditor.tsx +14 -6
  24. package/components/builder/editors/ImageBlockEditor.tsx +8 -3
  25. package/components/builder/editors/ImageGridBlockEditor.tsx +8 -3
  26. package/components/builder/editors/ProjectGridEditor.tsx +7 -46
  27. package/components/builder/editors/SpacerBlockEditor.tsx +4 -1
  28. package/components/builder/editors/StaggerSettings.tsx +2 -1
  29. package/components/builder/editors/TextBlockEditor.tsx +8 -3
  30. package/components/builder/editors/VideoBlockEditor.tsx +10 -4
  31. package/components/builder/editors/section-icons.tsx +492 -0
  32. package/components/builder/editors/shared.tsx +23 -4
  33. package/components/builder/live-preview/LiveTextEditor.tsx +1 -1
  34. package/components/builder/live-preview/ProjectCardWrapper.tsx +3 -3
  35. package/components/builder/live-preview/drag-utils.tsx +2 -2
  36. package/components/builder/settings-panel/AnimationTab.tsx +2 -16
  37. package/components/builder/settings-panel/BlockLayoutTab.tsx +13 -58
  38. package/components/builder/settings-panel/ColumnV2Settings.tsx +4 -1
  39. package/components/builder/settings-panel/PageSettings.tsx +10 -4
  40. package/components/builder/settings-panel/ParallaxGroupSettings.tsx +6 -2
  41. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +8 -3
  42. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +11 -47
  43. package/components/builder/settings-panel/SectionV2Settings.tsx +6 -27
  44. package/components/builder/settings-panel/index.ts +0 -1
  45. package/components/builder/settings-panel/responsive-helpers.ts +2 -50
  46. package/components/builder/settings-panel/useSettingsPanelSelection.ts +1 -16
  47. package/components/ui/Navbar.tsx +151 -30
  48. package/lib/builder/constants.ts +5 -4
  49. package/lib/builder/serializer/normalizers.ts +2 -40
  50. package/lib/builder/serializer/serializers.ts +3 -74
  51. package/lib/builder/store-blocks.ts +3 -19
  52. package/lib/builder/store-helpers.ts +2 -2
  53. package/lib/builder/store-sections.ts +26 -64
  54. package/lib/builder/store.ts +3 -6
  55. package/lib/builder/templates.ts +9 -45
  56. package/lib/builder/types.ts +4 -11
  57. package/lib/sanity/queries.ts +6 -29
  58. package/lib/sanity/types.ts +24 -70
  59. package/package.json +4 -1
  60. package/sanity/schemas/index.ts +0 -5
  61. package/sanity/schemas/objects/parallaxGroup.ts +2 -2
  62. package/sanity/schemas/page.ts +1 -1
  63. package/sanity/schemas/pageSectionV2.ts +1 -0
  64. package/sanity/schemas/siteSettings.ts +42 -0
  65. package/styles/base.css +8 -2
  66. package/components/blocks/SectionRenderer.tsx +0 -171
  67. package/components/builder/settings-panel/LayoutTab.tsx +0 -382
  68. package/sanity/schemas/pageSection.ts +0 -157
@@ -10,7 +10,8 @@ import {
10
10
  SelectInput,
11
11
  SegmentedControl,
12
12
  RangeSlider,
13
- Section,
13
+ CardSection,
14
+ Divider,
14
15
  } from "./NavSettingsFields";
15
16
 
16
17
  // ============================================
@@ -46,31 +47,47 @@ export default function NavMobileSettings({
46
47
  [design, onChange]
47
48
  );
48
49
 
50
+ // ── Icon components for card sections ──
51
+ const OverlayIcon = (
52
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#7c3aed" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
53
+ <rect x="2" y="3" width="20" height="14" rx="2" /><line x1="8" y1="21" x2="16" y2="21" /><line x1="12" y1="17" x2="12" y2="21" />
54
+ </svg>
55
+ );
56
+ const NavbarBarIcon = (
57
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#2563eb" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
58
+ <rect x="3" y="3" width="18" height="18" rx="2" /><line x1="3" y1="9" x2="21" y2="9" />
59
+ </svg>
60
+ );
61
+
49
62
  return (
50
- <div className="bg-white rounded-2xl overflow-hidden border border-neutral-200">
51
- {/* Header */}
52
- <div className="px-5 py-3 border-b border-neutral-200 flex items-center justify-between">
53
- <div>
54
- <div className="text-sm font-semibold text-neutral-900">
63
+ <div className="bg-white rounded-2xl overflow-hidden border border-neutral-200 flex flex-col" style={{ minHeight: "calc(100vh - 180px)" }}>
64
+ {/* ── Gradient header (matching Desktop panel) ── */}
65
+ <div className="relative flex items-center px-4 py-3.5 overflow-hidden" style={{ background: "linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 40%, #ddd6fe 100%)" }}>
66
+ <div className="absolute inset-0 pointer-events-none" style={{ background: "linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.05) 100%)" }} />
67
+ <div className="relative shrink-0 flex items-center justify-center" style={{ width: 34, height: 34, borderRadius: 10, background: "rgba(255,255,255,0.4)", backdropFilter: "blur(8px)", boxShadow: "0 2px 8px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.5)" }}>
68
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="rgba(0,0,0,0.55)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
69
+ <rect x="5" y="2" width="14" height="20" rx="2" /><line x1="12" y1="18" x2="12.01" y2="18" />
70
+ </svg>
71
+ </div>
72
+ <div className="relative z-10 ml-2.5 min-w-0 flex-1">
73
+ <h3 className="text-[13px] font-semibold truncate" style={{ color: "rgba(0,0,0,0.72)", textShadow: "0 1px 0 rgba(255,255,255,0.3)" }}>
55
74
  Mobile Menu
56
- </div>
57
- <div className="text-[11px] text-neutral-400 mt-0.5">
58
- Customize the hamburger menu independently from the desktop navbar
59
- </div>
75
+ </h3>
76
+ <p className="text-[10px] mt-0.5" style={{ color: "rgba(0,0,0,0.38)" }}>
77
+ Independent from the desktop navbar
78
+ </p>
60
79
  </div>
61
- <div className="flex items-center gap-3">
80
+ <div className="relative z-10 flex items-center gap-3">
62
81
  {hasChanges && (
63
- <span className="text-[11px] text-amber-500 font-medium">
64
- Unsaved changes
65
- </span>
82
+ <span className="text-[11px] text-amber-600 font-medium">Unsaved</span>
66
83
  )}
67
84
  <button
68
85
  onClick={onSave}
69
86
  disabled={saving || !hasChanges}
70
- className={`px-5 py-1.5 text-sm font-medium rounded-lg transition-all ${
87
+ className={`px-4 py-1.5 text-[11px] font-medium rounded-lg transition-all ${
71
88
  saving || !hasChanges
72
- ? "bg-neutral-100 text-neutral-400 cursor-not-allowed"
73
- : "bg-[#076bff] text-white hover:bg-[#0559d4]"
89
+ ? "bg-white/40 text-neutral-400 cursor-not-allowed"
90
+ : "bg-white text-[#076bff] shadow-sm hover:shadow-md"
74
91
  }`}
75
92
  >
76
93
  {saving ? "Saving..." : "Save"}
@@ -78,139 +95,141 @@ export default function NavMobileSettings({
78
95
  </div>
79
96
  </div>
80
97
 
81
- {/* Side-by-side: settings left, preview right */}
82
- <div className="flex">
98
+ {/* Side-by-side: settings left, preview right — flex-1 fills remaining height */}
99
+ <div className="flex flex-1 min-h-0">
83
100
  {/* Settings content */}
84
- <div className="flex-1 min-w-0 px-5 py-4 border-r border-neutral-200">
85
- <div className="max-w-md">
86
- {/* ── Menu Overlay (expanded fullscreen menu) ── */}
87
- <Section title="MENU OVERLAY">
88
- <Field label="Background">
89
- <ColorSwatchPicker
90
- value={design.overlay_bg || ""}
91
- onChange={(v) => update({ overlay_bg: typeof v === "string" ? v : "" })}
92
- swatches={swatches}
93
- />
94
- </Field>
95
- <Field label="Text color">
96
- <ColorSwatchPicker
97
- value={design.text_color || ""}
98
- onChange={(v) => update({ text_color: typeof v === "string" ? v : "" })}
99
- swatches={swatches}
100
- />
101
- </Field>
102
- <Field label="Font size">
103
- <TextInput
104
- value={design.font_size ?? 24}
105
- onChange={(v) =>
106
- update({
107
- font_size: Math.max(12, Math.min(72, parseInt(v) || 24)),
108
- })
109
- }
110
- type="number"
111
- />
112
- </Field>
113
- <Field label="Transform">
114
- <SelectInput
115
- value={design.text_transform || "uppercase"}
116
- onChange={(v) =>
117
- update({
118
- text_transform: v as MobileNavDesign["text_transform"],
119
- })
120
- }
121
- options={[
122
- { value: "uppercase", label: "UPPERCASE" },
123
- { value: "none", label: "None" },
124
- { value: "lowercase", label: "lowercase" },
125
- { value: "capitalize", label: "Capitalize" },
126
- ]}
127
- />
128
- </Field>
129
- <Field label="Align">
130
- <SegmentedControl
131
- value={design.items_align || "center"}
132
- onChange={(v) =>
133
- update({ items_align: v as MobileNavDesign["items_align"] })
134
- }
135
- options={[
136
- { value: "left", label: "Left" },
137
- { value: "center", label: "Center" },
138
- { value: "right", label: "Right" },
139
- ]}
140
- />
141
- </Field>
142
- <Field label="Items gap">
143
- <RangeSlider
144
- value={design.items_gap ?? 32}
145
- onChange={(v) => update({ items_gap: v })}
146
- min={8}
147
- max={80}
148
- suffix="px"
149
- />
150
- </Field>
151
- </Section>
101
+ <div className="flex-1 min-w-0 py-2 border-r border-neutral-200 overflow-y-auto">
102
+ {/* ── Menu Overlay card ── */}
103
+ <CardSection title="Overlay" icon={OverlayIcon} iconBg="#ede9fe">
104
+ <Field label="BG">
105
+ <ColorSwatchPicker
106
+ value={design.overlay_bg || ""}
107
+ onChange={(v) => update({ overlay_bg: typeof v === "string" ? v : "" })}
108
+ swatches={swatches}
109
+ />
110
+ </Field>
111
+ <Field label="Text">
112
+ <ColorSwatchPicker
113
+ value={design.text_color || ""}
114
+ onChange={(v) => update({ text_color: typeof v === "string" ? v : "" })}
115
+ swatches={swatches}
116
+ />
117
+ </Field>
118
+ <Divider />
119
+ <Field label="Size">
120
+ <TextInput
121
+ value={design.font_size ?? 24}
122
+ onChange={(v) =>
123
+ update({
124
+ font_size: Math.max(12, Math.min(72, parseInt(v) || 24)),
125
+ })
126
+ }
127
+ type="number"
128
+ />
129
+ </Field>
130
+ <Field label="Case">
131
+ <SelectInput
132
+ value={design.text_transform || "uppercase"}
133
+ onChange={(v) =>
134
+ update({
135
+ text_transform: v as MobileNavDesign["text_transform"],
136
+ })
137
+ }
138
+ options={[
139
+ { value: "uppercase", label: "AA" },
140
+ { value: "capitalize", label: "Aa" },
141
+ { value: "lowercase", label: "aa" },
142
+ { value: "none", label: "" },
143
+ ]}
144
+ />
145
+ </Field>
146
+ <Field label="Align">
147
+ <SegmentedControl
148
+ value={design.items_align || "center"}
149
+ onChange={(v) =>
150
+ update({ items_align: v as MobileNavDesign["items_align"] })
151
+ }
152
+ options={[
153
+ { value: "left", label: "Left" },
154
+ { value: "center", label: "Center" },
155
+ { value: "right", label: "Right" },
156
+ ]}
157
+ />
158
+ </Field>
159
+ <Divider />
160
+ <Field label="Gap">
161
+ <RangeSlider
162
+ value={design.items_gap ?? 32}
163
+ onChange={(v) => update({ items_gap: v })}
164
+ min={8}
165
+ max={80}
166
+ suffix="px"
167
+ />
168
+ </Field>
169
+ </CardSection>
152
170
 
153
- {/* ── Navbar Bar (logo + hamburger row) ── */}
154
- <Section title="NAVBAR BAR">
155
- <Field label="BG color">
156
- <ColorSwatchPicker
157
- value={design.navbar_bg || ""}
158
- onChange={(v) => update({ navbar_bg: typeof v === "string" ? v : "" })}
159
- swatches={swatches}
160
- />
161
- </Field>
162
- {design.navbar_bg && (
163
- <Field label="BG opacity">
164
- <RangeSlider
165
- value={design.navbar_bg_opacity ?? 100}
166
- onChange={(v) => update({ navbar_bg_opacity: v })}
167
- min={0}
168
- max={100}
169
- suffix="%"
170
- />
171
- </Field>
172
- )}
173
- <Field label="Icon color">
174
- <ColorSwatchPicker
175
- value={design.hamburger_color || ""}
176
- onChange={(v) => update({ hamburger_color: typeof v === "string" ? v : "" })}
177
- swatches={swatches}
178
- />
179
- </Field>
180
- <Field label="Pad H">
171
+ {/* ── Navbar Bar card ── */}
172
+ <CardSection title="Top Bar" icon={NavbarBarIcon} iconBg="#dbeafe">
173
+ <Field label="BG">
174
+ <ColorSwatchPicker
175
+ value={design.navbar_bg || ""}
176
+ onChange={(v) => update({ navbar_bg: typeof v === "string" ? v : "" })}
177
+ swatches={swatches}
178
+ />
179
+ </Field>
180
+ {design.navbar_bg && (
181
+ <Field label="Opacity">
181
182
  <RangeSlider
182
- value={design.padding_h ?? 24}
183
- onChange={(v) => update({ padding_h: v })}
183
+ value={design.navbar_bg_opacity ?? 100}
184
+ onChange={(v) => update({ navbar_bg_opacity: v })}
184
185
  min={0}
185
- max={60}
186
- suffix="px"
186
+ max={100}
187
+ suffix="%"
187
188
  />
188
189
  </Field>
189
- <Field label="Pad V">
190
- <RangeSlider
191
- value={design.padding_v ?? 27}
192
- onChange={(v) => update({ padding_v: v })}
193
- min={0}
194
- max={60}
195
- suffix="px"
196
- />
197
- </Field>
198
- </Section>
190
+ )}
191
+ <Field label="Icon">
192
+ <ColorSwatchPicker
193
+ value={design.hamburger_color || ""}
194
+ onChange={(v) => update({ hamburger_color: typeof v === "string" ? v : "" })}
195
+ swatches={swatches}
196
+ />
197
+ </Field>
198
+ <Divider />
199
+ <Field label="Pad H">
200
+ <RangeSlider
201
+ value={design.padding_h ?? 24}
202
+ onChange={(v) => update({ padding_h: v })}
203
+ min={0}
204
+ max={60}
205
+ suffix="px"
206
+ />
207
+ </Field>
208
+ <Field label="Pad V">
209
+ <RangeSlider
210
+ value={design.padding_v ?? 27}
211
+ onChange={(v) => update({ padding_v: v })}
212
+ min={0}
213
+ max={60}
214
+ suffix="px"
215
+ />
216
+ </Field>
217
+ </CardSection>
199
218
 
200
- {/* Info notice */}
201
- <div className="mt-4 p-3 bg-blue-50 rounded-xl border border-blue-200">
202
- <p className="text-[11px] text-blue-600 leading-relaxed">
203
- <strong>Independent from page overrides:</strong> Page-level{" "}
204
- <code className="bg-blue-100 px-1 rounded text-[10px]">nav_color</code>{" "}
205
- and parallax slide color changes only affect the desktop navbar.
206
- The mobile menu always uses these dedicated styles.
207
- </p>
208
- </div>
219
+ {/* Info notice — card style */}
220
+ <div className="mx-2.5 my-1 px-3 py-2.5 bg-blue-50/60 rounded-[10px] border border-blue-100">
221
+ <p className="text-[10px] text-blue-500 leading-relaxed">
222
+ <strong>Independent from page overrides.</strong> Page-level{" "}
223
+ <code className="bg-blue-100 px-1 rounded text-[9px]">nav_color</code>{" "}
224
+ and parallax color changes only affect the desktop navbar.
225
+ </p>
209
226
  </div>
227
+
228
+ <div className="h-2" />
210
229
  </div>
211
230
 
212
- {/* Live preview — sticky on the right */}
213
- <div className="w-[340px] shrink-0 self-start sticky top-0">
231
+ {/* Live preview — stretches full height */}
232
+ <div className="w-[340px] shrink-0 sticky top-0 self-stretch">
214
233
  <NavMobileLivePreview
215
234
  items={items}
216
235
  design={desktopDesign}