@morphika/andami 0.2.26 → 0.4.0
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/app/admin/pages/[slug]/page.tsx +41 -47
- package/app/api/admin/assets/scan/route.ts +40 -13
- package/app/api/admin/custom-sections/[slug]/route.ts +4 -1
- package/app/api/admin/custom-sections/route.ts +4 -1
- package/app/api/admin/pages/[slug]/route.ts +7 -1
- package/app/api/admin/pages/route.ts +4 -1
- package/app/api/admin/r2/connect/route.ts +19 -1
- package/app/api/admin/r2/disconnect/route.ts +3 -0
- package/app/api/admin/r2/rename/route.ts +52 -13
- package/app/api/admin/r2/upload-url/route.ts +8 -1
- package/app/api/admin/settings/route.ts +4 -1
- package/app/api/admin/styles/route.ts +4 -1
- package/components/admin/styles/GridLayoutEditor.tsx +46 -46
- package/components/blocks/BlockRenderer.tsx +15 -2
- package/components/blocks/CoverSectionRenderer.tsx +75 -3
- package/components/blocks/ImageGridBlockRenderer.tsx +17 -11
- package/components/blocks/ParallaxGroupRenderer.tsx +45 -10
- package/components/blocks/ProjectCarouselBlockRenderer.tsx +527 -0
- package/components/blocks/ShaderCanvas.tsx +10 -6
- package/components/builder/BlockCardIcons.tsx +227 -0
- package/components/builder/BlockLivePreview.tsx +5 -0
- package/components/builder/BlockTypePicker.tsx +36 -63
- package/components/builder/BuilderCanvas.tsx +6 -2
- package/components/builder/ColumnDragOverlay.tsx +3 -3
- package/components/builder/CoverRowResizeHandle.tsx +5 -2
- package/components/builder/CoverSectionCanvas.tsx +45 -52
- package/components/builder/DndWrapper.tsx +1 -1
- package/components/builder/InsertionLines.tsx +1 -1
- package/components/builder/ParallaxGroupCanvas.tsx +12 -71
- package/components/builder/ReadOnlyFrame.tsx +4 -23
- package/components/builder/SectionCardIcons.tsx +320 -0
- package/components/builder/SectionEditorBar.tsx +17 -12
- package/components/builder/SectionTypePicker.tsx +34 -138
- package/components/builder/SectionV2Canvas.tsx +1 -1
- package/components/builder/SectionV2Column.tsx +19 -30
- package/components/builder/SettingsPanel.tsx +8 -32
- package/components/builder/SortableBlock.tsx +42 -50
- package/components/builder/SortableRow.tsx +207 -19
- package/components/builder/blockStyles.tsx +59 -180
- package/components/builder/editors/ProjectCarouselBlockEditor.tsx +443 -0
- package/components/builder/editors/index.ts +1 -0
- package/components/builder/iconPrimitives.tsx +78 -0
- package/components/builder/live-preview/LiveImagePreview.tsx +16 -2
- package/components/builder/live-preview/LiveProjectCarouselPreview.tsx +227 -0
- package/components/builder/live-preview/LiveVideoPreview.tsx +15 -2
- package/components/builder/live-preview/index.ts +1 -0
- package/components/builder/settings-panel/BlockSettings.tsx +7 -0
- package/components/builder/settings-panel/ColumnV2Settings.tsx +5 -5
- package/components/builder/settings-panel/CoverSectionSettings.tsx +28 -1
- package/components/builder/settings-panel/SectionV2Settings.tsx +14 -14
- package/lib/animation/enter-types.ts +1 -0
- package/lib/animation/hover-effect-types.ts +1 -0
- package/lib/assets.ts +17 -2
- package/lib/builder/block-registrations.ts +268 -0
- package/lib/builder/block-registry.ts +195 -0
- package/lib/builder/constants.ts +22 -15
- package/lib/builder/defaults.ts +21 -0
- package/lib/builder/format.ts +25 -0
- package/lib/builder/history.ts +0 -3
- package/lib/builder/index.ts +16 -0
- package/lib/builder/layout-styles.ts +1 -1
- package/lib/builder/registry.ts +44 -0
- package/lib/builder/section-visibility.ts +36 -0
- package/lib/builder/serializer/normalizers.ts +15 -6
- package/lib/builder/serializer/serializers.ts +3 -3
- package/lib/builder/store-blocks.ts +16 -9
- package/lib/builder/store-cover.ts +76 -8
- package/lib/builder/store-sections.ts +1 -1
- package/lib/builder/store.ts +0 -2
- package/lib/builder/types.ts +9 -5
- package/lib/csrf.ts +31 -0
- package/lib/sanity/types.ts +54 -2
- package/lib/security.ts +50 -0
- package/lib/version.ts +1 -1
- package/package.json +1 -1
- package/sanity/schemas/blocks/index.ts +2 -1
- package/sanity/schemas/blocks/projectCarouselBlock.ts +218 -0
- package/sanity/schemas/index.ts +4 -1
- package/sanity/schemas/objects/coverSection.ts +35 -3
- package/sanity/schemas/pageSectionV2.ts +1 -0
- package/components/builder/ParallaxSlideHeader.tsx +0 -113
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Full-size block card icons (220×120) used in the Add Block modal.
|
|
5
|
+
*
|
|
6
|
+
* Visual language: soft grid background with a 24-rect brick overlay that
|
|
7
|
+
* fades to #F4F4F4 on the right (matches the card's clean gray). 3D white
|
|
8
|
+
* shapes with cool-blue bevel + vector drop shadow. Dashed blue (#4794E2)
|
|
9
|
+
* construction guides on top.
|
|
10
|
+
*
|
|
11
|
+
* IDs are namespaced per icon (`tBlk`, `iBlk`, `igBlk`, `vBlk`, `sBlk`,
|
|
12
|
+
* `bBlk`) so multiple cards can render side by side without filter /
|
|
13
|
+
* gradient collisions.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Bg, BgDefs, ShadowFilter, VertBevel } from "./iconPrimitives";
|
|
17
|
+
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
19
|
+
// Text
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
21
|
+
export function TextBlockCardIcon() {
|
|
22
|
+
return (
|
|
23
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 120" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
|
|
24
|
+
<defs>
|
|
25
|
+
<BgDefs prefix="tBlk" />
|
|
26
|
+
<ShadowFilter id="shadTBlk" />
|
|
27
|
+
<linearGradient id="bevelATBlk" x1="61.7" y1="19.7" x2="61.7" y2="99.7" gradientUnits="userSpaceOnUse">
|
|
28
|
+
<stop offset="0" stopColor="#FFFFFF" />
|
|
29
|
+
<stop offset="1" stopColor="#E6ECF6" />
|
|
30
|
+
</linearGradient>
|
|
31
|
+
<linearGradient id="bevelaTBlk" x1="125.3" y1="44.9" x2="125.2" y2="84.4" gradientUnits="userSpaceOnUse">
|
|
32
|
+
<stop offset="0" stopColor="#FFFFFF" />
|
|
33
|
+
<stop offset="1" stopColor="#E6ECF6" />
|
|
34
|
+
</linearGradient>
|
|
35
|
+
</defs>
|
|
36
|
+
<Bg prefix="tBlk" />
|
|
37
|
+
|
|
38
|
+
{/* Uppercase A — 3D body with shadow */}
|
|
39
|
+
<g filter="url(#shadTBlk)">
|
|
40
|
+
<path fillRule="evenodd" clipRule="evenodd" fill="url(#bevelATBlk)" stroke="#C9D3E4" strokeWidth="0.7"
|
|
41
|
+
d="M93.5,97.8H74.3l-3.1-13.6H51.8l-3.2,13.6H29.8l21.5-81.2h20.6L93.5,97.8z M67.7,69.7c-2.7-12.1-4.9-23.6-6.2-34.4h-0.3c-1.5,11.2-3.5,22.7-6.2,34.4H67.7z" />
|
|
42
|
+
</g>
|
|
43
|
+
{/* A dashed accent */}
|
|
44
|
+
<path fillRule="evenodd" clipRule="evenodd" fill="none" stroke="#4794E2" strokeWidth="2" strokeMiterlimit="10" strokeDasharray="3,3" opacity="0.69"
|
|
45
|
+
d="M93.5,97.8H74.3l-3.1-13.6H51.8l-3.2,13.6H29.8l21.5-81.2h20.6L93.5,97.8z M67.7,69.7c-2.7-12.1-4.9-23.6-6.2-34.4h-0.3c-1.5,11.2-3.5,22.7-6.2,34.4H67.7z" />
|
|
46
|
+
|
|
47
|
+
{/* Lowercase a — 3D body with shadow */}
|
|
48
|
+
<g filter="url(#shadTBlk)">
|
|
49
|
+
<path fill="url(#bevelaTBlk)" stroke="#C9D3E4" strokeWidth="0.7"
|
|
50
|
+
d="M136.7,98l-0.7-6.2h-0.3c-2.8,3.9-8.1,7.4-15.1,7.4c-10,0-15.1-7.1-15.1-14.2c0-12,10.6-18.5,29.7-18.4v-1c0-4.1-1.1-11.4-11.2-11.4c-4.6,0-9.4,1.4-12.9,3.7l-2-5.9c4.1-2.7,10-4.4,16.3-4.4c15.1,0,18.8,10.3,18.8,20.2v18.5c0,4.3,0.2,8.5,0.8,11.9H136.7z M135.4,72.8c-9.8-0.2-21,1.5-21,11.1c0,5.8,3.9,8.6,8.5,8.6c6.4,0,10.5-4.1,12-8.3c0.3-0.9,0.5-1.9,0.5-2.9V72.8z" />
|
|
51
|
+
</g>
|
|
52
|
+
{/* a dashed accent (matches A) */}
|
|
53
|
+
<path fill="none" stroke="#4794E2" strokeWidth="2" strokeMiterlimit="10" strokeDasharray="3,3" opacity="0.69"
|
|
54
|
+
d="M136.7,98l-0.7-6.2h-0.3c-2.8,3.9-8.1,7.4-15.1,7.4c-10,0-15.1-7.1-15.1-14.2c0-12,10.6-18.5,29.7-18.4v-1c0-4.1-1.1-11.4-11.2-11.4c-4.6,0-9.4,1.4-12.9,3.7l-2-5.9c4.1-2.7,10-4.4,16.3-4.4c15.1,0,18.8,10.3,18.8,20.2v18.5c0,4.3,0.2,8.5,0.8,11.9H136.7z M135.4,72.8c-9.8-0.2-21,1.5-21,11.1c0,5.8,3.9,8.6,8.5,8.6c6.4,0,10.5-4.1,12-8.3c0.3-0.9,0.5-1.9,0.5-2.9V72.8z" />
|
|
55
|
+
|
|
56
|
+
{/* Right-side vertical bracket */}
|
|
57
|
+
<g fill="none" stroke="#4794E2" strokeWidth="2" strokeMiterlimit="10">
|
|
58
|
+
<line x1="159.7" y1="18.5" x2="159.7" y2="101.4" />
|
|
59
|
+
<line x1="153.4" y1="18.5" x2="166.1" y2="18.5" />
|
|
60
|
+
<line x1="153.4" y1="101.4" x2="166.1" y2="101.4" />
|
|
61
|
+
</g>
|
|
62
|
+
</svg>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
67
|
+
// Image
|
|
68
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
69
|
+
export function ImageBlockCardIcon() {
|
|
70
|
+
return (
|
|
71
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 120" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
|
|
72
|
+
<defs>
|
|
73
|
+
<BgDefs prefix="iBlk" />
|
|
74
|
+
<ShadowFilter id="shadIBlk" />
|
|
75
|
+
<VertBevel id="bevelIBlk" />
|
|
76
|
+
</defs>
|
|
77
|
+
<Bg prefix="iBlk" />
|
|
78
|
+
|
|
79
|
+
<g filter="url(#shadIBlk)">
|
|
80
|
+
<path d="M26.8,19h112.7c1.3,0,2.3,1.2,2.3,2.7v75c0,1.5-1.1,2.7-2.3,2.7H26.8c-1.3-0.1-2.3-1.3-2.3-2.7V21.6C24.5,20.2,25.5,19,26.8,19z"
|
|
81
|
+
fill="url(#bevelIBlk)" stroke="#C9D3E4" strokeWidth="0.7" />
|
|
82
|
+
</g>
|
|
83
|
+
<path d="M31.5,25.3h103.4c0.7,0,1.4,0.7,1.4,1.6v64.4c0,0.9-0.6,1.6-1.4,1.6H31.5c-0.7,0-1.4-0.7-1.4-1.6V26.9C30.1,25.9,30.7,25.3,31.5,25.3z"
|
|
84
|
+
fill="#DDE6F5" stroke="#C9D3E4" strokeWidth="0.5" />
|
|
85
|
+
<ellipse cx="116.3" cy="41.3" rx="5.1" ry="5.1" fill="#FFFFFF" stroke="#4794E2" strokeWidth="2" />
|
|
86
|
+
<path d="M37.1,79.6l20.3-23c1.4-1.5,3.7-1.6,5.2-0.1l11.6,11.7c1.4,1.4,3.6,1.4,5,0L89.5,58c1.2-1.2,3.1-1.4,4.5-0.4l31,21.5c2.9,1.9,1.5,6.5-2,6.5H39.7C36.7,85.5,35.1,81.9,37.1,79.6z"
|
|
87
|
+
fill="#FFFFFF" stroke="#4794E2" strokeWidth="2" strokeMiterlimit="10" />
|
|
88
|
+
<path d="M26.8,19h112.7c1.3,0,2.3,1.2,2.3,2.7v75c0,1.5-1.1,2.7-2.3,2.7H26.8c-1.3-0.1-2.3-1.3-2.3-2.7V21.6C24.5,20.2,25.5,19,26.8,19z"
|
|
89
|
+
fill="none" stroke="#4794E2" strokeWidth="2" strokeDasharray="3,3" />
|
|
90
|
+
</svg>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
95
|
+
// Image Grid
|
|
96
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
97
|
+
export function ImageGridBlockCardIcon() {
|
|
98
|
+
return (
|
|
99
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 120" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
|
|
100
|
+
<defs>
|
|
101
|
+
<BgDefs prefix="igBlk" />
|
|
102
|
+
<ShadowFilter id="shadIGBlk" />
|
|
103
|
+
<VertBevel id="bevelIGBlk" />
|
|
104
|
+
</defs>
|
|
105
|
+
<Bg prefix="igBlk" />
|
|
106
|
+
|
|
107
|
+
<g filter="url(#shadIGBlk)">
|
|
108
|
+
<rect x="24" y="26" width="58" height="34" rx="2" fill="url(#bevelIGBlk)" stroke="#C9D3E4" strokeWidth="0.7" />
|
|
109
|
+
<rect x="90" y="26" width="58" height="34" rx="2" fill="url(#bevelIGBlk)" stroke="#C9D3E4" strokeWidth="0.7" />
|
|
110
|
+
<rect x="24" y="68" width="58" height="34" rx="2" fill="url(#bevelIGBlk)" stroke="#C9D3E4" strokeWidth="0.7" />
|
|
111
|
+
<rect x="90" y="68" width="58" height="34" rx="2" fill="url(#bevelIGBlk)" stroke="#C9D3E4" strokeWidth="0.7" />
|
|
112
|
+
</g>
|
|
113
|
+
<rect x="30" y="32" width="46" height="22" rx="1.2" fill="#DDE6F5" />
|
|
114
|
+
<rect x="96" y="32" width="46" height="22" rx="1.2" fill="#DDE6F5" />
|
|
115
|
+
<rect x="30" y="74" width="46" height="22" rx="1.2" fill="#DDE6F5" />
|
|
116
|
+
<rect x="96" y="74" width="46" height="22" rx="1.2" fill="#DDE6F5" />
|
|
117
|
+
<g fill="none" stroke="#4794E2" strokeWidth="2" strokeDasharray="3,3">
|
|
118
|
+
<rect x="24" y="26" width="58" height="34" rx="2" />
|
|
119
|
+
<rect x="90" y="26" width="58" height="34" rx="2" />
|
|
120
|
+
<rect x="24" y="68" width="58" height="34" rx="2" />
|
|
121
|
+
<rect x="90" y="68" width="58" height="34" rx="2" />
|
|
122
|
+
</g>
|
|
123
|
+
</svg>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
128
|
+
// Video
|
|
129
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
130
|
+
export function VideoBlockCardIcon() {
|
|
131
|
+
return (
|
|
132
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 120" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
|
|
133
|
+
<defs>
|
|
134
|
+
<BgDefs prefix="vBlk" />
|
|
135
|
+
<ShadowFilter id="shadVBlk" />
|
|
136
|
+
<VertBevel id="bevelVBlk" />
|
|
137
|
+
</defs>
|
|
138
|
+
<Bg prefix="vBlk" />
|
|
139
|
+
|
|
140
|
+
<g filter="url(#shadVBlk)">
|
|
141
|
+
<path d="M26.8,19h112.7c1.3,0,2.3,1.2,2.3,2.7v75c0,1.5-1.1,2.7-2.3,2.7H26.8c-1.3-0.1-2.3-1.3-2.3-2.7V21.6C24.5,20.2,25.5,19,26.8,19z"
|
|
142
|
+
fill="url(#bevelVBlk)" stroke="#C9D3E4" strokeWidth="0.7" />
|
|
143
|
+
</g>
|
|
144
|
+
<path d="M31.5,25.3h103.4c0.7,0,1.4,0.7,1.4,1.6v64.4c0,0.9-0.6,1.6-1.4,1.6H31.5c-0.7,0-1.4-0.7-1.4-1.6V26.9C30.1,25.9,30.7,25.3,31.5,25.3z"
|
|
145
|
+
fill="#DDE6F5" stroke="#C9D3E4" strokeWidth="0.5" />
|
|
146
|
+
<path d="M26.8,19h112.7c1.3,0,2.3,1.2,2.3,2.7v75c0,1.5-1.1,2.7-2.3,2.7H26.8c-1.3-0.1-2.3-1.3-2.3-2.7V21.6C24.5,20.2,25.5,19,26.8,19z"
|
|
147
|
+
fill="none" stroke="#4794E2" strokeWidth="2" strokeDasharray="3,3" />
|
|
148
|
+
<path d="M75.2,45.9l21.3,11.4c2.4,1.2,2.4,4.6,0,5.9L75.2,74.5c-2.3,1.2-4.9-0.5-4.9-2.9V48.8C70.3,46.3,73,44.6,75.2,45.9z"
|
|
149
|
+
fill="#FFFFFF" stroke="#4794E2" strokeWidth="2" strokeMiterlimit="10" />
|
|
150
|
+
</svg>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
155
|
+
// Spacer (edge case — horizontal gradient, no shadow)
|
|
156
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
157
|
+
export function SpacerBlockCardIcon() {
|
|
158
|
+
return (
|
|
159
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 120" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
|
|
160
|
+
<defs>
|
|
161
|
+
<BgDefs prefix="sBlk" />
|
|
162
|
+
<linearGradient id="bevelSBlk" x1="15" y1="59" x2="144.7" y2="59" gradientUnits="userSpaceOnUse">
|
|
163
|
+
<stop offset="0" stopColor="#FFFFFF" stopOpacity="0" />
|
|
164
|
+
<stop offset="0.26" stopColor="#F2F5FA" />
|
|
165
|
+
<stop offset="0.79" stopColor="#F2F5FA" />
|
|
166
|
+
<stop offset="1" stopColor="#E6ECF6" stopOpacity="0" />
|
|
167
|
+
</linearGradient>
|
|
168
|
+
</defs>
|
|
169
|
+
<Bg prefix="sBlk" />
|
|
170
|
+
|
|
171
|
+
{/* Main frame — no shadow (edge case: horizontal gradient fill) */}
|
|
172
|
+
<path d="M26.8,19h112.7c1.3,0,2.3,1.2,2.3,2.7v75c0,1.5-1.1,2.7-2.3,2.7H26.8c-1.3-0.1-2.3-1.3-2.3-2.7V21.6C24.5,20.2,25.5,19,26.8,19z"
|
|
173
|
+
fill="url(#bevelSBlk)" />
|
|
174
|
+
|
|
175
|
+
{/* Blue up/down arrows */}
|
|
176
|
+
<g fill="#4794E2">
|
|
177
|
+
<path d="M82.7,27v18.8h2V27l2,1.9c0.3,0.3,0.9,0.3,1.2,0l0.2-0.2c0.3-0.3,0.3-0.9,0-1.2l-3.7-3.6c-0.4-0.4-1-0.4-1.4,0l-3.7,3.6c-0.3,0.3-0.3,0.9,0,1.2l0.2,0.2c0.3,0.3,0.9,0.3,1.2,0L82.7,27z" />
|
|
178
|
+
<path d="M86.7,89.7l-2,2v-18c0-0.5-0.4-0.8-0.8-0.8h-0.3c-0.5,0-0.8,0.4-0.8,0.8v18l-2-2c-0.3-0.3-0.9-0.3-1.2,0l-0.2,0.2c-0.3,0.3-0.3,0.9,0,1.2l3.7,3.7c0.2,0.2,0.4,0.3,0.7,0.3s0.5-0.1,0.7-0.3l3.7-3.7c0.3-0.3,0.3-0.9,0-1.2l-0.2-0.2C87.5,89.3,87,89.3,86.7,89.7z" />
|
|
179
|
+
</g>
|
|
180
|
+
|
|
181
|
+
{/* Dashed blue boundary lines */}
|
|
182
|
+
<line x1="22.1" y1="20.9" x2="146.5" y2="20.9" fill="none" stroke="#4794E2" strokeWidth="2" strokeDasharray="3,3" />
|
|
183
|
+
<line x1="22.1" y1="99.2" x2="146.5" y2="99.2" fill="none" stroke="#4794E2" strokeWidth="2" strokeDasharray="3,3" />
|
|
184
|
+
</svg>
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
189
|
+
// Button
|
|
190
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
191
|
+
export function ButtonBlockCardIcon() {
|
|
192
|
+
return (
|
|
193
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 120" width="100%" height="100%" preserveAspectRatio="xMidYMid meet">
|
|
194
|
+
<defs>
|
|
195
|
+
<BgDefs prefix="bBlk" />
|
|
196
|
+
<ShadowFilter id="shadBBlk" />
|
|
197
|
+
<VertBevel id="bevelBBlk" />
|
|
198
|
+
</defs>
|
|
199
|
+
<Bg prefix="bBlk" />
|
|
200
|
+
|
|
201
|
+
<g filter="url(#shadBBlk)">
|
|
202
|
+
<path d="M43,39h83.3c12.7,0,22.9,9.2,22.9,20.7l0,0c0,11.5-10.2,20.7-22.9,20.7H43c-12.7,0-22.9-9.2-22.9-20.7l0,0C20.1,48.3,30.4,39,43,39z"
|
|
203
|
+
fill="url(#bevelBBlk)" stroke="#C9D3E4" strokeWidth="0.7" />
|
|
204
|
+
</g>
|
|
205
|
+
<path d="M43,39h83.3c12.7,0,22.9,9.2,22.9,20.7l0,0c0,11.5-10.2,20.7-22.9,20.7H43c-12.7,0-22.9-9.2-22.9-20.7l0,0C20.1,48.3,30.4,39,43,39z"
|
|
206
|
+
fill="none" stroke="#4794E2" strokeWidth="2" strokeDasharray="3,3" />
|
|
207
|
+
|
|
208
|
+
{/* Cursor icon */}
|
|
209
|
+
<g transform="translate(-240 -432)">
|
|
210
|
+
<path fillRule="evenodd" clipRule="evenodd" fill="#FFFFFF" stroke="#4794E2" strokeWidth="2" strokeMiterlimit="10"
|
|
211
|
+
d="M353.1,513.5l8.2,8.2c0.9,0.9,2.5,0.9,3.4,0c0.9-0.9,2.3-2.3,3.1-3.1c0.5-0.5,0.7-1.1,0.7-1.7c0-0.6-0.3-1.3-0.7-1.7l-8.2-8.2l5.6-3.8c0.8-0.5,1.2-1.4,1-2.3c-0.1-0.9-0.8-1.7-1.6-2l-24.8-8.2c-0.9-0.3-1.8-0.1-2.5,0.6c-0.6,0.6-0.9,1.6-0.6,2.5l8.2,24.8c0.3,0.9,1.1,1.5,2,1.6c0.9,0.1,1.8-0.3,2.3-1L353.1,513.5z" />
|
|
212
|
+
</g>
|
|
213
|
+
</svg>
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
218
|
+
// Lookup map
|
|
219
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
220
|
+
export const BLOCK_CARD_ICONS: Record<string, React.FC> = {
|
|
221
|
+
textBlock: TextBlockCardIcon,
|
|
222
|
+
imageBlock: ImageBlockCardIcon,
|
|
223
|
+
imageGridBlock: ImageGridBlockCardIcon,
|
|
224
|
+
videoBlock: VideoBlockCardIcon,
|
|
225
|
+
spacerBlock: SpacerBlockCardIcon,
|
|
226
|
+
buttonBlock: ButtonBlockCardIcon,
|
|
227
|
+
};
|
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
SpacerBlock,
|
|
24
24
|
ButtonBlock,
|
|
25
25
|
ProjectGridBlock,
|
|
26
|
+
ProjectCarouselBlock,
|
|
26
27
|
} from "../../lib/sanity/types";
|
|
27
28
|
|
|
28
29
|
import { LiveTextEditor } from "./live-preview";
|
|
@@ -32,6 +33,7 @@ import { LiveVideoPreview } from "./live-preview";
|
|
|
32
33
|
import { LiveSpacerPreview } from "./live-preview";
|
|
33
34
|
import { LiveButtonPreview } from "./live-preview";
|
|
34
35
|
import { LiveProjectGridPreview } from "./live-preview";
|
|
36
|
+
import { LiveProjectCarouselPreview } from "./live-preview";
|
|
35
37
|
import { LivePlaceholder } from "./live-preview";
|
|
36
38
|
|
|
37
39
|
// ============================================
|
|
@@ -74,6 +76,9 @@ function BlockLivePreviewInner({ block, viewport = "desktop", editable = false }
|
|
|
74
76
|
case "projectGridBlock":
|
|
75
77
|
content = <LiveProjectGridPreview block={resolved as ProjectGridBlock} viewport={viewport} />;
|
|
76
78
|
break;
|
|
79
|
+
case "projectCarouselBlock":
|
|
80
|
+
content = <LiveProjectCarouselPreview block={resolved as ProjectCarouselBlock} viewport={viewport} />;
|
|
81
|
+
break;
|
|
77
82
|
default:
|
|
78
83
|
content = <LivePlaceholder type={(resolved as ContentBlock)._type} />;
|
|
79
84
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useState } from "react";
|
|
4
4
|
import { BLOCK_TYPE_REGISTRY } from "../../lib/builder/types";
|
|
5
5
|
import type { BlockType, BlockTypeInfo } from "../../lib/builder/types";
|
|
6
|
-
import {
|
|
6
|
+
import { BLOCK_CARD_ICONS } from "./BlockCardIcons";
|
|
7
7
|
|
|
8
8
|
interface BlockTypePickerProps {
|
|
9
9
|
onSelect: (type: BlockType) => void;
|
|
@@ -35,68 +35,48 @@ function BlockCard({
|
|
|
35
35
|
onHover: () => void;
|
|
36
36
|
onLeave: () => void;
|
|
37
37
|
}) {
|
|
38
|
-
const cardGradient = BLOCK_GRADIENTS[block.type];
|
|
39
38
|
const labels = BLOCK_LABELS[block.type];
|
|
40
|
-
const
|
|
39
|
+
const CardIcon = BLOCK_CARD_ICONS[block.type];
|
|
41
40
|
|
|
42
41
|
return (
|
|
43
42
|
<button
|
|
44
43
|
onClick={onSelect}
|
|
45
44
|
onMouseEnter={onHover}
|
|
46
45
|
onMouseLeave={onLeave}
|
|
47
|
-
className="relative flex items-center
|
|
46
|
+
className="relative flex items-center rounded-2xl text-left group overflow-hidden border-0 h-[96px]"
|
|
48
47
|
style={{
|
|
49
|
-
background:
|
|
50
|
-
transform: isHovered ? "translateY(-1px)
|
|
51
|
-
|
|
52
|
-
? "0 8px 24px rgba(0,0,0,0.18), inset 0 1px 0 rgba(255,255,255,0.3)"
|
|
53
|
-
: "0 2px 8px rgba(0,0,0,0.08), inset 0 1px 0 rgba(255,255,255,0.2)",
|
|
54
|
-
transition: "all 0.3s cubic-bezier(0.23, 1, 0.32, 1)",
|
|
48
|
+
background: "#f4f4f4",
|
|
49
|
+
transform: isHovered ? "translateY(-1px)" : "translateY(0)",
|
|
50
|
+
transition: "transform 200ms cubic-bezier(0.23, 1, 0.32, 1)",
|
|
55
51
|
}}
|
|
56
52
|
>
|
|
57
|
-
{/*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
background: "linear-gradient(135deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.05) 100%)",
|
|
62
|
-
}}
|
|
63
|
-
/>
|
|
64
|
-
|
|
65
|
-
{/* Icon container — frosted glass */}
|
|
66
|
-
<div
|
|
67
|
-
className="relative shrink-0 flex items-center justify-center"
|
|
68
|
-
style={{
|
|
69
|
-
width: 44,
|
|
70
|
-
height: 44,
|
|
71
|
-
borderRadius: 12,
|
|
72
|
-
background: "rgba(255,255,255,0.4)",
|
|
73
|
-
backdropFilter: "blur(8px)",
|
|
74
|
-
boxShadow: "0 2px 8px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.5)",
|
|
75
|
-
transition: "transform 0.3s",
|
|
76
|
-
transform: isHovered ? "scale(1.08)" : "scale(1)",
|
|
77
|
-
}}
|
|
78
|
-
>
|
|
79
|
-
{IconComponent ? <IconComponent /> : null}
|
|
53
|
+
{/* Icon artwork — full-bleed on the left. Width = height × 220/120
|
|
54
|
+
so the SVG's viewBox fills the container exactly (no sub-pixel seam). */}
|
|
55
|
+
<div className="shrink-0 h-full" style={{ width: 176 }}>
|
|
56
|
+
{CardIcon ? <CardIcon /> : null}
|
|
80
57
|
</div>
|
|
81
58
|
|
|
82
59
|
{/* Text */}
|
|
83
|
-
<div className="
|
|
84
|
-
<p
|
|
85
|
-
className="text-sm font-semibold truncate"
|
|
86
|
-
style={{
|
|
87
|
-
color: "rgba(0,0,0,0.72)",
|
|
88
|
-
textShadow: "0 1px 0 rgba(255,255,255,0.3)",
|
|
89
|
-
}}
|
|
90
|
-
>
|
|
60
|
+
<div className="min-w-0 pr-5 py-4">
|
|
61
|
+
<p className="text-[17px] font-semibold text-[#2b2f38] truncate leading-tight">
|
|
91
62
|
{labels?.label || block.label}
|
|
92
63
|
</p>
|
|
93
|
-
<p
|
|
94
|
-
className="text-xs truncate leading-snug mt-0.5"
|
|
95
|
-
style={{ color: "rgba(0,0,0,0.42)" }}
|
|
96
|
-
>
|
|
64
|
+
<p className="text-[13px] text-[#9096a0] truncate leading-snug mt-1">
|
|
97
65
|
{labels?.description || block.description}
|
|
98
66
|
</p>
|
|
99
67
|
</div>
|
|
68
|
+
|
|
69
|
+
{/* Hover stroke overlay — absolute so it renders ON TOP of the SVG icon;
|
|
70
|
+
inset box-shadow painted on the card's edge would otherwise be covered
|
|
71
|
+
by the SVG's own background fill in the icon half of the card. */}
|
|
72
|
+
<span
|
|
73
|
+
aria-hidden="true"
|
|
74
|
+
className="absolute inset-0 rounded-2xl pointer-events-none"
|
|
75
|
+
style={{
|
|
76
|
+
boxShadow: isHovered ? "inset 0 0 0 2px #4794E2" : "inset 0 0 0 2px transparent",
|
|
77
|
+
transition: "box-shadow 160ms ease",
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
100
80
|
</button>
|
|
101
81
|
);
|
|
102
82
|
}
|
|
@@ -117,7 +97,7 @@ export default function BlockTypePicker({
|
|
|
117
97
|
onClick={onClose}
|
|
118
98
|
>
|
|
119
99
|
<div
|
|
120
|
-
className="w-full max-w-
|
|
100
|
+
className="w-full max-w-4xl rounded-2xl bg-white max-h-[80vh] flex flex-col shadow-2xl border border-neutral-200/50 overflow-hidden"
|
|
121
101
|
style={{ fontFamily: "Inter, system-ui, sans-serif" }}
|
|
122
102
|
onClick={(e) => e.stopPropagation()}
|
|
123
103
|
>
|
|
@@ -127,23 +107,16 @@ export default function BlockTypePicker({
|
|
|
127
107
|
<h3 className="text-lg font-semibold text-neutral-900">
|
|
128
108
|
Add Block
|
|
129
109
|
</h3>
|
|
130
|
-
<
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
>
|
|
141
|
-
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
142
|
-
<line x1="18" y1="6" x2="6" y2="18" />
|
|
143
|
-
<line x1="6" y1="6" x2="18" y2="18" />
|
|
144
|
-
</svg>
|
|
145
|
-
</button>
|
|
146
|
-
</div>
|
|
110
|
+
<button
|
|
111
|
+
onClick={onClose}
|
|
112
|
+
className="w-8 h-8 rounded-lg flex items-center justify-center text-neutral-400 hover:text-neutral-600 hover:bg-neutral-100 transition-colors"
|
|
113
|
+
aria-label="Close block picker"
|
|
114
|
+
>
|
|
115
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
|
|
116
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
117
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
118
|
+
</svg>
|
|
119
|
+
</button>
|
|
147
120
|
</div>
|
|
148
121
|
<p className="text-sm text-neutral-400">
|
|
149
122
|
Choose a block and add it to your page
|
|
@@ -22,8 +22,12 @@ import { getSiteConfig } from "../../lib/config";
|
|
|
22
22
|
// double-click zoom, cursor changes, sessionStorage
|
|
23
23
|
// ============================================
|
|
24
24
|
|
|
25
|
-
/** Gap between device frames in canvas pixels
|
|
26
|
-
|
|
25
|
+
/** Gap between device frames in canvas pixels.
|
|
26
|
+
* Needs breathing room for the floating section pill (rendered by SortableRow)
|
|
27
|
+
* which sits ~98px left of its row at 1× zoom, and up to ~143px at low zoom
|
|
28
|
+
* due to the counter-scale cap of 1.5×. 140 clears it without making the
|
|
29
|
+
* canvas feel sparse. */
|
|
30
|
+
const FRAME_GAP = 140;
|
|
27
31
|
|
|
28
32
|
/** Device order for horizontal layout */
|
|
29
33
|
const DEVICE_ORDER: DeviceViewport[] = ["desktop", "tablet", "phone"];
|
|
@@ -44,12 +44,12 @@ const ColumnDragOverlay = memo(function ColumnDragOverlay({
|
|
|
44
44
|
style={{
|
|
45
45
|
width: 180,
|
|
46
46
|
minHeight: 80,
|
|
47
|
-
background: "rgba(
|
|
47
|
+
background: "rgba(71, 148, 226, 0.08)",
|
|
48
48
|
backdropFilter: "blur(8px)",
|
|
49
49
|
opacity: 0.85,
|
|
50
50
|
borderRadius: 8,
|
|
51
51
|
border: `2px solid ${BUILDER_BLUE}`,
|
|
52
|
-
boxShadow: "0 8px 32px rgba(
|
|
52
|
+
boxShadow: "0 8px 32px rgba(71, 148, 226, 0.3)",
|
|
53
53
|
}}
|
|
54
54
|
>
|
|
55
55
|
{/* Column header badge */}
|
|
@@ -59,7 +59,7 @@ const ColumnDragOverlay = memo(function ColumnDragOverlay({
|
|
|
59
59
|
alignItems: "center",
|
|
60
60
|
gap: 8,
|
|
61
61
|
padding: "8px 12px",
|
|
62
|
-
borderBottom: "1px solid rgba(
|
|
62
|
+
borderBottom: "1px solid rgba(71, 148, 226, 0.2)",
|
|
63
63
|
}}
|
|
64
64
|
>
|
|
65
65
|
<svg width="10" height="10" viewBox="0 0 10 10" fill={BUILDER_BLUE}>
|
|
@@ -8,9 +8,12 @@ import { useBuilderStore } from "../../lib/builder/store";
|
|
|
8
8
|
*
|
|
9
9
|
* 3-state visual matching the column resize handles in SectionV2Column:
|
|
10
10
|
* - Idle: invisible (transparent hit area only)
|
|
11
|
-
* - Hover:
|
|
11
|
+
* - Hover: violet pill line appears
|
|
12
12
|
* - Dragging: full-width line with glow + center dot
|
|
13
13
|
*
|
|
14
|
+
* Uses the section accent (#7500d5) to match the Cover Section's side pill
|
|
15
|
+
* and outline — same semantic colour system as Parallax / Custom sections.
|
|
16
|
+
*
|
|
14
17
|
* Session 176: Cover Sections — Phase 5.
|
|
15
18
|
*/
|
|
16
19
|
|
|
@@ -23,7 +26,7 @@ interface CoverRowResizeHandleProps {
|
|
|
23
26
|
isSectionHovered: boolean;
|
|
24
27
|
}
|
|
25
28
|
|
|
26
|
-
const HANDLE_COLOR = "#
|
|
29
|
+
const HANDLE_COLOR = "#7500d5";
|
|
27
30
|
|
|
28
31
|
export default function CoverRowResizeHandle({
|
|
29
32
|
sectionKey,
|
|
@@ -8,6 +8,7 @@ import SectionV2Canvas from "./SectionV2Canvas";
|
|
|
8
8
|
import CoverRowResizeHandle from "./CoverRowResizeHandle";
|
|
9
9
|
import { DEVICE_HEIGHTS } from "../../lib/builder/types";
|
|
10
10
|
import { adminAssetUrl } from "../../lib/assets";
|
|
11
|
+
import { normalizeRowHeights } from "../../lib/builder/store-cover";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* CoverSectionCanvas — renders a CoverSection in the builder canvas.
|
|
@@ -35,13 +36,24 @@ function getEffectiveCoverRows(section: CoverSection, viewport: DeviceViewport):
|
|
|
35
36
|
const vp = viewport as "tablet" | "phone";
|
|
36
37
|
const overrides = section.responsive?.[vp]?.cover_rows;
|
|
37
38
|
if (!overrides || overrides.length === 0) return section.cover_rows;
|
|
38
|
-
|
|
39
|
+
|
|
40
|
+
// Partial overrides (only some rows have a tablet/phone value) produce a
|
|
41
|
+
// merged array whose sum is not necessarily 100 — desktop values for the
|
|
42
|
+
// non-overridden rows don't know about the tablet overrides. Normalize the
|
|
43
|
+
// final set so the CSS `grid-template-rows` stays valid.
|
|
44
|
+
const merged = section.cover_rows.map((row) => {
|
|
39
45
|
const override = overrides.find((o) => o._key === row._key);
|
|
40
46
|
if (override?.height_percent !== undefined) {
|
|
41
47
|
return { ...row, height_percent: override.height_percent };
|
|
42
48
|
}
|
|
43
49
|
return row;
|
|
44
50
|
});
|
|
51
|
+
|
|
52
|
+
const total = merged.reduce((acc, r) => acc + r.height_percent, 0);
|
|
53
|
+
if (Math.abs(total - 100) <= 0.5) return merged;
|
|
54
|
+
|
|
55
|
+
const normalized = normalizeRowHeights(merged.map((r) => r.height_percent));
|
|
56
|
+
return merged.map((r, i) => ({ ...r, height_percent: normalized[i] ?? r.height_percent }));
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
export default function CoverSectionCanvas({
|
|
@@ -102,37 +114,9 @@ export default function CoverSectionCanvas({
|
|
|
102
114
|
onMouseEnter={() => setIsSectionHovered(true)}
|
|
103
115
|
onMouseLeave={() => setIsSectionHovered(false)}
|
|
104
116
|
>
|
|
105
|
-
{/*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
style={{
|
|
109
|
-
background: store.selectedRowKey === section._key
|
|
110
|
-
? `linear-gradient(135deg, #ccfbf1 0%, #b2f5ea 100%)`
|
|
111
|
-
: `linear-gradient(135deg, #f0fdfa 0%, #e6fffa 100%)`,
|
|
112
|
-
borderBottom: `1px solid ${COVER_ACCENT}25`,
|
|
113
|
-
borderRadius: "12px 12px 0 0",
|
|
114
|
-
}}
|
|
115
|
-
onClick={(e) => {
|
|
116
|
-
e.stopPropagation();
|
|
117
|
-
store.selectRow(section._key);
|
|
118
|
-
}}
|
|
119
|
-
>
|
|
120
|
-
<span className="text-[11px] font-semibold" style={{ color: COVER_ACCENT }}>
|
|
121
|
-
◆ Cover Section
|
|
122
|
-
</span>
|
|
123
|
-
<span
|
|
124
|
-
className="inline-flex items-center justify-center rounded-full text-[9px] font-bold text-white min-w-[18px] h-[18px] px-1"
|
|
125
|
-
style={{ background: COVER_ACCENT }}
|
|
126
|
-
>
|
|
127
|
-
{section.cover_rows.length}
|
|
128
|
-
</span>
|
|
129
|
-
<div className="flex-1" />
|
|
130
|
-
<span className="text-[9px] text-neutral-400 uppercase tracking-wider">
|
|
131
|
-
{section.height}
|
|
132
|
-
</span>
|
|
133
|
-
</div>
|
|
134
|
-
|
|
135
|
-
{/* Cover container — simulated viewport height */}
|
|
117
|
+
{/* Cover container — simulated viewport height. The previous top
|
|
118
|
+
header bar was removed; row settings now live in the section pill
|
|
119
|
+
(see SortableRow). */}
|
|
136
120
|
<div
|
|
137
121
|
className="relative"
|
|
138
122
|
style={{ height: containerHeight }}
|
|
@@ -183,10 +167,28 @@ export default function CoverSectionCanvas({
|
|
|
183
167
|
/>
|
|
184
168
|
)}
|
|
185
169
|
|
|
186
|
-
{/* Proportional rows
|
|
170
|
+
{/* Proportional rows.
|
|
171
|
+
*
|
|
172
|
+
* IMPORTANT — do NOT add `zIndex`, `isolate`, `transform`, `filter`,
|
|
173
|
+
* or any other property that establishes a new CSS stacking context
|
|
174
|
+
* on this wrapper. The column chrome (drag handle, delete button)
|
|
175
|
+
* inside each SectionV2Canvas/SortableColumn is absolutely positioned
|
|
176
|
+
* at z-[6] and must stack ABOVE the side pill (z-[5]) rendered by
|
|
177
|
+
* the parent SortableRow. If this wrapper creates a stacking context,
|
|
178
|
+
* the chrome gets trapped below the pill regardless of its local
|
|
179
|
+
* z-index — the whole subtree ends up at this wrapper's level in the
|
|
180
|
+
* parent stacking context.
|
|
181
|
+
*
|
|
182
|
+
* The rows visually stack above the absolute background/overlay divs
|
|
183
|
+
* naturally because they come LATER in the DOM and those bg divs
|
|
184
|
+
* don't have a positive z-index either.
|
|
185
|
+
*
|
|
186
|
+
* See `lib/builder/__tests__/section-visibility.test.ts` for the
|
|
187
|
+
* related nav-colour logic and this file's git history for the
|
|
188
|
+
* original bug (Session 178+: column chrome clipped in Cover).
|
|
189
|
+
*/}
|
|
187
190
|
<div
|
|
188
191
|
className="relative flex flex-col h-full"
|
|
189
|
-
style={{ zIndex: 1 }}
|
|
190
192
|
>
|
|
191
193
|
{virtualSectionsPerRow.map(({ row, rowNumber, virtualSection }, rowIndex) => {
|
|
192
194
|
const alignMap = { start: "flex-start", center: "center", end: "flex-end" };
|
|
@@ -205,22 +207,13 @@ export default function CoverSectionCanvas({
|
|
|
205
207
|
justifyContent: alignMap[row.vertical_align] || "flex-start",
|
|
206
208
|
}}
|
|
207
209
|
>
|
|
208
|
-
{/* Row label */}
|
|
209
|
-
<div
|
|
210
|
-
className="absolute top-1 right-2 pointer-events-none z-10"
|
|
211
|
-
style={{
|
|
212
|
-
fontSize: 9,
|
|
213
|
-
color: `${COVER_ACCENT}99`,
|
|
214
|
-
fontWeight: 600,
|
|
215
|
-
}}
|
|
216
|
-
>
|
|
217
|
-
Row {rowNumber} · {row.height_percent}%
|
|
218
|
-
{row.vertical_align !== "start" && ` · ${row.vertical_align}`}
|
|
219
|
-
</div>
|
|
220
|
-
|
|
221
210
|
{hasColumns ? (
|
|
222
|
-
/* V2 grid for this row's columns — overflow
|
|
223
|
-
|
|
211
|
+
/* V2 grid for this row's columns — overflow visible so the
|
|
212
|
+
column chrome (drag handle, delete button) that translates
|
|
213
|
+
outside column bounds doesn't get clipped. The public
|
|
214
|
+
renderer still clips at the <section> level so overflowing
|
|
215
|
+
content never leaves the cover on the live site. */
|
|
216
|
+
<div className="flex-1 min-h-0 flex flex-col" style={{ overflow: "visible" }}>
|
|
224
217
|
<SectionV2Canvas
|
|
225
218
|
section={virtualSection}
|
|
226
219
|
onAddBlockTarget={onAddBlockTarget}
|
|
@@ -247,9 +240,9 @@ export default function CoverSectionCanvas({
|
|
|
247
240
|
className="rounded-full text-[10px] font-medium transition-all hover:scale-105"
|
|
248
241
|
style={{
|
|
249
242
|
padding: "5px 16px",
|
|
250
|
-
background: `rgba(
|
|
251
|
-
color: "#
|
|
252
|
-
border: "1px dashed rgba(
|
|
243
|
+
background: `rgba(71, 148, 226, 0.10)`,
|
|
244
|
+
color: "#4794e2",
|
|
245
|
+
border: "1px dashed rgba(71, 148, 226, 0.4)",
|
|
253
246
|
opacity: isSectionHovered ? 1 : 0,
|
|
254
247
|
pointerEvents: isSectionHovered ? "auto" : "none",
|
|
255
248
|
transition: "opacity 150ms",
|
|
@@ -137,7 +137,7 @@ const BlockDragOverlay = memo(function BlockDragOverlay({ blockKey, rowKey }: {
|
|
|
137
137
|
if (!block) return null;
|
|
138
138
|
const info = ALL_BLOCK_INFO.find((b) => b.type === block!._type);
|
|
139
139
|
return (
|
|
140
|
-
<div className="rounded border border-[#
|
|
140
|
+
<div className="rounded border border-[#4794e2] bg-[#4794e2]/10 px-3 py-2 shadow-lg shadow-[#4794e2]/20 backdrop-blur-sm">
|
|
141
141
|
<div className="flex items-center gap-2">
|
|
142
142
|
<span className="text-xs">{info?.icon || "▪"}</span>
|
|
143
143
|
<span className="text-xs text-white">{info?.label || block._type}</span>
|
|
@@ -165,7 +165,7 @@ export const InsertionLines = memo(function InsertionLines({
|
|
|
165
165
|
borderRadius: "50%",
|
|
166
166
|
background: BUILDER_BLUE,
|
|
167
167
|
border: "2px solid white",
|
|
168
|
-
boxShadow: "0 1px 4px rgba(
|
|
168
|
+
boxShadow: "0 1px 4px rgba(71, 148, 226, 0.4)",
|
|
169
169
|
display: "flex",
|
|
170
170
|
alignItems: "center",
|
|
171
171
|
justifyContent: "center",
|