blocks-dusted 0.1.10 → 0.1.11

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 (246) hide show
  1. package/README.md +35 -0
  2. package/package.json +1 -1
  3. package/src/utils/paths.js +13 -13
  4. package/templates/blocks/DD-Accordion/Component.tsx +229 -0
  5. package/templates/blocks/DD-Accordion/README.md +3 -0
  6. package/templates/blocks/DD-Accordion/config.ts +73 -0
  7. package/templates/blocks/DD-Accordion/manifest.json +75 -0
  8. package/templates/blocks/DD-AnimText/Component.tsx +788 -0
  9. package/templates/blocks/DD-AnimText/README.md +3 -0
  10. package/templates/blocks/DD-AnimText/config.ts +295 -0
  11. package/templates/blocks/DD-AnimText/manifest.json +85 -0
  12. package/templates/blocks/DD-AsideNav/Component.tsx +321 -0
  13. package/templates/blocks/DD-AsideNav/config.ts +204 -0
  14. package/templates/blocks/DD-AsideNav/manifest.json +80 -0
  15. package/templates/blocks/DD-Banner/Component.tsx +51 -0
  16. package/templates/blocks/DD-Banner/config.ts +57 -0
  17. package/templates/blocks/DD-Banner/manifest.json +72 -0
  18. package/templates/blocks/DD-BgLightning/Component.tsx +44 -0
  19. package/templates/blocks/DD-BgLightning/README.md +3 -0
  20. package/templates/blocks/DD-BgLightning/config.ts +97 -0
  21. package/templates/blocks/DD-BgLightning/manifest.json +79 -0
  22. package/templates/blocks/DD-BookCall/Component.tsx +10 -11
  23. package/templates/blocks/DD-BookCall/config.ts +238 -238
  24. package/templates/blocks/DD-ButtonPop/Component.tsx +117 -0
  25. package/templates/blocks/DD-ButtonPop/config.ts +105 -0
  26. package/templates/blocks/DD-ButtonPop/manifest.json +88 -0
  27. package/templates/blocks/DD-ButtonPop/svg-icons/Dust2.tsx +167 -0
  28. package/templates/blocks/DD-Card/Component.tsx +251 -0
  29. package/templates/blocks/DD-Card/README.md +4 -0
  30. package/templates/blocks/DD-Card/config.ts +154 -0
  31. package/templates/blocks/DD-Card/manifest.json +82 -0
  32. package/templates/blocks/DD-CardTemplate01/Component.tsx +5 -6
  33. package/templates/blocks/DD-CardTemplate01/config.ts +106 -106
  34. package/templates/blocks/DD-Carousel-A/Component.tsx +249 -249
  35. package/templates/blocks/DD-Carousel-B/Component.tsx +403 -403
  36. package/templates/blocks/DD-Chip/Component.tsx +250 -0
  37. package/templates/blocks/DD-Chip/EXAMPLES.md +313 -0
  38. package/templates/blocks/DD-Chip/README.md +202 -0
  39. package/templates/blocks/DD-Chip/config.ts +369 -0
  40. package/templates/blocks/DD-Chip/manifest.json +74 -0
  41. package/templates/blocks/DD-ComparisonTable/Component.tsx +256 -256
  42. package/templates/blocks/DD-Contact/Component.tsx +434 -434
  43. package/templates/blocks/DD-Contact/config.ts +22 -22
  44. package/templates/blocks/DD-Counter/Component.tsx +190 -0
  45. package/templates/blocks/DD-Counter/README.md +4 -0
  46. package/templates/blocks/DD-Counter/config.ts +291 -0
  47. package/templates/blocks/DD-Counter/manifest.json +74 -0
  48. package/templates/blocks/DD-CustomCSS/Component.tsx +8 -0
  49. package/templates/blocks/DD-CustomCSS/README.md +4 -0
  50. package/templates/blocks/DD-CustomCSS/addToLayout.md +5 -0
  51. package/templates/blocks/DD-CustomCSS/config.ts +22 -0
  52. package/templates/blocks/DD-CustomCSS/manifest.json +72 -0
  53. package/templates/blocks/DD-CustomCSS/renderCustomCSS.md +7 -0
  54. package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
  55. package/templates/blocks/DD-FeatCat/config.ts +201 -201
  56. package/templates/blocks/DD-FeatStrip/Component.tsx +585 -585
  57. package/templates/blocks/DD-FeatStrip/config.ts +134 -134
  58. package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +628 -0
  59. package/templates/blocks/DD-FluidBtn/Component.tsx +43 -0
  60. package/templates/blocks/DD-FluidBtn/README.md +6 -0
  61. package/templates/blocks/DD-FluidBtn/config.ts +102 -0
  62. package/templates/blocks/DD-FluidBtn/manifest.json +86 -0
  63. package/templates/blocks/DD-GooeyInfo/Component.tsx +149 -0
  64. package/templates/blocks/DD-GooeyInfo/config.ts +135 -0
  65. package/templates/blocks/DD-GooeyInfo/manifest.json +72 -0
  66. package/templates/blocks/DD-Hero/Component.tsx +40 -17
  67. package/templates/blocks/DD-Hero/config.ts +267 -267
  68. package/templates/blocks/DD-HorizontalScroll/Component.tsx +4 -2
  69. package/templates/blocks/DD-HorizontalScroll/config.ts +231 -231
  70. package/templates/blocks/DD-Icon/Component.tsx +287 -0
  71. package/templates/blocks/DD-Icon/README.md +4 -0
  72. package/templates/blocks/DD-Icon/config.ts +258 -0
  73. package/templates/blocks/DD-Icon/hooks/revalidateBlockIds.ts +66 -0
  74. package/templates/blocks/DD-Icon/manifest.json +82 -0
  75. package/templates/blocks/DD-IconButton/Component.tsx +455 -0
  76. package/templates/blocks/DD-IconButton/README.md +326 -0
  77. package/templates/blocks/DD-IconButton/config.ts +239 -0
  78. package/templates/blocks/DD-IconButton/manifest.json +85 -0
  79. package/templates/blocks/DD-IdTag/Component.tsx +21 -0
  80. package/templates/blocks/DD-IdTag/README.md +4 -0
  81. package/templates/blocks/DD-IdTag/config.ts +41 -0
  82. package/templates/blocks/DD-IdTag/manifest.json +72 -0
  83. package/templates/blocks/DD-Iframe/Component.tsx +45 -0
  84. package/templates/blocks/DD-Iframe/README.md +4 -0
  85. package/templates/blocks/DD-Iframe/config.ts +47 -0
  86. package/templates/blocks/DD-Iframe/manifest.json +72 -0
  87. package/templates/blocks/DD-IframeMedia/Component.tsx +129 -0
  88. package/templates/blocks/DD-IframeMedia/README.md +4 -0
  89. package/templates/blocks/DD-IframeMedia/config.ts +79 -0
  90. package/templates/blocks/DD-IframeMedia/manifest.json +72 -0
  91. package/templates/blocks/DD-LineAnim/Component.tsx +36 -0
  92. package/templates/blocks/DD-LineAnim/README.md +4 -0
  93. package/templates/blocks/DD-LineAnim/config.ts +90 -0
  94. package/templates/blocks/DD-LineAnim/manifest.json +80 -0
  95. package/templates/blocks/DD-LinkGrid/Component.tsx +119 -0
  96. package/templates/blocks/DD-LinkGrid/README.md +4 -0
  97. package/templates/blocks/DD-LinkGrid/config.ts +51 -0
  98. package/templates/blocks/DD-LinkGrid/manifest.json +74 -0
  99. package/templates/blocks/DD-LogoMarq/Component.tsx +132 -0
  100. package/templates/blocks/DD-LogoMarq/README.md +4 -0
  101. package/templates/blocks/DD-LogoMarq/config.ts +264 -0
  102. package/templates/blocks/DD-LogoMarq/manifest.json +82 -0
  103. package/templates/blocks/DD-Lottie/Component.tsx +459 -0
  104. package/templates/blocks/DD-Lottie/README.md +4 -0
  105. package/templates/blocks/DD-Lottie/config.ts +379 -0
  106. package/templates/blocks/DD-Lottie/index.tsx +28 -0
  107. package/templates/blocks/DD-Lottie/manifest.json +83 -0
  108. package/templates/blocks/DD-MagicBento/Component.tsx +777 -0
  109. package/templates/blocks/DD-MagicBento/README.md +4 -0
  110. package/templates/blocks/DD-MagicBento/config.ts +264 -0
  111. package/templates/blocks/DD-MagicBento/manifest.json +74 -0
  112. package/templates/blocks/DD-MasonaryMedia/Component.tsx +547 -545
  113. package/templates/blocks/DD-MasonaryMedia/config.ts +2 -2
  114. package/templates/blocks/DD-MasonaryMedia/vendor.d.ts +2 -2
  115. package/templates/blocks/DD-Masonry/Component.tsx +645 -0
  116. package/templates/blocks/DD-Masonry/README.md +4 -0
  117. package/templates/blocks/DD-Masonry/config.ts +475 -0
  118. package/templates/blocks/DD-Masonry/manifest.json +82 -0
  119. package/templates/blocks/DD-Pricing/Component.tsx +372 -372
  120. package/templates/blocks/DD-Process/Component.tsx +149 -149
  121. package/templates/blocks/DD-Process/config.ts +150 -150
  122. package/templates/blocks/DD-Progress/Component.tsx +141 -0
  123. package/templates/blocks/DD-Progress/README.md +4 -0
  124. package/templates/blocks/DD-Progress/config.ts +175 -0
  125. package/templates/blocks/DD-Progress/manifest.json +74 -0
  126. package/templates/blocks/DD-ProjHeader/Component.tsx +91 -0
  127. package/templates/blocks/DD-ProjHeader/README.md +4 -0
  128. package/templates/blocks/DD-ProjHeader/config.ts +96 -0
  129. package/templates/blocks/DD-ProjHeader/manifest.json +72 -0
  130. package/templates/blocks/DD-ProjSlider/Component.tsx +123 -0
  131. package/templates/blocks/DD-ProjSlider/README.md +4 -0
  132. package/templates/blocks/DD-ProjSlider/config.ts +64 -0
  133. package/templates/blocks/DD-ProjSlider/manifest.json +72 -0
  134. package/templates/blocks/DD-ScrollText/Component.tsx +114 -0
  135. package/templates/blocks/DD-ScrollText/README.md +4 -0
  136. package/templates/blocks/DD-ScrollText/config.ts +58 -0
  137. package/templates/blocks/DD-ScrollText/manifest.json +72 -0
  138. package/templates/blocks/DD-Section/Component.tsx +266 -266
  139. package/templates/blocks/DD-Separator/Component.tsx +66 -0
  140. package/templates/blocks/DD-Separator/README.md +4 -0
  141. package/templates/blocks/DD-Separator/config.ts +58 -0
  142. package/templates/blocks/DD-Separator/manifest.json +72 -0
  143. package/templates/blocks/DD-Services/Component.tsx +176 -176
  144. package/templates/blocks/DD-Services/config.ts +149 -149
  145. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +307 -307
  146. package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
  147. package/templates/blocks/DD-StackCards/Component.tsx +473 -473
  148. package/templates/blocks/DD-StackCards/config.ts +399 -399
  149. package/templates/blocks/DD-Team/Component.tsx +247 -247
  150. package/templates/blocks/DD-Team/config.ts +173 -173
  151. package/templates/blocks/DD-TeamCards/Component.tsx +251 -0
  152. package/templates/blocks/DD-TeamCards/README.md +4 -0
  153. package/templates/blocks/DD-TeamCards/config.ts +302 -0
  154. package/templates/blocks/DD-TeamCards/manifest.json +74 -0
  155. package/templates/blocks/DD-TechStack/Component.tsx +168 -162
  156. package/templates/blocks/DD-TechStack/config.ts +153 -153
  157. package/templates/blocks/DD-TestMarq/Component.tsx +132 -0
  158. package/templates/blocks/DD-TestMarq/README.md +4 -0
  159. package/templates/blocks/DD-TestMarq/RowLabel.tsx +9 -0
  160. package/templates/blocks/DD-TestMarq/config.ts +88 -0
  161. package/templates/blocks/DD-TestMarq/manifest.json +72 -0
  162. package/templates/blocks/DD-Testim/Component.tsx +247 -0
  163. package/templates/blocks/DD-Testim/README.md +4 -0
  164. package/templates/blocks/DD-Testim/config.ts +101 -0
  165. package/templates/blocks/DD-Testim/manifest.json +75 -0
  166. package/templates/blocks/DD-Testim2/Component.tsx +256 -0
  167. package/templates/blocks/DD-Testim2/README.md +4 -0
  168. package/templates/blocks/DD-Testim2/config.ts +100 -0
  169. package/templates/blocks/DD-Testim2/manifest.json +74 -0
  170. package/templates/blocks/DD-Testimonials/Component.tsx +147 -147
  171. package/templates/blocks/DD-Testimonials/config.ts +120 -120
  172. package/templates/blocks/DD-TextMarq/Component.tsx +157 -0
  173. package/templates/blocks/DD-TextMarq/README.md +4 -0
  174. package/templates/blocks/DD-TextMarq/config.ts +237 -0
  175. package/templates/blocks/DD-TextMarq/manifest.json +82 -0
  176. package/templates/blocks/DD-Thanks/Component.tsx +500 -0
  177. package/templates/blocks/DD-Thanks/README.md +4 -0
  178. package/templates/blocks/DD-Thanks/config.ts +224 -0
  179. package/templates/blocks/DD-Thanks/manifest.json +72 -0
  180. package/templates/blocks/DD-VertMarq/Component.tsx +154 -0
  181. package/templates/blocks/DD-VertMarq/README.md +4 -0
  182. package/templates/blocks/DD-VertMarq/config.ts +236 -0
  183. package/templates/blocks/DD-VertMarq/dist/index.css +6 -0
  184. package/templates/blocks/DD-VertMarq/dist/index.min.css +1 -0
  185. package/templates/blocks/DD-VertMarq/manifest.json +74 -0
  186. package/templates/blocks/DD-Work/Component.tsx +318 -315
  187. package/templates/blocks/DD-Work/config.ts +165 -165
  188. package/templates/blocks/DD-Work-B/Component.tsx +294 -294
  189. package/templates/blocks/DDHG-CTASection2/Component.tsx +185 -0
  190. package/templates/blocks/DDHG-CTASection2/config.ts +140 -0
  191. package/templates/blocks/DDHG-CTASection2/manifest.json +45 -0
  192. package/templates/blocks/DDHG-HighlightArc/Component.tsx +243 -0
  193. package/templates/blocks/DDHG-HighlightArc/config.ts +85 -0
  194. package/templates/blocks/DDHG-HighlightArc/manifest.json +46 -0
  195. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +93 -0
  196. package/templates/blocks/DDHG-LogoMarquee/config.ts +66 -0
  197. package/templates/blocks/DDHG-LogoMarquee/manifest.json +39 -0
  198. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +213 -0
  199. package/templates/blocks/DDHG-NavCodeGrid/config.ts +64 -0
  200. package/templates/blocks/DDHG-NavCodeGrid/manifest.json +45 -0
  201. package/templates/blocks/DDHG-ProcessSec/Component.tsx +575 -0
  202. package/templates/blocks/DDHG-ProcessSec/config.ts +170 -0
  203. package/templates/blocks/DDHG-ProcessSec/manifest.json +56 -0
  204. package/templates/blocks/DDHG-ServiceDetails/Component.tsx +164 -0
  205. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +394 -0
  206. package/templates/blocks/DDHG-ServiceDetails/config.ts +77 -0
  207. package/templates/blocks/DDHG-ServiceDetails/manifest.json +50 -0
  208. package/templates/blocks/DDHG-ServiceHero/Component.tsx +234 -0
  209. package/templates/blocks/DDHG-ServiceHero/config.ts +49 -0
  210. package/templates/blocks/DDHG-ServiceHero/manifest.json +26 -0
  211. package/templates/blocks/DDHG-ServiceOverview/Component.tsx +277 -0
  212. package/templates/blocks/DDHG-ServiceOverview/config.ts +64 -0
  213. package/templates/blocks/DDHG-ServiceOverview/manifest.json +44 -0
  214. package/templates/blocks/DDHG-StatsFAQ/Component.tsx +389 -0
  215. package/templates/blocks/DDHG-StatsFAQ/config.ts +204 -0
  216. package/templates/blocks/DDHG-StatsFAQ/manifest.json +45 -0
  217. package/templates/blocks/DDHG-TeamCards/Component.tsx +131 -0
  218. package/templates/blocks/DDHG-TeamCards/config.ts +48 -0
  219. package/templates/blocks/DDHG-TeamCards/manifest.json +29 -0
  220. package/templates/shared/src/components/BgLightning/index.tsx +242 -0
  221. package/templates/shared/src/components/ui/ShareButton/index.tsx +80 -0
  222. package/templates/shared/src/components/ui/anims/FadeOnScroll.tsx +48 -0
  223. package/templates/shared/src/components/ui/anims/FadeUpOnInView.tsx +63 -0
  224. package/templates/shared/src/components/ui/anims/GlareHover.tsx +111 -0
  225. package/templates/shared/src/components/ui/anims/LineAnims.tsx +81 -0
  226. package/templates/shared/src/components/ui/anims/TitleAnims01.tsx +54 -0
  227. package/templates/shared/src/components/ui/anims/animation-container.tsx +29 -0
  228. package/templates/shared/src/components/ui/anims/blur-in.tsx +26 -0
  229. package/templates/shared/src/components/ui/anims/counter.tsx +53 -0
  230. package/templates/shared/src/components/ui/anims/fade.tsx +83 -0
  231. package/templates/shared/src/components/ui/anims/gradual-spacing.tsx +39 -0
  232. package/templates/shared/src/components/ui/anims/pull-up.tsx +93 -0
  233. package/templates/shared/src/components/ui/anims/reveal-text.tsx +76 -0
  234. package/templates/shared/src/components/ui/anims/rotate-words.tsx +43 -0
  235. package/templates/shared/src/components/ui/anims/shiny-text.tsx +58 -0
  236. package/templates/shared/src/components/ui/anims/shuffle-text.tsx +85 -0
  237. package/templates/shared/src/components/ui/anims/split-text.tsx +211 -0
  238. package/templates/shared/src/components/ui/anims/staggered-fade.tsx +133 -0
  239. package/templates/shared/src/components/ui/anims/text-scroll.tsx +111 -0
  240. package/templates/shared/src/components/ui/anims/typing-effect.tsx +112 -0
  241. package/templates/shared/src/components/ui/button-dusted-fluid/index.tsx +1521 -0
  242. package/templates/shared/src/components/ui/separator.tsx +85 -0
  243. package/templates/shared/src/hooks/useBodyScrollLock.ts +97 -0
  244. package/templates/shared/src/hooks/useClickOutside.ts +28 -0
  245. package/templates/shared/src/utilities/renderCustomCSS.ts +5 -0
  246. 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/') || src.startsWith('/assets/') || src.startsWith('/logo') || src.startsWith('data:'))
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(46deg) rotateZ(-34deg);
202
+ --sg-top: -80%;
203
+ --sg-bottom: -80%;
204
+ --sg-sides: -18%;
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: -35%;
212
+ --sg-bottom: -35%;
213
+ --sg-sides: -25%;
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: -20%;
224
+ --sg-sides: -20%;
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
+
254
+ <div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-32 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-32 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