@morphika/andami 0.5.0 → 0.5.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.
- package/README.md +151 -36
- package/app/admin/assets/page.tsx +6 -6
- package/app/admin/database/page.tsx +302 -302
- package/app/admin/error.tsx +53 -53
- package/app/admin/layout.tsx +320 -327
- package/app/admin/navigation/page.tsx +255 -255
- package/app/admin/pages/[slug]/page.tsx +6 -6
- package/app/admin/pages/page.tsx +11 -11
- package/app/admin/projects/page.tsx +14 -14
- package/app/admin/setup/page.tsx +1 -1
- package/app/admin/styles/page.tsx +1 -1
- package/components/admin/MetadataEditor.tsx +6 -6
- package/components/admin/nav-builder/NavBuilder.tsx +1 -1
- package/components/admin/nav-builder/NavBuilderGrid.tsx +3 -3
- package/components/admin/nav-builder/NavGridCell.tsx +48 -48
- package/components/admin/nav-builder/NavGridItem.tsx +4 -4
- package/components/admin/nav-builder/NavItemSettings.tsx +331 -331
- package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -102
- package/components/admin/nav-builder/NavLivePreview.tsx +1 -1
- package/components/admin/nav-builder/NavMobileLivePreview.tsx +226 -226
- package/components/admin/nav-builder/NavMobileSettings.tsx +242 -242
- package/components/admin/nav-builder/NavSettingsFields.tsx +514 -514
- package/components/admin/setup-wizard/BrandingStep.tsx +3 -3
- package/components/admin/setup-wizard/DatabaseStep.tsx +2 -2
- package/components/admin/setup-wizard/DoneStep.tsx +1 -1
- package/components/admin/setup-wizard/SetupWizard.tsx +4 -4
- package/components/admin/setup-wizard/StorageStep.tsx +2 -2
- package/components/admin/setup-wizard/WelcomeStep.tsx +2 -2
- package/components/admin/styles/ColorsEditor.tsx +2 -2
- package/components/admin/styles/FontsEditor.tsx +6 -6
- package/components/admin/styles/GridLayoutEditor.tsx +9 -9
- package/components/admin/styles/LinksButtonsEditor.tsx +5 -5
- package/components/admin/styles/TypographyEditor.tsx +6 -6
- package/components/admin/styles/shared.tsx +68 -68
- package/components/blocks/AudioBlockRenderer.tsx +286 -0
- package/components/blocks/BeforeAfterBlockRenderer.tsx +274 -0
- package/components/blocks/MarqueeBlockRenderer.tsx +316 -0
- package/components/blocks/ProjectCarouselBlockRenderer.tsx +1 -1
- package/components/builder/BlockCardIcons.tsx +316 -227
- package/components/builder/BlockTypePicker.tsx +3 -1
- package/components/builder/BubbleIcons.tsx +90 -0
- package/components/builder/BuilderCanvas.tsx +2 -0
- package/components/builder/CanvasMinimap.tsx +2 -2
- package/components/builder/CoverSectionCanvas.tsx +363 -275
- package/components/builder/DeviceFrame.tsx +1 -1
- package/components/builder/DndWrapper.tsx +3 -3
- package/components/builder/InsertionLines.tsx +1 -1
- package/components/builder/SectionCardIcons.tsx +421 -320
- package/components/builder/SectionEditorBar.tsx +1 -1
- package/components/builder/SectionTypePicker.tsx +4 -4
- package/components/builder/SectionV2Canvas.tsx +20 -4
- package/components/builder/SectionV2Column.tsx +74 -68
- package/components/builder/SortableBlock.tsx +93 -73
- package/components/builder/SortableRow.tsx +27 -26
- package/components/builder/VirtualAssetGrid.tsx +2 -2
- package/components/builder/asset-browser/R2BrowserContent.tsx +34 -17
- package/components/builder/asset-browser/helpers.ts +4 -0
- package/components/builder/asset-browser/types.ts +2 -1
- package/components/builder/blockStyles.tsx +192 -173
- package/components/builder/color-picker/AlphaSlider.tsx +141 -141
- package/components/builder/color-picker/ColorInputs.tsx +105 -105
- package/components/builder/color-picker/EyedropperButton.tsx +74 -74
- package/components/builder/color-picker/HueSlider.tsx +124 -124
- package/components/builder/color-picker/SaturationCanvas.tsx +142 -142
- package/components/builder/color-picker/SwatchBar.tsx +93 -93
- package/components/builder/editors/AudioBlockEditor.tsx +242 -0
- package/components/builder/editors/BeforeAfterBlockEditor.tsx +360 -0
- package/components/builder/editors/ButtonBlockEditor.tsx +4 -4
- package/components/builder/editors/EnterAnimationPicker.tsx +2 -2
- package/components/builder/editors/HoverEffectPicker.tsx +2 -2
- package/components/builder/editors/ImageBlockEditor.tsx +2 -2
- package/components/builder/editors/ImageGridBlockEditor.tsx +4 -4
- package/components/builder/editors/MarqueeBlockEditor.tsx +621 -0
- package/components/builder/editors/ProjectCarouselBlockEditor.tsx +443 -443
- package/components/builder/editors/ProjectGridEditor.tsx +9 -9
- package/components/builder/editors/SpacerBlockEditor.tsx +5 -5
- package/components/builder/editors/StaggerSettings.tsx +109 -109
- package/components/builder/editors/TextBlockEditor.tsx +3 -3
- package/components/builder/editors/TextStylePicker.tsx +1 -1
- package/components/builder/editors/VideoBlockEditor.tsx +2 -2
- package/components/builder/editors/index.ts +11 -10
- package/components/builder/editors/shared.tsx +7 -7
- package/components/builder/live-preview/LiveAudioPreview.tsx +120 -0
- package/components/builder/live-preview/LiveBeforeAfterPreview.tsx +176 -0
- package/components/builder/live-preview/LiveImageGridPreview.tsx +10 -2
- package/components/builder/live-preview/LiveImagePreview.tsx +1 -1
- package/components/builder/live-preview/LiveMarqueePreview.tsx +39 -0
- package/components/builder/live-preview/LiveProjectCarouselPreview.tsx +1 -1
- package/components/builder/live-preview/LiveVideoPreview.tsx +1 -1
- package/components/builder/live-preview/ProjectCardWrapper.tsx +291 -291
- package/components/builder/settings-panel/AnimationTab.tsx +138 -138
- package/components/builder/settings-panel/BlockLayoutTab.tsx +7 -7
- package/components/builder/settings-panel/CardEntranceSection.tsx +114 -114
- package/components/builder/settings-panel/ColumnV2Settings.tsx +5 -5
- package/components/builder/settings-panel/CoverSectionLayoutTab.tsx +71 -71
- package/components/builder/settings-panel/CoverSectionSettings.tsx +335 -335
- package/components/builder/settings-panel/PageSettings.tsx +3 -3
- package/components/builder/settings-panel/ParallaxSlideSettings.tsx +2 -2
- package/components/builder/settings-panel/SectionV2AnimationTab.tsx +4 -4
- package/components/builder/settings-panel/SectionV2LayoutTab.tsx +356 -356
- package/components/builder/settings-panel/SectionV2Settings.tsx +14 -14
- package/components/builder/settings-panel/TRBLInputs.tsx +1 -1
- package/lib/animation/enter-types.ts +3 -0
- package/lib/animation/hover-effect-presets.ts +210 -210
- package/lib/animation/hover-effect-types.ts +3 -0
- package/lib/builder/block-registrations.ts +468 -335
- package/lib/builder/constants.ts +111 -111
- package/lib/builder/store-sections.ts +2 -2
- package/lib/builder/types-slices.ts +414 -414
- package/lib/builder/types.ts +6 -1
- package/lib/config/index.ts +27 -27
- package/lib/sanity/types.ts +156 -1
- package/lib/version.ts +1 -1
- package/package.json +1 -1
- package/sanity/schemas/blocks/audioBlock.ts +69 -0
- package/sanity/schemas/blocks/beforeAfterBlock.ts +121 -0
- package/sanity/schemas/blocks/index.ts +12 -9
- package/sanity/schemas/blocks/marqueeBlock.ts +292 -0
- package/sanity/schemas/index.ts +120 -111
- package/styles/admin.css +85 -85
- package/styles/animations.css +237 -237
- package/styles/base.css +114 -114
package/lib/builder/types.ts
CHANGED
|
@@ -51,6 +51,8 @@ export const BLOCK_TYPE_REGISTRY: BlockTypeInfo[] = [
|
|
|
51
51
|
{ type: "videoBlock", label: "Video", description: "Vimeo, YouTube, or MP4", group: "generic", icon: "▶", category: "content" },
|
|
52
52
|
{ type: "spacerBlock", label: "Spacer", description: "Vertical spacing", group: "generic", icon: "↕", category: "content" },
|
|
53
53
|
{ type: "buttonBlock", label: "Button", description: "Call-to-action button", group: "generic", icon: "▣", category: "content" },
|
|
54
|
+
{ type: "beforeAfterBlock", label: "Before / After", description: "Drag-slider comparison between two images or videos", group: "generic", icon: "◫", category: "content" },
|
|
55
|
+
{ type: "audioBlock", label: "Audio", description: "Minimal audio player with cover art and metadata", group: "generic", icon: "♪", category: "content" },
|
|
54
56
|
];
|
|
55
57
|
|
|
56
58
|
/**
|
|
@@ -63,6 +65,7 @@ export const ALL_BLOCK_INFO: BlockTypeInfo[] = [
|
|
|
63
65
|
// Section blocks — not in the content picker but still need label/icon lookup
|
|
64
66
|
{ type: "projectGridBlock", label: "Project Grid", description: "Staggered project showcase grid", group: "generic", icon: "⬡", category: "section" },
|
|
65
67
|
{ type: "projectCarouselBlock", label: "Project Carousel", description: "Horizontal carousel of projects — great for end-of-page 'keep browsing'", group: "generic", icon: "▸", category: "section" },
|
|
68
|
+
{ type: "marqueeBlock", label: "Marquee", description: "Horizontal scrolling ticker of text and images", group: "generic", icon: "⇄", category: "section" },
|
|
66
69
|
];
|
|
67
70
|
|
|
68
71
|
// Parallax group info — used by BuilderCanvas/SortableRow for label/icon lookup (not a block)
|
|
@@ -73,12 +76,13 @@ export const PARALLAX_GROUP_INFO = { label: "Parallax Showcase", icon: "▽" };
|
|
|
73
76
|
// ============================================
|
|
74
77
|
|
|
75
78
|
/** Section block types that create a full-width row with a pre-populated block */
|
|
76
|
-
export type SectionBlockType = "projectGridBlock" | "projectCarouselBlock";
|
|
79
|
+
export type SectionBlockType = "projectGridBlock" | "projectCarouselBlock" | "marqueeBlock";
|
|
77
80
|
|
|
78
81
|
/** Set for fast lookup — used by SortableBlock, ColumnDropZone, SortableRow to suppress inner chrome */
|
|
79
82
|
const SECTION_BLOCK_TYPES: ReadonlySet<string> = new Set<string>([
|
|
80
83
|
"projectGridBlock",
|
|
81
84
|
"projectCarouselBlock",
|
|
85
|
+
"marqueeBlock",
|
|
82
86
|
]);
|
|
83
87
|
|
|
84
88
|
/** Check if a block type is a section-level block (should render without block/column chrome) */
|
|
@@ -112,6 +116,7 @@ export const SECTION_TYPE_REGISTRY: SectionTypeInfo[] = [
|
|
|
112
116
|
{ type: "coverSection", label: "Cover Section", description: "Full-viewport section with background and proportional rows", icon: "◆" },
|
|
113
117
|
{ type: "projectGridBlock", label: "Project Grid", description: "Staggered project showcase grid", icon: "⬡", blockType: "projectGridBlock" },
|
|
114
118
|
{ type: "projectCarouselBlock", label: "Project Carousel", description: "Horizontal 'keep browsing' carousel of projects", icon: "▸", blockType: "projectCarouselBlock" },
|
|
119
|
+
{ type: "marqueeBlock", label: "Marquee", description: "Horizontal scrolling ticker of text and images", icon: "⇄", blockType: "marqueeBlock" },
|
|
115
120
|
{ type: "parallaxGroup", label: "Parallax Section", description: "Full-screen parallax showcase with V2 slides", icon: "▽" },
|
|
116
121
|
];
|
|
117
122
|
|
package/lib/config/index.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Site configuration accessor.
|
|
3
|
-
*
|
|
4
|
-
* Uses globalThis + Symbol.for to guarantee a true singleton even when
|
|
5
|
-
* the bundler creates multiple module instances of this file.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { SiteConfig } from "./types";
|
|
9
|
-
|
|
10
|
-
const CONFIG_KEY = Symbol.for("@morphika/andami/siteConfig");
|
|
11
|
-
const g = globalThis as unknown as Record<symbol, SiteConfig | undefined>;
|
|
12
|
-
|
|
13
|
-
export function registerConfig(config: SiteConfig): void {
|
|
14
|
-
g[CONFIG_KEY] = config;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function getSiteConfig(): SiteConfig {
|
|
18
|
-
const cfg = g[CONFIG_KEY];
|
|
19
|
-
if (!cfg) {
|
|
20
|
-
throw new Error(
|
|
21
|
-
"SiteConfig not registered. Call registerConfig(config) in your root layout before using getSiteConfig().\n" +
|
|
22
|
-
"See: https://github.com/MorphikaStudio/Morphika_Andami#quick-start",
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
return cfg;
|
|
26
|
-
}
|
|
27
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Site configuration accessor.
|
|
3
|
+
*
|
|
4
|
+
* Uses globalThis + Symbol.for to guarantee a true singleton even when
|
|
5
|
+
* the bundler creates multiple module instances of this file.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SiteConfig } from "./types";
|
|
9
|
+
|
|
10
|
+
const CONFIG_KEY = Symbol.for("@morphika/andami/siteConfig");
|
|
11
|
+
const g = globalThis as unknown as Record<symbol, SiteConfig | undefined>;
|
|
12
|
+
|
|
13
|
+
export function registerConfig(config: SiteConfig): void {
|
|
14
|
+
g[CONFIG_KEY] = config;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getSiteConfig(): SiteConfig {
|
|
18
|
+
const cfg = g[CONFIG_KEY];
|
|
19
|
+
if (!cfg) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
"SiteConfig not registered. Call registerConfig(config) in your root layout before using getSiteConfig().\n" +
|
|
22
|
+
"See: https://github.com/MorphikaStudio/Morphika_Andami#quick-start",
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
return cfg;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
28
|
export type { SiteConfig } from "./types";
|
package/lib/sanity/types.ts
CHANGED
|
@@ -210,6 +210,62 @@ export interface ButtonBlock {
|
|
|
210
210
|
responsive?: ResponsiveOverrides<ButtonBlock>;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
export interface BeforeAfterBlock {
|
|
214
|
+
_type: "beforeAfterBlock";
|
|
215
|
+
_key: string;
|
|
216
|
+
// Before side
|
|
217
|
+
before_media_type?: "image" | "video";
|
|
218
|
+
before_asset_path: string;
|
|
219
|
+
before_alt?: string;
|
|
220
|
+
// After side
|
|
221
|
+
after_media_type?: "image" | "video";
|
|
222
|
+
after_asset_path: string;
|
|
223
|
+
after_alt?: string;
|
|
224
|
+
// Slider
|
|
225
|
+
orientation?: "horizontal" | "vertical";
|
|
226
|
+
initial_position?: number; // 0–100
|
|
227
|
+
handle_color?: string; // hex
|
|
228
|
+
// Layout
|
|
229
|
+
width?: "full" | "contained" | "small" | "fill";
|
|
230
|
+
aspect_ratio?: "auto" | "16:9" | "4:3" | "1:1" | "21:9";
|
|
231
|
+
// Video playback (applies when either side is video)
|
|
232
|
+
video_autoplay?: boolean;
|
|
233
|
+
video_loop?: boolean;
|
|
234
|
+
video_muted?: boolean;
|
|
235
|
+
// Appearance
|
|
236
|
+
border_radius?: string;
|
|
237
|
+
shadow?: boolean;
|
|
238
|
+
enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
|
|
239
|
+
hover_effect?: import("../../lib/animation/hover-effect-types").HoverEffectConfig;
|
|
240
|
+
layout?: BlockLayout;
|
|
241
|
+
responsive?: ResponsiveOverrides<BeforeAfterBlock>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export interface AudioBlock {
|
|
245
|
+
_type: "audioBlock";
|
|
246
|
+
_key: string;
|
|
247
|
+
// Source
|
|
248
|
+
asset_path: string;
|
|
249
|
+
alt?: string;
|
|
250
|
+
// Metadata
|
|
251
|
+
title?: string;
|
|
252
|
+
artist?: string;
|
|
253
|
+
cover_path?: string;
|
|
254
|
+
// Appearance
|
|
255
|
+
accent_color?: string;
|
|
256
|
+
width?: "full" | "contained" | "small" | "fill";
|
|
257
|
+
border_radius?: string;
|
|
258
|
+
shadow?: boolean;
|
|
259
|
+
// Playback
|
|
260
|
+
autoplay?: boolean;
|
|
261
|
+
loop?: boolean;
|
|
262
|
+
muted?: boolean;
|
|
263
|
+
enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
|
|
264
|
+
hover_effect?: import("../../lib/animation/hover-effect-types").HoverEffectConfig;
|
|
265
|
+
layout?: BlockLayout;
|
|
266
|
+
responsive?: ResponsiveOverrides<AudioBlock>;
|
|
267
|
+
}
|
|
268
|
+
|
|
213
269
|
// ============================================
|
|
214
270
|
// Project Grid Block v2 (template-only, Session 105)
|
|
215
271
|
// ============================================
|
|
@@ -312,6 +368,102 @@ export interface ProjectGridBlock {
|
|
|
312
368
|
responsive?: ResponsiveOverrides<ProjectGridBlock>;
|
|
313
369
|
}
|
|
314
370
|
|
|
371
|
+
// ============================================
|
|
372
|
+
// Marquee Block — horizontal infinite-scroll ticker (Session 184)
|
|
373
|
+
// ============================================
|
|
374
|
+
|
|
375
|
+
/** Plain-text item in a marquee. */
|
|
376
|
+
export interface MarqueeTextItem {
|
|
377
|
+
_key: string;
|
|
378
|
+
_type: "marqueeText";
|
|
379
|
+
text: string;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Image item in a marquee — asset_path is relative (resolved by the asset proxy). */
|
|
383
|
+
export interface MarqueeImageItem {
|
|
384
|
+
_key: string;
|
|
385
|
+
_type: "marqueeImage";
|
|
386
|
+
asset_path: string;
|
|
387
|
+
alt?: string;
|
|
388
|
+
/** Per-item override, 0–200 px. */
|
|
389
|
+
border_radius?: number;
|
|
390
|
+
/** Fixed width in px. When absent, derived from row_height × natural aspect ratio. */
|
|
391
|
+
width?: number;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** Separator item — inherits block-level typography (size/color/weight/style). */
|
|
395
|
+
export interface MarqueeSeparatorItem {
|
|
396
|
+
_key: string;
|
|
397
|
+
_type: "marqueeSeparator";
|
|
398
|
+
/** 1–4 chars — glyph or emoji. Common: • · — / ▸ ★ */
|
|
399
|
+
character: string;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export type MarqueeItem =
|
|
403
|
+
| MarqueeTextItem
|
|
404
|
+
| MarqueeImageItem
|
|
405
|
+
| MarqueeSeparatorItem;
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Font-size scale for text/separator items in a marquee.
|
|
409
|
+
* Mapped to concrete rem/px values by the renderer.
|
|
410
|
+
*/
|
|
411
|
+
export type MarqueeFontSize =
|
|
412
|
+
| "s"
|
|
413
|
+
| "base"
|
|
414
|
+
| "l"
|
|
415
|
+
| "xl"
|
|
416
|
+
| "2xl"
|
|
417
|
+
| "3xl"
|
|
418
|
+
| "4xl"
|
|
419
|
+
| "5xl"
|
|
420
|
+
| "6xl";
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Marquee Block — horizontal infinite-scroll ticker.
|
|
424
|
+
*
|
|
425
|
+
* Section-level block: lives inside a full-width column of a `PageSectionV2`.
|
|
426
|
+
* Available on both pages and projects (unlike projectGrid/projectCarousel
|
|
427
|
+
* which are pages-only) — makes sense as an end-of-project "keep browsing"
|
|
428
|
+
* style band.
|
|
429
|
+
*
|
|
430
|
+
* Motion is driven by CSS `@keyframes` with an IntersectionObserver pause
|
|
431
|
+
* when off-screen. Respects `prefers-reduced-motion`. Hover effects are
|
|
432
|
+
* deliberately not supported — a block that is already animating fights
|
|
433
|
+
* scale/tilt hover presets.
|
|
434
|
+
*/
|
|
435
|
+
export interface MarqueeBlock {
|
|
436
|
+
_type: "marqueeBlock";
|
|
437
|
+
_key: string;
|
|
438
|
+
|
|
439
|
+
// ─── Content ───
|
|
440
|
+
items: MarqueeItem[];
|
|
441
|
+
|
|
442
|
+
// ─── Motion ───
|
|
443
|
+
direction?: "left" | "right"; // default "left"
|
|
444
|
+
speed?: number; // px/s, 5–600, default 60
|
|
445
|
+
pause_on_hover?: boolean; // default true
|
|
446
|
+
|
|
447
|
+
// ─── Typography (applies to text + separator items) ───
|
|
448
|
+
font_size?: MarqueeFontSize; // default "3xl"
|
|
449
|
+
font_weight?: "400" | "500" | "700" | "900"; // default "700"
|
|
450
|
+
color?: string; // hex or palette token, default "#111111"
|
|
451
|
+
text_style?: "solid" | "outline" | "italic-outline"; // default "solid"
|
|
452
|
+
letter_spacing?: number; // em, default 0
|
|
453
|
+
text_transform?: "none" | "uppercase" | "lowercase"; // default "uppercase"
|
|
454
|
+
|
|
455
|
+
// ─── Layout ───
|
|
456
|
+
gap?: number; // px between items, default 48
|
|
457
|
+
row_height?: number; // px, default 120 (controls image height)
|
|
458
|
+
padding_y?: number; // px, default 16
|
|
459
|
+
background_color?: string; // hex or palette; empty = transparent
|
|
460
|
+
|
|
461
|
+
// ─── Standard block fields ───
|
|
462
|
+
enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
|
|
463
|
+
layout?: BlockLayout;
|
|
464
|
+
responsive?: ResponsiveOverrides<MarqueeBlock>;
|
|
465
|
+
}
|
|
466
|
+
|
|
315
467
|
// ============================================
|
|
316
468
|
// Parallax V2 — Group + Slide types (Session 123)
|
|
317
469
|
// ============================================
|
|
@@ -635,8 +787,11 @@ export type ContentBlock =
|
|
|
635
787
|
| VideoBlock
|
|
636
788
|
| SpacerBlock
|
|
637
789
|
| ButtonBlock
|
|
790
|
+
| BeforeAfterBlock
|
|
791
|
+
| AudioBlock
|
|
638
792
|
| ProjectGridBlock
|
|
639
|
-
| ProjectCarouselBlock
|
|
793
|
+
| ProjectCarouselBlock
|
|
794
|
+
| MarqueeBlock;
|
|
640
795
|
|
|
641
796
|
// ============================================
|
|
642
797
|
// Structural types
|
package/lib/version.ts
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineField, defineType } from "sanity";
|
|
2
|
+
import { blockLayoutField, blockAnimationFields } from "./blockLayout";
|
|
3
|
+
|
|
4
|
+
export const audioBlock = defineType({
|
|
5
|
+
name: "audioBlock",
|
|
6
|
+
title: "Audio Block",
|
|
7
|
+
type: "object",
|
|
8
|
+
fields: [
|
|
9
|
+
// ── Source ──
|
|
10
|
+
defineField({
|
|
11
|
+
name: "asset_path",
|
|
12
|
+
title: "Audio File",
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Relative path to the audio file (mp3, wav, ogg, m4a, aac, flac)",
|
|
15
|
+
validation: (Rule) => Rule.required(),
|
|
16
|
+
}),
|
|
17
|
+
defineField({ name: "alt", title: "Alt Text", type: "string" }),
|
|
18
|
+
|
|
19
|
+
// ── Metadata ──
|
|
20
|
+
defineField({ name: "title", title: "Title", type: "string" }),
|
|
21
|
+
defineField({ name: "artist", title: "Artist", type: "string" }),
|
|
22
|
+
defineField({
|
|
23
|
+
name: "cover_path",
|
|
24
|
+
title: "Cover Art",
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Optional relative path to a cover image",
|
|
27
|
+
}),
|
|
28
|
+
|
|
29
|
+
// ── Appearance ──
|
|
30
|
+
defineField({
|
|
31
|
+
name: "accent_color",
|
|
32
|
+
title: "Accent Color",
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Hex color for the play button + progress fill",
|
|
35
|
+
initialValue: "#3580f9",
|
|
36
|
+
}),
|
|
37
|
+
defineField({
|
|
38
|
+
name: "width",
|
|
39
|
+
title: "Width",
|
|
40
|
+
type: "string",
|
|
41
|
+
options: {
|
|
42
|
+
list: [
|
|
43
|
+
{ title: "Full", value: "full" },
|
|
44
|
+
{ title: "Contained", value: "contained" },
|
|
45
|
+
{ title: "Small", value: "small" },
|
|
46
|
+
{ title: "Fill", value: "fill" },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
initialValue: "contained",
|
|
50
|
+
}),
|
|
51
|
+
defineField({ name: "border_radius", title: "Border Radius", type: "string" }),
|
|
52
|
+
defineField({ name: "shadow", title: "Shadow", type: "boolean", initialValue: false }),
|
|
53
|
+
|
|
54
|
+
// ── Playback ──
|
|
55
|
+
defineField({ name: "autoplay", title: "Autoplay", type: "boolean", initialValue: false }),
|
|
56
|
+
defineField({ name: "loop", title: "Loop", type: "boolean", initialValue: false }),
|
|
57
|
+
defineField({ name: "muted", title: "Muted", type: "boolean", initialValue: false }),
|
|
58
|
+
|
|
59
|
+
...blockAnimationFields,
|
|
60
|
+
blockLayoutField,
|
|
61
|
+
],
|
|
62
|
+
preview: {
|
|
63
|
+
select: { path: "asset_path", title: "title", artist: "artist" },
|
|
64
|
+
prepare({ path, title, artist }) {
|
|
65
|
+
const label = title ? (artist ? `${title} — ${artist}` : title) : path || "Audio block";
|
|
66
|
+
return { title: label };
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { defineField, defineType } from "sanity";
|
|
2
|
+
import { blockLayoutField, blockAnimationFields } from "./blockLayout";
|
|
3
|
+
|
|
4
|
+
export const beforeAfterBlock = defineType({
|
|
5
|
+
name: "beforeAfterBlock",
|
|
6
|
+
title: "Before / After Block",
|
|
7
|
+
type: "object",
|
|
8
|
+
fields: [
|
|
9
|
+
// ── Before side ──
|
|
10
|
+
defineField({
|
|
11
|
+
name: "before_media_type",
|
|
12
|
+
title: "Before Media Type",
|
|
13
|
+
type: "string",
|
|
14
|
+
options: { list: [{ title: "Image", value: "image" }, { title: "Video", value: "video" }] },
|
|
15
|
+
initialValue: "image",
|
|
16
|
+
}),
|
|
17
|
+
defineField({
|
|
18
|
+
name: "before_asset_path",
|
|
19
|
+
title: "Before Asset Path",
|
|
20
|
+
type: "string",
|
|
21
|
+
description: "Relative path to the image or video file shown before the slider",
|
|
22
|
+
validation: (Rule) => Rule.required(),
|
|
23
|
+
}),
|
|
24
|
+
defineField({ name: "before_alt", title: "Before Alt Text", type: "string" }),
|
|
25
|
+
|
|
26
|
+
// ── After side ──
|
|
27
|
+
defineField({
|
|
28
|
+
name: "after_media_type",
|
|
29
|
+
title: "After Media Type",
|
|
30
|
+
type: "string",
|
|
31
|
+
options: { list: [{ title: "Image", value: "image" }, { title: "Video", value: "video" }] },
|
|
32
|
+
initialValue: "image",
|
|
33
|
+
}),
|
|
34
|
+
defineField({
|
|
35
|
+
name: "after_asset_path",
|
|
36
|
+
title: "After Asset Path",
|
|
37
|
+
type: "string",
|
|
38
|
+
description: "Relative path to the image or video file shown after the slider",
|
|
39
|
+
validation: (Rule) => Rule.required(),
|
|
40
|
+
}),
|
|
41
|
+
defineField({ name: "after_alt", title: "After Alt Text", type: "string" }),
|
|
42
|
+
|
|
43
|
+
// ── Slider behavior ──
|
|
44
|
+
defineField({
|
|
45
|
+
name: "orientation",
|
|
46
|
+
title: "Orientation",
|
|
47
|
+
type: "string",
|
|
48
|
+
options: {
|
|
49
|
+
list: [
|
|
50
|
+
{ title: "Horizontal (slider left-right)", value: "horizontal" },
|
|
51
|
+
{ title: "Vertical (slider top-bottom)", value: "vertical" },
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
initialValue: "horizontal",
|
|
55
|
+
}),
|
|
56
|
+
defineField({
|
|
57
|
+
name: "initial_position",
|
|
58
|
+
title: "Initial Position",
|
|
59
|
+
type: "number",
|
|
60
|
+
description: "Starting split position (0–100%)",
|
|
61
|
+
initialValue: 50,
|
|
62
|
+
validation: (Rule) => Rule.min(0).max(100),
|
|
63
|
+
}),
|
|
64
|
+
defineField({
|
|
65
|
+
name: "handle_color",
|
|
66
|
+
title: "Handle Color",
|
|
67
|
+
type: "string",
|
|
68
|
+
description: "Hex color for the slider line + handle",
|
|
69
|
+
initialValue: "#FFFFFF",
|
|
70
|
+
}),
|
|
71
|
+
|
|
72
|
+
// ── Layout ──
|
|
73
|
+
defineField({
|
|
74
|
+
name: "width",
|
|
75
|
+
title: "Width",
|
|
76
|
+
type: "string",
|
|
77
|
+
options: {
|
|
78
|
+
list: [
|
|
79
|
+
{ title: "Full", value: "full" },
|
|
80
|
+
{ title: "Contained", value: "contained" },
|
|
81
|
+
{ title: "Small", value: "small" },
|
|
82
|
+
{ title: "Fill", value: "fill" },
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
initialValue: "full",
|
|
86
|
+
}),
|
|
87
|
+
defineField({
|
|
88
|
+
name: "aspect_ratio",
|
|
89
|
+
title: "Aspect Ratio",
|
|
90
|
+
type: "string",
|
|
91
|
+
options: {
|
|
92
|
+
list: [
|
|
93
|
+
{ title: "Auto", value: "auto" },
|
|
94
|
+
{ title: "16:9", value: "16:9" },
|
|
95
|
+
{ title: "4:3", value: "4:3" },
|
|
96
|
+
{ title: "1:1", value: "1:1" },
|
|
97
|
+
{ title: "21:9", value: "21:9" },
|
|
98
|
+
],
|
|
99
|
+
},
|
|
100
|
+
initialValue: "16:9",
|
|
101
|
+
}),
|
|
102
|
+
|
|
103
|
+
// ── Video playback (applies when either side is video) ──
|
|
104
|
+
defineField({ name: "video_autoplay", title: "Video Autoplay", type: "boolean", initialValue: true }),
|
|
105
|
+
defineField({ name: "video_loop", title: "Video Loop", type: "boolean", initialValue: true }),
|
|
106
|
+
defineField({ name: "video_muted", title: "Video Muted", type: "boolean", initialValue: true }),
|
|
107
|
+
|
|
108
|
+
// ── Appearance ──
|
|
109
|
+
defineField({ name: "border_radius", title: "Border Radius", type: "string" }),
|
|
110
|
+
defineField({ name: "shadow", title: "Shadow", type: "boolean", initialValue: false }),
|
|
111
|
+
|
|
112
|
+
...blockAnimationFields,
|
|
113
|
+
blockLayoutField,
|
|
114
|
+
],
|
|
115
|
+
preview: {
|
|
116
|
+
select: { before: "before_asset_path", after: "after_asset_path" },
|
|
117
|
+
prepare({ before, after }) {
|
|
118
|
+
return { title: `Before/After: ${before || "—"} ↔ ${after || "—"}` };
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
// Block schemas (
|
|
2
|
-
export { textBlock } from "./textBlock";
|
|
3
|
-
export { imageBlock } from "./imageBlock";
|
|
4
|
-
export { imageGridBlock } from "./imageGridBlock";
|
|
5
|
-
export { videoBlock } from "./videoBlock";
|
|
6
|
-
export { spacerBlock } from "./spacerBlock";
|
|
7
|
-
export { buttonBlock } from "./buttonBlock";
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
1
|
+
// Block schemas (11)
|
|
2
|
+
export { textBlock } from "./textBlock";
|
|
3
|
+
export { imageBlock } from "./imageBlock";
|
|
4
|
+
export { imageGridBlock } from "./imageGridBlock";
|
|
5
|
+
export { videoBlock } from "./videoBlock";
|
|
6
|
+
export { spacerBlock } from "./spacerBlock";
|
|
7
|
+
export { buttonBlock } from "./buttonBlock";
|
|
8
|
+
export { beforeAfterBlock } from "./beforeAfterBlock";
|
|
9
|
+
export { audioBlock } from "./audioBlock";
|
|
10
|
+
export { projectGridBlock } from "./projectGridBlock";
|
|
11
|
+
export { projectCarouselBlock } from "./projectCarouselBlock";
|
|
12
|
+
export { marqueeBlock } from "./marqueeBlock";
|