blocks-dusted 0.1.10 → 0.1.12

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.
Files changed (249) hide show
  1. package/README.md +35 -0
  2. package/package.json +5 -2
  3. package/src/cli.js +39 -3
  4. package/src/commands/shop.js +59 -0
  5. package/src/commands/warehouse.js +73 -0
  6. package/src/utils/paths.js +13 -13
  7. package/templates/blocks/DD-Accordion/Component.tsx +229 -0
  8. package/templates/blocks/DD-Accordion/README.md +3 -0
  9. package/templates/blocks/DD-Accordion/config.ts +73 -0
  10. package/templates/blocks/DD-Accordion/manifest.json +75 -0
  11. package/templates/blocks/DD-AnimText/Component.tsx +788 -0
  12. package/templates/blocks/DD-AnimText/README.md +3 -0
  13. package/templates/blocks/DD-AnimText/config.ts +295 -0
  14. package/templates/blocks/DD-AnimText/manifest.json +85 -0
  15. package/templates/blocks/DD-AsideNav/Component.tsx +321 -0
  16. package/templates/blocks/DD-AsideNav/config.ts +204 -0
  17. package/templates/blocks/DD-AsideNav/manifest.json +80 -0
  18. package/templates/blocks/DD-Banner/Component.tsx +51 -0
  19. package/templates/blocks/DD-Banner/config.ts +57 -0
  20. package/templates/blocks/DD-Banner/manifest.json +72 -0
  21. package/templates/blocks/DD-BgLightning/Component.tsx +44 -0
  22. package/templates/blocks/DD-BgLightning/README.md +3 -0
  23. package/templates/blocks/DD-BgLightning/config.ts +97 -0
  24. package/templates/blocks/DD-BgLightning/manifest.json +79 -0
  25. package/templates/blocks/DD-BookCall/Component.tsx +535 -535
  26. package/templates/blocks/DD-BookCall/config.ts +238 -238
  27. package/templates/blocks/DD-ButtonPop/Component.tsx +117 -0
  28. package/templates/blocks/DD-ButtonPop/config.ts +105 -0
  29. package/templates/blocks/DD-ButtonPop/manifest.json +88 -0
  30. package/templates/blocks/DD-ButtonPop/svg-icons/Dust2.tsx +167 -0
  31. package/templates/blocks/DD-Card/Component.tsx +251 -0
  32. package/templates/blocks/DD-Card/README.md +4 -0
  33. package/templates/blocks/DD-Card/config.ts +154 -0
  34. package/templates/blocks/DD-Card/manifest.json +82 -0
  35. package/templates/blocks/DD-CardTemplate01/Component.tsx +140 -140
  36. package/templates/blocks/DD-CardTemplate01/config.ts +106 -106
  37. package/templates/blocks/DD-Carousel-A/Component.tsx +249 -249
  38. package/templates/blocks/DD-Carousel-B/Component.tsx +403 -403
  39. package/templates/blocks/DD-Chip/Component.tsx +250 -0
  40. package/templates/blocks/DD-Chip/EXAMPLES.md +313 -0
  41. package/templates/blocks/DD-Chip/README.md +202 -0
  42. package/templates/blocks/DD-Chip/config.ts +369 -0
  43. package/templates/blocks/DD-Chip/manifest.json +74 -0
  44. package/templates/blocks/DD-ComparisonTable/Component.tsx +256 -256
  45. package/templates/blocks/DD-Contact/Component.tsx +435 -434
  46. package/templates/blocks/DD-Contact/config.ts +22 -22
  47. package/templates/blocks/DD-Counter/Component.tsx +190 -0
  48. package/templates/blocks/DD-Counter/README.md +4 -0
  49. package/templates/blocks/DD-Counter/config.ts +291 -0
  50. package/templates/blocks/DD-Counter/manifest.json +74 -0
  51. package/templates/blocks/DD-CustomCSS/Component.tsx +8 -0
  52. package/templates/blocks/DD-CustomCSS/README.md +4 -0
  53. package/templates/blocks/DD-CustomCSS/addToLayout.md +5 -0
  54. package/templates/blocks/DD-CustomCSS/config.ts +22 -0
  55. package/templates/blocks/DD-CustomCSS/manifest.json +72 -0
  56. package/templates/blocks/DD-CustomCSS/renderCustomCSS.md +7 -0
  57. package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
  58. package/templates/blocks/DD-FeatCat/config.ts +201 -201
  59. package/templates/blocks/DD-FeatStrip/Component.tsx +585 -585
  60. package/templates/blocks/DD-FeatStrip/config.ts +134 -134
  61. package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +628 -0
  62. package/templates/blocks/DD-FluidBtn/Component.tsx +43 -0
  63. package/templates/blocks/DD-FluidBtn/README.md +6 -0
  64. package/templates/blocks/DD-FluidBtn/config.ts +102 -0
  65. package/templates/blocks/DD-FluidBtn/manifest.json +86 -0
  66. package/templates/blocks/DD-GooeyInfo/Component.tsx +149 -0
  67. package/templates/blocks/DD-GooeyInfo/config.ts +135 -0
  68. package/templates/blocks/DD-GooeyInfo/manifest.json +72 -0
  69. package/templates/blocks/DD-Hero/Component.tsx +297 -297
  70. package/templates/blocks/DD-Hero/config.ts +267 -267
  71. package/templates/blocks/DD-HorizontalScroll/Component.tsx +936 -936
  72. package/templates/blocks/DD-HorizontalScroll/config.ts +231 -231
  73. package/templates/blocks/DD-Icon/Component.tsx +287 -0
  74. package/templates/blocks/DD-Icon/README.md +4 -0
  75. package/templates/blocks/DD-Icon/config.ts +258 -0
  76. package/templates/blocks/DD-Icon/hooks/revalidateBlockIds.ts +66 -0
  77. package/templates/blocks/DD-Icon/manifest.json +82 -0
  78. package/templates/blocks/DD-IconButton/Component.tsx +455 -0
  79. package/templates/blocks/DD-IconButton/README.md +326 -0
  80. package/templates/blocks/DD-IconButton/config.ts +239 -0
  81. package/templates/blocks/DD-IconButton/manifest.json +85 -0
  82. package/templates/blocks/DD-IdTag/Component.tsx +21 -0
  83. package/templates/blocks/DD-IdTag/README.md +4 -0
  84. package/templates/blocks/DD-IdTag/config.ts +41 -0
  85. package/templates/blocks/DD-IdTag/manifest.json +72 -0
  86. package/templates/blocks/DD-Iframe/Component.tsx +45 -0
  87. package/templates/blocks/DD-Iframe/README.md +4 -0
  88. package/templates/blocks/DD-Iframe/config.ts +47 -0
  89. package/templates/blocks/DD-Iframe/manifest.json +72 -0
  90. package/templates/blocks/DD-IframeMedia/Component.tsx +133 -0
  91. package/templates/blocks/DD-IframeMedia/README.md +4 -0
  92. package/templates/blocks/DD-IframeMedia/config.ts +79 -0
  93. package/templates/blocks/DD-IframeMedia/manifest.json +72 -0
  94. package/templates/blocks/DD-LineAnim/Component.tsx +36 -0
  95. package/templates/blocks/DD-LineAnim/README.md +4 -0
  96. package/templates/blocks/DD-LineAnim/config.ts +90 -0
  97. package/templates/blocks/DD-LineAnim/manifest.json +80 -0
  98. package/templates/blocks/DD-LinkGrid/Component.tsx +119 -0
  99. package/templates/blocks/DD-LinkGrid/README.md +4 -0
  100. package/templates/blocks/DD-LinkGrid/config.ts +51 -0
  101. package/templates/blocks/DD-LinkGrid/manifest.json +74 -0
  102. package/templates/blocks/DD-LogoMarq/Component.tsx +132 -0
  103. package/templates/blocks/DD-LogoMarq/README.md +4 -0
  104. package/templates/blocks/DD-LogoMarq/config.ts +264 -0
  105. package/templates/blocks/DD-LogoMarq/manifest.json +82 -0
  106. package/templates/blocks/DD-Lottie/Component.tsx +459 -0
  107. package/templates/blocks/DD-Lottie/README.md +4 -0
  108. package/templates/blocks/DD-Lottie/config.ts +379 -0
  109. package/templates/blocks/DD-Lottie/index.tsx +28 -0
  110. package/templates/blocks/DD-Lottie/manifest.json +83 -0
  111. package/templates/blocks/DD-MagicBento/Component.tsx +777 -0
  112. package/templates/blocks/DD-MagicBento/README.md +4 -0
  113. package/templates/blocks/DD-MagicBento/config.ts +264 -0
  114. package/templates/blocks/DD-MagicBento/manifest.json +74 -0
  115. package/templates/blocks/DD-MasonaryMedia/Component.tsx +545 -545
  116. package/templates/blocks/DD-MasonaryMedia/config.ts +2 -2
  117. package/templates/blocks/DD-MasonaryMedia/vendor.d.ts +2 -2
  118. package/templates/blocks/DD-Masonry/Component.tsx +645 -0
  119. package/templates/blocks/DD-Masonry/README.md +4 -0
  120. package/templates/blocks/DD-Masonry/config.ts +475 -0
  121. package/templates/blocks/DD-Masonry/manifest.json +82 -0
  122. package/templates/blocks/DD-Pricing/Component.tsx +2 -5
  123. package/templates/blocks/DD-Process/Component.tsx +2 -4
  124. package/templates/blocks/DD-Process/config.ts +150 -150
  125. package/templates/blocks/DD-Progress/Component.tsx +156 -0
  126. package/templates/blocks/DD-Progress/README.md +4 -0
  127. package/templates/blocks/DD-Progress/config.ts +175 -0
  128. package/templates/blocks/DD-Progress/manifest.json +74 -0
  129. package/templates/blocks/DD-ProjHeader/Component.tsx +91 -0
  130. package/templates/blocks/DD-ProjHeader/README.md +4 -0
  131. package/templates/blocks/DD-ProjHeader/config.ts +96 -0
  132. package/templates/blocks/DD-ProjHeader/manifest.json +72 -0
  133. package/templates/blocks/DD-ProjSlider/Component.tsx +123 -0
  134. package/templates/blocks/DD-ProjSlider/README.md +4 -0
  135. package/templates/blocks/DD-ProjSlider/config.ts +64 -0
  136. package/templates/blocks/DD-ProjSlider/manifest.json +72 -0
  137. package/templates/blocks/DD-ScrollText/Component.tsx +114 -0
  138. package/templates/blocks/DD-ScrollText/README.md +4 -0
  139. package/templates/blocks/DD-ScrollText/config.ts +58 -0
  140. package/templates/blocks/DD-ScrollText/manifest.json +72 -0
  141. package/templates/blocks/DD-Section/Component.tsx +266 -266
  142. package/templates/blocks/DD-Separator/Component.tsx +66 -0
  143. package/templates/blocks/DD-Separator/README.md +4 -0
  144. package/templates/blocks/DD-Separator/config.ts +58 -0
  145. package/templates/blocks/DD-Separator/manifest.json +72 -0
  146. package/templates/blocks/DD-Services/Component.tsx +176 -176
  147. package/templates/blocks/DD-Services/config.ts +149 -149
  148. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +307 -307
  149. package/templates/blocks/DD-Slider-A/Component.tsx +1 -1
  150. package/templates/blocks/DD-StackCards/Component.tsx +1 -1
  151. package/templates/blocks/DD-StackCards/config.ts +399 -399
  152. package/templates/blocks/DD-Team/Component.tsx +247 -247
  153. package/templates/blocks/DD-Team/config.ts +173 -173
  154. package/templates/blocks/DD-TeamCards/Component.tsx +251 -0
  155. package/templates/blocks/DD-TeamCards/README.md +4 -0
  156. package/templates/blocks/DD-TeamCards/config.ts +302 -0
  157. package/templates/blocks/DD-TeamCards/manifest.json +74 -0
  158. package/templates/blocks/DD-TechStack/Component.tsx +174 -174
  159. package/templates/blocks/DD-TechStack/config.ts +153 -153
  160. package/templates/blocks/DD-TestMarq/Component.tsx +132 -0
  161. package/templates/blocks/DD-TestMarq/README.md +4 -0
  162. package/templates/blocks/DD-TestMarq/RowLabel.tsx +9 -0
  163. package/templates/blocks/DD-TestMarq/config.ts +88 -0
  164. package/templates/blocks/DD-TestMarq/manifest.json +72 -0
  165. package/templates/blocks/DD-Testim/Component.tsx +247 -0
  166. package/templates/blocks/DD-Testim/README.md +4 -0
  167. package/templates/blocks/DD-Testim/config.ts +101 -0
  168. package/templates/blocks/DD-Testim/manifest.json +75 -0
  169. package/templates/blocks/DD-Testim2/Component.tsx +256 -0
  170. package/templates/blocks/DD-Testim2/README.md +4 -0
  171. package/templates/blocks/DD-Testim2/config.ts +100 -0
  172. package/templates/blocks/DD-Testim2/manifest.json +74 -0
  173. package/templates/blocks/DD-Testimonials/Component.tsx +139 -147
  174. package/templates/blocks/DD-Testimonials/config.ts +120 -120
  175. package/templates/blocks/DD-TextMarq/Component.tsx +157 -0
  176. package/templates/blocks/DD-TextMarq/README.md +4 -0
  177. package/templates/blocks/DD-TextMarq/config.ts +237 -0
  178. package/templates/blocks/DD-TextMarq/manifest.json +82 -0
  179. package/templates/blocks/DD-Thanks/Component.tsx +501 -0
  180. package/templates/blocks/DD-Thanks/README.md +4 -0
  181. package/templates/blocks/DD-Thanks/config.ts +224 -0
  182. package/templates/blocks/DD-Thanks/manifest.json +72 -0
  183. package/templates/blocks/DD-VertMarq/Component.tsx +154 -0
  184. package/templates/blocks/DD-VertMarq/README.md +4 -0
  185. package/templates/blocks/DD-VertMarq/config.ts +236 -0
  186. package/templates/blocks/DD-VertMarq/dist/index.css +6 -0
  187. package/templates/blocks/DD-VertMarq/dist/index.min.css +1 -0
  188. package/templates/blocks/DD-VertMarq/manifest.json +74 -0
  189. package/templates/blocks/DD-Work/Component.tsx +318 -315
  190. package/templates/blocks/DD-Work/config.ts +165 -165
  191. package/templates/blocks/DD-Work-B/Component.tsx +294 -294
  192. package/templates/blocks/DDHG-CTASection2/Component.tsx +185 -0
  193. package/templates/blocks/DDHG-CTASection2/config.ts +140 -0
  194. package/templates/blocks/DDHG-CTASection2/manifest.json +45 -0
  195. package/templates/blocks/DDHG-HighlightArc/Component.tsx +243 -0
  196. package/templates/blocks/DDHG-HighlightArc/config.ts +85 -0
  197. package/templates/blocks/DDHG-HighlightArc/manifest.json +46 -0
  198. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +143 -0
  199. package/templates/blocks/DDHG-LogoMarquee/config.ts +154 -0
  200. package/templates/blocks/DDHG-LogoMarquee/manifest.json +39 -0
  201. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +227 -0
  202. package/templates/blocks/DDHG-NavCodeGrid/config.ts +64 -0
  203. package/templates/blocks/DDHG-NavCodeGrid/manifest.json +45 -0
  204. package/templates/blocks/DDHG-ProcessSec/Component.tsx +574 -0
  205. package/templates/blocks/DDHG-ProcessSec/config.ts +170 -0
  206. package/templates/blocks/DDHG-ProcessSec/manifest.json +56 -0
  207. package/templates/blocks/DDHG-ServiceDetails/Component.tsx +164 -0
  208. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +390 -0
  209. package/templates/blocks/DDHG-ServiceDetails/config.ts +77 -0
  210. package/templates/blocks/DDHG-ServiceDetails/manifest.json +50 -0
  211. package/templates/blocks/DDHG-ServiceHero/Component.tsx +234 -0
  212. package/templates/blocks/DDHG-ServiceHero/config.ts +49 -0
  213. package/templates/blocks/DDHG-ServiceHero/manifest.json +26 -0
  214. package/templates/blocks/DDHG-ServiceOverview/Component.tsx +277 -0
  215. package/templates/blocks/DDHG-ServiceOverview/config.ts +64 -0
  216. package/templates/blocks/DDHG-ServiceOverview/manifest.json +44 -0
  217. package/templates/blocks/DDHG-StatsFAQ/Component.tsx +389 -0
  218. package/templates/blocks/DDHG-StatsFAQ/config.ts +204 -0
  219. package/templates/blocks/DDHG-StatsFAQ/manifest.json +45 -0
  220. package/templates/blocks/DDHG-TeamCards/Component.tsx +169 -0
  221. package/templates/blocks/DDHG-TeamCards/config.ts +48 -0
  222. package/templates/blocks/DDHG-TeamCards/manifest.json +29 -0
  223. package/templates/shared/src/components/BgLightning/index.tsx +242 -0
  224. package/templates/shared/src/components/ui/ShareButton/index.tsx +80 -0
  225. package/templates/shared/src/components/ui/anims/FadeOnScroll.tsx +48 -0
  226. package/templates/shared/src/components/ui/anims/FadeUpOnInView.tsx +63 -0
  227. package/templates/shared/src/components/ui/anims/GlareHover.tsx +111 -0
  228. package/templates/shared/src/components/ui/anims/LineAnims.tsx +81 -0
  229. package/templates/shared/src/components/ui/anims/TitleAnims01.tsx +54 -0
  230. package/templates/shared/src/components/ui/anims/animation-container.tsx +29 -0
  231. package/templates/shared/src/components/ui/anims/blur-in.tsx +26 -0
  232. package/templates/shared/src/components/ui/anims/counter.tsx +53 -0
  233. package/templates/shared/src/components/ui/anims/fade.tsx +83 -0
  234. package/templates/shared/src/components/ui/anims/gradual-spacing.tsx +39 -0
  235. package/templates/shared/src/components/ui/anims/pull-up.tsx +93 -0
  236. package/templates/shared/src/components/ui/anims/reveal-text.tsx +76 -0
  237. package/templates/shared/src/components/ui/anims/rotate-words.tsx +43 -0
  238. package/templates/shared/src/components/ui/anims/shiny-text.tsx +58 -0
  239. package/templates/shared/src/components/ui/anims/shuffle-text.tsx +85 -0
  240. package/templates/shared/src/components/ui/anims/split-text.tsx +211 -0
  241. package/templates/shared/src/components/ui/anims/staggered-fade.tsx +133 -0
  242. package/templates/shared/src/components/ui/anims/text-scroll.tsx +111 -0
  243. package/templates/shared/src/components/ui/anims/typing-effect.tsx +112 -0
  244. package/templates/shared/src/components/ui/button-dusted-fluid/index.tsx +1521 -0
  245. package/templates/shared/src/components/ui/separator.tsx +85 -0
  246. package/templates/shared/src/hooks/useBodyScrollLock.ts +97 -0
  247. package/templates/shared/src/hooks/useClickOutside.ts +28 -0
  248. package/templates/shared/src/utilities/renderCustomCSS.ts +5 -0
  249. package/templates/shared/src/utilities/smoothScroll.ts +65 -0
