@morphika/andami 0.2.12 → 0.2.13
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/PageRenderer.tsx +13 -9
- 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/index.ts +0 -1
- package/components/builder/index.ts +1 -0
- 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/types.ts +79 -43
- package/lib/version.ts +1 -1
- package/package.json +1 -1
- package/sanity/schemas/blocks/index.ts +1 -2
- 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
|
@@ -15,8 +15,9 @@ import type {
|
|
|
15
15
|
ParallaxSlideV2,
|
|
16
16
|
SectionColumn,
|
|
17
17
|
SectionV2Preset,
|
|
18
|
+
CoverSection,
|
|
18
19
|
} from "../../lib/sanity/types";
|
|
19
|
-
import { isPageSectionV2, isParallaxGroup } from "../../lib/sanity/types";
|
|
20
|
+
import { isPageSectionV2, isParallaxGroup, isCoverSection } from "../../lib/sanity/types";
|
|
20
21
|
import { columnsFromPreset, detectPreset } from "./cascade";
|
|
21
22
|
import { resizeColumnLeft as cascadeResizeLeft, moveColumn as cascadeMoveColumn, type ResizeLeftResult } from "./cascade";
|
|
22
23
|
import { applyBlocksToColumns, toCascadeColumns, type CascadeColumn } from "./cascade-helpers";
|
|
@@ -74,6 +75,14 @@ export function moveBlockInState(
|
|
|
74
75
|
);
|
|
75
76
|
break;
|
|
76
77
|
}
|
|
78
|
+
} else if (isCoverSection(item)) {
|
|
79
|
+
const updatedCols = removeFromColumns((item as CoverSection).columns);
|
|
80
|
+
if (movedBlock) {
|
|
81
|
+
rowsAfterRemove = rows.map((r, idx) =>
|
|
82
|
+
idx === i ? { ...r, columns: updatedCols } as ContentItem : r
|
|
83
|
+
);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
77
86
|
} else if (isParallaxGroup(item)) {
|
|
78
87
|
const group = item as ParallaxGroup;
|
|
79
88
|
let foundInSlide = false;
|
|
@@ -217,8 +226,8 @@ export function addSectionV2InState(
|
|
|
217
226
|
grid_columns: gridColumns,
|
|
218
227
|
col_gap: 20,
|
|
219
228
|
row_gap: 20,
|
|
220
|
-
spacing_top: "
|
|
221
|
-
spacing_bottom: "
|
|
229
|
+
spacing_top: "0",
|
|
230
|
+
spacing_bottom: "0",
|
|
222
231
|
},
|
|
223
232
|
};
|
|
224
233
|
|
|
@@ -369,7 +378,8 @@ export function moveColumnToGapV2InState(
|
|
|
369
378
|
|
|
370
379
|
export type SectionPath =
|
|
371
380
|
| { type: "v2"; index: number }
|
|
372
|
-
| { type: "parallaxSlide"; groupIndex: number; slideIndex: number }
|
|
381
|
+
| { type: "parallaxSlide"; groupIndex: number; slideIndex: number }
|
|
382
|
+
| { type: "cover"; index: number };
|
|
373
383
|
|
|
374
384
|
/**
|
|
375
385
|
* Find a V2 section by key — searches both top-level PageSectionV2
|
|
@@ -388,6 +398,9 @@ export function findSectionPath(
|
|
|
388
398
|
if (item._key === sectionKey && isPageSectionV2(item)) {
|
|
389
399
|
return { type: "v2", index: i };
|
|
390
400
|
}
|
|
401
|
+
if (item._key === sectionKey && isCoverSection(item)) {
|
|
402
|
+
return { type: "cover", index: i };
|
|
403
|
+
}
|
|
391
404
|
if (isParallaxGroup(item)) {
|
|
392
405
|
const group = item as ParallaxGroup;
|
|
393
406
|
for (let j = 0; j < group.slides.length; j++) {
|
|
@@ -412,6 +425,29 @@ export function getSectionFromPath(
|
|
|
412
425
|
const item = rows[path.index];
|
|
413
426
|
return isPageSectionV2(item) ? (item as PageSectionV2) : null;
|
|
414
427
|
}
|
|
428
|
+
if (path.type === "cover") {
|
|
429
|
+
const item = rows[path.index];
|
|
430
|
+
if (!isCoverSection(item)) return null;
|
|
431
|
+
const cover = item as CoverSection;
|
|
432
|
+
return {
|
|
433
|
+
_type: "pageSectionV2",
|
|
434
|
+
_key: cover._key,
|
|
435
|
+
section_type: "empty-v2",
|
|
436
|
+
columns: cover.columns,
|
|
437
|
+
settings: {
|
|
438
|
+
preset: "custom",
|
|
439
|
+
grid_columns: cover.settings.grid_columns,
|
|
440
|
+
col_gap: cover.settings.col_gap,
|
|
441
|
+
row_gap: cover.settings.row_gap,
|
|
442
|
+
spacing_top: cover.settings.spacing_top,
|
|
443
|
+
spacing_right: cover.settings.spacing_right,
|
|
444
|
+
spacing_bottom: cover.settings.spacing_bottom,
|
|
445
|
+
spacing_left: cover.settings.spacing_left,
|
|
446
|
+
enter_animation: cover.settings.enter_animation,
|
|
447
|
+
stagger: cover.settings.stagger,
|
|
448
|
+
},
|
|
449
|
+
};
|
|
450
|
+
}
|
|
415
451
|
const group = rows[path.groupIndex];
|
|
416
452
|
if (!isParallaxGroup(group)) return null;
|
|
417
453
|
const slide = (group as ParallaxGroup).slides[path.slideIndex];
|
|
@@ -440,6 +476,47 @@ export function updateSectionAtPath(
|
|
|
440
476
|
return updater(item as PageSectionV2) as ContentItem;
|
|
441
477
|
});
|
|
442
478
|
}
|
|
479
|
+
if (path.type === "cover") {
|
|
480
|
+
return rows.map((item, i) => {
|
|
481
|
+
if (i !== path.index || !isCoverSection(item)) return item;
|
|
482
|
+
const cover = item as CoverSection;
|
|
483
|
+
const virtualSection: PageSectionV2 = {
|
|
484
|
+
_type: "pageSectionV2",
|
|
485
|
+
_key: cover._key,
|
|
486
|
+
section_type: "empty-v2",
|
|
487
|
+
columns: cover.columns,
|
|
488
|
+
settings: {
|
|
489
|
+
preset: "custom",
|
|
490
|
+
grid_columns: cover.settings.grid_columns,
|
|
491
|
+
col_gap: cover.settings.col_gap,
|
|
492
|
+
row_gap: cover.settings.row_gap,
|
|
493
|
+
spacing_top: cover.settings.spacing_top,
|
|
494
|
+
spacing_right: cover.settings.spacing_right,
|
|
495
|
+
spacing_bottom: cover.settings.spacing_bottom,
|
|
496
|
+
spacing_left: cover.settings.spacing_left,
|
|
497
|
+
enter_animation: cover.settings.enter_animation,
|
|
498
|
+
stagger: cover.settings.stagger,
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
const updated = updater(virtualSection);
|
|
502
|
+
return {
|
|
503
|
+
...cover,
|
|
504
|
+
columns: updated.columns,
|
|
505
|
+
settings: {
|
|
506
|
+
...cover.settings,
|
|
507
|
+
grid_columns: updated.settings.grid_columns,
|
|
508
|
+
col_gap: updated.settings.col_gap,
|
|
509
|
+
row_gap: updated.settings.row_gap,
|
|
510
|
+
spacing_top: updated.settings.spacing_top,
|
|
511
|
+
spacing_right: updated.settings.spacing_right,
|
|
512
|
+
spacing_bottom: updated.settings.spacing_bottom,
|
|
513
|
+
spacing_left: updated.settings.spacing_left,
|
|
514
|
+
enter_animation: updated.settings.enter_animation,
|
|
515
|
+
stagger: updated.settings.stagger,
|
|
516
|
+
},
|
|
517
|
+
} as ContentItem;
|
|
518
|
+
});
|
|
519
|
+
}
|
|
443
520
|
// parallaxSlide path
|
|
444
521
|
return rows.map((item, i) => {
|
|
445
522
|
if (i !== path.groupIndex || !isParallaxGroup(item)) return item;
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
CustomSectionInstance,
|
|
8
8
|
ParallaxGroup,
|
|
9
9
|
ParallaxSlideV2,
|
|
10
|
+
CoverSection,
|
|
10
11
|
SectionV2Preset,
|
|
11
12
|
EnterAnimationConfig,
|
|
12
13
|
} from "../../lib/sanity/types";
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
isPageSectionV2,
|
|
15
16
|
isCustomSectionInstance,
|
|
16
17
|
isParallaxGroup,
|
|
18
|
+
isCoverSection,
|
|
17
19
|
} from "../../lib/sanity/types";
|
|
18
20
|
import { createDefaultBlock, createDefaultParallaxSlide } from "./defaults";
|
|
19
21
|
import { generateKey } from "./utils";
|
|
@@ -78,8 +80,8 @@ export function createSectionActions(set: StoreSet, get: StoreGet) {
|
|
|
78
80
|
grid_columns: gridColumns,
|
|
79
81
|
col_gap: 20,
|
|
80
82
|
row_gap: 20,
|
|
81
|
-
spacing_top: "
|
|
82
|
-
spacing_bottom: "
|
|
83
|
+
spacing_top: "0",
|
|
84
|
+
spacing_bottom: "0",
|
|
83
85
|
},
|
|
84
86
|
};
|
|
85
87
|
|
|
@@ -141,6 +143,14 @@ export function createSectionActions(set: StoreSet, get: StoreGet) {
|
|
|
141
143
|
blocks: col.blocks.map((b) => ({ ...b, _key: generateKey() })),
|
|
142
144
|
})
|
|
143
145
|
);
|
|
146
|
+
} else if (isCoverSection(clone)) {
|
|
147
|
+
const cover = clone as CoverSection;
|
|
148
|
+
cover.cover_rows = cover.cover_rows.map((r) => ({ ...r, _key: generateKey() }));
|
|
149
|
+
cover.columns = cover.columns.map((col) => ({
|
|
150
|
+
...col,
|
|
151
|
+
_key: generateKey(),
|
|
152
|
+
blocks: col.blocks.map((b) => ({ ...b, _key: generateKey() })),
|
|
153
|
+
}));
|
|
144
154
|
} else if (isParallaxGroup(clone)) {
|
|
145
155
|
const group = clone as ParallaxGroup;
|
|
146
156
|
group.slides = group.slides.map((slide) => ({
|
package/lib/builder/store.ts
CHANGED
|
@@ -3,8 +3,8 @@ import type { BuilderStore, BuilderState, BlockType, PageSettings, CanvasTool, D
|
|
|
3
3
|
import { DEFAULT_PAGE_SETTINGS, DEFAULT_GRID_SETTINGS, DEVICE_WIDTHS } from "./types";
|
|
4
4
|
import { stateToDocument, documentToState } from "./serializer";
|
|
5
5
|
import { generateKey } from "./utils";
|
|
6
|
-
import type { ContentBlock, ContentItem, PageSectionV2, SectionV2Settings, PageMetadata, CustomSectionInstance, ParallaxGroup, ParallaxSlideV2 } from "../../lib/sanity/types";
|
|
7
|
-
import { isPageSectionV2, isCustomSectionInstance, isParallaxGroup } from "../../lib/sanity/types";
|
|
6
|
+
import type { ContentBlock, ContentItem, PageSectionV2, SectionV2Settings, PageMetadata, CustomSectionInstance, ParallaxGroup, ParallaxSlideV2, CoverSection } from "../../lib/sanity/types";
|
|
7
|
+
import { isPageSectionV2, isCustomSectionInstance, isParallaxGroup, isCoverSection } from "../../lib/sanity/types";
|
|
8
8
|
import { createDefaultBlock, createDefaultParallaxSlide } from "./defaults";
|
|
9
9
|
import { MAX_HISTORY, pushSnapshot } from "./history";
|
|
10
10
|
import {
|
|
@@ -33,6 +33,7 @@ import { revalidateSite } from "../../lib/revalidate";
|
|
|
33
33
|
import { createSectionActions } from "./store-sections";
|
|
34
34
|
import { createBlockActions } from "./store-blocks";
|
|
35
35
|
import { createCanvasActions } from "./store-canvas";
|
|
36
|
+
import { createCoverActions } from "./store-cover";
|
|
36
37
|
|
|
37
38
|
// ============================================
|
|
38
39
|
// RC-003 fix: Block parent key cache — O(1) lookup in selectBlock().
|
|
@@ -55,6 +56,13 @@ function getBlockParentCache(rows: ContentItem[]): Map<string, BlockParent> {
|
|
|
55
56
|
cache.set(b._key, { rowKey: item._key, colKey: col._key });
|
|
56
57
|
}
|
|
57
58
|
}
|
|
59
|
+
} else if (isCoverSection(item)) {
|
|
60
|
+
const cover = item as CoverSection;
|
|
61
|
+
for (const col of cover.columns) {
|
|
62
|
+
for (const b of col.blocks) {
|
|
63
|
+
cache.set(b._key, { rowKey: item._key, colKey: col._key });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
58
66
|
} else if (isParallaxGroup(item)) {
|
|
59
67
|
const group = item as ParallaxGroup;
|
|
60
68
|
for (const slide of group.slides) {
|
|
@@ -346,4 +354,5 @@ export const useBuilderStore = create<BuilderStore>((set, get) => ({
|
|
|
346
354
|
...createSectionActions(set, get),
|
|
347
355
|
...createBlockActions(set, get),
|
|
348
356
|
...createCanvasActions(set, get),
|
|
357
|
+
...createCoverActions(set, get),
|
|
349
358
|
}));
|
package/lib/builder/types.ts
CHANGED
|
@@ -15,6 +15,9 @@ import type {
|
|
|
15
15
|
SectionColumn,
|
|
16
16
|
PageMetadata,
|
|
17
17
|
ColorField,
|
|
18
|
+
CoverSection,
|
|
19
|
+
CoverSectionSettings,
|
|
20
|
+
CoverRow,
|
|
18
21
|
} from "../../lib/sanity/types";
|
|
19
22
|
import { DEFAULT_BG_COLOR, DEFAULT_TEXT_COLOR, DEFAULT_GRID_WIDTH } from "./constants";
|
|
20
23
|
|
|
@@ -48,7 +51,6 @@ export const BLOCK_TYPE_REGISTRY: BlockTypeInfo[] = [
|
|
|
48
51
|
{ type: "videoBlock", label: "Video", description: "Vimeo, YouTube, or MP4", group: "generic", icon: "▶", category: "content" },
|
|
49
52
|
{ type: "spacerBlock", label: "Spacer", description: "Vertical spacing", group: "generic", icon: "↕", category: "content" },
|
|
50
53
|
{ type: "buttonBlock", label: "Button", description: "Call-to-action button", group: "generic", icon: "▣", category: "content" },
|
|
51
|
-
{ type: "coverBlock", label: "Cover", description: "Full-screen hero section", group: "generic", icon: "◈", category: "content" },
|
|
52
54
|
];
|
|
53
55
|
|
|
54
56
|
/**
|
|
@@ -90,7 +92,7 @@ export function isSectionBlockSection(section: { columns?: Array<{ blocks?: Arra
|
|
|
90
92
|
return blocks.length === 1 && SECTION_BLOCK_TYPES.has(blocks[0]._type);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
export type SectionType = "empty-v2" | "parallaxGroup" | SectionBlockType;
|
|
95
|
+
export type SectionType = "empty-v2" | "parallaxGroup" | "coverSection" | SectionBlockType;
|
|
94
96
|
|
|
95
97
|
export interface SectionTypeInfo {
|
|
96
98
|
type: SectionType;
|
|
@@ -103,6 +105,7 @@ export interface SectionTypeInfo {
|
|
|
103
105
|
|
|
104
106
|
export const SECTION_TYPE_REGISTRY: SectionTypeInfo[] = [
|
|
105
107
|
{ type: "empty-v2", label: "Empty Section", description: "Grid section with flexible columns", icon: "⊞" },
|
|
108
|
+
{ type: "coverSection", label: "Cover Section", description: "Full-viewport section with background and proportional rows", icon: "◆" },
|
|
106
109
|
{ type: "projectGridBlock", label: "Project Grid", description: "Staggered project showcase grid", icon: "⬡", blockType: "projectGridBlock" },
|
|
107
110
|
{ type: "parallaxGroup", label: "Parallax Section", description: "Full-screen parallax showcase with V2 slides", icon: "▽" },
|
|
108
111
|
];
|
|
@@ -374,6 +377,16 @@ export interface BuilderActions {
|
|
|
374
377
|
/** Update group-level settings (transition effect, snap, etc.) */
|
|
375
378
|
updateParallaxGroupSettings: (groupKey: string, fields: Partial<Pick<import("../../lib/sanity/types").ParallaxGroup, "transition_effect" | "snap_enabled" | "parallax_intensity">>) => void;
|
|
376
379
|
|
|
380
|
+
// ---- Cover Section operations (Session 176) ----
|
|
381
|
+
addCoverSection: (afterRowKey?: string | null) => void;
|
|
382
|
+
addCoverRow: (sectionKey: string) => void;
|
|
383
|
+
removeCoverRow: (sectionKey: string, rowKey: string) => void;
|
|
384
|
+
resizeCoverRow: (sectionKey: string, handleIndex: number, deltaPercent: number, startAbove: number, startBelow: number) => void;
|
|
385
|
+
updateCoverRowAlign: (sectionKey: string, rowKey: string, align: CoverRow["vertical_align"]) => void;
|
|
386
|
+
updateCoverBackground: (sectionKey: string, fields: Partial<Pick<CoverSection, "background_type" | "background_image" | "background_video" | "background_position" | "background_size" | "background_overlay_color" | "background_overlay_opacity">>) => void;
|
|
387
|
+
updateCoverSettings: (sectionKey: string, settings: Partial<CoverSectionSettings>) => void;
|
|
388
|
+
updateCoverHeight: (sectionKey: string, height: CoverSection["height"]) => void;
|
|
389
|
+
|
|
377
390
|
// Page-level settings
|
|
378
391
|
updatePageSettings: (settings: Partial<PageSettings>) => void;
|
|
379
392
|
applyGlobalStyles: () => Promise<void>;
|
package/lib/sanity/types.ts
CHANGED
|
@@ -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
|
|