@morphika/andami 0.1.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/LICENSE +21 -0
- package/README.md +50 -0
- package/admin/assets.ts +4 -0
- package/admin/database.ts +4 -0
- package/admin/index.ts +6 -0
- package/admin/login.ts +4 -0
- package/admin/navigation.ts +4 -0
- package/admin/pages-editor.ts +4 -0
- package/admin/pages.ts +4 -0
- package/admin/projects-editor.ts +4 -0
- package/admin/projects.ts +4 -0
- package/admin/settings.ts +4 -0
- package/admin/setup.ts +4 -0
- package/admin/storage.ts +4 -0
- package/admin/styles.ts +4 -0
- package/app/(site)/[slug]/loading.tsx +20 -0
- package/app/(site)/[slug]/page.tsx +83 -0
- package/app/(site)/error.tsx +32 -0
- package/app/(site)/layout.tsx +53 -0
- package/app/(site)/loading.tsx +20 -0
- package/app/(site)/not-found.tsx +41 -0
- package/app/(site)/page.tsx +43 -0
- package/app/(site)/preview/page.tsx +99 -0
- package/app/(site)/work/[slug]/loading.tsx +23 -0
- package/app/(site)/work/[slug]/page.tsx +84 -0
- package/app/admin/assets/page.tsx +573 -0
- package/app/admin/database/page.tsx +302 -0
- package/app/admin/error.tsx +53 -0
- package/app/admin/layout.tsx +273 -0
- package/app/admin/login/page.tsx +88 -0
- package/app/admin/navigation/page.tsx +157 -0
- package/app/admin/page.tsx +17 -0
- package/app/admin/pages/[slug]/page.tsx +849 -0
- package/app/admin/pages/page.tsx +588 -0
- package/app/admin/projects/[slug]/page.tsx +3 -0
- package/app/admin/projects/page.tsx +669 -0
- package/app/admin/settings/page.tsx +132 -0
- package/app/admin/setup/page.tsx +64 -0
- package/app/admin/storage/page.tsx +518 -0
- package/app/admin/styles/page.tsx +243 -0
- package/app/api/admin/assets/file/route.ts +81 -0
- package/app/api/admin/assets/health/route.ts +170 -0
- package/app/api/admin/assets/register/route.ts +163 -0
- package/app/api/admin/assets/registry/route.ts +98 -0
- package/app/api/admin/assets/relink/confirm/route.ts +242 -0
- package/app/api/admin/assets/relink/route.ts +202 -0
- package/app/api/admin/assets/scan/route.ts +271 -0
- package/app/api/admin/auth/route.ts +160 -0
- package/app/api/admin/custom-sections/[slug]/route.ts +159 -0
- package/app/api/admin/custom-sections/route.ts +127 -0
- package/app/api/admin/database/route.ts +53 -0
- package/app/api/admin/pages/[slug]/duplicate/route.ts +91 -0
- package/app/api/admin/pages/[slug]/route.ts +617 -0
- package/app/api/admin/pages/[slug]/set-home/route.ts +76 -0
- package/app/api/admin/pages/route.ts +129 -0
- package/app/api/admin/preview/route.ts +53 -0
- package/app/api/admin/r2/connect/route.ts +181 -0
- package/app/api/admin/r2/delete/route.ts +198 -0
- package/app/api/admin/r2/disconnect/route.ts +42 -0
- package/app/api/admin/r2/rename/route.ts +265 -0
- package/app/api/admin/r2/status/route.ts +106 -0
- package/app/api/admin/r2/upload-url/route.ts +148 -0
- package/app/api/admin/revalidate/route.ts +55 -0
- package/app/api/admin/settings/route.ts +279 -0
- package/app/api/admin/setup/complete/route.ts +51 -0
- package/app/api/admin/setup/route.ts +118 -0
- package/app/api/admin/storage/switch/route.ts +117 -0
- package/app/api/admin/styles/fonts/route.ts +97 -0
- package/app/api/admin/styles/route.ts +304 -0
- package/app/api/assets/[...path]/route.ts +98 -0
- package/app/api/custom-sections/[id]/route.ts +43 -0
- package/app/api/draft-mode/disable/route.ts +10 -0
- package/app/api/draft-mode/enable/route.ts +26 -0
- package/app/api/projects/route.ts +42 -0
- package/app/api/styles/route.ts +88 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +7 -0
- package/app/layout.tsx +53 -0
- package/app/robots.ts +17 -0
- package/app/sitemap.ts +48 -0
- package/app/studio/[[...index]]/page.tsx +8 -0
- package/components/admin/MetadataEditor.tsx +173 -0
- package/components/admin/PublishToggle.tsx +130 -0
- package/components/admin/icons.tsx +40 -0
- package/components/admin/nav-builder/NavBuilder.tsx +182 -0
- package/components/admin/nav-builder/NavBuilderGrid.tsx +326 -0
- package/components/admin/nav-builder/NavGeneralSettings.tsx +275 -0
- package/components/admin/nav-builder/NavGridCell.tsx +48 -0
- package/components/admin/nav-builder/NavGridItem.tsx +189 -0
- package/components/admin/nav-builder/NavItemSettings.tsx +288 -0
- package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -0
- package/components/admin/nav-builder/NavLivePreview.tsx +125 -0
- package/components/admin/nav-builder/NavSettingsFields.tsx +248 -0
- package/components/admin/nav-builder/NavSettingsPanel.tsx +127 -0
- package/components/admin/nav-builder/index.ts +10 -0
- package/components/admin/nav-builder/nav-builder-utils.ts +238 -0
- package/components/admin/setup-wizard/BrandingStep.tsx +218 -0
- package/components/admin/setup-wizard/DatabaseStep.tsx +331 -0
- package/components/admin/setup-wizard/DoneStep.tsx +187 -0
- package/components/admin/setup-wizard/SetupWizard.tsx +166 -0
- package/components/admin/setup-wizard/StorageStep.tsx +308 -0
- package/components/admin/setup-wizard/WelcomeStep.tsx +96 -0
- package/components/admin/setup-wizard/index.ts +9 -0
- package/components/admin/styles/ColorsEditor.tsx +214 -0
- package/components/admin/styles/FontsEditor.tsx +258 -0
- package/components/admin/styles/GridLayoutEditor.tsx +292 -0
- package/components/admin/styles/LinksButtonsEditor.tsx +120 -0
- package/components/admin/styles/TypographyEditor.tsx +266 -0
- package/components/admin/styles/index.ts +9 -0
- package/components/admin/styles/shared.tsx +68 -0
- package/components/blocks/BlockRenderer.tsx +404 -0
- package/components/blocks/ButtonBlockRenderer.tsx +52 -0
- package/components/blocks/CoverBlockRenderer.tsx +239 -0
- package/components/blocks/CustomSectionInstanceRenderer.tsx +82 -0
- package/components/blocks/EnterAnimationWrapper.tsx +140 -0
- package/components/blocks/HoverAnimationWrapper.tsx +308 -0
- package/components/blocks/ImageBlockRenderer.tsx +61 -0
- package/components/blocks/ImageGridBlockRenderer.tsx +545 -0
- package/components/blocks/PageBackground.tsx +28 -0
- package/components/blocks/PageNavAnimation.tsx +35 -0
- package/components/blocks/PageNavColor.tsx +24 -0
- package/components/blocks/PageRenderer.tsx +142 -0
- package/components/blocks/ParallaxGroupRenderer.tsx +448 -0
- package/components/blocks/ParallaxSlideRenderer.tsx +175 -0
- package/components/blocks/ProjectGridBlockRenderer.tsx +556 -0
- package/components/blocks/SectionRenderer.tsx +170 -0
- package/components/blocks/SectionV2Renderer.tsx +330 -0
- package/components/blocks/ShaderCanvas.tsx +392 -0
- package/components/blocks/SpacerBlockRenderer.tsx +17 -0
- package/components/blocks/TextBlockRenderer.tsx +87 -0
- package/components/blocks/TypewriterRichText.tsx +464 -0
- package/components/blocks/TypewriterWrapper.tsx +149 -0
- package/components/blocks/VideoBlockRenderer.tsx +304 -0
- package/components/blocks/index.ts +2 -0
- package/components/builder/AssetBrowser.tsx +2 -0
- package/components/builder/BlockLivePreview.tsx +101 -0
- package/components/builder/BlockTypePicker.tsx +178 -0
- package/components/builder/BuilderCanvas.tsx +354 -0
- package/components/builder/CanvasMinimap.tsx +200 -0
- package/components/builder/CanvasToolbar.tsx +202 -0
- package/components/builder/ColorPicker.tsx +243 -0
- package/components/builder/ColorSwatchPicker.tsx +274 -0
- package/components/builder/ColumnDragContext.tsx +51 -0
- package/components/builder/ColumnDragOverlay.tsx +110 -0
- package/components/builder/CustomSectionInstanceCard.tsx +97 -0
- package/components/builder/DeviceFrame.tsx +123 -0
- package/components/builder/DndWrapper.tsx +337 -0
- package/components/builder/InsertionLines.tsx +186 -0
- package/components/builder/ParallaxGroupCanvas.tsx +228 -0
- package/components/builder/ParallaxSlideHeader.tsx +113 -0
- package/components/builder/ReadOnlyFrame.tsx +417 -0
- package/components/builder/SectionEditorBar.tsx +288 -0
- package/components/builder/SectionTypePicker.tsx +422 -0
- package/components/builder/SectionV2Canvas.tsx +297 -0
- package/components/builder/SectionV2Column.tsx +488 -0
- package/components/builder/SettingsPanel.tsx +911 -0
- package/components/builder/SortableBlock.tsx +230 -0
- package/components/builder/SortableRow.tsx +362 -0
- package/components/builder/VirtualAssetGrid.tsx +397 -0
- package/components/builder/asset-browser/AssetBrowser.tsx +178 -0
- package/components/builder/asset-browser/FileLightbox.tsx +116 -0
- package/components/builder/asset-browser/FolderTreeItem.tsx +55 -0
- package/components/builder/asset-browser/R2BrowserContent.tsx +436 -0
- package/components/builder/asset-browser/R2ContextMenu.tsx +98 -0
- package/components/builder/asset-browser/VideoThumbnail.tsx +63 -0
- package/components/builder/asset-browser/helpers.ts +88 -0
- package/components/builder/asset-browser/index.ts +1 -0
- package/components/builder/asset-browser/types.ts +49 -0
- package/components/builder/asset-browser/useAssetBrowser.ts +344 -0
- package/components/builder/asset-browser/useR2DragDrop.ts +116 -0
- package/components/builder/asset-browser/useR2Operations.ts +189 -0
- package/components/builder/blockStyles.tsx +295 -0
- package/components/builder/editors/ButtonBlockEditor.tsx +184 -0
- package/components/builder/editors/CoverBlockEditor.tsx +488 -0
- package/components/builder/editors/EnterAnimationPicker.tsx +297 -0
- package/components/builder/editors/HoverEffectPicker.tsx +209 -0
- package/components/builder/editors/ImageBlockEditor.tsx +206 -0
- package/components/builder/editors/ImageGridBlockEditor.tsx +386 -0
- package/components/builder/editors/ProjectGridEditor.tsx +648 -0
- package/components/builder/editors/SpacerBlockEditor.tsx +167 -0
- package/components/builder/editors/StaggerSettings.tsx +108 -0
- package/components/builder/editors/TextAlignmentIcons.tsx +39 -0
- package/components/builder/editors/TextBlockEditor.tsx +462 -0
- package/components/builder/editors/TextStylePicker.tsx +183 -0
- package/components/builder/editors/VideoBlockEditor.tsx +278 -0
- package/components/builder/editors/index.ts +10 -0
- package/components/builder/editors/shared.tsx +345 -0
- package/components/builder/hooks/useColumnDrag.ts +472 -0
- package/components/builder/hooks/useColumnResize.ts +221 -0
- package/components/builder/index.ts +12 -0
- package/components/builder/live-preview/LiveButtonPreview.tsx +38 -0
- package/components/builder/live-preview/LiveCoverPreview.tsx +146 -0
- package/components/builder/live-preview/LiveImageGridPreview.tsx +123 -0
- package/components/builder/live-preview/LiveImagePreview.tsx +107 -0
- package/components/builder/live-preview/LiveProjectGridPreview.tsx +1010 -0
- package/components/builder/live-preview/LiveSpacerPreview.tsx +9 -0
- package/components/builder/live-preview/LiveTextEditor.tsx +198 -0
- package/components/builder/live-preview/LiveVideoPreview.tsx +98 -0
- package/components/builder/live-preview/index.ts +10 -0
- package/components/builder/live-preview/shared.tsx +153 -0
- package/components/builder/settings-panel/BlockLayoutTab.tsx +532 -0
- package/components/builder/settings-panel/BlockSettings.tsx +94 -0
- package/components/builder/settings-panel/ColumnV2Settings.tsx +160 -0
- package/components/builder/settings-panel/LayoutTab.tsx +310 -0
- package/components/builder/settings-panel/PageSettings.tsx +200 -0
- package/components/builder/settings-panel/ParallaxGroupSettings.tsx +118 -0
- package/components/builder/settings-panel/ParallaxSlideSettings.tsx +178 -0
- package/components/builder/settings-panel/SectionV2AnimationTab.tsx +103 -0
- package/components/builder/settings-panel/SectionV2LayoutTab.tsx +312 -0
- package/components/builder/settings-panel/SectionV2Settings.tsx +323 -0
- package/components/builder/settings-panel/TRBLInputs.tsx +51 -0
- package/components/builder/settings-panel/index.ts +19 -0
- package/components/builder/settings-panel/responsive-helpers.ts +524 -0
- package/components/ui/CustomCursor.tsx +118 -0
- package/components/ui/NavContentLightbox.tsx +152 -0
- package/components/ui/Navbar.tsx +582 -0
- package/components/ui/PortfolioTracker.tsx +87 -0
- package/components/ui/ScrollToTop.tsx +47 -0
- package/lib/animation/enter-presets.ts +147 -0
- package/lib/animation/enter-resolve.ts +90 -0
- package/lib/animation/enter-types.ts +128 -0
- package/lib/animation/hover-effect-presets.ts +210 -0
- package/lib/animation/hover-effect-types.ts +126 -0
- package/lib/asset-retry.ts +111 -0
- package/lib/assets.ts +92 -0
- package/lib/audit.ts +35 -0
- package/lib/auth-token.ts +94 -0
- package/lib/auth.ts +13 -0
- package/lib/builder/cascade-helpers.ts +51 -0
- package/lib/builder/cascade.ts +533 -0
- package/lib/builder/constants.ts +103 -0
- package/lib/builder/defaults.ts +182 -0
- package/lib/builder/history.ts +48 -0
- package/lib/builder/index.ts +21 -0
- package/lib/builder/layout-styles.ts +344 -0
- package/lib/builder/masonry.ts +166 -0
- package/lib/builder/responsive.ts +156 -0
- package/lib/builder/serializer.ts +845 -0
- package/lib/builder/store-blocks.ts +193 -0
- package/lib/builder/store-canvas.ts +319 -0
- package/lib/builder/store-helpers.ts +490 -0
- package/lib/builder/store-sections.ts +709 -0
- package/lib/builder/store.ts +333 -0
- package/lib/builder/templates.ts +297 -0
- package/lib/builder/types.ts +374 -0
- package/lib/builder/utils.ts +37 -0
- package/lib/color-utils.ts +116 -0
- package/lib/config/index.ts +57 -0
- package/lib/config/types.ts +122 -0
- package/lib/contexts/AssetContext.tsx +79 -0
- package/lib/contexts/NavAnimationContext.tsx +44 -0
- package/lib/contexts/NavColorContext.tsx +38 -0
- package/lib/contexts/PageExitContext.tsx +194 -0
- package/lib/contexts/ThumbStatusContext.tsx +83 -0
- package/lib/csrf-client.ts +34 -0
- package/lib/csrf.ts +68 -0
- package/lib/format-utils.ts +24 -0
- package/lib/hooks/useViewport.ts +42 -0
- package/lib/logger.ts +81 -0
- package/lib/revalidate.ts +23 -0
- package/lib/sanitize.ts +91 -0
- package/lib/sanity/client.ts +8 -0
- package/lib/sanity/queries.ts +486 -0
- package/lib/sanity/types.ts +869 -0
- package/lib/sanity/writeClient.ts +24 -0
- package/lib/security.ts +402 -0
- package/lib/setup/detect.ts +156 -0
- package/lib/shader/glsl/index.ts +27 -0
- package/lib/shader/glsl/pixelate.ts +51 -0
- package/lib/shader/glsl/rgb-shift.ts +45 -0
- package/lib/shader/glsl/ripple.ts +46 -0
- package/lib/shader/glsl/vertex.ts +14 -0
- package/lib/storage/index.ts +211 -0
- package/lib/storage/r2-adapter.ts +286 -0
- package/lib/storage/types.ts +125 -0
- package/lib/styles/provider.tsx +267 -0
- package/lib/thumbnails/generate.ts +151 -0
- package/lib/utils.ts +6 -0
- package/package.json +212 -0
- package/sanity/compose.ts +65 -0
- package/sanity/sanity.config.ts +126 -0
- package/sanity/schemas/assetRegistry.ts +301 -0
- package/sanity/schemas/blocks/blockLayout.ts +90 -0
- package/sanity/schemas/blocks/buttonBlock.ts +82 -0
- package/sanity/schemas/blocks/coverBlock.ts +229 -0
- package/sanity/schemas/blocks/imageBlock.ts +58 -0
- package/sanity/schemas/blocks/imageGridBlock.ts +112 -0
- package/sanity/schemas/blocks/index.ts +9 -0
- package/sanity/schemas/blocks/projectGridBlock.ts +251 -0
- package/sanity/schemas/blocks/spacerBlock.ts +41 -0
- package/sanity/schemas/blocks/textBlock.ts +139 -0
- package/sanity/schemas/blocks/videoBlock.ts +80 -0
- package/sanity/schemas/customSection.ts +69 -0
- package/sanity/schemas/customSectionInstance.ts +163 -0
- package/sanity/schemas/index.ts +111 -0
- package/sanity/schemas/objects/enterAnimationConfig.ts +72 -0
- package/sanity/schemas/objects/hoverEffectConfig.ts +90 -0
- package/sanity/schemas/objects/parallaxGroup.ts +66 -0
- package/sanity/schemas/objects/parallaxSlide.ts +217 -0
- package/sanity/schemas/objects/typewriterConfig.ts +38 -0
- package/sanity/schemas/page.ts +162 -0
- package/sanity/schemas/pageSection.ts +157 -0
- package/sanity/schemas/pageSectionV2.ts +269 -0
- package/sanity/schemas/siteSettings.ts +256 -0
- package/sanity/schemas/siteStyles.ts +212 -0
- package/site/error.ts +4 -0
- package/site/index.ts +8 -0
- package/site/not-found.ts +4 -0
- package/site/page.ts +4 -0
- package/site/preview.ts +4 -0
- package/site/robots.ts +4 -0
- package/site/sitemap.ts +4 -0
- package/site/work.ts +4 -0
- package/studio/index.ts +4 -0
- package/styles/admin.css +85 -0
- package/styles/animations.css +237 -0
- package/styles/base.css +148 -0
- package/styles/globals.css +10 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useBuilderStore } from "../../lib/builder/store";
|
|
4
|
+
import type { ParallaxGroup, ParallaxSlideV2, PageSectionV2 } from "../../lib/sanity/types";
|
|
5
|
+
import SectionV2Canvas from "./SectionV2Canvas";
|
|
6
|
+
import ParallaxSlideHeader from "./ParallaxSlideHeader";
|
|
7
|
+
import { BUILDER_GREEN, BUILDER_VIOLET } from "../../lib/builder/constants";
|
|
8
|
+
import { DEVICE_HEIGHTS } from "../../lib/builder/types";
|
|
9
|
+
import { useAssetUrl } from "../../lib/contexts/AssetContext";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* ParallaxGroupCanvas — renders a ParallaxGroup in the builder canvas.
|
|
13
|
+
*
|
|
14
|
+
* Each slide is displayed as a stacked section with:
|
|
15
|
+
* - ParallaxSlideHeader (index, bg indicator, reorder, delete)
|
|
16
|
+
* - SectionV2Canvas (full V2 grid editor reuse)
|
|
17
|
+
* - Faint background preview when a bg image is set
|
|
18
|
+
* - Empty state message when slide has no background and no content blocks
|
|
19
|
+
*
|
|
20
|
+
* An "Add Slide" button appears at the bottom.
|
|
21
|
+
*
|
|
22
|
+
* Session 123: Parallax V2 Phase 2
|
|
23
|
+
* Session 127: Phase 5 — empty state, slide counter badge, smooth reorder
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
interface ParallaxGroupCanvasProps {
|
|
27
|
+
group: ParallaxGroup;
|
|
28
|
+
onAddBlockTarget: (sectionKey: string, colKey: string, insertIndex?: number) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Check whether a slide is empty (no background set and no content blocks) */
|
|
32
|
+
function isSlideEmpty(slide: ParallaxSlideV2): boolean {
|
|
33
|
+
const hasBg = slide.background_type === "video"
|
|
34
|
+
? !!slide.background_video
|
|
35
|
+
: !!slide.background_image;
|
|
36
|
+
const hasBlocks = slide.columns.some((col) => col.blocks.length > 0);
|
|
37
|
+
return !hasBg && !hasBlocks;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default function ParallaxGroupCanvas({
|
|
41
|
+
group,
|
|
42
|
+
onAddBlockTarget,
|
|
43
|
+
}: ParallaxGroupCanvasProps) {
|
|
44
|
+
const store = useBuilderStore();
|
|
45
|
+
const selectedRowKey = store.selectedRowKey;
|
|
46
|
+
const activeViewport = store.activeViewport || "desktop";
|
|
47
|
+
const slidePreviewHeight = DEVICE_HEIGHTS[activeViewport];
|
|
48
|
+
const assetUrl = useAssetUrl();
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div
|
|
52
|
+
className="relative"
|
|
53
|
+
style={{
|
|
54
|
+
borderRadius: 12,
|
|
55
|
+
border: "1.5px solid rgba(139, 92, 246, 0.25)",
|
|
56
|
+
overflow: "visible",
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{/* Group header */}
|
|
60
|
+
<div
|
|
61
|
+
className="flex items-center gap-2 px-3 py-2 cursor-pointer"
|
|
62
|
+
style={{
|
|
63
|
+
background: selectedRowKey === group._key
|
|
64
|
+
? "linear-gradient(135deg, #e8deff 0%, #ddd0ff 100%)"
|
|
65
|
+
: "linear-gradient(135deg, #f3f0ff 0%, #ede5ff 100%)",
|
|
66
|
+
borderBottom: "1px solid rgba(139, 92, 246, 0.15)",
|
|
67
|
+
borderRadius: "12px 12px 0 0",
|
|
68
|
+
}}
|
|
69
|
+
onClick={(e) => {
|
|
70
|
+
e.stopPropagation();
|
|
71
|
+
store.selectRow(group._key);
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
<span className="text-[11px] font-semibold text-[#8b5cf6]">
|
|
75
|
+
▽ Parallax Showcase
|
|
76
|
+
</span>
|
|
77
|
+
{/* Slide counter badge */}
|
|
78
|
+
<span
|
|
79
|
+
className="inline-flex items-center justify-center rounded-full text-[9px] font-bold text-white min-w-[18px] h-[18px] px-1"
|
|
80
|
+
style={{ background: BUILDER_VIOLET }}
|
|
81
|
+
>
|
|
82
|
+
{group.slides.length}
|
|
83
|
+
</span>
|
|
84
|
+
<div className="flex-1" />
|
|
85
|
+
<span className="text-[9px] text-neutral-400 uppercase tracking-wider">
|
|
86
|
+
{group.transition_effect}
|
|
87
|
+
</span>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
{/* Slides — CSS transition for smooth reorder */}
|
|
91
|
+
{group.slides.map((slide, slideIndex) => {
|
|
92
|
+
const isSlideSelected = selectedRowKey === slide._key;
|
|
93
|
+
const bgImagePath = slide.background_type === "image" && slide.background_image
|
|
94
|
+
? assetUrl(slide.background_image)
|
|
95
|
+
: null;
|
|
96
|
+
// For video backgrounds, show the first frame as a poster-like preview
|
|
97
|
+
const bgVideoPath = slide.background_type === "video" && slide.background_video
|
|
98
|
+
? assetUrl(slide.background_video)
|
|
99
|
+
: null;
|
|
100
|
+
const hasBgPreview = !!(bgImagePath || bgVideoPath);
|
|
101
|
+
const slideEmpty = isSlideEmpty(slide);
|
|
102
|
+
|
|
103
|
+
// Create a virtual PageSectionV2 to pass to SectionV2Canvas
|
|
104
|
+
const virtualSection: PageSectionV2 = {
|
|
105
|
+
_type: "pageSectionV2",
|
|
106
|
+
_key: slide._key,
|
|
107
|
+
section_type: "empty-v2",
|
|
108
|
+
columns: slide.columns,
|
|
109
|
+
settings: slide.section_settings,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<div
|
|
114
|
+
key={slide._key}
|
|
115
|
+
className="relative"
|
|
116
|
+
style={{
|
|
117
|
+
borderTop: slideIndex > 0 ? "1px solid rgba(139, 92, 246, 0.1)" : undefined,
|
|
118
|
+
transition: "opacity 0.2s ease",
|
|
119
|
+
}}
|
|
120
|
+
>
|
|
121
|
+
{/* Slide header */}
|
|
122
|
+
<ParallaxSlideHeader
|
|
123
|
+
slide={slide}
|
|
124
|
+
slideIndex={slideIndex}
|
|
125
|
+
totalSlides={group.slides.length}
|
|
126
|
+
groupKey={group._key}
|
|
127
|
+
isSelected={isSlideSelected}
|
|
128
|
+
onSelect={() => store.selectRow(slide._key)}
|
|
129
|
+
/>
|
|
130
|
+
|
|
131
|
+
{/* Slide content with optional background preview — 100vh equivalent */}
|
|
132
|
+
<div className="relative" style={{ minHeight: slidePreviewHeight }}>
|
|
133
|
+
{/* Background preview — faint image behind the grid */}
|
|
134
|
+
{bgImagePath && (
|
|
135
|
+
<div
|
|
136
|
+
className="absolute inset-0 bg-cover bg-center pointer-events-none"
|
|
137
|
+
style={{
|
|
138
|
+
backgroundImage: `url(${bgImagePath})`,
|
|
139
|
+
backgroundPosition: slide.background_position || "center center",
|
|
140
|
+
opacity: 0.12,
|
|
141
|
+
}}
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
144
|
+
|
|
145
|
+
{/* Video background preview — muted autoplay thumbnail */}
|
|
146
|
+
{bgVideoPath && (
|
|
147
|
+
<video
|
|
148
|
+
src={bgVideoPath}
|
|
149
|
+
muted
|
|
150
|
+
playsInline
|
|
151
|
+
autoPlay
|
|
152
|
+
loop
|
|
153
|
+
className="absolute inset-0 w-full h-full object-cover pointer-events-none"
|
|
154
|
+
style={{ opacity: 0.12 }}
|
|
155
|
+
/>
|
|
156
|
+
)}
|
|
157
|
+
|
|
158
|
+
{/* Overlay preview */}
|
|
159
|
+
{(slide.background_overlay_opacity ?? 0) > 0 && hasBgPreview && (
|
|
160
|
+
<div
|
|
161
|
+
className="absolute inset-0 pointer-events-none"
|
|
162
|
+
style={{
|
|
163
|
+
backgroundColor: slide.background_overlay_color || "#000000",
|
|
164
|
+
opacity: (slide.background_overlay_opacity ?? 0) / 100 * 0.3,
|
|
165
|
+
}}
|
|
166
|
+
/>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
{/* Empty state message */}
|
|
170
|
+
{slideEmpty && (
|
|
171
|
+
<div
|
|
172
|
+
className="absolute inset-0 flex items-center justify-center pointer-events-none"
|
|
173
|
+
style={{ zIndex: 5 }}
|
|
174
|
+
>
|
|
175
|
+
<div className="text-center px-6 py-4 rounded-lg" style={{ background: "rgba(139, 92, 246, 0.06)" }}>
|
|
176
|
+
<p className="text-[12px] font-medium text-[#8b5cf6] mb-1">
|
|
177
|
+
Empty slide
|
|
178
|
+
</p>
|
|
179
|
+
<p className="text-[11px] text-neutral-400 leading-relaxed">
|
|
180
|
+
Set a background image and add content to this slide
|
|
181
|
+
</p>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
)}
|
|
185
|
+
|
|
186
|
+
{/* V2 section grid editor — stretch to fill slide height */}
|
|
187
|
+
<div
|
|
188
|
+
className="relative"
|
|
189
|
+
style={{
|
|
190
|
+
minHeight: slidePreviewHeight,
|
|
191
|
+
display: "flex",
|
|
192
|
+
flexDirection: "column",
|
|
193
|
+
}}
|
|
194
|
+
>
|
|
195
|
+
<SectionV2Canvas
|
|
196
|
+
section={virtualSection}
|
|
197
|
+
onAddBlockTarget={onAddBlockTarget}
|
|
198
|
+
fillHeight
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
})}
|
|
205
|
+
|
|
206
|
+
{/* Add Slide button */}
|
|
207
|
+
<div
|
|
208
|
+
className="flex justify-center py-3"
|
|
209
|
+
style={{
|
|
210
|
+
background: "linear-gradient(135deg, #f9f7ff 0%, #f3f0ff 100%)",
|
|
211
|
+
borderTop: "1px solid rgba(139, 92, 246, 0.1)",
|
|
212
|
+
borderRadius: "0 0 12px 12px",
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
<button
|
|
216
|
+
className="flex items-center gap-1.5 rounded-lg px-4 py-1.5 text-[11px] font-medium text-white transition-colors hover:opacity-90"
|
|
217
|
+
style={{ background: BUILDER_GREEN }}
|
|
218
|
+
onClick={(e) => {
|
|
219
|
+
e.stopPropagation();
|
|
220
|
+
store.addParallaxSlide(group._key);
|
|
221
|
+
}}
|
|
222
|
+
>
|
|
223
|
+
<span className="text-sm leading-none">+</span> Add Slide
|
|
224
|
+
</button>
|
|
225
|
+
</div>
|
|
226
|
+
</div>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useBuilderStore } from "../../lib/builder/store";
|
|
4
|
+
import type { ParallaxSlideV2 } from "../../lib/sanity/types";
|
|
5
|
+
import { BUILDER_GREEN } from "../../lib/builder/constants";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ParallaxSlideHeader — thin header bar for each slide in a parallax group.
|
|
9
|
+
* Shows slide index, background preview thumbnail, settings icon,
|
|
10
|
+
* reorder arrows, and delete button.
|
|
11
|
+
*
|
|
12
|
+
* Session 123: Parallax V2 Phase 2
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface ParallaxSlideHeaderProps {
|
|
16
|
+
slide: ParallaxSlideV2;
|
|
17
|
+
slideIndex: number;
|
|
18
|
+
totalSlides: number;
|
|
19
|
+
groupKey: string;
|
|
20
|
+
isSelected: boolean;
|
|
21
|
+
onSelect: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default function ParallaxSlideHeader({
|
|
25
|
+
slide,
|
|
26
|
+
slideIndex,
|
|
27
|
+
totalSlides,
|
|
28
|
+
groupKey,
|
|
29
|
+
isSelected,
|
|
30
|
+
onSelect,
|
|
31
|
+
}: ParallaxSlideHeaderProps) {
|
|
32
|
+
const store = useBuilderStore();
|
|
33
|
+
const hasBg = slide.background_type === "image"
|
|
34
|
+
? !!slide.background_image
|
|
35
|
+
: !!slide.background_video;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<div
|
|
39
|
+
className="flex items-center gap-2 px-3 py-1.5 rounded-t-lg cursor-pointer select-none transition-colors"
|
|
40
|
+
style={{
|
|
41
|
+
background: isSelected
|
|
42
|
+
? "linear-gradient(135deg, #c8a8ff 0%, #d8b8ff 100%)"
|
|
43
|
+
: "#f5f0ff",
|
|
44
|
+
borderBottom: "1px solid rgba(139, 92, 246, 0.15)",
|
|
45
|
+
}}
|
|
46
|
+
onClick={(e) => {
|
|
47
|
+
e.stopPropagation();
|
|
48
|
+
onSelect();
|
|
49
|
+
}}
|
|
50
|
+
>
|
|
51
|
+
{/* Slide number */}
|
|
52
|
+
<span
|
|
53
|
+
className="flex items-center justify-center rounded-md text-[10px] font-bold text-white min-w-[22px] h-[22px] px-1"
|
|
54
|
+
style={{ background: "#8b5cf6" }}
|
|
55
|
+
>
|
|
56
|
+
{slideIndex + 1}
|
|
57
|
+
</span>
|
|
58
|
+
|
|
59
|
+
{/* Background type indicator */}
|
|
60
|
+
<span className="text-[10px] text-neutral-500 uppercase tracking-wider font-medium">
|
|
61
|
+
{slide.background_type === "video" ? "Video BG" : "Image BG"}
|
|
62
|
+
{hasBg && <span className="ml-1 text-green-500">●</span>}
|
|
63
|
+
</span>
|
|
64
|
+
|
|
65
|
+
{/* Spacer */}
|
|
66
|
+
<div className="flex-1" />
|
|
67
|
+
|
|
68
|
+
{/* Reorder arrows */}
|
|
69
|
+
<button
|
|
70
|
+
className="p-0.5 text-neutral-400 hover:text-neutral-700 disabled:opacity-30 transition-colors"
|
|
71
|
+
disabled={slideIndex === 0}
|
|
72
|
+
onClick={(e) => {
|
|
73
|
+
e.stopPropagation();
|
|
74
|
+
store.moveParallaxSlide(groupKey, slide._key, "up");
|
|
75
|
+
}}
|
|
76
|
+
title="Move slide up"
|
|
77
|
+
>
|
|
78
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
|
79
|
+
<path d="M6 3L9 7H3L6 3Z" fill="currentColor" />
|
|
80
|
+
</svg>
|
|
81
|
+
</button>
|
|
82
|
+
<button
|
|
83
|
+
className="p-0.5 text-neutral-400 hover:text-neutral-700 disabled:opacity-30 transition-colors"
|
|
84
|
+
disabled={slideIndex === totalSlides - 1}
|
|
85
|
+
onClick={(e) => {
|
|
86
|
+
e.stopPropagation();
|
|
87
|
+
store.moveParallaxSlide(groupKey, slide._key, "down");
|
|
88
|
+
}}
|
|
89
|
+
title="Move slide down"
|
|
90
|
+
>
|
|
91
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
|
92
|
+
<path d="M6 9L3 5H9L6 9Z" fill="currentColor" />
|
|
93
|
+
</svg>
|
|
94
|
+
</button>
|
|
95
|
+
|
|
96
|
+
{/* Delete slide */}
|
|
97
|
+
{totalSlides > 1 && (
|
|
98
|
+
<button
|
|
99
|
+
className="p-0.5 text-neutral-400 hover:text-red-500 transition-colors"
|
|
100
|
+
onClick={(e) => {
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
store.removeParallaxSlide(groupKey, slide._key);
|
|
103
|
+
}}
|
|
104
|
+
title="Delete slide"
|
|
105
|
+
>
|
|
106
|
+
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
|
|
107
|
+
<path d="M3 3L9 9M9 3L3 9" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
|
|
108
|
+
</svg>
|
|
109
|
+
</button>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|