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,315 +1,318 @@
1
- /* eslint-disable @next/next/no-img-element */
2
- 'use client'
3
-
4
- import React, { useRef, useState } from 'react'
5
- import Image from 'next/image'
6
- import { BarChart2, ExternalLink } from 'lucide-react'
7
- import { motion, type Variants } from 'framer-motion'
8
-
9
- import { cn } from '@/utilities/ui'
10
- export type DDWorkProps = Record<string, any>
11
- type Media = { url?: string | null; alt?: string | null; [key: string]: any }
12
- type CategoryItem = { id?: string | null; label?: string | null }
13
- type TagItem = { id?: string | null; tag?: string | null }
14
- type ProjectItem = {
15
- id?: string | null
16
- title?: string | null
17
- category?: string | null
18
- description?: string | null
19
- highlight?: string | null
20
- year?: string | null
21
- image?: Media | string | null
22
- imageFallbackUrl?: string | null
23
- tags?: TagItem[] | null
24
- link?: string | null
25
- }
26
-
27
- function isLocalImageSrc(src?: string | null): src is string {
28
- return typeof src === 'string' && src.startsWith('/api/media/file/')
29
- }
30
-
31
- // ─── SpotlightCard ────────────────────────────────────────────────────────────
32
- interface SpotlightCardProps {
33
- children: React.ReactNode
34
- className?: string
35
- spotlightColor?: string
36
- }
37
-
38
- function SpotlightCard({
39
- children,
40
- className,
41
- spotlightColor = 'rgba(214,211,209,0.08)',
42
- }: SpotlightCardProps) {
43
- const cardRef = useRef<HTMLDivElement>(null)
44
- const [pos, setPos] = useState<{ x: number; y: number } | null>(null)
45
-
46
- const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
47
- if (!cardRef.current) return
48
-
49
- const rect = cardRef.current.getBoundingClientRect()
50
-
51
- setPos({
52
- x: e.clientX - rect.left,
53
- y: e.clientY - rect.top,
54
- })
55
- }
56
-
57
- const handleMouseLeave = () => setPos(null)
58
-
59
- return (
60
- <div
61
- ref={cardRef}
62
- className={cn('relative overflow-hidden', className)}
63
- onMouseMove={handleMouseMove}
64
- onMouseLeave={handleMouseLeave}
65
- >
66
- {pos && (
67
- <div
68
- className="pointer-events-none absolute inset-0 z-0 transition-opacity duration-300"
69
- style={{
70
- background: `radial-gradient(400px circle at ${pos.x}px ${pos.y}px, ${spotlightColor}, transparent 60%)`,
71
- }}
72
- />
73
- )}
74
-
75
- <div className="relative z-10">{children}</div>
76
- </div>
77
- )
78
- }
79
-
80
- // ─── Animation variants ───────────────────────────────────────────────────────
81
- const containerVariants: Variants = {
82
- hidden: {},
83
- show: {
84
- transition: {
85
- staggerChildren: 0.1,
86
- delayChildren: 0.05,
87
- },
88
- },
89
- }
90
-
91
- const itemVariants: Variants = {
92
- hidden: {
93
- opacity: 0,
94
- y: 24,
95
- },
96
- show: {
97
- opacity: 1,
98
- y: 0,
99
- transition: {
100
- duration: 0.45,
101
- ease: 'easeOut',
102
- },
103
- },
104
- }
105
-
106
- // ─── Component ────────────────────────────────────────────────────────────────
107
- export const DDWork: React.FC<DDWorkProps> = ({
108
- eyebrow,
109
- headlineMain,
110
- headlineAccent,
111
- categories,
112
- projects,
113
- sectionId,
114
- customCSS,
115
- }) => {
116
- const categoryItems = (categories ?? []) as CategoryItem[]
117
- const projectItems = (projects ?? []) as ProjectItem[]
118
- const allLabel = categoryItems[0]?.label ?? 'All'
119
- const [activeCategory, setActiveCategory] = useState(allLabel)
120
-
121
- const filtered =
122
- activeCategory === allLabel
123
- ? projectItems
124
- : projectItems.filter((p) => p.category === activeCategory)
125
-
126
- return (
127
- <>
128
- {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
129
-
130
- <div id={sectionId ?? 'work'} className="py-24">
131
- <div className="mx-auto max-w-6xl lg:max-w-7xl px-6">
132
- {/* Header */}#BlockName-DDWork
133
- <motion.div
134
- variants={containerVariants}
135
- initial="hidden"
136
- whileInView="show"
137
- viewport={{ once: false, amount: 0.2 }}
138
- className="mb-12 flex flex-col gap-6 overflow-hidden md:flex-row md:items-end md:justify-between px-0 md:px-4"
139
- >
140
- <motion.div variants={itemVariants}>
141
- {eyebrow && (
142
- <p className="top-caption mb-3 text-sm font-semibold uppercase tracking-widest text-stone-500 dark:text-stone-400">
143
- {eyebrow}
144
- </p>
145
- )}
146
-
147
- <h2 className="text-4xl font-bold leading-tight text-stone-900 dark:text-stone-50 md:text-5xl">
148
- {headlineMain}
149
- {headlineAccent && (
150
- <>
151
- <br />
152
- <span className="text-stone-600 dark:text-stone-400">{headlineAccent}</span>
153
- </>
154
- )}
155
- </h2>
156
- </motion.div>
157
-
158
- {/* Filter tabs */}
159
- {categoryItems.length > 1 && (
160
- <motion.div variants={itemVariants} className="flex flex-wrap gap-2 py-4">
161
- {categoryItems.map((cat, i) => {
162
- const categoryLabel = cat.label ?? ''
163
-
164
- return (
165
- <button
166
- key={cat.id ?? i}
167
- onClick={() => setActiveCategory(categoryLabel)}
168
- className={cn(
169
- 'cursor-pointer whitespace-nowrap rounded-full px-4 py-1.5 text-xs font-medium transition-all duration-300',
170
- activeCategory === categoryLabel
171
- ? 'border-indigo-500 bg-indigo-800 text-stone-200 shadow-lg shadow-blue-500/20'
172
- : 'border-stone-300 dark:border-stone-700 bg-stone-50/80 dark:bg-stone-900/80 text-stone-600 dark:text-stone-400 hover:border-stone-400 dark:hover:border-stone-500 hover:bg-stone-100 dark:hover:bg-stone-800 hover:text-stone-900 dark:hover:text-stone-50',
173
- )}
174
- >
175
- {categoryLabel}
176
- </button>
177
- )
178
- })}
179
- </motion.div>
180
- )}
181
- </motion.div>
182
-
183
- {/* Projects Grid */}
184
- <motion.div
185
- variants={containerVariants}
186
- initial="hidden"
187
- whileInView="show"
188
- viewport={{ once: false, amount: 0.1 }}
189
- className="grid grid-cols-1 gap-5 py-4 md:grid-cols-2 lg:grid-cols-3"
190
- >
191
- {filtered.map((project, i) => {
192
- const projectTitle = project.title ?? ''
193
- const projectDescription = project.description ?? ''
194
- const projectCategory = project.category ?? ''
195
- const projectYear = project.year ?? ''
196
- const projectHighlight = project.highlight ?? ''
197
- const projectLink = project.link ?? ''
198
- const imgSrc =
199
- project.image && typeof project.image === 'object'
200
- ? ((project.image as Media).url ?? project.imageFallbackUrl ?? undefined)
201
- : (project.imageFallbackUrl ?? undefined)
202
- const safeImgSrc = isLocalImageSrc(imgSrc) ? imgSrc : null
203
-
204
- const CardInner = (
205
- <SpotlightCard
206
- spotlightColor="rgba(214,211,209,0.08)"
207
- className="group h-full overflow-hidden rounded-3xl border border-stone-200 dark:border-stone-800 bg-stone-50 dark:bg-stone-900 shadow-md outline-none transition-colors duration-300 hover:border-indigo-500 hover:shadow-2xl hover:outline-4 hover:outline-stone-200/10 dark:hover:outline-stone-200/10"
208
- style={{ cornerShape: 'squircle' } as React.CSSProperties}
209
- >
210
- {/* Image */}
211
- <div className="relative h-52 w-full overflow-hidden rounded-t-2xl">
212
- {safeImgSrc ? (
213
- <Image
214
- src={safeImgSrc}
215
- alt={projectTitle}
216
- fill
217
- className="studio-card-image corner-squircle scale-105 rounded-[2.5rem] object-cover object-top transition-transform duration-500 group-hover:scale-110"
218
- sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
219
- priority={false}
220
- style={{ cornerShape: 'squircle' } as React.CSSProperties}
221
- />
222
- ) : (
223
- <div className="h-full w-full bg-stone-200 dark:bg-stone-800" />
224
- )}
225
-
226
- <div className="absolute inset-0 bg-linear-to-t from-black/60 via-black/20 to-transparent dark:from-stone-950/80 dark:via-stone-950/20 dark:to-transparent" />
227
-
228
- {projectCategory && (
229
- <span className="absolute top-2 left-2 z-20 border bg-lime-500/50 border-lime-500 text-stone-50 text-xs font-semibold px-2.5 py-1 rounded-full">
230
- {projectCategory}
231
- </span>
232
- )}
233
-
234
- {projectYear && (
235
- <span className="absolute top-2 right-2 z-20 border bg-stone-900/70 border-stone-700 text-stone-300 text-xs font-mono px-2.5 py-1 rounded-full">
236
- {projectYear}
237
- </span>
238
- )}
239
- </div>
240
-
241
- {/* Content */}
242
- <div className="card-content relative z-5 mx-2 mb-2 -mt-6 rounded-2xl bg-stone-50/90 dark:bg-stone-950/50 p-4 backdrop-blur-md drop-shadow-md transition-all duration-300 group-hover:-translate-y-1 group-hover:brightness-110 group-hover:shadow-2xl hover:duration-300 corner-squircle">
243
- <div className="mb-4 flex items-start justify-between gap-4">
244
- <div>
245
- <h3 className="mb-2 text-lg font-bold text-stone-900 dark:text-stone-50">
246
- {projectTitle}
247
- </h3>
248
-
249
- {projectDescription && (
250
- <p className="text-md mb-5 leading-relaxed text-stone-800 dark:text-stone-400">
251
- {projectDescription}
252
- </p>
253
- )}
254
- </div>
255
-
256
- {projectLink && (
257
- <ExternalLink className="mt-1 h-4 w-4 shrink-0 text-stone-400 dark:text-stone-400 transition-colors duration-300 group-hover:text-indigo-800" />
258
- )}
259
- </div>
260
-
261
- {/* Highlight */}
262
- {projectHighlight && (
263
- <div className="mb-4 flex items-center gap-2 rounded-lg border border-stone-200 dark:border-stone-800 bg-stone-100/60 group-hover:bg-stone-300 group-hover:shadow-xl dark:bg-stone-800/60 p-2.5">
264
- <BarChart2
265
- size={14}
266
- className="shrink-0 text-indigo-800 dark:text-indigo-400"
267
- />
268
- <span className="text-xs font-medium text-indigo-800 dark:text-indigo-400">
269
- {projectHighlight}
270
- </span>
271
- </div>
272
- )}
273
-
274
- {/* Tags */}
275
- {project.tags && project.tags.length > 0 && (
276
- <div className="flex flex-wrap gap-1.5">
277
- {project.tags.map((t, ti) => (
278
- <span
279
- key={t.id ?? ti}
280
- className="rounded-full border border-stone-200 dark:border-indigo-700 bg-stone-100 dark:bg-indigo-900/50 px-2 py-0.5 text-[11.5px] text-stone-700 dark:text-stone-300 transition-all duration-300"
281
- >
282
- {t.tag}
283
- </span>
284
- ))}
285
- </div>
286
- )}
287
- </div>
288
- </SpotlightCard>
289
- )
290
-
291
- return (
292
- <motion.div key={project.id ?? i} variants={itemVariants} className="h-full">
293
- {projectLink ? (
294
- <a
295
- href={projectLink}
296
- target="_blank"
297
- rel="noopener noreferrer"
298
- className="block h-full"
299
- >
300
- {CardInner}
301
- </a>
302
- ) : (
303
- CardInner
304
- )}
305
- </motion.div>
306
- )
307
- })}
308
- </motion.div>
309
- </div>
310
- </div>
311
- </>
312
- )
313
- }
314
-
315
- export default DDWork
1
+ /* eslint-disable @next/next/no-img-element */
2
+ 'use client'
3
+
4
+ import React, { useRef, useState } from 'react'
5
+ import Image from 'next/image'
6
+ import { BarChart2, ExternalLink } from 'lucide-react'
7
+ import { motion, type Variants } from 'framer-motion'
8
+
9
+ import { cn } from '@/utilities/ui'
10
+ export type DDWorkProps = Record<string, any>
11
+ type Media = { url?: string | null; alt?: string | null; [key: string]: any }
12
+ type CategoryItem = { id?: string | null; label?: string | null }
13
+ type TagItem = { id?: string | null; tag?: string | null }
14
+ type ProjectItem = {
15
+ id?: string | null
16
+ title?: string | null
17
+ category?: string | null
18
+ description?: string | null
19
+ highlight?: string | null
20
+ year?: string | null
21
+ image?: Media | string | null
22
+ imageFallbackUrl?: string | null
23
+ tags?: TagItem[] | null
24
+ link?: string | null
25
+ }
26
+
27
+ function isLocalImageSrc(src?: string | null): src is string {
28
+ return typeof src === 'string' && src.startsWith('/api/media/file/')
29
+ }
30
+
31
+ // ─── SpotlightCard ────────────────────────────────────────────────────────────
32
+ interface SpotlightCardProps {
33
+ children: React.ReactNode
34
+ className?: string
35
+ spotlightColor?: string
36
+ style?: React.CSSProperties
37
+ }
38
+
39
+ function SpotlightCard({
40
+ children,
41
+ className,
42
+ spotlightColor = 'rgba(214,211,209,0.08)',
43
+ style,
44
+ }: SpotlightCardProps) {
45
+ const cardRef = useRef<HTMLDivElement>(null)
46
+ const [pos, setPos] = useState<{ x: number; y: number } | null>(null)
47
+
48
+ const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
49
+ if (!cardRef.current) return
50
+
51
+ const rect = cardRef.current.getBoundingClientRect()
52
+
53
+ setPos({
54
+ x: e.clientX - rect.left,
55
+ y: e.clientY - rect.top,
56
+ })
57
+ }
58
+
59
+ const handleMouseLeave = () => setPos(null)
60
+
61
+ return (
62
+ <div
63
+ ref={cardRef}
64
+ className={cn('relative overflow-hidden', className)}
65
+ style={style}
66
+ onMouseMove={handleMouseMove}
67
+ onMouseLeave={handleMouseLeave}
68
+ >
69
+ {pos && (
70
+ <div
71
+ className="pointer-events-none absolute inset-0 z-0 transition-opacity duration-300"
72
+ style={{
73
+ background: `radial-gradient(400px circle at ${pos.x}px ${pos.y}px, ${spotlightColor}, transparent 60%)`,
74
+ }}
75
+ />
76
+ )}
77
+
78
+ <div className="relative z-10">{children}</div>
79
+ </div>
80
+ )
81
+ }
82
+
83
+ // ─── Animation variants ───────────────────────────────────────────────────────
84
+ const containerVariants: Variants = {
85
+ hidden: {},
86
+ show: {
87
+ transition: {
88
+ staggerChildren: 0.1,
89
+ delayChildren: 0.05,
90
+ },
91
+ },
92
+ }
93
+
94
+ const itemVariants: Variants = {
95
+ hidden: {
96
+ opacity: 0,
97
+ y: 24,
98
+ },
99
+ show: {
100
+ opacity: 1,
101
+ y: 0,
102
+ transition: {
103
+ duration: 0.45,
104
+ ease: 'easeOut',
105
+ },
106
+ },
107
+ }
108
+
109
+ // ─── Component ────────────────────────────────────────────────────────────────
110
+ export const DDWork: React.FC<DDWorkProps> = ({
111
+ eyebrow,
112
+ headlineMain,
113
+ headlineAccent,
114
+ categories,
115
+ projects,
116
+ sectionId,
117
+ customCSS,
118
+ }) => {
119
+ const categoryItems = (categories ?? []) as CategoryItem[]
120
+ const projectItems = (projects ?? []) as ProjectItem[]
121
+ const allLabel = categoryItems[0]?.label ?? 'All'
122
+ const [activeCategory, setActiveCategory] = useState(allLabel)
123
+
124
+ const filtered =
125
+ activeCategory === allLabel
126
+ ? projectItems
127
+ : projectItems.filter((p) => p.category === activeCategory)
128
+
129
+ return (
130
+ <>
131
+ {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
132
+
133
+ <div id={sectionId ?? 'work'} className="py-24">
134
+ <div className="mx-auto max-w-6xl lg:max-w-7xl px-6">
135
+ {/* Header */}#BlockName-DDWork
136
+ <motion.div
137
+ variants={containerVariants}
138
+ initial="hidden"
139
+ whileInView="show"
140
+ viewport={{ once: false, amount: 0.2 }}
141
+ className="mb-12 flex flex-col gap-6 overflow-hidden md:flex-row md:items-end md:justify-between px-0 md:px-4"
142
+ >
143
+ <motion.div variants={itemVariants}>
144
+ {eyebrow && (
145
+ <p className="top-caption mb-3 text-sm font-semibold uppercase tracking-widest text-stone-500 dark:text-stone-400">
146
+ {eyebrow}
147
+ </p>
148
+ )}
149
+
150
+ <h2 className="text-4xl font-bold leading-tight text-stone-900 dark:text-stone-50 md:text-5xl">
151
+ {headlineMain}
152
+ {headlineAccent && (
153
+ <>
154
+ <br />
155
+ <span className="text-stone-600 dark:text-stone-400">{headlineAccent}</span>
156
+ </>
157
+ )}
158
+ </h2>
159
+ </motion.div>
160
+
161
+ {/* Filter tabs */}
162
+ {categoryItems.length > 1 && (
163
+ <motion.div variants={itemVariants} className="flex flex-wrap gap-2 py-4">
164
+ {categoryItems.map((cat, i) => {
165
+ const categoryLabel = cat.label ?? ''
166
+
167
+ return (
168
+ <button
169
+ key={cat.id ?? i}
170
+ onClick={() => setActiveCategory(categoryLabel)}
171
+ className={cn(
172
+ 'cursor-pointer whitespace-nowrap rounded-full px-4 py-1.5 text-xs font-medium transition-all duration-300',
173
+ activeCategory === categoryLabel
174
+ ? 'border-indigo-500 bg-indigo-800 text-stone-200 shadow-lg shadow-blue-500/20'
175
+ : 'border-stone-300 dark:border-stone-700 bg-stone-50/80 dark:bg-stone-900/80 text-stone-600 dark:text-stone-400 hover:border-stone-400 dark:hover:border-stone-500 hover:bg-stone-100 dark:hover:bg-stone-800 hover:text-stone-900 dark:hover:text-stone-50',
176
+ )}
177
+ >
178
+ {categoryLabel}
179
+ </button>
180
+ )
181
+ })}
182
+ </motion.div>
183
+ )}
184
+ </motion.div>
185
+
186
+ {/* Projects Grid */}
187
+ <motion.div
188
+ variants={containerVariants}
189
+ initial="hidden"
190
+ whileInView="show"
191
+ viewport={{ once: false, amount: 0.1 }}
192
+ className="grid grid-cols-1 gap-5 py-4 md:grid-cols-2 lg:grid-cols-3"
193
+ >
194
+ {filtered.map((project, i) => {
195
+ const projectTitle = project.title ?? ''
196
+ const projectDescription = project.description ?? ''
197
+ const projectCategory = project.category ?? ''
198
+ const projectYear = project.year ?? ''
199
+ const projectHighlight = project.highlight ?? ''
200
+ const projectLink = project.link ?? ''
201
+ const imgSrc =
202
+ project.image && typeof project.image === 'object'
203
+ ? ((project.image as Media).url ?? project.imageFallbackUrl ?? undefined)
204
+ : (project.imageFallbackUrl ?? undefined)
205
+ const safeImgSrc = isLocalImageSrc(imgSrc) ? imgSrc : null
206
+
207
+ const CardInner = (
208
+ <SpotlightCard
209
+ spotlightColor="rgba(214,211,209,0.08)"
210
+ className="group h-full overflow-hidden rounded-3xl border border-stone-200 dark:border-stone-800 bg-stone-50 dark:bg-stone-900 shadow-md outline-none transition-colors duration-300 hover:border-indigo-500 hover:shadow-2xl hover:outline-4 hover:outline-stone-200/10 dark:hover:outline-stone-200/10"
211
+ style={{ cornerShape: 'squircle' } as React.CSSProperties}
212
+ >
213
+ {/* Image */}
214
+ <div className="relative h-52 w-full overflow-hidden rounded-t-2xl">
215
+ {safeImgSrc ? (
216
+ <Image
217
+ src={safeImgSrc}
218
+ alt={projectTitle}
219
+ fill
220
+ className="studio-card-image corner-squircle scale-105 rounded-[2.5rem] object-cover object-top transition-transform duration-500 group-hover:scale-110"
221
+ sizes="(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw"
222
+ priority={false}
223
+ style={{ cornerShape: 'squircle' } as React.CSSProperties}
224
+ />
225
+ ) : (
226
+ <div className="h-full w-full bg-stone-200 dark:bg-stone-800" />
227
+ )}
228
+
229
+ <div className="absolute inset-0 bg-linear-to-t from-black/60 via-black/20 to-transparent dark:from-stone-950/80 dark:via-stone-950/20 dark:to-transparent" />
230
+
231
+ {projectCategory && (
232
+ <span className="absolute top-2 left-2 z-20 border bg-lime-500/50 border-lime-500 text-stone-50 text-xs font-semibold px-2.5 py-1 rounded-full">
233
+ {projectCategory}
234
+ </span>
235
+ )}
236
+
237
+ {projectYear && (
238
+ <span className="absolute top-2 right-2 z-20 border bg-stone-900/70 border-stone-700 text-stone-300 text-xs font-mono px-2.5 py-1 rounded-full">
239
+ {projectYear}
240
+ </span>
241
+ )}
242
+ </div>
243
+
244
+ {/* Content */}
245
+ <div className="card-content relative z-5 mx-2 mb-2 -mt-6 rounded-2xl bg-stone-50/90 dark:bg-stone-950/50 p-4 backdrop-blur-md drop-shadow-md transition-all duration-300 group-hover:-translate-y-1 group-hover:brightness-110 group-hover:shadow-2xl hover:duration-300 corner-squircle">
246
+ <div className="mb-4 flex items-start justify-between gap-4">
247
+ <div>
248
+ <h3 className="mb-2 text-lg font-bold text-stone-900 dark:text-stone-50">
249
+ {projectTitle}
250
+ </h3>
251
+
252
+ {projectDescription && (
253
+ <p className="text-md mb-5 leading-relaxed text-stone-800 dark:text-stone-400">
254
+ {projectDescription}
255
+ </p>
256
+ )}
257
+ </div>
258
+
259
+ {projectLink && (
260
+ <ExternalLink className="mt-1 h-4 w-4 shrink-0 text-stone-400 dark:text-stone-400 transition-colors duration-300 group-hover:text-indigo-800" />
261
+ )}
262
+ </div>
263
+
264
+ {/* Highlight */}
265
+ {projectHighlight && (
266
+ <div className="mb-4 flex items-center gap-2 rounded-lg border border-stone-200 dark:border-stone-800 bg-stone-100/60 group-hover:bg-stone-300 group-hover:shadow-xl dark:bg-stone-800/60 p-2.5">
267
+ <BarChart2
268
+ size={14}
269
+ className="shrink-0 text-indigo-800 dark:text-indigo-400"
270
+ />
271
+ <span className="text-xs font-medium text-indigo-800 dark:text-indigo-400">
272
+ {projectHighlight}
273
+ </span>
274
+ </div>
275
+ )}
276
+
277
+ {/* Tags */}
278
+ {project.tags && project.tags.length > 0 && (
279
+ <div className="flex flex-wrap gap-1.5">
280
+ {project.tags.map((t, ti) => (
281
+ <span
282
+ key={t.id ?? ti}
283
+ className="rounded-full border border-stone-200 dark:border-indigo-700 bg-stone-100 dark:bg-indigo-900/50 px-2 py-0.5 text-[11.5px] text-stone-700 dark:text-stone-300 transition-all duration-300"
284
+ >
285
+ {t.tag}
286
+ </span>
287
+ ))}
288
+ </div>
289
+ )}
290
+ </div>
291
+ </SpotlightCard>
292
+ )
293
+
294
+ return (
295
+ <motion.div key={project.id ?? i} variants={itemVariants} className="h-full">
296
+ {projectLink ? (
297
+ <a
298
+ href={projectLink}
299
+ target="_blank"
300
+ rel="noopener noreferrer"
301
+ className="block h-full"
302
+ >
303
+ {CardInner}
304
+ </a>
305
+ ) : (
306
+ CardInner
307
+ )}
308
+ </motion.div>
309
+ )
310
+ })}
311
+ </motion.div>
312
+ </div>
313
+ </div>
314
+ </>
315
+ )
316
+ }
317
+
318
+ export default DDWork