@morphika/andami 0.2.12 → 0.2.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/app/admin/pages/[slug]/page.tsx +39 -2
- package/components/blocks/BlockRenderer.tsx +0 -7
- package/components/blocks/CoverSectionRenderer.tsx +295 -0
- package/components/blocks/ImageBlockRenderer.tsx +12 -10
- package/components/blocks/PageRenderer.tsx +13 -9
- package/components/blocks/VideoBlockRenderer.tsx +11 -6
- package/components/builder/BlockLivePreview.tsx +0 -5
- package/components/builder/BlockTypePicker.tsx +0 -1
- package/components/builder/ColorSwatchPicker.tsx +2 -2
- package/components/builder/CoverRowResizeHandle.tsx +180 -0
- package/components/builder/CoverSectionCanvas.tsx +260 -0
- package/components/builder/ReadOnlyFrame.tsx +127 -3
- package/components/builder/SectionTypePicker.tsx +29 -0
- package/components/builder/SectionV2Canvas.tsx +4 -1
- package/components/builder/SectionV2Column.tsx +15 -20
- package/components/builder/SettingsPanel.tsx +14 -0
- package/components/builder/SortableRow.tsx +7 -21
- package/components/builder/blockStyles.tsx +13 -14
- package/components/builder/editors/ImageBlockEditor.tsx +1 -0
- package/components/builder/editors/VideoBlockEditor.tsx +1 -0
- package/components/builder/editors/index.ts +0 -1
- package/components/builder/index.ts +1 -0
- package/components/builder/live-preview/LiveImagePreview.tsx +21 -2
- package/components/builder/live-preview/LiveVideoPreview.tsx +8 -3
- package/components/builder/live-preview/RichTextEditor.tsx +23 -2
- package/components/builder/live-preview/index.ts +0 -1
- package/components/builder/settings-panel/BlockSettings.tsx +0 -7
- package/components/builder/settings-panel/CoverSectionSettings.tsx +296 -0
- package/components/builder/settings-panel/index.ts +1 -0
- package/components/builder/settings-panel/useSettingsPanelSelection.ts +36 -2
- package/lib/animation/enter-types.ts +0 -1
- package/lib/animation/hover-effect-types.ts +0 -1
- package/lib/builder/defaults.ts +43 -22
- package/lib/builder/serializer/normalizers.ts +34 -1
- package/lib/builder/serializer/serializers.ts +39 -2
- package/lib/builder/store-blocks.ts +11 -3
- package/lib/builder/store-cover.ts +220 -0
- package/lib/builder/store-helpers.ts +81 -4
- package/lib/builder/store-sections.ts +12 -2
- package/lib/builder/store.ts +11 -2
- package/lib/builder/types.ts +15 -2
- package/lib/sanity/queries.ts +18 -4
- package/lib/sanity/types.ts +81 -45
- package/lib/version.ts +1 -1
- package/package.json +1 -1
- package/sanity/schemas/blocks/imageBlock.ts +1 -0
- package/sanity/schemas/blocks/index.ts +1 -2
- package/sanity/schemas/blocks/videoBlock.ts +1 -0
- package/sanity/schemas/index.ts +5 -3
- package/sanity/schemas/objects/coverSection.ts +317 -0
- package/sanity/schemas/objects/parallaxSlide.ts +0 -1
- package/sanity/schemas/page.ts +1 -1
- package/sanity/schemas/pageSectionV2.ts +0 -1
- package/components/blocks/CoverBlockRenderer.tsx +0 -261
- package/components/builder/editors/CoverBlockEditor.tsx +0 -550
- package/components/builder/live-preview/LiveCoverPreview.tsx +0 -146
- package/sanity/schemas/blocks/coverBlock.ts +0 -229
package/lib/sanity/types.ts
CHANGED
|
@@ -135,7 +135,7 @@ export interface ImageBlock {
|
|
|
135
135
|
asset_path: string;
|
|
136
136
|
alt?: string;
|
|
137
137
|
caption?: string;
|
|
138
|
-
width?: "full" | "contained" | "small";
|
|
138
|
+
width?: "full" | "contained" | "small" | "fill";
|
|
139
139
|
aspect_ratio?: "auto" | "16:9" | "4:3" | "1:1" | "21:9";
|
|
140
140
|
lazy?: boolean;
|
|
141
141
|
border_radius?: string;
|
|
@@ -174,7 +174,7 @@ export interface VideoBlock {
|
|
|
174
174
|
loop?: boolean;
|
|
175
175
|
muted?: boolean;
|
|
176
176
|
controls?: boolean;
|
|
177
|
-
width?: "full" | "contained";
|
|
177
|
+
width?: "full" | "contained" | "fill";
|
|
178
178
|
aspect_ratio?: "16:9" | "21:9" | "4:3" | "auto";
|
|
179
179
|
border_radius?: string;
|
|
180
180
|
enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
|
|
@@ -210,47 +210,6 @@ export interface ButtonBlock {
|
|
|
210
210
|
responsive?: ResponsiveOverrides<ButtonBlock>;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
export interface CoverBlock {
|
|
214
|
-
_type: "coverBlock";
|
|
215
|
-
_key: string;
|
|
216
|
-
// Content
|
|
217
|
-
headline?: string;
|
|
218
|
-
subheadline?: string;
|
|
219
|
-
cta_button?: {
|
|
220
|
-
text?: string;
|
|
221
|
-
url?: string;
|
|
222
|
-
target_blank?: boolean;
|
|
223
|
-
style?: "primary" | "secondary" | "outline" | "text";
|
|
224
|
-
};
|
|
225
|
-
// Media
|
|
226
|
-
media_type?: "image" | "video";
|
|
227
|
-
media_path?: string;
|
|
228
|
-
video_poster?: string;
|
|
229
|
-
background_size?: "cover" | "contain" | "none";
|
|
230
|
-
background_position?: string;
|
|
231
|
-
background_repeat?: "no-repeat" | "repeat" | "repeat-x" | "repeat-y";
|
|
232
|
-
// Overlay
|
|
233
|
-
overlay?: "none" | "dark" | "light" | "gradient-bottom" | "gradient-top";
|
|
234
|
-
overlay_opacity?: number;
|
|
235
|
-
/** Custom overlay gradient (Phase 4). JSON-serialized ColorField.
|
|
236
|
-
* When set, takes precedence over overlay + overlay_opacity. */
|
|
237
|
-
overlay_gradient?: string;
|
|
238
|
-
// Layout
|
|
239
|
-
content_align_h?: "left" | "center" | "right";
|
|
240
|
-
content_align_v?: "top" | "center" | "bottom";
|
|
241
|
-
content_max_width?: string;
|
|
242
|
-
height?: "100vh" | "80vh" | "60vh" | "40vh" | "custom";
|
|
243
|
-
custom_height?: string;
|
|
244
|
-
mobile_height?: "same" | "100vh" | "80vh" | "60vh" | "500px" | "400px";
|
|
245
|
-
// Appearance
|
|
246
|
-
text_color?: string;
|
|
247
|
-
show_scroll_indicator?: boolean;
|
|
248
|
-
enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
|
|
249
|
-
hover_effect?: import("../../lib/animation/hover-effect-types").HoverEffectConfig;
|
|
250
|
-
layout?: BlockLayout;
|
|
251
|
-
responsive?: ResponsiveOverrides<CoverBlock>;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
213
|
// ============================================
|
|
255
214
|
// Project Grid Block v2 (template-only, Session 105)
|
|
256
215
|
// ============================================
|
|
@@ -477,11 +436,84 @@ export interface CustomSectionInstance {
|
|
|
477
436
|
responsive_overrides?: PageSectionV2["responsive"];
|
|
478
437
|
}
|
|
479
438
|
|
|
439
|
+
// ============================================
|
|
440
|
+
// Cover Section — proportional rows (Session 176)
|
|
441
|
+
// ============================================
|
|
442
|
+
// Full-viewport section with fixed-height rows and background media.
|
|
443
|
+
// Uses the same SectionColumn/block system as V2 but with explicit
|
|
444
|
+
// row heights (percentages) instead of content-driven auto rows.
|
|
445
|
+
|
|
446
|
+
export type CoverSectionHeight = "100vh" | "80vh" | "50vh";
|
|
447
|
+
|
|
448
|
+
/** A single proportional row within a Cover Section */
|
|
449
|
+
export interface CoverRow {
|
|
450
|
+
_key: string;
|
|
451
|
+
height_percent: number; // 5–95, all rows must sum to 100
|
|
452
|
+
vertical_align: "start" | "center" | "end";
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/** Cover Section settings (subset of SectionV2Settings — no background/border) */
|
|
456
|
+
export interface CoverSectionSettings {
|
|
457
|
+
grid_columns: number; // default 12
|
|
458
|
+
col_gap: number;
|
|
459
|
+
row_gap: number;
|
|
460
|
+
spacing_top?: string;
|
|
461
|
+
spacing_right?: string;
|
|
462
|
+
spacing_bottom?: string;
|
|
463
|
+
spacing_left?: string;
|
|
464
|
+
enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
|
|
465
|
+
stagger?: {
|
|
466
|
+
enabled?: boolean;
|
|
467
|
+
delayPerChild?: number;
|
|
468
|
+
direction?: "left-to-right" | "right-to-left";
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/** Responsive override for Cover Sections */
|
|
473
|
+
export interface CoverSectionResponsiveOverride {
|
|
474
|
+
columns?: ColumnOverride[];
|
|
475
|
+
cover_rows?: Array<{ _key: string; height_percent?: number }>;
|
|
476
|
+
settings?: Partial<CoverSectionSettings>;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** Cover Section — full-viewport section with background and proportional rows */
|
|
480
|
+
export interface CoverSection {
|
|
481
|
+
_type: "coverSection";
|
|
482
|
+
_key: string;
|
|
483
|
+
|
|
484
|
+
// Background
|
|
485
|
+
background_type: "image" | "video";
|
|
486
|
+
background_image?: string;
|
|
487
|
+
background_video?: string;
|
|
488
|
+
background_position?: string;
|
|
489
|
+
background_size?: "cover" | "contain" | "auto";
|
|
490
|
+
background_overlay_color?: string;
|
|
491
|
+
background_overlay_opacity?: number; // 0–100
|
|
492
|
+
|
|
493
|
+
// Height
|
|
494
|
+
height: CoverSectionHeight;
|
|
495
|
+
|
|
496
|
+
// Rows (explicit proportional heights)
|
|
497
|
+
cover_rows: CoverRow[];
|
|
498
|
+
|
|
499
|
+
// Columns (same as V2 — flat array with grid_row referencing cover_rows index)
|
|
500
|
+
columns: SectionColumn[];
|
|
501
|
+
|
|
502
|
+
// Settings
|
|
503
|
+
settings: CoverSectionSettings;
|
|
504
|
+
|
|
505
|
+
// Responsive
|
|
506
|
+
responsive?: {
|
|
507
|
+
tablet?: CoverSectionResponsiveOverride;
|
|
508
|
+
phone?: CoverSectionResponsiveOverride;
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
480
512
|
// ============================================
|
|
481
513
|
// Content Item — union of section types in content_rows
|
|
482
514
|
// ============================================
|
|
483
515
|
|
|
484
|
-
export type ContentItem = PageSectionV2 | CustomSectionInstance | ParallaxGroup;
|
|
516
|
+
export type ContentItem = PageSectionV2 | CustomSectionInstance | ParallaxGroup | CoverSection;
|
|
485
517
|
|
|
486
518
|
/** Type guard: check if a content item is a PageSectionV2 */
|
|
487
519
|
export function isPageSectionV2(item: ContentItem): item is PageSectionV2 {
|
|
@@ -498,6 +530,11 @@ export function isParallaxGroup(item: ContentItem): item is ParallaxGroup {
|
|
|
498
530
|
return (item as ParallaxGroup)._type === "parallaxGroup";
|
|
499
531
|
}
|
|
500
532
|
|
|
533
|
+
/** Type guard: check if a content item is a CoverSection (Session 176) */
|
|
534
|
+
export function isCoverSection(item: ContentItem): item is CoverSection {
|
|
535
|
+
return (item as CoverSection)._type === "coverSection";
|
|
536
|
+
}
|
|
537
|
+
|
|
501
538
|
// ============================================
|
|
502
539
|
// Shared references
|
|
503
540
|
// ============================================
|
|
@@ -520,7 +557,6 @@ export type ContentBlock =
|
|
|
520
557
|
| VideoBlock
|
|
521
558
|
| SpacerBlock
|
|
522
559
|
| ButtonBlock
|
|
523
|
-
| CoverBlock
|
|
524
560
|
| ProjectGridBlock;
|
|
525
561
|
|
|
526
562
|
// ============================================
|
package/lib/version.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
// Block schemas (
|
|
1
|
+
// Block schemas (7)
|
|
2
2
|
export { textBlock } from "./textBlock";
|
|
3
3
|
export { imageBlock } from "./imageBlock";
|
|
4
4
|
export { imageGridBlock } from "./imageGridBlock";
|
|
5
5
|
export { videoBlock } from "./videoBlock";
|
|
6
6
|
export { spacerBlock } from "./spacerBlock";
|
|
7
7
|
export { buttonBlock } from "./buttonBlock";
|
|
8
|
-
export { coverBlock } from "./coverBlock";
|
|
9
8
|
export { projectGridBlock } from "./projectGridBlock";
|
package/sanity/schemas/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import hoverEffectConfig from "./objects/hoverEffectConfig";
|
|
|
10
10
|
import typewriterConfig from "./objects/typewriterConfig";
|
|
11
11
|
import parallaxSlide from "./objects/parallaxSlide";
|
|
12
12
|
import parallaxGroup from "./objects/parallaxGroup";
|
|
13
|
+
import coverSection from "./objects/coverSection";
|
|
13
14
|
import {
|
|
14
15
|
textBlock,
|
|
15
16
|
imageBlock,
|
|
@@ -17,7 +18,6 @@ import {
|
|
|
17
18
|
videoBlock,
|
|
18
19
|
spacerBlock,
|
|
19
20
|
buttonBlock,
|
|
20
|
-
coverBlock,
|
|
21
21
|
projectGridBlock,
|
|
22
22
|
} from "./blocks";
|
|
23
23
|
|
|
@@ -58,6 +58,9 @@ export {
|
|
|
58
58
|
export {
|
|
59
59
|
default as parallaxGroup,
|
|
60
60
|
} from "./objects/parallaxGroup";
|
|
61
|
+
export {
|
|
62
|
+
default as coverSection,
|
|
63
|
+
} from "./objects/coverSection";
|
|
61
64
|
export {
|
|
62
65
|
textBlock,
|
|
63
66
|
imageBlock,
|
|
@@ -65,7 +68,6 @@ export {
|
|
|
65
68
|
videoBlock,
|
|
66
69
|
spacerBlock,
|
|
67
70
|
buttonBlock,
|
|
68
|
-
coverBlock,
|
|
69
71
|
projectGridBlock,
|
|
70
72
|
} from "./blocks";
|
|
71
73
|
|
|
@@ -87,6 +89,7 @@ export const schemaTypes = [
|
|
|
87
89
|
typewriterConfig, // Typewriter config for textBlock (Session 117)
|
|
88
90
|
parallaxSlide, // Parallax V2 slide (Session 123)
|
|
89
91
|
parallaxGroup, // Parallax V2 group (Session 123)
|
|
92
|
+
coverSection, // Cover Section — proportional rows (Session 176)
|
|
90
93
|
|
|
91
94
|
// Blocks (8)
|
|
92
95
|
textBlock,
|
|
@@ -95,7 +98,6 @@ export const schemaTypes = [
|
|
|
95
98
|
videoBlock,
|
|
96
99
|
spacerBlock,
|
|
97
100
|
buttonBlock,
|
|
98
|
-
coverBlock,
|
|
99
101
|
projectGridBlock,
|
|
100
102
|
];
|
|
101
103
|
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { defineField, defineType } from "sanity";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* coverSection — Full-viewport section with proportional rows and background media.
|
|
5
|
+
*
|
|
6
|
+
* A new ContentItem type alongside pageSectionV2, parallaxGroup, and customSectionInstance.
|
|
7
|
+
* Uses the same column/block system as V2 but with fixed-height proportional rows
|
|
8
|
+
* instead of content-driven rows. Overflow is always hidden.
|
|
9
|
+
*
|
|
10
|
+
* Session 176: Cover Sections — Phase 1 (Schema).
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const columnFields = [
|
|
14
|
+
defineField({
|
|
15
|
+
name: "grid_column",
|
|
16
|
+
title: "Grid Column",
|
|
17
|
+
type: "number",
|
|
18
|
+
description: "1-based start position (1–12)",
|
|
19
|
+
validation: (Rule) => Rule.required().min(1).max(12),
|
|
20
|
+
}),
|
|
21
|
+
defineField({
|
|
22
|
+
name: "grid_row",
|
|
23
|
+
title: "Grid Row",
|
|
24
|
+
type: "number",
|
|
25
|
+
description: "1-based row index (maps to cover_rows)",
|
|
26
|
+
validation: (Rule) => Rule.required().min(1),
|
|
27
|
+
}),
|
|
28
|
+
defineField({
|
|
29
|
+
name: "span",
|
|
30
|
+
title: "Column Span",
|
|
31
|
+
type: "number",
|
|
32
|
+
description: "How many grid columns (1–12)",
|
|
33
|
+
validation: (Rule) => Rule.required().min(1).max(12),
|
|
34
|
+
}),
|
|
35
|
+
defineField({
|
|
36
|
+
name: "blocks",
|
|
37
|
+
title: "Blocks",
|
|
38
|
+
type: "array",
|
|
39
|
+
of: [
|
|
40
|
+
{ type: "textBlock" },
|
|
41
|
+
{ type: "imageBlock" },
|
|
42
|
+
{ type: "imageGridBlock" },
|
|
43
|
+
{ type: "videoBlock" },
|
|
44
|
+
{ type: "spacerBlock" },
|
|
45
|
+
{ type: "buttonBlock" },
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
defineField({
|
|
49
|
+
name: "enter_animation",
|
|
50
|
+
title: "Enter Animation",
|
|
51
|
+
type: "enterAnimationConfig",
|
|
52
|
+
}),
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const responsiveColumnOverrideFields = [
|
|
56
|
+
defineField({
|
|
57
|
+
name: "columns",
|
|
58
|
+
title: "Column Overrides",
|
|
59
|
+
type: "array",
|
|
60
|
+
of: [
|
|
61
|
+
{
|
|
62
|
+
type: "object",
|
|
63
|
+
name: "columnOverride",
|
|
64
|
+
fields: [
|
|
65
|
+
defineField({ name: "grid_column", title: "Grid Column", type: "number" }),
|
|
66
|
+
defineField({ name: "grid_row", title: "Grid Row", type: "number" }),
|
|
67
|
+
defineField({ name: "span", title: "Span", type: "number" }),
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
}),
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const responsiveRowOverrideFields = [
|
|
75
|
+
defineField({
|
|
76
|
+
name: "cover_rows",
|
|
77
|
+
title: "Row Height Overrides",
|
|
78
|
+
type: "array",
|
|
79
|
+
of: [
|
|
80
|
+
{
|
|
81
|
+
type: "object",
|
|
82
|
+
name: "coverRowOverride",
|
|
83
|
+
fields: [
|
|
84
|
+
defineField({ name: "height_percent", title: "Height %", type: "number" }),
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
}),
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
const responsiveSettingsFields = [
|
|
92
|
+
defineField({ name: "col_gap", title: "Column Gap", type: "number" }),
|
|
93
|
+
defineField({ name: "row_gap", title: "Row Gap", type: "number" }),
|
|
94
|
+
defineField({ name: "spacing_top", type: "string", title: "Spacing Top" }),
|
|
95
|
+
defineField({ name: "spacing_right", type: "string", title: "Spacing Right" }),
|
|
96
|
+
defineField({ name: "spacing_bottom", type: "string", title: "Spacing Bottom" }),
|
|
97
|
+
defineField({ name: "spacing_left", type: "string", title: "Spacing Left" }),
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
export default defineType({
|
|
101
|
+
name: "coverSection",
|
|
102
|
+
title: "Cover Section",
|
|
103
|
+
type: "object",
|
|
104
|
+
fields: [
|
|
105
|
+
// ── Background ──
|
|
106
|
+
defineField({
|
|
107
|
+
name: "background_type",
|
|
108
|
+
title: "Background Type",
|
|
109
|
+
type: "string",
|
|
110
|
+
options: {
|
|
111
|
+
list: [
|
|
112
|
+
{ title: "Image", value: "image" },
|
|
113
|
+
{ title: "Video", value: "video" },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
initialValue: "image",
|
|
117
|
+
}),
|
|
118
|
+
defineField({
|
|
119
|
+
name: "background_image",
|
|
120
|
+
title: "Background Image",
|
|
121
|
+
type: "string",
|
|
122
|
+
description: "Asset path for background image",
|
|
123
|
+
}),
|
|
124
|
+
defineField({
|
|
125
|
+
name: "background_video",
|
|
126
|
+
title: "Background Video",
|
|
127
|
+
type: "string",
|
|
128
|
+
description: "Asset path for background video",
|
|
129
|
+
}),
|
|
130
|
+
defineField({
|
|
131
|
+
name: "background_position",
|
|
132
|
+
title: "Background Position",
|
|
133
|
+
type: "string",
|
|
134
|
+
description: "CSS background-position value",
|
|
135
|
+
initialValue: "center center",
|
|
136
|
+
}),
|
|
137
|
+
defineField({
|
|
138
|
+
name: "background_size",
|
|
139
|
+
title: "Background Size",
|
|
140
|
+
type: "string",
|
|
141
|
+
options: {
|
|
142
|
+
list: [
|
|
143
|
+
{ title: "Cover", value: "cover" },
|
|
144
|
+
{ title: "Contain", value: "contain" },
|
|
145
|
+
{ title: "Auto", value: "auto" },
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
initialValue: "cover",
|
|
149
|
+
}),
|
|
150
|
+
defineField({
|
|
151
|
+
name: "background_overlay_color",
|
|
152
|
+
title: "Overlay Color",
|
|
153
|
+
type: "string",
|
|
154
|
+
description: "Hex color for background overlay",
|
|
155
|
+
initialValue: "#000000",
|
|
156
|
+
}),
|
|
157
|
+
defineField({
|
|
158
|
+
name: "background_overlay_opacity",
|
|
159
|
+
title: "Overlay Opacity",
|
|
160
|
+
type: "number",
|
|
161
|
+
description: "0–100",
|
|
162
|
+
initialValue: 0,
|
|
163
|
+
validation: (Rule) => Rule.min(0).max(100),
|
|
164
|
+
}),
|
|
165
|
+
|
|
166
|
+
// ── Section Height ──
|
|
167
|
+
defineField({
|
|
168
|
+
name: "height",
|
|
169
|
+
title: "Section Height",
|
|
170
|
+
type: "string",
|
|
171
|
+
options: {
|
|
172
|
+
list: [
|
|
173
|
+
{ title: "Full Viewport (100vh)", value: "100vh" },
|
|
174
|
+
{ title: "80% Viewport (80vh)", value: "80vh" },
|
|
175
|
+
{ title: "50% Viewport (50vh)", value: "50vh" },
|
|
176
|
+
],
|
|
177
|
+
},
|
|
178
|
+
initialValue: "100vh",
|
|
179
|
+
}),
|
|
180
|
+
|
|
181
|
+
// ── Cover Rows (explicit, proportional) ──
|
|
182
|
+
defineField({
|
|
183
|
+
name: "cover_rows",
|
|
184
|
+
title: "Cover Rows",
|
|
185
|
+
type: "array",
|
|
186
|
+
description: "Proportional rows — heights must sum to 100%",
|
|
187
|
+
of: [
|
|
188
|
+
{
|
|
189
|
+
type: "object",
|
|
190
|
+
name: "coverRow",
|
|
191
|
+
fields: [
|
|
192
|
+
defineField({
|
|
193
|
+
name: "height_percent",
|
|
194
|
+
title: "Height (%)",
|
|
195
|
+
type: "number",
|
|
196
|
+
description: "Row height as percentage of section (5–95)",
|
|
197
|
+
validation: (Rule) => Rule.required().min(5).max(95),
|
|
198
|
+
}),
|
|
199
|
+
defineField({
|
|
200
|
+
name: "vertical_align",
|
|
201
|
+
title: "Vertical Alignment",
|
|
202
|
+
type: "string",
|
|
203
|
+
description: "How content is aligned vertically within this row",
|
|
204
|
+
options: {
|
|
205
|
+
list: [
|
|
206
|
+
{ title: "Top", value: "start" },
|
|
207
|
+
{ title: "Center", value: "center" },
|
|
208
|
+
{ title: "Bottom", value: "end" },
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
initialValue: "start",
|
|
212
|
+
}),
|
|
213
|
+
],
|
|
214
|
+
preview: {
|
|
215
|
+
select: { height: "height_percent", align: "vertical_align" },
|
|
216
|
+
prepare({ height, align }) {
|
|
217
|
+
return { title: `${height ?? 0}%`, subtitle: align ?? "start" };
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
validation: (Rule) => Rule.required().min(1).max(5),
|
|
223
|
+
}),
|
|
224
|
+
|
|
225
|
+
// ── Columns (same structure as V2) ──
|
|
226
|
+
defineField({
|
|
227
|
+
name: "columns",
|
|
228
|
+
title: "Columns",
|
|
229
|
+
type: "array",
|
|
230
|
+
of: [
|
|
231
|
+
{
|
|
232
|
+
type: "object",
|
|
233
|
+
name: "sectionColumn",
|
|
234
|
+
fields: columnFields,
|
|
235
|
+
},
|
|
236
|
+
],
|
|
237
|
+
}),
|
|
238
|
+
|
|
239
|
+
// ── Section Settings (subset of V2 — no background/border, those are section-level) ──
|
|
240
|
+
defineField({
|
|
241
|
+
name: "settings",
|
|
242
|
+
title: "Section Settings",
|
|
243
|
+
type: "object",
|
|
244
|
+
fields: [
|
|
245
|
+
defineField({ name: "grid_columns", title: "Grid Columns", type: "number" }),
|
|
246
|
+
defineField({ name: "col_gap", title: "Column Gap", type: "number" }),
|
|
247
|
+
defineField({ name: "row_gap", title: "Row Gap", type: "number" }),
|
|
248
|
+
// Spacing (padding TRBL)
|
|
249
|
+
defineField({ name: "spacing_top", title: "Spacing Top", type: "string" }),
|
|
250
|
+
defineField({ name: "spacing_right", title: "Spacing Right", type: "string" }),
|
|
251
|
+
defineField({ name: "spacing_bottom", title: "Spacing Bottom", type: "string" }),
|
|
252
|
+
defineField({ name: "spacing_left", title: "Spacing Left", type: "string" }),
|
|
253
|
+
// Animation
|
|
254
|
+
defineField({
|
|
255
|
+
name: "enter_animation",
|
|
256
|
+
title: "Enter Animation",
|
|
257
|
+
type: "enterAnimationConfig",
|
|
258
|
+
}),
|
|
259
|
+
defineField({
|
|
260
|
+
name: "stagger",
|
|
261
|
+
title: "Stagger",
|
|
262
|
+
type: "object",
|
|
263
|
+
fields: [
|
|
264
|
+
defineField({ name: "enabled", title: "Enabled", type: "boolean" }),
|
|
265
|
+
defineField({ name: "delayPerChild", title: "Delay Per Child", type: "number" }),
|
|
266
|
+
defineField({
|
|
267
|
+
name: "direction",
|
|
268
|
+
title: "Direction",
|
|
269
|
+
type: "string",
|
|
270
|
+
options: { list: ["left-to-right", "right-to-left"] },
|
|
271
|
+
}),
|
|
272
|
+
],
|
|
273
|
+
}),
|
|
274
|
+
],
|
|
275
|
+
}),
|
|
276
|
+
|
|
277
|
+
// ── Responsive Overrides ──
|
|
278
|
+
defineField({
|
|
279
|
+
name: "responsive",
|
|
280
|
+
title: "Responsive Overrides",
|
|
281
|
+
type: "object",
|
|
282
|
+
hidden: true,
|
|
283
|
+
fields: [
|
|
284
|
+
defineField({
|
|
285
|
+
name: "tablet",
|
|
286
|
+
title: "Tablet",
|
|
287
|
+
type: "object",
|
|
288
|
+
fields: [
|
|
289
|
+
...responsiveColumnOverrideFields,
|
|
290
|
+
...responsiveRowOverrideFields,
|
|
291
|
+
...responsiveSettingsFields,
|
|
292
|
+
],
|
|
293
|
+
}),
|
|
294
|
+
defineField({
|
|
295
|
+
name: "phone",
|
|
296
|
+
title: "Phone",
|
|
297
|
+
type: "object",
|
|
298
|
+
fields: [
|
|
299
|
+
...responsiveColumnOverrideFields,
|
|
300
|
+
...responsiveRowOverrideFields,
|
|
301
|
+
...responsiveSettingsFields,
|
|
302
|
+
],
|
|
303
|
+
}),
|
|
304
|
+
],
|
|
305
|
+
}),
|
|
306
|
+
],
|
|
307
|
+
preview: {
|
|
308
|
+
select: { height: "height", rows: "cover_rows" },
|
|
309
|
+
prepare({ height, rows }) {
|
|
310
|
+
const rowCount = Array.isArray(rows) ? rows.length : 0;
|
|
311
|
+
return {
|
|
312
|
+
title: "Cover Section",
|
|
313
|
+
subtitle: `${height || "100vh"} · ${rowCount} row${rowCount !== 1 ? "s" : ""}`,
|
|
314
|
+
};
|
|
315
|
+
},
|
|
316
|
+
},
|
|
317
|
+
});
|
package/sanity/schemas/page.ts
CHANGED
|
@@ -42,7 +42,7 @@ export default defineType({
|
|
|
42
42
|
name: "content_rows",
|
|
43
43
|
title: "Content Rows",
|
|
44
44
|
type: "array",
|
|
45
|
-
of: [{ type: "pageSectionV2" }, { type: "parallaxGroup" }, { type: "customSectionInstance" }],
|
|
45
|
+
of: [{ type: "pageSectionV2" }, { type: "parallaxGroup" }, { type: "customSectionInstance" }, { type: "coverSection" }],
|
|
46
46
|
}),
|
|
47
47
|
defineField({
|
|
48
48
|
name: "metadata",
|