@@ -1,307 +1,307 @@
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({ images, speed, initialOffset, priority = false }: ScrollColumnProps) {
38
- const colRef = useRef<HTMLDivElement>(null)
39
- const posRef = useRef(initialOffset)
40
- const rafRef = useRef<number | null>(null)
41
- const [mounted, setMounted] = useState(false)
42
-
43
- const resolveSrc = (item: ColumnImage): string | null => {
44
- if (item.image && typeof item.image === 'object') {
45
- const media = item.image as Media
46
- const url = media.url
47
-
48
- if (!url) return isLocalImageSrc(item.imageUrl) ? item.imageUrl : null
49
-
50
- return isLocalImageSrc(url) ? url : null
51
- }
52
-
53
- return isLocalImageSrc(item.imageUrl) ? item.imageUrl : null
54
- }
55
-
56
- const validImages = useMemo(() => {
57
- return (images ?? []).filter((item) => resolveSrc(item) !== null)
58
- }, [images])
59
-
60
- useEffect(() => {
61
- setMounted(true)
62
- }, [])
63
-
64
- useEffect(() => {
65
- if (!mounted) return
66
-
67
- const el = colRef.current
68
- if (!el) return
69
-
70
- const animate = () => {
71
- posRef.current -= speed
72
-
73
- const totalHeight = el.scrollHeight / 2
74
-
75
- if (totalHeight > 0 && Math.abs(posRef.current) >= totalHeight) {
76
- posRef.current = 0
77
- }
78
-
79
- el.style.transform = `translateY(${posRef.current}px)`
80
- rafRef.current = requestAnimationFrame(animate)
81
- }
82
-
83
- const timer = window.setTimeout(() => {
84
- rafRef.current = requestAnimationFrame(animate)
85
- }, 1200)
86
-
87
- return () => {
88
- window.clearTimeout(timer)
89
-
90
- if (rafRef.current) {
91
- cancelAnimationFrame(rafRef.current)
92
- rafRef.current = null
93
- }
94
- }
95
- }, [mounted, speed])
96
-
97
- if (validImages.length === 0) return null
98
-
99
- const renderImages = mounted ? [...validImages, ...validImages] : validImages
100
-
101
- return (
102
- <div className="flex flex-col gap-4 will-change-transform" ref={colRef}>
103
- {renderImages.map((item, i) => {
104
- const src = resolveSrc(item)
105
- if (!src) return null
106
-
107
- const isClone = i >= validImages.length
108
- const shouldPrioritize = Boolean(priority) && !isClone
109
-
110
- return (
111
- <div
112
- key={`${item.id ?? 'image'}-${i}`}
113
- className="relative aspect-4/3 w-full shrink-0 overflow-hidden rounded-lg border border-white/6"
114
- >
115
- <Image
116
- src={src}
117
- alt={item.alt ?? ''}
118
- fill
119
- sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw"
120
- className="object-cover object-top"
121
- priority={shouldPrioritize}
122
- loading="eager"
123
- // loading={shouldPrioritize ? 'eager' : 'lazy'}
124
- fetchPriority={shouldPrioritize ? 'high' : 'auto'}
125
- />
126
- </div>
127
- )
128
- })}
129
- </div>
130
- )
131
- }
132
-
133
- export const DDShowcaseGrid: React.FC<DDShowcaseGridProps> = ({
134
- columns,
135
- height = 520,
136
- columnHeight = 3200,
137
- sectionId,
138
- customCSS,
139
- priority = false,
140
- stickToTop = false,
141
- fadeOutOnScroll = false,
142
- }) => {
143
- const sectionHeight = height ?? 520
144
- const colHeight = columnHeight ?? 3200
145
- const shouldStick = Boolean(stickToTop)
146
- const shouldFade = Boolean(fadeOutOnScroll)
147
- const columnItems = (columns ?? []) as ShowcaseColumn[]
148
- const wrapperRef = useRef<HTMLDivElement | null>(null)
149
- const stickyRef = useRef<HTMLDivElement | null>(null)
150
- const fadeDistance = Math.max(1, sectionHeight)
151
-
152
- useEffect(() => {
153
- if (!shouldFade) {
154
- if (stickyRef.current) stickyRef.current.style.opacity = '1'
155
- return
156
- }
157
-
158
- let rafId = 0
159
-
160
- const updateOpacity = () => {
161
- if (rafId) window.cancelAnimationFrame(rafId)
162
-
163
- rafId = window.requestAnimationFrame(() => {
164
- const wrapper = wrapperRef.current
165
- const sticky = stickyRef.current
166
-
167
- if (!wrapper || !sticky) return
168
-
169
- const rect = wrapper.getBoundingClientRect()
170
- const progress = Math.min(1, Math.max(0, -rect.top / fadeDistance))
171
-
172
- sticky.style.opacity = String(1 - progress)
173
- })
174
- }
175
-
176
- updateOpacity()
177
-
178
- window.addEventListener('scroll', updateOpacity, { passive: true })
179
- window.addEventListener('resize', updateOpacity)
180
- window.addEventListener('locomotive:scroll', updateOpacity)
181
- window.addEventListener('locomotive:resize', updateOpacity)
182
-
183
- return () => {
184
- window.removeEventListener('scroll', updateOpacity)
185
- window.removeEventListener('resize', updateOpacity)
186
- window.removeEventListener('locomotive:scroll', updateOpacity)
187
- window.removeEventListener('locomotive:resize', updateOpacity)
188
-
189
- if (rafId) {
190
- window.cancelAnimationFrame(rafId)
191
- }
192
- }
193
- }, [fadeDistance, shouldFade])
194
-
195
- return (
196
- <>
197
- <style>{`
198
- .studio-showcase-grid {
199
- --sg-perspective: 900px;
200
- --sg-perspective-origin: 50% 40%;
201
- --sg-transform: rotateX(52deg) rotateZ(-42deg);
202
- --sg-top: -160%;
203
- --sg-bottom: -90%;
204
- --sg-sides: -30%;
205
- }
206
-
207
- @media (max-width: 1023px) {
208
- .studio-showcase-grid {
209
- --sg-perspective-origin: 50% 40%;
210
- --sg-transform: rotateX(52deg) rotateZ(-42deg);
211
- --sg-top: -90%;
212
- --sg-bottom: -30%;
213
- --sg-sides: -50%;
214
- max-height: 420px;
215
- }
216
- }
217
-
218
- @media (max-width: 639px) {
219
- .studio-showcase-grid {
220
- --sg-perspective-origin: 50% 40%;
221
- --sg-transform: rotateX(52deg) rotateZ(-42deg);
222
- --sg-top: -60%;
223
- --sg-bottom: 0%;
224
- --sg-sides: -90%;
225
- max-height: 300px;
226
- }
227
- }
228
- `}</style>
229
-
230
- {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
231
-
232
- <div
233
- data-content-type="ddShowcaseGrid"
234
- ref={wrapperRef}
235
- className={cn('relative overflow-clip', shouldStick || shouldFade ? 'z-0' : '')}
236
- style={{
237
- height: shouldStick || shouldFade ? `${sectionHeight + fadeDistance}px` : undefined,
238
- }}
239
- >
240
- <div
241
- ref={stickyRef}
242
- className={cn(
243
- 'relative will-change-opacity',
244
- shouldStick || shouldFade ? 'sticky top-0' : '',
245
- )}
246
- >
247
- <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" />
248
- <div
249
- id={sectionId ?? undefined}
250
- className="studio-showcase-grid relative z-0! overflow-hidden"
251
- style={{ height: `${sectionHeight}px` }}
252
- >
253
- #BlockName-DDShowcaseGrid
254
- <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]" />
255
- <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]" />
256
- <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]" />
257
- <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]" />
258
-
259
- <div
260
- className="absolute inset-0"
261
- style={{
262
- perspective: 'var(--sg-perspective)',
263
- perspectiveOrigin: 'var(--sg-perspective-origin)',
264
- }}
265
- >
266
- <div
267
- style={{
268
- transform: 'var(--sg-transform)',
269
- transformOrigin: 'center center',
270
- position: 'absolute',
271
- top: 'var(--sg-top)',
272
- left: 'var(--sg-sides)',
273
- right: 'var(--sg-sides)',
274
- bottom: 'var(--sg-bottom)',
275
- display: 'flex',
276
- gap: '16px',
277
- alignItems: 'flex-start',
278
- }}
279
- >
280
- {columnItems.map((col, colIndex) => (
281
- <div
282
- key={col.id ?? colIndex}
283
- className="studio-showcase-col"
284
- style={{
285
- flex: '1',
286
- overflow: 'hidden',
287
- height: `${colHeight}px`,
288
- }}
289
- >
290
- <ScrollColumn
291
- images={col.images ?? []}
292
- speed={col.speed ?? 0.5}
293
- initialOffset={col.initialOffset ?? 0}
294
- priority={Boolean(priority)}
295
- />
296
- </div>
297
- ))}
298
- </div>
299
- </div>
300
- </div>
301
- </div>
302
- </div>
303
- </>
304
- )
305
- }
306
-
307
- export default DDShowcaseGrid
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({ images, speed, initialOffset, priority = false }: ScrollColumnProps) {
38
+ const colRef = useRef<HTMLDivElement>(null)
39
+ const posRef = useRef(initialOffset)
40
+ const rafRef = useRef<number | null>(null)
41
+ const [mounted, setMounted] = useState(false)
42
+
43
+ const resolveSrc = (item: ColumnImage): string | null => {
44
+ if (item.image && typeof item.image === 'object') {
45
+ const media = item.image as Media
46
+ const url = media.url
47
+
48
+ if (!url) return isLocalImageSrc(item.imageUrl) ? item.imageUrl : null
49
+
50
+ return isLocalImageSrc(url) ? url : null
51
+ }
52
+
53
+ return isLocalImageSrc(item.imageUrl) ? item.imageUrl : null
54
+ }
55
+
56
+ const validImages = useMemo(() => {
57
+ return (images ?? []).filter((item) => resolveSrc(item) !== null)
58
+ }, [images])
59
+
60
+ useEffect(() => {
61
+ setMounted(true)
62
+ }, [])
63
+
64
+ useEffect(() => {
65
+ if (!mounted) return
66
+
67
+ const el = colRef.current
68
+ if (!el) return
69
+
70
+ const animate = () => {
71
+ posRef.current -= speed
72
+
73
+ const totalHeight = el.scrollHeight / 2
74
+
75
+ if (totalHeight > 0 && Math.abs(posRef.current) >= totalHeight) {
76
+ posRef.current = 0
77
+ }
78
+
79
+ el.style.transform = `translateY(${posRef.current}px)`
80
+ rafRef.current = requestAnimationFrame(animate)
81
+ }
82
+
83
+ const timer = window.setTimeout(() => {
84
+ rafRef.current = requestAnimationFrame(animate)
85
+ }, 1200)
86
+
87
+ return () => {
88
+ window.clearTimeout(timer)
89
+
90
+ if (rafRef.current) {
91
+ cancelAnimationFrame(rafRef.current)
92
+ rafRef.current = null
93
+ }
94
+ }
95
+ }, [mounted, speed])
96
+
97
+ if (validImages.length === 0) return null
98
+
99
+ const renderImages = mounted ? [...validImages, ...validImages] : validImages
100
+
101
+ return (
102
+ <div className="flex flex-col gap-4 will-change-transform" ref={colRef}>
103
+ {renderImages.map((item, i) => {
104
+ const src = resolveSrc(item)
105
+ if (!src) return null
106
+
107
+ const isClone = i >= validImages.length
108
+ const shouldPrioritize = Boolean(priority) && !isClone
109
+
110
+ return (
111
+ <div
112
+ key={`${item.id ?? 'image'}-${i}`}
113
+ className="relative aspect-4/3 w-full shrink-0 overflow-hidden rounded-lg border border-white/6"
114
+ >
115
+ <Image
116
+ src={src}
117
+ alt={item.alt ?? ''}
118
+ fill
119
+ sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, 25vw"
120
+ className="object-cover object-top"
121
+ priority={shouldPrioritize}
122
+ loading="eager"
123
+ // loading={shouldPrioritize ? 'eager' : 'lazy'}
124
+ fetchPriority={shouldPrioritize ? 'high' : 'auto'}
125
+ />
126
+ </div>
127
+ )
128
+ })}
129
+ </div>
130
+ )
131
+ }
132
+
133
+ export const DDShowcaseGrid: React.FC<DDShowcaseGridProps> = ({
134
+ columns,
135
+ height = 520,
136
+ columnHeight = 3200,
137
+ sectionId,
138
+ customCSS,
139
+ priority = false,
140
+ stickToTop = false,
141
+ fadeOutOnScroll = false,
142
+ }) => {
143
+ const sectionHeight = height ?? 520
144
+ const colHeight = columnHeight ?? 3200
145
+ const shouldStick = Boolean(stickToTop)
146
+ const shouldFade = Boolean(fadeOutOnScroll)
147
+ const columnItems = (columns ?? []) as ShowcaseColumn[]
148
+ const wrapperRef = useRef<HTMLDivElement | null>(null)
149
+ const stickyRef = useRef<HTMLDivElement | null>(null)
150
+ const fadeDistance = Math.max(1, sectionHeight)
151
+
152
+ useEffect(() => {
153
+ if (!shouldFade) {
154
+ if (stickyRef.current) stickyRef.current.style.opacity = '1'
155
+ return
156
+ }
157
+
158
+ let rafId = 0
159
+
160
+ const updateOpacity = () => {
161
+ if (rafId) window.cancelAnimationFrame(rafId)
162
+
163
+ rafId = window.requestAnimationFrame(() => {
164
+ const wrapper = wrapperRef.current
165
+ const sticky = stickyRef.current
166
+
167
+ if (!wrapper || !sticky) return
168
+
169
+ const rect = wrapper.getBoundingClientRect()
170
+ const progress = Math.min(1, Math.max(0, -rect.top / fadeDistance))
171
+
172
+ sticky.style.opacity = String(1 - progress)
173
+ })
174
+ }
175
+
176
+ updateOpacity()
177
+
178
+ window.addEventListener('scroll', updateOpacity, { passive: true })
179
+ window.addEventListener('resize', updateOpacity)
180
+ window.addEventListener('locomotive:scroll', updateOpacity)
181
+ window.addEventListener('locomotive:resize', updateOpacity)
182
+
183
+ return () => {
184
+ window.removeEventListener('scroll', updateOpacity)
185
+ window.removeEventListener('resize', updateOpacity)
186
+ window.removeEventListener('locomotive:scroll', updateOpacity)
187
+ window.removeEventListener('locomotive:resize', updateOpacity)
188
+
189
+ if (rafId) {
190
+ window.cancelAnimationFrame(rafId)
191
+ }
192
+ }
193
+ }, [fadeDistance, shouldFade])
194
+
195
+ return (
196
+ <>
197
+ <style>{`
198
+ .studio-showcase-grid {
199
+ --sg-perspective: 900px;
200
+ --sg-perspective-origin: 50% 40%;
201
+ --sg-transform: rotateX(52deg) rotateZ(-42deg);
202
+ --sg-top: -160%;
203
+ --sg-bottom: -90%;
204
+ --sg-sides: -30%;
205
+ }
206
+
207
+ @media (max-width: 1023px) {
208
+ .studio-showcase-grid {
209
+ --sg-perspective-origin: 50% 40%;
210
+ --sg-transform: rotateX(52deg) rotateZ(-42deg);
211
+ --sg-top: -90%;
212
+ --sg-bottom: -30%;
213
+ --sg-sides: -50%;
214
+ max-height: 420px;
215
+ }
216
+ }
217
+
218
+ @media (max-width: 639px) {
219
+ .studio-showcase-grid {
220
+ --sg-perspective-origin: 50% 40%;
221
+ --sg-transform: rotateX(52deg) rotateZ(-42deg);
222
+ --sg-top: -60%;
223
+ --sg-bottom: 0%;
224
+ --sg-sides: -90%;
225
+ max-height: 300px;
226
+ }
227
+ }
228
+ `}</style>
229
+
230
+ {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
231
+
232
+ <div
233
+ data-content-type="ddShowcaseGrid"
234
+ ref={wrapperRef}
235
+ className={cn('relative overflow-clip', shouldStick || shouldFade ? 'z-0' : '')}
236
+ style={{
237
+ height: shouldStick || shouldFade ? `${sectionHeight + fadeDistance}px` : undefined,
238
+ }}
239
+ >
240
+ <div
241
+ ref={stickyRef}
242
+ className={cn(
243
+ 'relative will-change-opacity',
244
+ shouldStick || shouldFade ? 'sticky top-0' : '',
245
+ )}
246
+ >
247
+ <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" />
248
+ <div
249
+ id={sectionId ?? undefined}
250
+ className="studio-showcase-grid relative z-0! overflow-hidden"
251
+ style={{ height: `${sectionHeight}px` }}
252
+ >
253
+ #BlockName-DDShowcaseGrid
254
+ <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]" />
255
+ <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]" />
256
+ <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]" />
257
+ <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]" />
258
+
259
+ <div
260
+ className="absolute inset-0"
261
+ style={{
262
+ perspective: 'var(--sg-perspective)',
263
+ perspectiveOrigin: 'var(--sg-perspective-origin)',
264
+ }}
265
+ >
266
+ <div
267
+ style={{
268
+ transform: 'var(--sg-transform)',
269
+ transformOrigin: 'center center',
270
+ position: 'absolute',
271
+ top: 'var(--sg-top)',
272
+ left: 'var(--sg-sides)',
273
+ right: 'var(--sg-sides)',
274
+ bottom: 'var(--sg-bottom)',
275
+ display: 'flex',
276
+ gap: '16px',
277
+ alignItems: 'flex-start',
278
+ }}
279
+ >
280
+ {columnItems.map((col, colIndex) => (
281
+ <div
282
+ key={col.id ?? colIndex}
283
+ className="studio-showcase-col"
284
+ style={{
285
+ flex: '1',
286
+ overflow: 'hidden',
287
+ height: `${colHeight}px`,
288
+ }}
289
+ >
290
+ <ScrollColumn
291
+ images={col.images ?? []}
292
+ speed={col.speed ?? 0.5}
293
+ initialOffset={col.initialOffset ?? 0}
294
+ priority={Boolean(priority)}
295
+ />
296
+ </div>
297
+ ))}
298
+ </div>
299
+ </div>
300
+ </div>
301
+ </div>
302
+ </div>
303
+ </>
304
+ )
305
+ }
306
+
307
+ export default DDShowcaseGrid
@@ -128,7 +128,7 @@ export const DDSliderA: React.FC<DDSliderAProps> = ({
128
128
  />
129
129
  </>
130
130
  ) : (
131
- <div className="w-full h-full bg-gradient-to-br from-violet-950 to-indigo-950" />
131
+ <div className="w-full h-full bg-linear-to-br from-violet-950 to-indigo-950" />
132
132
  )}
133
133
 
134
134
  {/* Gradient overlay */}
@@ -336,7 +336,7 @@ export function DDStackCards(props: DDStackCardsProps) {
336
336
  <div>
337
337
  {card.number && (
338
338
  <div
339
- className="mb-0.5 text-[10px] font-black uppercase tracking-widest md:text-xs"
339
+ className="mb-0.5 text-xs font-black uppercase tracking-widest md:text-xs"
340
340
  style={{ color: `rgba(${accent.rgb},0.6)` }}
341
341
  >
342
342
  {card.number} / {String(totalCards).padStart(2, '0')}