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
@@ -0,0 +1,501 @@
1
+ 'use client'
2
+
3
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
+ import Link from 'next/link'
5
+ import {
6
+ Check,
7
+ DownloadCloud,
8
+ Home,
9
+ LayoutGrid,
10
+ MailCheck,
11
+ ShieldCheck,
12
+ Sparkles,
13
+ } from 'lucide-react'
14
+ import { cn } from '@/utilities/ui'
15
+
16
+ type InfoPill = {
17
+ id?: string | null
18
+ label?: string | null
19
+ icon?: 'mail' | 'download' | 'shield' | null
20
+ }
21
+
22
+ type CtaLink = {
23
+ label?: string | null
24
+ href?: string | null
25
+ variant?: 'primary' | 'secondary' | null
26
+ icon?: 'home' | 'grid' | 'download' | null
27
+ }
28
+
29
+ type ThanksConfettiProps = {
30
+ sectionId?: string | null
31
+ customCSS?: string | null
32
+ headline?: string | null
33
+ description?: string | null
34
+ subDescription?: string | null
35
+ replayLabel?: string | null
36
+ brandLabel?: string | null
37
+ showBrandLabel?: boolean | null
38
+ showReplayButton?: boolean | null
39
+ autoPlayConfetti?: boolean | null
40
+ confettiDuration?: number | null
41
+ infoPills?: InfoPill[] | null
42
+ ctaLinks?: CtaLink[] | null
43
+ className?: string
44
+ disableInnerContainer?: boolean
45
+ }
46
+
47
+ type ConfettiParticle = {
48
+ x: number
49
+ y: number
50
+ vx: number
51
+ vy: number
52
+ size: number
53
+ rotation: number
54
+ rotationSpeed: number
55
+ color: string
56
+ shape: 'circle' | 'square'
57
+ life: number
58
+ maxLife: number
59
+ }
60
+
61
+ const CONFETTI_COLORS = ['#d6d3d1', '#a8a29e', '#10b981', '#6ee7b7', '#ffffff', '#f5f5f4']
62
+
63
+ const defaultInfoPills: Required<Pick<InfoPill, 'label' | 'icon'>>[] = [
64
+ { icon: 'mail', label: 'Confirmation sent' },
65
+ { icon: 'download', label: 'Files ready' },
66
+ { icon: 'shield', label: 'License activated' },
67
+ ]
68
+
69
+ const defaultCtaLinks: Required<Pick<CtaLink, 'label' | 'href' | 'variant' | 'icon'>>[] = [
70
+ { label: 'Back to Home', href: '/', variant: 'primary', icon: 'home' },
71
+ { label: 'Continue Browsing', href: '/components', variant: 'secondary', icon: 'grid' },
72
+ ]
73
+
74
+ function PillIcon({ icon }: { icon?: InfoPill['icon'] }) {
75
+ if (icon === 'download') return <DownloadCloud className="h-3.5 w-3.5" />
76
+ if (icon === 'shield') return <ShieldCheck className="h-3.5 w-3.5" />
77
+
78
+ return <MailCheck className="h-3.5 w-3.5" />
79
+ }
80
+
81
+ function CtaIcon({ icon }: { icon?: CtaLink['icon'] }) {
82
+ if (icon === 'grid') return <LayoutGrid className="h-4 w-4" />
83
+ if (icon === 'download') return <DownloadCloud className="h-4 w-4" />
84
+
85
+ return <Home className="h-4 w-4" />
86
+ }
87
+
88
+ function getSafeDuration(duration?: number | null) {
89
+ if (typeof duration !== 'number' || !Number.isFinite(duration)) return 3000
90
+ return Math.min(Math.max(duration, 1000), 8000)
91
+ }
92
+
93
+ function createParticles({
94
+ amount,
95
+ originX,
96
+ originY,
97
+ angle,
98
+ spread,
99
+ velocity,
100
+ width,
101
+ height,
102
+ }: {
103
+ amount: number
104
+ originX: number
105
+ originY: number
106
+ angle?: number
107
+ spread: number
108
+ velocity: number
109
+ width: number
110
+ height: number
111
+ }): ConfettiParticle[] {
112
+ return Array.from({ length: amount }, () => {
113
+ const baseAngle = typeof angle === 'number' ? angle : Math.random() * 360
114
+ const randomAngle = (baseAngle - spread / 2 + Math.random() * spread) * (Math.PI / 180)
115
+ const speed = velocity * (0.45 + Math.random() * 0.75)
116
+
117
+ return {
118
+ x: originX * width,
119
+ y: originY * height,
120
+ vx: Math.cos(randomAngle) * speed,
121
+ vy: Math.sin(randomAngle) * speed,
122
+ size: 4 + Math.random() * 7,
123
+ rotation: Math.random() * Math.PI,
124
+ rotationSpeed: -0.22 + Math.random() * 0.44,
125
+ color: CONFETTI_COLORS[Math.floor(Math.random() * CONFETTI_COLORS.length)] ?? '#ffffff',
126
+ shape: Math.random() > 0.5 ? 'circle' : 'square',
127
+ life: 0,
128
+ maxLife: 90 + Math.random() * 70,
129
+ }
130
+ })
131
+ }
132
+
133
+ export function DDThanks({
134
+ sectionId,
135
+ headline = 'Thank you! 🎉',
136
+ description = 'Your order has been confirmed and is on its way.',
137
+ subDescription = "We've sent a confirmation email with your download links and license keys. Check your inbox — and maybe your spam folder too.",
138
+ replayLabel = 'Replay confetti',
139
+ brandLabel = 'DesignsDusted',
140
+ showBrandLabel = true,
141
+ showReplayButton = true,
142
+ autoPlayConfetti = true,
143
+ confettiDuration = 3000,
144
+ infoPills,
145
+ ctaLinks,
146
+ className,
147
+ }: ThanksConfettiProps) {
148
+ const hasRun = useRef(false)
149
+ const canvasRef = useRef<HTMLCanvasElement | null>(null)
150
+ const animationRef = useRef<number | null>(null)
151
+ const particlesRef = useRef<ConfettiParticle[]>([])
152
+ const [visible, setVisible] = useState(false)
153
+
154
+ const pills = useMemo(() => {
155
+ const source = infoPills?.length ? infoPills : defaultInfoPills
156
+ return source.filter((pill) => pill.label)
157
+ }, [infoPills])
158
+
159
+ const links = useMemo(() => {
160
+ const source = ctaLinks?.length ? ctaLinks : defaultCtaLinks
161
+ return source.filter((link) => link.label && link.href)
162
+ }, [ctaLinks])
163
+
164
+ const stopAnimation = useCallback(() => {
165
+ if (animationRef.current) {
166
+ cancelAnimationFrame(animationRef.current)
167
+ animationRef.current = null
168
+ }
169
+ }, [])
170
+
171
+ const fireConfetti = useCallback(() => {
172
+ const canvas = canvasRef.current
173
+ const ctx = canvas?.getContext('2d')
174
+ if (!canvas || !ctx) return
175
+
176
+ const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
177
+ if (reducedMotion) return
178
+
179
+ const pixelRatio = Math.min(window.devicePixelRatio || 1, 2)
180
+ const width = window.innerWidth
181
+ const height = window.innerHeight
182
+
183
+ canvas.width = width * pixelRatio
184
+ canvas.height = height * pixelRatio
185
+ canvas.style.width = `${width}px`
186
+ canvas.style.height = `${height}px`
187
+ ctx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0)
188
+
189
+ const duration = getSafeDuration(confettiDuration)
190
+ const end = performance.now() + duration
191
+
192
+ particlesRef.current = [
193
+ ...particlesRef.current,
194
+ ...createParticles({
195
+ amount: 120,
196
+ originX: 0.5,
197
+ originY: 0.5,
198
+ spread: 100,
199
+ velocity: 20,
200
+ width,
201
+ height,
202
+ }),
203
+ ...createParticles({
204
+ amount: 60,
205
+ originX: 0,
206
+ originY: 0.6,
207
+ angle: -35,
208
+ spread: 70,
209
+ velocity: 18,
210
+ width,
211
+ height,
212
+ }),
213
+ ...createParticles({
214
+ amount: 60,
215
+ originX: 1,
216
+ originY: 0.6,
217
+ angle: 215,
218
+ spread: 70,
219
+ velocity: 18,
220
+ width,
221
+ height,
222
+ }),
223
+ ]
224
+
225
+ stopAnimation()
226
+
227
+ const frame = (now: number) => {
228
+ ctx.clearRect(0, 0, width, height)
229
+
230
+ if (now < end && Math.random() > 0.35) {
231
+ particlesRef.current.push(
232
+ ...createParticles({
233
+ amount: 2,
234
+ originX: 0,
235
+ originY: 0.3,
236
+ angle: -25,
237
+ spread: 55,
238
+ velocity: 10,
239
+ width,
240
+ height,
241
+ }),
242
+ ...createParticles({
243
+ amount: 2,
244
+ originX: 1,
245
+ originY: 0.3,
246
+ angle: 205,
247
+ spread: 55,
248
+ velocity: 10,
249
+ width,
250
+ height,
251
+ }),
252
+ )
253
+ }
254
+
255
+ particlesRef.current = particlesRef.current.filter((particle) => {
256
+ particle.life += 1
257
+ particle.x += particle.vx
258
+ particle.y += particle.vy
259
+ particle.vy += 0.28
260
+ particle.vx *= 0.992
261
+ particle.rotation += particle.rotationSpeed
262
+
263
+ const opacity = Math.max(0, 1 - particle.life / particle.maxLife)
264
+ if (opacity <= 0 || particle.y > height + 40) return false
265
+
266
+ ctx.save()
267
+ ctx.globalAlpha = opacity
268
+ ctx.translate(particle.x, particle.y)
269
+ ctx.rotate(particle.rotation)
270
+ ctx.fillStyle = particle.color
271
+
272
+ if (particle.shape === 'circle') {
273
+ ctx.beginPath()
274
+ ctx.arc(0, 0, particle.size * 0.5, 0, Math.PI * 2)
275
+ ctx.fill()
276
+ } else {
277
+ ctx.fillRect(-particle.size / 2, -particle.size / 2, particle.size, particle.size)
278
+ }
279
+
280
+ ctx.restore()
281
+ return true
282
+ })
283
+
284
+ if (particlesRef.current.length || now < end) {
285
+ animationRef.current = requestAnimationFrame(frame)
286
+ } else {
287
+ ctx.clearRect(0, 0, width, height)
288
+ animationRef.current = null
289
+ }
290
+ }
291
+
292
+ animationRef.current = requestAnimationFrame(frame)
293
+ }, [confettiDuration, stopAnimation])
294
+
295
+ useEffect(() => {
296
+ if (hasRun.current) return
297
+ hasRun.current = true
298
+
299
+ const timer = window.setTimeout(() => {
300
+ setVisible(true)
301
+ if (autoPlayConfetti !== false) fireConfetti()
302
+ }, 200)
303
+
304
+ return () => window.clearTimeout(timer)
305
+ }, [autoPlayConfetti, fireConfetti])
306
+
307
+ useEffect(() => {
308
+ return () => {
309
+ stopAnimation()
310
+ particlesRef.current = []
311
+ }
312
+ }, [stopAnimation])
313
+
314
+ return (
315
+ <section
316
+ id={sectionId ?? undefined}
317
+ className={cn(
318
+ 'thanks-confetti-section relative flex min-h-screen w-full flex-col items-center justify-center overflow-hidden bg-stone-transparent dark:bg-stone-950 px-4 py-24 text-center hue-rotate-300 invert-100 dark:invert-0',
319
+ className,
320
+ )}
321
+ >
322
+ <canvas
323
+ ref={canvasRef}
324
+ aria-hidden="true"
325
+ className="pointer-events-none fixed inset-0 z-60 h-screen w-screen"
326
+ />
327
+
328
+ <div className="pointer-events-none absolute inset-0">
329
+ <div
330
+ className="absolute inset-0 opacity-20"
331
+ style={{
332
+ backgroundImage:
333
+ 'radial-gradient(circle at 20% 50%, rgba(16,185,129,0.12) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(255,255,255,0.05) 0%, transparent 50%)',
334
+ }}
335
+ />
336
+ <div
337
+ className="absolute inset-0 opacity-5"
338
+ style={{
339
+ backgroundImage:
340
+ 'linear-gradient(rgba(255,255,255,0.5) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.5) 1px, transparent 1px)',
341
+ backgroundSize: '60px 60px',
342
+ }}
343
+ />
344
+ </div>
345
+
346
+ <div
347
+ className="relative z-10 flex w-full max-w-lg flex-col items-center px-2"
348
+ style={{
349
+ opacity: visible ? 1 : 0,
350
+ transform: visible ? 'translateY(0) scale(1)' : 'translateY(32px) scale(0.96)',
351
+ transition:
352
+ 'opacity 0.7s cubic-bezier(0.34,1.56,0.64,1), transform 0.7s cubic-bezier(0.34,1.56,0.64,1)',
353
+ }}
354
+ >
355
+ <div className="relative mb-8">
356
+ <div
357
+ className="flex h-24 w-24 items-center justify-center rounded-full border border-emerald-500/10"
358
+ style={{
359
+ background: 'linear-gradient(135deg, #10b981 0%, #6ee7b7 100%)',
360
+ boxShadow: '0 0 60px rgba(16,185,129,0.35), 0 0 120px rgba(16,185,129,0.15)',
361
+ opacity: visible ? 1 : 0,
362
+ transform: visible ? 'scale(1)' : 'scale(0.5)',
363
+ transition:
364
+ 'opacity 0.5s ease 0.3s, transform 0.6s cubic-bezier(0.34,1.56,0.64,1) 0.3s',
365
+ }}
366
+ >
367
+ <Check className="h-11 w-11 stroke-[3px] text-emerald-500 dark:text-white" />
368
+ </div>
369
+ <div
370
+ className="absolute inset-0 rounded-full bg-emerald-500/20"
371
+ style={{
372
+ animation: visible
373
+ ? 'thanks-confetti-ping 2s cubic-bezier(0,0,0.2,1) infinite 0.8s'
374
+ : 'none',
375
+ }}
376
+ />
377
+ </div>
378
+
379
+ <div
380
+ style={{
381
+ opacity: visible ? 1 : 0,
382
+ transform: visible ? 'translateY(0)' : 'translateY(20px)',
383
+ transition: 'opacity 0.6s ease 0.5s, transform 0.6s ease 0.5s',
384
+ }}
385
+ >
386
+ {headline && (
387
+ <h1 className="mb-3 text-4xl font-bold tracking-tight text-white md:text-5xl">
388
+ {headline}
389
+ </h1>
390
+ )}
391
+
392
+ {description && (
393
+ <p className="mb-2 text-lg leading-relaxed text-stone-400">{description}</p>
394
+ )}
395
+
396
+ {subDescription && (
397
+ <p className="mb-10 text-sm leading-relaxed text-stone-500">{subDescription}</p>
398
+ )}
399
+ </div>
400
+
401
+ {pills.length > 0 && (
402
+ <div
403
+ className="mb-10 flex flex-wrap items-center justify-center gap-3"
404
+ style={{
405
+ opacity: visible ? 1 : 0,
406
+ transform: visible ? 'translateY(0)' : 'translateY(16px)',
407
+ transition: 'opacity 0.6s ease 0.7s, transform 0.6s ease 0.7s',
408
+ }}
409
+ >
410
+ {pills.map((item, index) => (
411
+ <div
412
+ key={
413
+ 'id' in item ? (item.id ?? `${item.label}-${index}`) : `${item.label}-${index}`
414
+ }
415
+ className="flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-4 py-2"
416
+ >
417
+ <span className="flex h-4 w-4 items-center justify-center text-emerald-400">
418
+ <PillIcon icon={item.icon} />
419
+ </span>
420
+ <span className="whitespace-nowrap text-sm text-stone-400">{item.label}</span>
421
+ </div>
422
+ ))}
423
+ </div>
424
+ )}
425
+
426
+ {links.length > 0 && (
427
+ <div
428
+ className="flex w-full flex-col gap-3 sm:flex-row"
429
+ style={{
430
+ opacity: visible ? 1 : 0,
431
+ transform: visible ? 'translateY(0)' : 'translateY(16px)',
432
+ transition: 'opacity 0.6s ease 0.9s, transform 0.6s ease 0.9s',
433
+ }}
434
+ >
435
+ {links.map((link, index) => {
436
+ const isPrimary =
437
+ (link.variant ?? (index === 0 ? 'primary' : 'secondary')) === 'primary'
438
+
439
+ return (
440
+ <Link
441
+ key={`${link.href}-${index}`}
442
+ href={link.href ?? '#'}
443
+ className={cn(
444
+ 'inline-flex flex-1 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-lg px-6 py-3.5 text-sm font-semibold transition-colors',
445
+ 'backdrop-blur-lg rounded-2xl squircle px-5 py-2.5 text-sm font-medium transition-all duration-300 0 hover:shadow-2xl hover:-translate-y-0.5 hover:transition-all hover:duration-300',
446
+ isPrimary
447
+ ? 'bg-white text-stone-900 hover:bg-stone-100'
448
+ : 'border border-white/15 bg-white/10 text-white hover:bg-white/20',
449
+ )}
450
+ >
451
+ <CtaIcon icon={link.icon} />
452
+ {link.label}
453
+ </Link>
454
+ )
455
+ })}
456
+ </div>
457
+ )}
458
+
459
+ {showReplayButton !== false && replayLabel && (
460
+ <div
461
+ className="mt-8"
462
+ style={{
463
+ opacity: visible ? 1 : 0,
464
+ transition: 'opacity 0.6s ease 1.1s',
465
+ }}
466
+ >
467
+ <button
468
+ type="button"
469
+ onClick={fireConfetti}
470
+ className="flex cursor-pointer items-center gap-1.5 text-xs text-stone-600 transition-colors hover:text-stone-400"
471
+ >
472
+ <Sparkles className="h-3 w-3" />
473
+ {replayLabel}
474
+ </button>
475
+ </div>
476
+ )}
477
+ </div>
478
+
479
+ {showBrandLabel !== false && brandLabel && (
480
+ <div
481
+ className="absolute bottom-8 left-1/2 z-10 -translate-x-1/2 text-xs font-black uppercase tracking-[0.35em] text-stone-500"
482
+ style={{
483
+ opacity: visible ? 0.4 : 0,
484
+ transition: 'opacity 0.6s ease 1.2s',
485
+ }}
486
+ >
487
+ {brandLabel}
488
+ </div>
489
+ )}
490
+
491
+ <style>{`
492
+ @keyframes thanks-confetti-ping {
493
+ 75%, 100% {
494
+ transform: scale(2);
495
+ opacity: 0;
496
+ }
497
+ }
498
+ `}</style>
499
+ </section>
500
+ )
501
+ }
@@ -0,0 +1,4 @@
1
+ # DD-Thanks
2
+
3
+ Portable first-pass conversion of `ThanksConfetti` from ROUND-TWO-CONVERSION. Project-specific roots and nested unconverted block dependencies were intentionally replaced with local rendering so the template can install into a clean Payload project.
4
+