blocks-dusted 0.1.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/LICENSE +21 -0
- package/README.md +68 -0
- package/bin/index.js +8 -0
- package/package.json +40 -0
- package/src/cli.js +79 -0
- package/src/commands/add.js +327 -0
- package/src/commands/doctor.js +21 -0
- package/src/commands/list.js +18 -0
- package/src/installers/checkRequirements.js +101 -0
- package/src/installers/copyTemplateFiles.js +79 -0
- package/src/installers/installDependencies.js +69 -0
- package/src/installers/patchPayloadBlocksArray.js +122 -0
- package/src/installers/patchRenderBlocks.js +110 -0
- package/src/installers/runGenerators.js +3 -0
- package/src/installers/writeInstalledBlockReadme.js +117 -0
- package/src/registry/listTemplates.js +17 -0
- package/src/registry/loadTemplateManifest.js +20 -0
- package/src/registry/validateTemplateManifest.js +69 -0
- package/src/utils/logger.js +8 -0
- package/src/utils/packageManager.js +42 -0
- package/src/utils/paths.js +7 -0
- package/src/utils/project.js +122 -0
- package/src/utils/strings.js +7 -0
- package/templates/blocks/DD-BookCall/Component.tsx +573 -0
- package/templates/blocks/DD-BookCall/config.ts +239 -0
- package/templates/blocks/DD-BookCall/manifest.json +112 -0
- package/templates/blocks/DD-CardTemplate01/Component.tsx +144 -0
- package/templates/blocks/DD-CardTemplate01/config.ts +106 -0
- package/templates/blocks/DD-CardTemplate01/manifest.json +84 -0
- package/templates/blocks/DD-Carousel-A/Component.tsx +266 -0
- package/templates/blocks/DD-Carousel-A/config.ts +163 -0
- package/templates/blocks/DD-Carousel-A/manifest.json +86 -0
- package/templates/blocks/DD-Carousel-B/Component.tsx +432 -0
- package/templates/blocks/DD-Carousel-B/config.ts +176 -0
- package/templates/blocks/DD-Carousel-B/manifest.json +86 -0
- package/templates/blocks/DD-ComparisonTable/Component.tsx +272 -0
- package/templates/blocks/DD-ComparisonTable/config.ts +105 -0
- package/templates/blocks/DD-ComparisonTable/manifest.json +78 -0
- package/templates/blocks/DD-Contact/Component.tsx +439 -0
- package/templates/blocks/DD-Contact/config.ts +217 -0
- package/templates/blocks/DD-Contact/manifest.json +107 -0
- package/templates/blocks/DD-FeatCat/Component.tsx +361 -0
- package/templates/blocks/DD-FeatCat/config.ts +204 -0
- package/templates/blocks/DD-FeatCat/manifest.json +87 -0
- package/templates/blocks/DD-FeatStrip/Component.tsx +615 -0
- package/templates/blocks/DD-FeatStrip/config.ts +134 -0
- package/templates/blocks/DD-FeatStrip/manifest.json +82 -0
- package/templates/blocks/DD-Hero/Component.tsx +317 -0
- package/templates/blocks/DD-Hero/config.ts +267 -0
- package/templates/blocks/DD-Hero/manifest.json +92 -0
- package/templates/blocks/DD-HorizontalScroll/Component.tsx +995 -0
- package/templates/blocks/DD-HorizontalScroll/config.ts +231 -0
- package/templates/blocks/DD-HorizontalScroll/manifest.json +85 -0
- package/templates/blocks/DD-MasonaryMedia/Component.tsx +571 -0
- package/templates/blocks/DD-MasonaryMedia/config.ts +390 -0
- package/templates/blocks/DD-MasonaryMedia/manifest.json +101 -0
- package/templates/blocks/DD-MasonaryMedia/vendor.d.ts +2 -0
- package/templates/blocks/DD-Pricing/Component.tsx +380 -0
- package/templates/blocks/DD-Pricing/config.ts +215 -0
- package/templates/blocks/DD-Pricing/manifest.json +78 -0
- package/templates/blocks/DD-Process/Component.tsx +155 -0
- package/templates/blocks/DD-Process/config.ts +150 -0
- package/templates/blocks/DD-Process/manifest.json +82 -0
- package/templates/blocks/DD-Section/Component.tsx +327 -0
- package/templates/blocks/DD-Section/config.ts +214 -0
- package/templates/blocks/DD-Section/manifest.json +85 -0
- package/templates/blocks/DD-Services/Component.tsx +188 -0
- package/templates/blocks/DD-Services/config.ts +149 -0
- package/templates/blocks/DD-Services/manifest.json +95 -0
- package/templates/blocks/DD-ShowcaseGrid/Component.tsx +317 -0
- package/templates/blocks/DD-ShowcaseGrid/config.ts +197 -0
- package/templates/blocks/DD-ShowcaseGrid/manifest.json +80 -0
- package/templates/blocks/DD-Slider-A/Component.tsx +252 -0
- package/templates/blocks/DD-Slider-A/config.ts +118 -0
- package/templates/blocks/DD-Slider-A/manifest.json +86 -0
- package/templates/blocks/DD-StackCards/Component.tsx +496 -0
- package/templates/blocks/DD-StackCards/config.ts +399 -0
- package/templates/blocks/DD-StackCards/manifest.json +87 -0
- package/templates/blocks/DD-Team/Component.tsx +265 -0
- package/templates/blocks/DD-Team/config.ts +173 -0
- package/templates/blocks/DD-Team/manifest.json +82 -0
- package/templates/blocks/DD-TechStack/Component.tsx +189 -0
- package/templates/blocks/DD-TechStack/config.ts +153 -0
- package/templates/blocks/DD-TechStack/manifest.json +118 -0
- package/templates/blocks/DD-Testimonials/Component.tsx +147 -0
- package/templates/blocks/DD-Testimonials/config.ts +120 -0
- package/templates/blocks/DD-Testimonials/manifest.json +87 -0
- package/templates/blocks/DD-Work/Component.tsx +328 -0
- package/templates/blocks/DD-Work/config.ts +165 -0
- package/templates/blocks/DD-Work/manifest.json +87 -0
- package/templates/blocks/DD-Work-B/Component.tsx +320 -0
- package/templates/blocks/DD-Work-B/config.ts +165 -0
- package/templates/blocks/DD-Work-B/manifest.json +75 -0
- package/templates/shared/components/ui/anims/fade.tsx +83 -0
- package/templates/shared/components/ui/button.tsx +233 -0
- package/templates/shared/components/ui/input.tsx +41 -0
- package/templates/shared/fields/lucide-icon-picker/Cell.tsx +24 -0
- package/templates/shared/fields/lucide-icon-picker/README.md +160 -0
- package/templates/shared/fields/lucide-icon-picker/field.ts +40 -0
- package/templates/shared/fields/lucide-icon-picker/index.tsx +203 -0
- package/templates/shared/utilities/canUseDOM.ts +1 -0
- package/templates/shared/utilities/getURL.ts +31 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import Image from "next/image";
|
|
4
|
+
import React, { useEffect, useMemo, useRef, useState } from "react";
|
|
5
|
+
import { cn } from "@/utilities/ui";
|
|
6
|
+
type Media = { url?: string | null; alt?: string | null; [key: string]: any };
|
|
7
|
+
|
|
8
|
+
export type DDShowcaseGridProps = Record<string, any> & {
|
|
9
|
+
stickToTop?: boolean | null;
|
|
10
|
+
fadeOutOnScroll?: boolean | null;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type ColumnImage = {
|
|
14
|
+
image?: Media | string | null;
|
|
15
|
+
imageUrl?: string | null;
|
|
16
|
+
[key: string]: any;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
type ShowcaseColumn = {
|
|
20
|
+
id?: string | null;
|
|
21
|
+
images?: ColumnImage[] | null;
|
|
22
|
+
speed?: number | null;
|
|
23
|
+
initialOffset?: number | null;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
interface ScrollColumnProps {
|
|
27
|
+
images: ColumnImage[];
|
|
28
|
+
speed: number;
|
|
29
|
+
initialOffset: number;
|
|
30
|
+
priority?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isLocalImageSrc(src?: string | null): src is string {
|
|
34
|
+
return typeof src === "string" && src.startsWith("/api/media/file/");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function ScrollColumn({
|
|
38
|
+
images,
|
|
39
|
+
speed,
|
|
40
|
+
initialOffset,
|
|
41
|
+
priority = false,
|
|
42
|
+
}: ScrollColumnProps) {
|
|
43
|
+
const colRef = useRef<HTMLDivElement>(null);
|
|
44
|
+
const posRef = useRef(initialOffset);
|
|
45
|
+
const rafRef = useRef<number | null>(null);
|
|
46
|
+
const [mounted, setMounted] = useState(false);
|
|
47
|
+
|
|
48
|
+
const resolveSrc = (item: ColumnImage): string | null => {
|
|
49
|
+
if (item.image && typeof item.image === "object") {
|
|
50
|
+
const media = item.image as Media;
|
|
51
|
+
const url = media.url;
|
|
52
|
+
|
|
53
|
+
if (!url) return isLocalImageSrc(item.imageUrl) ? item.imageUrl : null;
|
|
54
|
+
|
|
55
|
+
return isLocalImageSrc(url) ? url : null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return isLocalImageSrc(item.imageUrl) ? item.imageUrl : null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const validImages = useMemo(() => {
|
|
62
|
+
return (images ?? []).filter((item) => resolveSrc(item) !== null);
|
|
63
|
+
}, [images]);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
setMounted(true);
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!mounted) return;
|
|
71
|
+
|
|
72
|
+
const el = colRef.current;
|
|
73
|
+
if (!el) return;
|
|
74
|
+
|
|
75
|
+
const animate = () => {
|
|
76
|
+
posRef.current -= speed;
|
|
77
|
+
|
|
78
|
+
const totalHeight = el.scrollHeight / 2;
|
|
79
|
+
|
|
80
|
+
if (totalHeight > 0 && Math.abs(posRef.current) >= totalHeight) {
|
|
81
|
+
posRef.current = 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
el.style.transform = `translateY(${posRef.current}px)`;
|
|
85
|
+
rafRef.current = requestAnimationFrame(animate);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const timer = window.setTimeout(() => {
|
|
89
|
+
rafRef.current = requestAnimationFrame(animate);
|
|
90
|
+
}, 1200);
|
|
91
|
+
|
|
92
|
+
return () => {
|
|
93
|
+
window.clearTimeout(timer);
|
|
94
|
+
|
|
95
|
+
if (rafRef.current) {
|
|
96
|
+
cancelAnimationFrame(rafRef.current);
|
|
97
|
+
rafRef.current = null;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}, [mounted, speed]);
|
|
101
|
+
|
|
102
|
+
if (validImages.length === 0) return null;
|
|
103
|
+
|
|
104
|
+
const renderImages = mounted ? [...validImages, ...validImages] : validImages;
|
|
105
|
+
|
|
106
|
+
return (
|
|
107
|
+
<div className="flex flex-col gap-4 will-change-transform" ref={colRef}>
|
|
108
|
+
{renderImages.map((item, i) => {
|
|
109
|
+
const src = resolveSrc(item);
|
|
110
|
+
if (!src) return null;
|
|
111
|
+
|
|
112
|
+
const isClone = i >= validImages.length;
|
|
113
|
+
const shouldPrioritize = Boolean(priority) && !isClone;
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<div
|
|
117
|
+
key={`${item.id ?? "image"}-${i}`}
|
|
118
|
+
className="relative aspect-4/3 w-full shrink-0 overflow-hidden rounded-lg border border-white/6"
|
|
119
|
+
>
|
|
120
|
+
<Image
|
|
121
|
+
src={src}
|
|
122
|
+
alt={item.alt ?? ""}
|
|
123
|
+
fill
|
|
124
|
+
sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw"
|
|
125
|
+
className="object-cover object-top"
|
|
126
|
+
priority={shouldPrioritize}
|
|
127
|
+
loading="eager"
|
|
128
|
+
// loading={shouldPrioritize ? 'eager' : 'lazy'}
|
|
129
|
+
fetchPriority={shouldPrioritize ? "high" : "auto"}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
})}
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const DDShowcaseGrid: React.FC<DDShowcaseGridProps> = ({
|
|
139
|
+
columns,
|
|
140
|
+
height = 520,
|
|
141
|
+
columnHeight = 3200,
|
|
142
|
+
sectionId,
|
|
143
|
+
customCSS,
|
|
144
|
+
priority = false,
|
|
145
|
+
stickToTop = false,
|
|
146
|
+
fadeOutOnScroll = false,
|
|
147
|
+
}) => {
|
|
148
|
+
const sectionHeight = height ?? 520;
|
|
149
|
+
const colHeight = columnHeight ?? 3200;
|
|
150
|
+
const shouldStick = Boolean(stickToTop);
|
|
151
|
+
const shouldFade = Boolean(fadeOutOnScroll);
|
|
152
|
+
const columnItems = (columns ?? []) as ShowcaseColumn[];
|
|
153
|
+
const wrapperRef = useRef<HTMLDivElement | null>(null);
|
|
154
|
+
const stickyRef = useRef<HTMLDivElement | null>(null);
|
|
155
|
+
const fadeDistance = Math.max(1, sectionHeight);
|
|
156
|
+
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (!shouldFade) {
|
|
159
|
+
if (stickyRef.current) stickyRef.current.style.opacity = "1";
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let rafId = 0;
|
|
164
|
+
|
|
165
|
+
const updateOpacity = () => {
|
|
166
|
+
if (rafId) window.cancelAnimationFrame(rafId);
|
|
167
|
+
|
|
168
|
+
rafId = window.requestAnimationFrame(() => {
|
|
169
|
+
const wrapper = wrapperRef.current;
|
|
170
|
+
const sticky = stickyRef.current;
|
|
171
|
+
|
|
172
|
+
if (!wrapper || !sticky) return;
|
|
173
|
+
|
|
174
|
+
const rect = wrapper.getBoundingClientRect();
|
|
175
|
+
const progress = Math.min(1, Math.max(0, -rect.top / fadeDistance));
|
|
176
|
+
|
|
177
|
+
sticky.style.opacity = String(1 - progress);
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
updateOpacity();
|
|
182
|
+
|
|
183
|
+
window.addEventListener("scroll", updateOpacity, { passive: true });
|
|
184
|
+
window.addEventListener("resize", updateOpacity);
|
|
185
|
+
window.addEventListener("locomotive:scroll", updateOpacity);
|
|
186
|
+
window.addEventListener("locomotive:resize", updateOpacity);
|
|
187
|
+
|
|
188
|
+
return () => {
|
|
189
|
+
window.removeEventListener("scroll", updateOpacity);
|
|
190
|
+
window.removeEventListener("resize", updateOpacity);
|
|
191
|
+
window.removeEventListener("locomotive:scroll", updateOpacity);
|
|
192
|
+
window.removeEventListener("locomotive:resize", updateOpacity);
|
|
193
|
+
|
|
194
|
+
if (rafId) {
|
|
195
|
+
window.cancelAnimationFrame(rafId);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}, [fadeDistance, shouldFade]);
|
|
199
|
+
|
|
200
|
+
return (
|
|
201
|
+
<>
|
|
202
|
+
<style>{`
|
|
203
|
+
.studio-showcase-grid {
|
|
204
|
+
--sg-perspective: 900px;
|
|
205
|
+
--sg-perspective-origin: 50% 40%;
|
|
206
|
+
--sg-transform: rotateX(52deg) rotateZ(-42deg);
|
|
207
|
+
--sg-top: -160%;
|
|
208
|
+
--sg-bottom: -90%;
|
|
209
|
+
--sg-sides: -30%;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
@media (max-width: 1023px) {
|
|
213
|
+
.studio-showcase-grid {
|
|
214
|
+
--sg-perspective-origin: 50% 40%;
|
|
215
|
+
--sg-transform: rotateX(52deg) rotateZ(-42deg);
|
|
216
|
+
--sg-top: -90%;
|
|
217
|
+
--sg-bottom: -30%;
|
|
218
|
+
--sg-sides: -50%;
|
|
219
|
+
max-height: 420px;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@media (max-width: 639px) {
|
|
224
|
+
.studio-showcase-grid {
|
|
225
|
+
--sg-perspective-origin: 50% 40%;
|
|
226
|
+
--sg-transform: rotateX(52deg) rotateZ(-42deg);
|
|
227
|
+
--sg-top: -60%;
|
|
228
|
+
--sg-bottom: 0%;
|
|
229
|
+
--sg-sides: -90%;
|
|
230
|
+
max-height: 300px;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
`}</style>
|
|
234
|
+
|
|
235
|
+
{customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
|
|
236
|
+
|
|
237
|
+
<div
|
|
238
|
+
data-content-type="ddShowcaseGrid"
|
|
239
|
+
ref={wrapperRef}
|
|
240
|
+
className={cn(
|
|
241
|
+
"relative overflow-clip",
|
|
242
|
+
shouldStick || shouldFade ? "z-0" : "",
|
|
243
|
+
)}
|
|
244
|
+
style={{
|
|
245
|
+
height:
|
|
246
|
+
shouldStick || shouldFade
|
|
247
|
+
? `${sectionHeight + fadeDistance}px`
|
|
248
|
+
: undefined,
|
|
249
|
+
}}
|
|
250
|
+
>
|
|
251
|
+
<div
|
|
252
|
+
ref={stickyRef}
|
|
253
|
+
className={cn(
|
|
254
|
+
"relative will-change-opacity",
|
|
255
|
+
shouldStick || shouldFade ? "sticky top-0" : "",
|
|
256
|
+
)}
|
|
257
|
+
>
|
|
258
|
+
<div className="pointer-events-none absolute left-0 right-0 top-0 bottom-0 z-10 h-[150vh] bg-[linear-gradient(0deg,#ffffff,#ffffff,#ffffff,#ffffff,transparent)] dark:bg-[linear-gradient(0deg,#0a0a0a,#0a0a0a,#0a0a0a,#0a0a0a,transparent)] opacity-80 mix-blend-color-dodge" />
|
|
259
|
+
<div
|
|
260
|
+
id={sectionId ?? undefined}
|
|
261
|
+
className="studio-showcase-grid relative z-0! overflow-hidden"
|
|
262
|
+
style={{ height: `${sectionHeight}px` }}
|
|
263
|
+
>
|
|
264
|
+
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-80 bg-linear-to-b from-white to-transparent dark:from-[#0a0a0a]" />
|
|
265
|
+
<div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 h-80 bg-linear-to-t from-white to-transparent dark:from-[#0a0a0a]" />
|
|
266
|
+
<div className="pointer-events-none absolute inset-y-0 left-0 z-10 w-40 bg-linear-to-r from-white to-transparent dark:from-[#0a0a0a]" />
|
|
267
|
+
<div className="pointer-events-none absolute inset-y-0 right-0 z-10 w-40 bg-linear-to-l from-white to-transparent dark:from-[#0a0a0a]" />
|
|
268
|
+
|
|
269
|
+
<div
|
|
270
|
+
className="absolute inset-0"
|
|
271
|
+
style={{
|
|
272
|
+
perspective: "var(--sg-perspective)",
|
|
273
|
+
perspectiveOrigin: "var(--sg-perspective-origin)",
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
<div
|
|
277
|
+
style={{
|
|
278
|
+
transform: "var(--sg-transform)",
|
|
279
|
+
transformOrigin: "center center",
|
|
280
|
+
position: "absolute",
|
|
281
|
+
top: "var(--sg-top)",
|
|
282
|
+
left: "var(--sg-sides)",
|
|
283
|
+
right: "var(--sg-sides)",
|
|
284
|
+
bottom: "var(--sg-bottom)",
|
|
285
|
+
display: "flex",
|
|
286
|
+
gap: "16px",
|
|
287
|
+
alignItems: "flex-start",
|
|
288
|
+
}}
|
|
289
|
+
>
|
|
290
|
+
{columnItems.map((col, colIndex) => (
|
|
291
|
+
<div
|
|
292
|
+
key={col.id ?? colIndex}
|
|
293
|
+
className="studio-showcase-col"
|
|
294
|
+
style={{
|
|
295
|
+
flex: "1",
|
|
296
|
+
overflow: "hidden",
|
|
297
|
+
height: `${colHeight}px`,
|
|
298
|
+
}}
|
|
299
|
+
>
|
|
300
|
+
<ScrollColumn
|
|
301
|
+
images={col.images ?? []}
|
|
302
|
+
speed={col.speed ?? 0.5}
|
|
303
|
+
initialOffset={col.initialOffset ?? 0}
|
|
304
|
+
priority={Boolean(priority)}
|
|
305
|
+
/>
|
|
306
|
+
</div>
|
|
307
|
+
))}
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</>
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
export default DDShowcaseGrid;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import type { Block } from 'payload'
|
|
2
|
+
|
|
3
|
+
export const DDShowcaseGrid: Block = {
|
|
4
|
+
slug: 'DD-ShowcaseGrid',
|
|
5
|
+
interfaceName: 'DDShowcaseGrid',
|
|
6
|
+
labels: {
|
|
7
|
+
singular: 'Studio Showcase Grid',
|
|
8
|
+
plural: 'Studio Showcase Grids'
|
|
9
|
+
},
|
|
10
|
+
fields: [
|
|
11
|
+
{
|
|
12
|
+
type: 'tabs',
|
|
13
|
+
tabs: [
|
|
14
|
+
{
|
|
15
|
+
label: 'Columns',
|
|
16
|
+
fields: [
|
|
17
|
+
{
|
|
18
|
+
name: 'columns',
|
|
19
|
+
type: 'array',
|
|
20
|
+
label: 'Scroll Columns',
|
|
21
|
+
minRows: 1,
|
|
22
|
+
maxRows: 8,
|
|
23
|
+
admin: {
|
|
24
|
+
description:
|
|
25
|
+
'Each column scrolls independently. Add images via upload or fallback URL.',
|
|
26
|
+
initCollapsed: true
|
|
27
|
+
},
|
|
28
|
+
defaultValue: [
|
|
29
|
+
{ speed: 0.45, initialOffset: 0 },
|
|
30
|
+
{ speed: 0.6, initialOffset: -260 },
|
|
31
|
+
{ speed: 0.5, initialOffset: -130 },
|
|
32
|
+
{ speed: 0.38, initialOffset: -390 },
|
|
33
|
+
{ speed: 0.55, initialOffset: -200 }
|
|
34
|
+
],
|
|
35
|
+
fields: [
|
|
36
|
+
{
|
|
37
|
+
type: 'row',
|
|
38
|
+
fields: [
|
|
39
|
+
{
|
|
40
|
+
name: 'speed',
|
|
41
|
+
type: 'number',
|
|
42
|
+
label: 'Scroll Speed (px/frame)',
|
|
43
|
+
defaultValue: 0.5,
|
|
44
|
+
admin: {
|
|
45
|
+
description: 'Pixels scrolled per animation frame. e.g. 0.5',
|
|
46
|
+
width: '50%'
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'initialOffset',
|
|
51
|
+
type: 'number',
|
|
52
|
+
label: 'Initial Offset (px)',
|
|
53
|
+
defaultValue: 0,
|
|
54
|
+
admin: {
|
|
55
|
+
description:
|
|
56
|
+
'Starting vertical offset in pixels. Use negatives to stagger.',
|
|
57
|
+
width: '50%'
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'images',
|
|
64
|
+
type: 'array',
|
|
65
|
+
label: 'Images',
|
|
66
|
+
minRows: 1,
|
|
67
|
+
admin: {
|
|
68
|
+
description:
|
|
69
|
+
'Images displayed in this column. Original images render first. Duplicate loop images are added after mount.'
|
|
70
|
+
},
|
|
71
|
+
fields: [
|
|
72
|
+
{
|
|
73
|
+
name: 'image',
|
|
74
|
+
type: 'upload',
|
|
75
|
+
relationTo: 'media',
|
|
76
|
+
label: 'Upload Image'
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: 'imageUrl',
|
|
80
|
+
type: 'text',
|
|
81
|
+
label: 'Image URL (fallback)',
|
|
82
|
+
admin: {
|
|
83
|
+
description: 'Used when no image is uploaded.'
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'alt',
|
|
88
|
+
type: 'text',
|
|
89
|
+
label: 'Alt Text',
|
|
90
|
+
admin: {
|
|
91
|
+
description: 'Accessible description for the image.'
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'priority',
|
|
96
|
+
type: 'checkbox',
|
|
97
|
+
label: 'LCP Preload (single image only)',
|
|
98
|
+
defaultValue: false,
|
|
99
|
+
admin: {
|
|
100
|
+
description:
|
|
101
|
+
'Only tick this on the ONE image Lighthouse reports as the LCP element. Adds a <link rel="preload"> in <head>. Too many preloads waste bandwidth — use the block-level "Hero Above Fold" checkbox to eagerly load all images instead.'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
label: 'Settings',
|
|
112
|
+
fields: [
|
|
113
|
+
{
|
|
114
|
+
type: 'row',
|
|
115
|
+
fields: [
|
|
116
|
+
{
|
|
117
|
+
name: 'height',
|
|
118
|
+
type: 'number',
|
|
119
|
+
label: 'Section Height (px)',
|
|
120
|
+
defaultValue: 520,
|
|
121
|
+
admin: {
|
|
122
|
+
width: '50%',
|
|
123
|
+
description: 'Height of the visible viewport area.'
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'columnHeight',
|
|
128
|
+
type: 'number',
|
|
129
|
+
label: 'Column Height (px)',
|
|
130
|
+
defaultValue: 3200,
|
|
131
|
+
admin: {
|
|
132
|
+
width: '50%',
|
|
133
|
+
description: 'Total height of each scrolling column container.'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'row',
|
|
140
|
+
fields: [
|
|
141
|
+
{
|
|
142
|
+
name: 'stickToTop',
|
|
143
|
+
type: 'checkbox',
|
|
144
|
+
label: 'Stick to Top',
|
|
145
|
+
defaultValue: false,
|
|
146
|
+
admin: {
|
|
147
|
+
width: '50%',
|
|
148
|
+
description:
|
|
149
|
+
'Pins the showcase grid to the top of the viewport while its own section scrolls.'
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'fadeOutOnScroll',
|
|
154
|
+
type: 'checkbox',
|
|
155
|
+
label: 'Fade Out On Scroll',
|
|
156
|
+
defaultValue: false,
|
|
157
|
+
admin: {
|
|
158
|
+
width: '50%',
|
|
159
|
+
description:
|
|
160
|
+
'Fades the pinned grid based on this block section height, not the entire page scroll.'
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'priority',
|
|
167
|
+
type: 'checkbox',
|
|
168
|
+
label: 'Hero Above Fold (eager load all images)',
|
|
169
|
+
defaultValue: true,
|
|
170
|
+
admin: {
|
|
171
|
+
description:
|
|
172
|
+
'Enable when this block is in the hero / above the fold. Sets loading="eager" + fetchpriority="high" on every original image, bypassing the lazy-load detection bug inside 3D-transform containers. Clone/loop images stay lazy.'
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'sectionId',
|
|
177
|
+
type: 'text',
|
|
178
|
+
label: 'Section ID',
|
|
179
|
+
admin: {
|
|
180
|
+
description: 'Optional HTML id attribute for anchor linking.'
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: 'customCSS',
|
|
185
|
+
type: 'code',
|
|
186
|
+
label: 'Custom CSS',
|
|
187
|
+
admin: {
|
|
188
|
+
language: 'css',
|
|
189
|
+
description: 'Injected as a <style> tag. Use with caution.'
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "block",
|
|
3
|
+
"name": "DD-ShowcaseGrid",
|
|
4
|
+
"label": "Studio Showcase Grid",
|
|
5
|
+
"sourceName": "StudioShowcaseGrid",
|
|
6
|
+
"portableName": "DD-ShowcaseGrid",
|
|
7
|
+
"slug": "DD-ShowcaseGrid",
|
|
8
|
+
"version": "0.0.1",
|
|
9
|
+
"source": {
|
|
10
|
+
"repo": "Hadizainal/DesignsDustedv3",
|
|
11
|
+
"originalPath": "src/blocks/StudioShowcaseGrid",
|
|
12
|
+
"originalSlug": "studioShowcaseGrid",
|
|
13
|
+
"originalConfigExport": "StudioShowcaseGrid",
|
|
14
|
+
"originalComponentExport": "StudioShowcaseGrid",
|
|
15
|
+
"originalInterfaceName": "StudioShowcaseGrid"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
{
|
|
19
|
+
"from": "Component.tsx",
|
|
20
|
+
"to": "src/blocks/DD-ShowcaseGrid/Component.tsx"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"from": "config.ts",
|
|
24
|
+
"to": "src/blocks/DD-ShowcaseGrid/config.ts"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"dependencies": [],
|
|
28
|
+
"sharedFiles": [],
|
|
29
|
+
"requiredProjectFiles": [
|
|
30
|
+
{
|
|
31
|
+
"path": "src/utilities/ui.ts",
|
|
32
|
+
"reason": "Block uses the cn utility from @/utilities/ui."
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"optionalProjectFiles": [],
|
|
36
|
+
"collectionRegistration": {
|
|
37
|
+
"enabled": true,
|
|
38
|
+
"collections": [
|
|
39
|
+
"Pages",
|
|
40
|
+
"Posts"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"renderBlocksRegistration": {
|
|
44
|
+
"enabled": true
|
|
45
|
+
},
|
|
46
|
+
"renderBlocksBehavior": {
|
|
47
|
+
"wrapperless": false
|
|
48
|
+
},
|
|
49
|
+
"pageRouteIntegration": {
|
|
50
|
+
"required": false,
|
|
51
|
+
"manualSteps": []
|
|
52
|
+
},
|
|
53
|
+
"manual": [
|
|
54
|
+
"Register DDShowcaseGrid in Pages/Posts blocks arrays if automatic registration was skipped.",
|
|
55
|
+
"Register DD-ShowcaseGrid in RenderBlocks if automatic registration was skipped.",
|
|
56
|
+
"Run payload generate:types after schema changes.",
|
|
57
|
+
"Run payload generate:importmap if admin/component imports changed."
|
|
58
|
+
],
|
|
59
|
+
"manualSteps": [
|
|
60
|
+
"Register DDShowcaseGrid in Pages/Posts blocks arrays if automatic registration was skipped.",
|
|
61
|
+
"Register DD-ShowcaseGrid in RenderBlocks if automatic registration was skipped.",
|
|
62
|
+
"Run payload generate:types after schema changes.",
|
|
63
|
+
"Run payload generate:importmap if admin/component imports changed."
|
|
64
|
+
],
|
|
65
|
+
"patchTargets": {
|
|
66
|
+
"blockConfigRegistration": {
|
|
67
|
+
"description": "Installer can add import { DDShowcaseGrid } from '@/blocks/DD-ShowcaseGrid/config' and add DDShowcaseGrid to clear Pages/Posts blocks arrays."
|
|
68
|
+
},
|
|
69
|
+
"renderBlocksRegistration": {
|
|
70
|
+
"description": "Installer can register DD-ShowcaseGrid in src/blocks/**/RenderBlocks.tsx when the blockComponents map is clear."
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"notes": [
|
|
74
|
+
"Converted from DesignsDustedv3 StudioShowcaseGrid.",
|
|
75
|
+
"Generated Payload type imports are replaced with portable fallback types when needed.",
|
|
76
|
+
"Installer must not overwrite existing target files."
|
|
77
|
+
],
|
|
78
|
+
"exportName": "DDShowcaseGrid",
|
|
79
|
+
"interfaceName": "DDShowcaseGrid"
|
|
80
|
+
}
|