@morphika/andami 0.2.16 → 0.2.19
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/components/blocks/BlockRenderer.tsx +5 -1
- package/components/blocks/CoverSectionRenderer.tsx +13 -1
- package/components/builder/CoverSectionCanvas.tsx +10 -0
- package/components/builder/ReadOnlyFrame.tsx +9 -0
- package/components/builder/live-preview/LiveImagePreview.tsx +1 -1
- package/components/builder/live-preview/LiveVideoPreview.tsx +1 -1
- package/components/builder/settings-panel/CoverSectionSettings.tsx +51 -39
- package/lib/builder/store-cover.ts +1 -1
- package/lib/builder/types.ts +1 -1
- package/lib/sanity/types.ts +2 -1
- package/lib/version.ts +1 -1
- package/package.json +1 -1
- package/sanity/schemas/objects/coverSection.ts +7 -0
|
@@ -395,7 +395,11 @@ export default function BlockRenderer({
|
|
|
395
395
|
// ── Enter animation: apply non-typewriter presets after layout wrapper ──
|
|
396
396
|
// Typewriter was already applied BEFORE the layout wrapper (above) so that
|
|
397
397
|
// block padding wraps both phases. All other presets wrap after layout.
|
|
398
|
-
|
|
398
|
+
// Fill blocks (position: absolute backgrounds) skip enter animations —
|
|
399
|
+
// their zero-height wrapper prevents IntersectionObserver from triggering.
|
|
400
|
+
const isFillBlock = (resolved._type === "imageBlock" || resolved._type === "videoBlock") &&
|
|
401
|
+
(resolved as unknown as { width?: string }).width === "fill";
|
|
402
|
+
if (resolvedEnter && resolvedEnter.preset !== "none" && !isTypewriter && !isFillBlock) {
|
|
399
403
|
content = (
|
|
400
404
|
<EnterAnimationWrapper config={resolvedEnter}>
|
|
401
405
|
{content}
|
|
@@ -106,6 +106,7 @@ export default function CoverSectionRenderer({ section, pageEnterAnimation }: Co
|
|
|
106
106
|
const bgVideoSrc = section.background_type === "video" && section.background_video
|
|
107
107
|
? assetUrl(section.background_video)
|
|
108
108
|
: null;
|
|
109
|
+
const bgColor = section.background_type === "color" ? section.background_color : null;
|
|
109
110
|
const overlayOpacity = section.background_overlay_opacity ?? 0;
|
|
110
111
|
|
|
111
112
|
const responsiveCss = buildCoverResponsiveCss(section);
|
|
@@ -178,6 +179,17 @@ export default function CoverSectionRenderer({ section, pageEnterAnimation }: Co
|
|
|
178
179
|
</video>
|
|
179
180
|
)}
|
|
180
181
|
|
|
182
|
+
{/* Background color */}
|
|
183
|
+
{bgColor && (
|
|
184
|
+
<div
|
|
185
|
+
style={{
|
|
186
|
+
position: "absolute",
|
|
187
|
+
inset: 0,
|
|
188
|
+
backgroundColor: bgColor,
|
|
189
|
+
}}
|
|
190
|
+
/>
|
|
191
|
+
)}
|
|
192
|
+
|
|
181
193
|
{/* Overlay */}
|
|
182
194
|
{overlayOpacity > 0 && (
|
|
183
195
|
<div
|
|
@@ -260,7 +272,7 @@ export default function CoverSectionRenderer({ section, pageEnterAnimation }: Co
|
|
|
260
272
|
</div>
|
|
261
273
|
);
|
|
262
274
|
|
|
263
|
-
if (blockEnter && blockEnter.preset !== "none") {
|
|
275
|
+
if (blockEnter && blockEnter.preset !== "none" && !isFillBlock) {
|
|
264
276
|
return (
|
|
265
277
|
<EnterAnimationWrapper key={block._key} config={blockEnter}>
|
|
266
278
|
{rendered}
|
|
@@ -148,6 +148,16 @@ export default function CoverSectionCanvas({
|
|
|
148
148
|
}}
|
|
149
149
|
/>
|
|
150
150
|
)}
|
|
151
|
+
{section.background_type === "color" && section.background_color && (
|
|
152
|
+
<div
|
|
153
|
+
className="absolute inset-0 pointer-events-none"
|
|
154
|
+
style={{
|
|
155
|
+
backgroundColor: section.background_color,
|
|
156
|
+
opacity: 0.25,
|
|
157
|
+
overflow: "hidden",
|
|
158
|
+
}}
|
|
159
|
+
/>
|
|
160
|
+
)}
|
|
151
161
|
|
|
152
162
|
{/* Overlay preview */}
|
|
153
163
|
{(section.background_overlay_opacity ?? 0) > 0 && (
|
|
@@ -412,6 +412,15 @@ const ReadOnlyCoverSection = memo(function ReadOnlyCoverSection({
|
|
|
412
412
|
}}
|
|
413
413
|
/>
|
|
414
414
|
)}
|
|
415
|
+
{section.background_type === "color" && section.background_color && (
|
|
416
|
+
<div
|
|
417
|
+
className="absolute inset-0 pointer-events-none"
|
|
418
|
+
style={{
|
|
419
|
+
backgroundColor: section.background_color,
|
|
420
|
+
opacity: 0.25,
|
|
421
|
+
}}
|
|
422
|
+
/>
|
|
423
|
+
)}
|
|
415
424
|
{(section.background_overlay_opacity ?? 0) > 0 && (
|
|
416
425
|
<div
|
|
417
426
|
className="absolute inset-0 pointer-events-none"
|
|
@@ -39,7 +39,7 @@ export default function LiveImagePreview({ block }: { block: ImageBlock }) {
|
|
|
39
39
|
|
|
40
40
|
if (isFill) {
|
|
41
41
|
return (
|
|
42
|
-
<div style={{
|
|
42
|
+
<div style={{ position: "absolute", inset: 0, overflow: "hidden", borderRadius: block.border_radius ? `${String(block.border_radius).replace(/px$/i, "")}px` : undefined }}>
|
|
43
43
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
44
44
|
<img
|
|
45
45
|
src={src}
|
|
@@ -61,7 +61,7 @@ export default function LiveVideoPreview({ block }: { block: VideoBlock }) {
|
|
|
61
61
|
const borderRadius = block.border_radius ? `${String(block.border_radius).replace(/px$/i, "")}px` : undefined;
|
|
62
62
|
|
|
63
63
|
const outerStyle: React.CSSProperties = isFill
|
|
64
|
-
? {
|
|
64
|
+
? { position: "absolute", inset: 0, minWidth: 0, borderRadius, overflow: "hidden" }
|
|
65
65
|
: { width: widthStyle, margin: block.width === "contained" ? "0 auto" : undefined, minWidth: 0, borderRadius, overflow: borderRadius ? "hidden" : undefined };
|
|
66
66
|
|
|
67
67
|
return (
|
|
@@ -81,7 +81,7 @@ export default function CoverSectionSettings({ section }: CoverSectionSettingsPr
|
|
|
81
81
|
const overlayOpacity = section.background_overlay_opacity ?? 0;
|
|
82
82
|
|
|
83
83
|
const updateBg = (fields: Partial<Pick<CoverSection,
|
|
84
|
-
"background_type" | "background_image" | "background_video" |
|
|
84
|
+
"background_type" | "background_color" | "background_image" | "background_video" |
|
|
85
85
|
"background_position" | "background_size" |
|
|
86
86
|
"background_overlay_color" | "background_overlay_opacity"
|
|
87
87
|
>>) => {
|
|
@@ -94,7 +94,7 @@ export default function CoverSectionSettings({ section }: CoverSectionSettingsPr
|
|
|
94
94
|
<SettingsSection title="Background" defaultOpen icon={<BackgroundIcon />}>
|
|
95
95
|
<SettingsField label="Type">
|
|
96
96
|
<div className="flex rounded-lg bg-[#f0f0f0] p-[3px]">
|
|
97
|
-
{(["image", "video"] as const).map((type) => (
|
|
97
|
+
{(["image", "video", "color"] as const).map((type) => (
|
|
98
98
|
<button
|
|
99
99
|
key={type}
|
|
100
100
|
onClick={() => updateBg({ background_type: type })}
|
|
@@ -104,50 +104,62 @@ export default function CoverSectionSettings({ section }: CoverSectionSettingsPr
|
|
|
104
104
|
: "text-neutral-400 hover:text-neutral-500"
|
|
105
105
|
}`}
|
|
106
106
|
>
|
|
107
|
-
{type === "image" ? "Image" : "Video"}
|
|
107
|
+
{type === "image" ? "Image" : type === "video" ? "Video" : "Color"}
|
|
108
108
|
</button>
|
|
109
109
|
))}
|
|
110
110
|
</div>
|
|
111
111
|
</SettingsField>
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
113
|
+
{bgType === "color" ? (
|
|
114
|
+
<SettingsField label="Color">
|
|
115
|
+
<ColorSwatchPicker
|
|
116
|
+
value={section.background_color || "#000000"}
|
|
117
|
+
onChange={(val) => updateBg({ background_color: typeof val === "string" ? val : "" })}
|
|
118
|
+
swatches={paletteSwatches}
|
|
119
|
+
/>
|
|
120
|
+
</SettingsField>
|
|
121
|
+
) : (
|
|
122
|
+
<>
|
|
123
|
+
<SettingsField label={bgType === "image" ? "Image" : "Video"}>
|
|
124
|
+
<AssetPathInput
|
|
125
|
+
value={bgType === "image" ? (section.background_image || "") : (section.background_video || "")}
|
|
126
|
+
onChange={(path) => {
|
|
127
|
+
if (bgType === "image") {
|
|
128
|
+
updateBg({ background_image: path });
|
|
129
|
+
} else {
|
|
130
|
+
updateBg({ background_video: path });
|
|
131
|
+
}
|
|
132
|
+
}}
|
|
133
|
+
filterType={bgType === "image" ? "image" : "video"}
|
|
134
|
+
placeholder={bgType === "image" ? "path/to/image.jpg" : "path/to/video.mp4"}
|
|
135
|
+
/>
|
|
136
|
+
</SettingsField>
|
|
127
137
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
<SettingsField label="Position">
|
|
139
|
+
<select
|
|
140
|
+
value={bgPosition}
|
|
141
|
+
onChange={(e) => updateBg({ background_position: e.target.value })}
|
|
142
|
+
className={SELECT_CLASS}
|
|
143
|
+
>
|
|
144
|
+
{BG_POSITION_OPTIONS.map((opt) => (
|
|
145
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
146
|
+
))}
|
|
147
|
+
</select>
|
|
148
|
+
</SettingsField>
|
|
139
149
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
<SettingsField label="Size">
|
|
151
|
+
<select
|
|
152
|
+
value={bgSize}
|
|
153
|
+
onChange={(e) => updateBg({ background_size: e.target.value as CoverSection["background_size"] })}
|
|
154
|
+
className={SELECT_CLASS}
|
|
155
|
+
>
|
|
156
|
+
{BG_SIZE_OPTIONS.map((opt) => (
|
|
157
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
158
|
+
))}
|
|
159
|
+
</select>
|
|
160
|
+
</SettingsField>
|
|
161
|
+
</>
|
|
162
|
+
)}
|
|
151
163
|
</SettingsSection>
|
|
152
164
|
|
|
153
165
|
{/* Overlay */}
|
|
@@ -175,7 +175,7 @@ export function createCoverActions(set: StoreSet, get: StoreGet) {
|
|
|
175
175
|
updateCoverBackground: (
|
|
176
176
|
sectionKey: string,
|
|
177
177
|
fields: Partial<Pick<CoverSection,
|
|
178
|
-
"background_type" | "background_image" | "background_video" |
|
|
178
|
+
"background_type" | "background_color" | "background_image" | "background_video" |
|
|
179
179
|
"background_position" | "background_size" |
|
|
180
180
|
"background_overlay_color" | "background_overlay_opacity"
|
|
181
181
|
>>
|
package/lib/builder/types.ts
CHANGED
|
@@ -383,7 +383,7 @@ export interface BuilderActions {
|
|
|
383
383
|
removeCoverRow: (sectionKey: string, rowKey: string) => void;
|
|
384
384
|
resizeCoverRow: (sectionKey: string, handleIndex: number, deltaPercent: number, startAbove: number, startBelow: number) => void;
|
|
385
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;
|
|
386
|
+
updateCoverBackground: (sectionKey: string, fields: Partial<Pick<CoverSection, "background_type" | "background_color" | "background_image" | "background_video" | "background_position" | "background_size" | "background_overlay_color" | "background_overlay_opacity">>) => void;
|
|
387
387
|
updateCoverSettings: (sectionKey: string, settings: Partial<CoverSectionSettings>) => void;
|
|
388
388
|
updateCoverHeight: (sectionKey: string, height: CoverSection["height"]) => void;
|
|
389
389
|
|
package/lib/sanity/types.ts
CHANGED
|
@@ -491,7 +491,8 @@ export interface CoverSection {
|
|
|
491
491
|
_key: string;
|
|
492
492
|
|
|
493
493
|
// Background
|
|
494
|
-
background_type: "image" | "video";
|
|
494
|
+
background_type: "image" | "video" | "color";
|
|
495
|
+
background_color?: string;
|
|
495
496
|
background_image?: string;
|
|
496
497
|
background_video?: string;
|
|
497
498
|
background_position?: string;
|
package/lib/version.ts
CHANGED
package/package.json
CHANGED
|
@@ -115,10 +115,17 @@ export default defineType({
|
|
|
115
115
|
list: [
|
|
116
116
|
{ title: "Image", value: "image" },
|
|
117
117
|
{ title: "Video", value: "video" },
|
|
118
|
+
{ title: "Color", value: "color" },
|
|
118
119
|
],
|
|
119
120
|
},
|
|
120
121
|
initialValue: "image",
|
|
121
122
|
}),
|
|
123
|
+
defineField({
|
|
124
|
+
name: "background_color",
|
|
125
|
+
title: "Background Color",
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Hex color for solid background",
|
|
128
|
+
}),
|
|
122
129
|
defineField({
|
|
123
130
|
name: "background_image",
|
|
124
131
|
title: "Background Image",
|