blocks-dusted 0.1.11 → 0.1.13

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 (161) hide show
  1. package/package.json +5 -2
  2. package/src/cli.js +39 -3
  3. package/src/commands/shop.js +59 -0
  4. package/src/commands/warehouse.js +73 -0
  5. package/src/installers/patchRenderBlocks.js +1 -1
  6. package/src/registry/validateTemplateManifest.js +2 -1
  7. package/templates/blocks/BannerDD790432/Component.tsx +63 -0
  8. package/templates/blocks/BannerDD790432/config.ts +54 -0
  9. package/templates/blocks/BannerDD790432/manifest.json +80 -0
  10. package/templates/blocks/Button47/Component.tsx +124 -0
  11. package/templates/blocks/Button47/config.ts +75 -0
  12. package/templates/blocks/Button47/link.ts +22 -0
  13. package/templates/blocks/Button47/manifest.json +105 -0
  14. package/templates/blocks/Button47/styles.module.scss +75 -0
  15. package/templates/blocks/DD-Accordion/Component.tsx +55 -36
  16. package/templates/blocks/DD-Accordion/config.ts +71 -59
  17. package/templates/blocks/DD-AnimText/Component.tsx +50 -32
  18. package/templates/blocks/DD-AnimText/config.ts +296 -295
  19. package/templates/blocks/DD-AsideNav/Component.tsx +323 -319
  20. package/templates/blocks/DD-Banner/Component.tsx +54 -51
  21. package/templates/blocks/DD-BookCall/Component.tsx +10 -9
  22. package/templates/blocks/DD-ButtonPop/Component.tsx +118 -117
  23. package/templates/blocks/DD-Card/Component.tsx +4 -4
  24. package/templates/blocks/DD-CardTemplate01/Component.tsx +5 -5
  25. package/templates/blocks/DD-CardsWithVariantV3/Component.tsx +472 -0
  26. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/index.tsx +203 -0
  27. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/styles.ts +87 -0
  28. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/useInjectStyle.ts +39 -0
  29. package/templates/blocks/DD-CardsWithVariantV3/config.ts +326 -0
  30. package/templates/blocks/DD-CardsWithVariantV3/manifest.json +110 -0
  31. package/templates/blocks/DD-Carousel-A/Component.tsx +251 -249
  32. package/templates/blocks/DD-Carousel-B/Component.tsx +405 -403
  33. package/templates/blocks/DD-Chip/Component.tsx +14 -14
  34. package/templates/blocks/DD-ComparisonTable/Component.tsx +255 -256
  35. package/templates/blocks/DD-Contact/Component.tsx +433 -434
  36. package/templates/blocks/DD-Counter/Component.tsx +18 -13
  37. package/templates/blocks/DD-DeliCard/Component.tsx +303 -0
  38. package/templates/blocks/DD-DeliCard/config.ts +186 -0
  39. package/templates/blocks/DD-DeliCard/manifest.json +94 -0
  40. package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
  41. package/templates/blocks/DD-FeatProd/CircularGallery.tsx +1179 -0
  42. package/templates/blocks/DD-FeatProd/Component.tsx +484 -0
  43. package/templates/blocks/DD-FeatProd/config.ts +173 -0
  44. package/templates/blocks/DD-FeatProd/manifest.json +93 -0
  45. package/templates/blocks/DD-FeatServ/Component.tsx +607 -0
  46. package/templates/blocks/DD-FeatServ/config.ts +213 -0
  47. package/templates/blocks/DD-FeatServ/manifest.json +113 -0
  48. package/templates/blocks/DD-FeatStrip/Component.tsx +584 -585
  49. package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +627 -628
  50. package/templates/blocks/DD-FluidBtn/Component.tsx +46 -43
  51. package/templates/blocks/DD-GooeyInfo/Component.tsx +150 -149
  52. package/templates/blocks/DD-GooeyInfo/config.ts +136 -135
  53. package/templates/blocks/DD-Hero/Component.tsx +16 -40
  54. package/templates/blocks/DD-HeroPlas/Component.tsx +50 -0
  55. package/templates/blocks/DD-HeroPlas/ThreeScene.tsx +109 -0
  56. package/templates/blocks/DD-HeroPlas/config.ts +33 -0
  57. package/templates/blocks/DD-HeroPlas/manifest.json +88 -0
  58. package/templates/blocks/DD-HorizontalScroll/Component.tsx +4 -5
  59. package/templates/blocks/DD-Icon/Component.tsx +46 -81
  60. package/templates/blocks/DD-IconButton/Component.tsx +13 -20
  61. package/templates/blocks/DD-IdTag/Component.tsx +22 -21
  62. package/templates/blocks/DD-IframeMedia/Component.tsx +14 -10
  63. package/templates/blocks/DD-LineAnim/Component.tsx +42 -36
  64. package/templates/blocks/DD-LogoMarq/Component.tsx +38 -48
  65. package/templates/blocks/DD-Lottie/Component.tsx +11 -3
  66. package/templates/blocks/DD-MasonaryMedia/Component.tsx +544 -547
  67. package/templates/blocks/DD-MasonaryMedia/config.ts +389 -389
  68. package/templates/blocks/DD-Masonry/Component.tsx +654 -644
  69. package/templates/blocks/DD-Masonry/config.ts +475 -475
  70. package/templates/blocks/DD-MaterialSwatches/Component.tsx +85 -0
  71. package/templates/blocks/DD-MaterialSwatches/config.ts +232 -0
  72. package/templates/blocks/DD-MaterialSwatches/manifest.json +84 -0
  73. package/templates/blocks/DD-MaterialSwatches/material.ts +107 -0
  74. package/templates/blocks/DD-NineCanvas/Component.tsx +129 -0
  75. package/templates/blocks/DD-NineCanvas/RenderBlocks.tsx +244 -0
  76. package/templates/blocks/DD-NineCanvas/allowedCanvasBlocks.ts +6 -0
  77. package/templates/blocks/DD-NineCanvas/blockComponents.tsx +8 -0
  78. package/templates/blocks/DD-NineCanvas/canvas/CanvasGrid.tsx +316 -0
  79. package/templates/blocks/DD-NineCanvas/canvas/RadialNav.tsx +432 -0
  80. package/templates/blocks/DD-NineCanvas/canvas/canvas-panels.css +889 -0
  81. package/templates/blocks/DD-NineCanvas/canvas/icons/custom-icons.tsx +364 -0
  82. package/templates/blocks/DD-NineCanvas/config.ts +61 -0
  83. package/templates/blocks/DD-NineCanvas/manifest.json +165 -0
  84. package/templates/blocks/DD-NineCanvas/styles.scss +152 -0
  85. package/templates/blocks/DD-NineCanvas/tooltip/get-strict-context.tsx +26 -0
  86. package/templates/blocks/DD-NineCanvas/tooltip/primitives.tsx +178 -0
  87. package/templates/blocks/DD-NineCanvas/tooltip/tooltip.tsx +71 -0
  88. package/templates/blocks/DD-NineCanvas/tooltip/use-controlled-state.tsx +33 -0
  89. package/templates/blocks/DD-NineCanvas/utilities/FadeOnScroll.tsx +68 -0
  90. package/templates/blocks/DD-NineCanvas/utilities/ParallaxWrapper.tsx +157 -0
  91. package/templates/blocks/DD-NineCanvas/utilities/renderCustomCSS.ts +5 -0
  92. package/templates/blocks/DD-NineCanvas/utilities/webglCleanup.ts +27 -0
  93. package/templates/blocks/DD-PayloadAIAssistant/Component.tsx +78 -0
  94. package/templates/blocks/DD-PayloadAIAssistant/DustedBot.tsx +590 -0
  95. package/templates/blocks/DD-PayloadAIAssistant/config.ts +37 -0
  96. package/templates/blocks/DD-PayloadAIAssistant/demo.css +125 -0
  97. package/templates/blocks/DD-PayloadAIAssistant/manifest.json +93 -0
  98. package/templates/blocks/DD-PayloadAIAssistant/source-defaults.ts +17 -0
  99. package/templates/blocks/DD-PayloadAIAssistant/styles.ts +306 -0
  100. package/templates/blocks/DD-Pricing/Component.tsx +368 -372
  101. package/templates/blocks/DD-Pricing/config.ts +215 -215
  102. package/templates/blocks/DD-Process/Component.tsx +147 -149
  103. package/templates/blocks/DD-Progress/Component.tsx +156 -141
  104. package/templates/blocks/DD-ProjHeader/Component.tsx +93 -91
  105. package/templates/blocks/DD-ProjSlider/Component.tsx +1 -1
  106. package/templates/blocks/DD-ProjSlider/config.ts +2 -2
  107. package/templates/blocks/DD-Section/Component.tsx +265 -266
  108. package/templates/blocks/DD-Separator/Component.tsx +66 -66
  109. package/templates/blocks/DD-Services/Component.tsx +176 -176
  110. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +306 -307
  111. package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
  112. package/templates/blocks/DD-StackCards/Component.tsx +474 -473
  113. package/templates/blocks/DD-Team/Component.tsx +247 -247
  114. package/templates/blocks/DD-TeamCards/Component.tsx +251 -251
  115. package/templates/blocks/DD-TechStack/Component.tsx +161 -168
  116. package/templates/blocks/DD-TestMarq/Component.tsx +158 -132
  117. package/templates/blocks/DD-Testim/Component.tsx +247 -247
  118. package/templates/blocks/DD-Testim2/Component.tsx +265 -256
  119. package/templates/blocks/DD-Testimonials/Component.tsx +139 -147
  120. package/templates/blocks/DD-TextMarq/Component.tsx +157 -157
  121. package/templates/blocks/DD-Thanks/Component.tsx +501 -500
  122. package/templates/blocks/DD-Work/Component.tsx +315 -315
  123. package/templates/blocks/DD-Work-B/Component.tsx +294 -294
  124. package/templates/blocks/DDHG-CTASection2/Component.tsx +183 -184
  125. package/templates/blocks/DDHG-HighlightArc/Component.tsx +246 -243
  126. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +180 -93
  127. package/templates/blocks/DDHG-LogoMarquee/config.ts +154 -66
  128. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +225 -213
  129. package/templates/blocks/DDHG-ProcessSec/Component.tsx +38 -39
  130. package/templates/blocks/DDHG-ServiceDetails/Component.tsx +11 -11
  131. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +30 -47
  132. package/templates/blocks/DDHG-ServiceHero/Component.tsx +10 -10
  133. package/templates/blocks/DDHG-ServiceOverview/Component.tsx +23 -23
  134. package/templates/blocks/DDHG-StatsFAQ/Component.tsx +40 -40
  135. package/templates/blocks/DDHG-TeamCards/Component.tsx +140 -131
  136. package/templates/blocks/Form/Checkbox/index.tsx +45 -0
  137. package/templates/blocks/Form/Component.tsx +163 -0
  138. package/templates/blocks/Form/Country/index.tsx +65 -0
  139. package/templates/blocks/Form/Country/options.ts +982 -0
  140. package/templates/blocks/Form/Email/index.tsx +38 -0
  141. package/templates/blocks/Form/Error/index.tsx +15 -0
  142. package/templates/blocks/Form/Message/index.tsx +13 -0
  143. package/templates/blocks/Form/Number/index.tsx +36 -0
  144. package/templates/blocks/Form/Select/index.tsx +63 -0
  145. package/templates/blocks/Form/State/index.tsx +64 -0
  146. package/templates/blocks/Form/State/options.ts +52 -0
  147. package/templates/blocks/Form/Text/index.tsx +32 -0
  148. package/templates/blocks/Form/Textarea/index.tsx +40 -0
  149. package/templates/blocks/Form/Width/index.tsx +13 -0
  150. package/templates/blocks/Form/config.ts +51 -0
  151. package/templates/blocks/Form/fields.tsx +21 -0
  152. package/templates/blocks/Form/manifest.json +168 -0
  153. package/templates/blocks/MediaBlock/Component.tsx +67 -0
  154. package/templates/blocks/MediaBlock/config.ts +14 -0
  155. package/templates/blocks/MediaBlock/manifest.json +92 -0
  156. package/templates/blocks/TransitionsDustedShowcase/Component.tsx +9 -0
  157. package/templates/blocks/TransitionsDustedShowcase/config.ts +13 -0
  158. package/templates/blocks/TransitionsDustedShowcase/manifest.json +80 -0
  159. package/templates/blocks/rawHtml/Component.tsx +25 -0
  160. package/templates/blocks/rawHtml/config.ts +56 -0
  161. package/templates/blocks/rawHtml/manifest.json +80 -0
@@ -1,66 +1,66 @@
1
- import React from 'react'
2
- import { cn } from '@/utilities/ui'
3
-
4
- export interface SeparatorGradientProps {
5
- /**
6
- * @default ""
7
- */
8
- label?: React.ReactNode
9
- /**
10
- * @default false
11
- */
12
- gradient?: boolean
13
- className?: string
14
- labelClassName?: string
15
- }
16
- //======================================
17
- export const DDSeparator = ({
18
- label,
19
- labelClassName,
20
- gradient = false,
21
- className
22
- }: SeparatorGradientProps) => {
23
- if (label) {
24
- return (
25
- <div className="separator-container flex items-center w-full max-w-full overflow-hidden">
26
- <div
27
- className={cn(
28
- 'rounded-full w-full h-[1px]',
29
- gradient
30
- ? 'bg-gradient-to-l from-zinc-500 dark:from-zinc-200 to-transparent dark:to-zinc-700'
31
- : 'bg-zinc-300 dark:bg-zinc-800',
32
- className
33
- )}
34
- ></div>
35
- <div
36
- className={cn(
37
- labelClassName,
38
- 'text-black/80 uppercase w-fit dark:text-stone-200/80 text-nowrap px-4'
39
- )}
40
- >
41
- {label}
42
- </div>
43
- <div
44
- className={cn(
45
- 'rounded-full w-full h-[1px]',
46
- gradient
47
- ? 'bg-gradient-to-r from-zinc-500 dark:from-zinc-200 to-transparent dark:to-zinc-700'
48
- : 'bg-zinc-300 dark:bg-zinc-800',
49
- className
50
- )}
51
- ></div>
52
- </div>
53
- )
54
- }
55
- return (
56
- <div
57
- className={cn(
58
- 'rounded-full w-full h-[1px]',
59
- gradient
60
- ? 'bg-gradient-to-r from-transparent via-zinc-500 dark:via-zinc-200 to-transparent dark:from-zinc-800 dark:to-zinc-700'
61
- : 'bg-zinc-300 dark:bg-zinc-800',
62
- className
63
- )}
64
- />
65
- )
66
- }
1
+ import React from 'react'
2
+ import { cn } from '@/utilities/ui'
3
+
4
+ export interface SeparatorGradientProps {
5
+ /**
6
+ * @default ""
7
+ */
8
+ label?: React.ReactNode
9
+ /**
10
+ * @default false
11
+ */
12
+ gradient?: boolean
13
+ className?: string
14
+ labelClassName?: string
15
+ }
16
+ //======================================
17
+ export const DDSeparator = ({
18
+ label,
19
+ labelClassName,
20
+ gradient = false,
21
+ className,
22
+ }: SeparatorGradientProps) => {
23
+ if (label) {
24
+ return (
25
+ <div className="separator-container flex min-h-[55vh] items-center w-full max-w-full overflow-hidden rounded-md bg-zinc-100 px-8 dark:bg-zinc-900">
26
+ <div
27
+ className={cn(
28
+ 'rounded-full w-full h-px',
29
+ gradient
30
+ ? 'bg-gradient-to-l from-zinc-500 dark:from-zinc-200 to-transparent dark:to-zinc-700'
31
+ : 'bg-zinc-300 dark:bg-zinc-800',
32
+ className,
33
+ )}
34
+ ></div>
35
+ <div
36
+ className={cn(
37
+ labelClassName,
38
+ 'text-black/80 uppercase w-fit dark:text-stone-200/80 text-nowrap px-4',
39
+ )}
40
+ >
41
+ {label} Separator
42
+ </div>
43
+ <div
44
+ className={cn(
45
+ 'rounded-full w-full h-px',
46
+ gradient
47
+ ? 'bg-gradient-to-r from-zinc-500 dark:from-zinc-200 to-transparent dark:to-zinc-700'
48
+ : 'bg-zinc-300 dark:bg-zinc-800',
49
+ className,
50
+ )}
51
+ ></div>
52
+ </div>
53
+ )
54
+ }
55
+ return (
56
+ <div
57
+ className={cn(
58
+ 'rounded-full w-full h-px',
59
+ gradient
60
+ ? 'bg-gradient-to-r from-transparent via-zinc-500 dark:via-zinc-200 to-transparent dark:from-zinc-800 dark:to-zinc-700'
61
+ : 'bg-zinc-300 dark:bg-zinc-800',
62
+ className,
63
+ )}
64
+ />
65
+ )
66
+ }
@@ -1,176 +1,176 @@
1
- 'use client'
2
- import React, { useRef, useState } from 'react'
3
- import * as LucideIcons from 'lucide-react'
4
- import { TextFade } from '@/components/ui/anims/fade'
5
- import { cn } from '@/utilities/ui'
6
- export type DDServicesProps = Record<string, any>
7
- type TagItem = { id?: string | null; tag?: string | null }
8
- type ServiceItem = {
9
- id?: string | null
10
- iconName?: string | null
11
- number?: string | null
12
- title?: string | null
13
- description?: string | null
14
- tags?: TagItem[] | null
15
- }
16
-
17
- // ─── Dynamic Icon ─────────────────────────────────────────────────────────────
18
- function DynamicIcon({
19
- name,
20
- size = 20,
21
- ...props
22
- }: { name?: string | null } & Omit<LucideIcons.LucideProps, 'name'>) {
23
- if (!name) return null
24
- const Icon = (LucideIcons as unknown as Record<string, React.FC<LucideIcons.LucideProps>>)[name]
25
- if (!Icon) return null
26
- return <Icon size={size} {...props} />
27
- }
28
-
29
- // ─── Spotlight Hover Card ─────────────────────────────────────────────────────
30
- function SpotlightCard({ children, className }: { children: React.ReactNode; className?: string }) {
31
- const ref = useRef<HTMLDivElement>(null)
32
- const [pos, setPos] = useState<{ x: number; y: number } | null>(null)
33
-
34
- const onMove = (e: React.MouseEvent<HTMLDivElement>) => {
35
- if (!ref.current) return
36
- const rect = ref.current.getBoundingClientRect()
37
- setPos({
38
- x: e.clientX - rect.left,
39
- y: e.clientY - rect.top,
40
- })
41
- }
42
-
43
- const onLeave = () => setPos(null)
44
-
45
- return (
46
- <div
47
- ref={ref}
48
- onMouseMove={onMove}
49
- onMouseLeave={onLeave}
50
- className={cn('relative overflow-hidden transition-all duration-300', className)}
51
- >
52
- {/* spotlight */}
53
- {pos && (
54
- <div
55
- className="pointer-events-none absolute inset-0 z-0 transition-opacity duration-300"
56
- style={{
57
- background: `radial-gradient(500px circle at ${pos.x}px ${pos.y}px, rgba(132,204,22,0.12), transparent 60%)`,
58
- }}
59
- />
60
- )}
61
-
62
- {/* animated border glow */}
63
- <div className="absolute inset-0 rounded-3xl border border-stone-100 dark:border-stone-800 group-hover:border-lime-500/60 transition-colors duration-300" />
64
-
65
- {/* subtle noise / sheen */}
66
- <div className="pointer-events-none absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 bg-[linear-gradient(120deg,transparent,rgba(255,255,255,0.05),transparent)]" />
67
-
68
- <div className="relative z-10">{children}</div>
69
- </div>
70
- )
71
- }
72
-
73
- // ─── Component ────────────────────────────────────────────────────────────────
74
- export const DDServices: React.FC<DDServicesProps> = ({
75
- eyebrow,
76
- headlineMain,
77
- headlineAccent,
78
- description,
79
- services,
80
- sectionId,
81
- customCSS,
82
- }) => {
83
- const serviceItems = (services ?? []) as ServiceItem[]
84
-
85
- return (
86
- <>
87
- {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
88
-
89
- <div id={sectionId ?? 'services'} className="py-24">
90
- <div className="max-w-6xl lg:max-w-7xl mx-auto px-4 md:px-6">
91
- {/* Header */}
92
- <div className="max-w-2xl mb-16">
93
- <TextFade direction="right" className="text-base" delay={0}>
94
- {eyebrow && (
95
- <p className="text-[11.5px] font-semibold text-stone-400 tracking-widest uppercase mb-3 top-caption">
96
- {eyebrow}
97
- </p>
98
- )}
99
- </TextFade>
100
-
101
- <TextFade direction="up" className="mt-0" delay={0.3}>
102
- <h2 className="text-3d text-4xl md:text-5xl font-bold text-stone-900 dark:text-stone-50 leading-tight mb-5">
103
- {headlineMain}
104
- </h2>
105
- </TextFade>
106
-
107
- <TextFade direction="up" className="-mt-6" delay={0.3}>
108
- <h2 className="text-3d text-4xl md:text-5xl font-bold text-stone-400 leading-tight mb-5">
109
- {headlineAccent}
110
- </h2>
111
- </TextFade>
112
-
113
- <TextFade direction="down" className="px-1" delay={0.5}>
114
- {description && (
115
- <p className="text-stone-700 dark:text-stone-300 text-base leading-relaxed">
116
- {description}
117
- </p>
118
- )}
119
- </TextFade>
120
- </div>
121
- {/* Services Grid */}
122
- {serviceItems.length > 0 && (
123
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
124
- {serviceItems.map((svc, i) => (
125
- <SpotlightCard
126
- key={svc.id ?? i}
127
- className="group bg-[#19171ca4] backdrop-blur-md rounded-3xl shadow-md hover:shadow-2xl hover:-translate-y-1 transition-all duration-300"
128
- >
129
- <div className="bg-dots h-full w-full shadow-inner brightness-90 absolute p-6 md:p-8">
130
- {/* Icon Row */}
131
- <div className="flex items-start justify-between mb-6">
132
- <div className="w-12 h-12 flex items-center justify-center rounded-xl bg-stone-800/10 bg-lines border border-stone-600 group-hover:bg-stone-700 group-hover:scale-110 transition-all duration-300">
133
- <DynamicIcon
134
- className="text-stone-300 group-hover:text-lime-400 transition-colors duration-300"
135
- name={svc.iconName}
136
- size={20}
137
- />
138
- </div>
139
-
140
- {svc.number && (
141
- <span className="text-xs font-mono text-stone-400 group-hover:text-lime-400 transition-colors duration-300">
142
- {svc.number}
143
- </span>
144
- )}
145
- </div>
146
- {/* Title */}
147
- <h3 className="text-lg font-semibold text-stone-900 dark:text-stone-50 mb-3 group-hover:text-lime-300 transition-colors duration-300">
148
- {svc.title}
149
- </h3>
150
- {/* Description */}
151
- <p className="text-md text-stone-400 leading-relaxed mb-5 group-hover:text-stone-300 transition-colors duration-300">
152
- {svc.description}
153
- </p>
154
- {/* Tags */}
155
- {svc.tags && svc.tags.length > 0 && (
156
- <div className="flex flex-wrap gap-2">
157
- {svc.tags.map((t, ti) => (
158
- <span
159
- key={t.id ?? ti}
160
- className="text-xs px-2.5 py-1 rounded-full bg-stone-800/10 bg-lines text-stone-400 border border-stone-700 group-hover:border-lime-500/40 group-hover:text-lime-300 transition-all duration-300"
161
- >
162
- {t.tag}
163
- </span>
164
- ))}
165
- </div>
166
- )}
167
- </div>
168
- </SpotlightCard>
169
- ))}
170
- </div>
171
- )}
172
- </div>
173
- </div>
174
- </>
175
- )
176
- }
1
+ 'use client'
2
+ import React, { useRef, useState } from 'react'
3
+ import * as LucideIcons from 'lucide-react'
4
+ import { TextFade } from '@/components/ui/anims/fade'
5
+ import { cn } from '@/utilities/ui'
6
+ export type DDServicesProps = Record<string, any>
7
+ type TagItem = { id?: string | null; tag?: string | null }
8
+ type ServiceItem = {
9
+ id?: string | null
10
+ iconName?: string | null
11
+ number?: string | null
12
+ title?: string | null
13
+ description?: string | null
14
+ tags?: TagItem[] | null
15
+ }
16
+
17
+ // ─── Dynamic Icon ─────────────────────────────────────────────────────────────
18
+ function DynamicIcon({
19
+ name,
20
+ size = 20,
21
+ ...props
22
+ }: { name?: string | null } & Omit<LucideIcons.LucideProps, 'name'>) {
23
+ if (!name) return null
24
+ const Icon = (LucideIcons as unknown as Record<string, React.FC<LucideIcons.LucideProps>>)[name]
25
+ if (!Icon) return null
26
+ return <Icon size={size} {...props} />
27
+ }
28
+
29
+ // ─── Spotlight Hover Card ─────────────────────────────────────────────────────
30
+ function SpotlightCard({ children, className }: { children: React.ReactNode; className?: string }) {
31
+ const ref = useRef<HTMLDivElement>(null)
32
+ const [pos, setPos] = useState<{ x: number; y: number } | null>(null)
33
+
34
+ const onMove = (e: React.MouseEvent<HTMLDivElement>) => {
35
+ if (!ref.current) return
36
+ const rect = ref.current.getBoundingClientRect()
37
+ setPos({
38
+ x: e.clientX - rect.left,
39
+ y: e.clientY - rect.top,
40
+ })
41
+ }
42
+
43
+ const onLeave = () => setPos(null)
44
+
45
+ return (
46
+ <div
47
+ ref={ref}
48
+ onMouseMove={onMove}
49
+ onMouseLeave={onLeave}
50
+ className={cn('relative overflow-hidden transition-all duration-300', className)}
51
+ >
52
+ {/* spotlight */}
53
+ {pos && (
54
+ <div
55
+ className="pointer-events-none absolute inset-0 z-0 transition-opacity duration-300"
56
+ style={{
57
+ background: `radial-gradient(500px circle at ${pos.x}px ${pos.y}px, rgba(132,204,22,0.12), transparent 60%)`,
58
+ }}
59
+ />
60
+ )}
61
+
62
+ {/* animated border glow */}
63
+ <div className="absolute inset-0 rounded-3xl border border-stone-100 dark:border-stone-800 group-hover:border-lime-500/60 transition-colors duration-300" />
64
+
65
+ {/* subtle noise / sheen */}
66
+ <div className="pointer-events-none absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-500 bg-[linear-gradient(120deg,transparent,rgba(255,255,255,0.05),transparent)]" />
67
+
68
+ <div className="relative z-10">{children}</div>
69
+ </div>
70
+ )
71
+ }
72
+
73
+ // ─── Component ────────────────────────────────────────────────────────────────
74
+ export const DDServices: React.FC<DDServicesProps> = ({
75
+ eyebrow,
76
+ headlineMain,
77
+ headlineAccent,
78
+ description,
79
+ services,
80
+ sectionId,
81
+ customCSS,
82
+ }) => {
83
+ const serviceItems = (services ?? []) as ServiceItem[]
84
+
85
+ return (
86
+ <>
87
+ {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
88
+
89
+ <div id={sectionId ?? 'services'} className="py-4">
90
+ <div className="max-w-6xl lg:max-w-7xl mx-auto px-4 md:px-6">
91
+ {/* Header */}
92
+ <div className="max-w-2xl mb-16">
93
+ <TextFade direction="right" className="text-base" delay={0}>
94
+ {eyebrow && (
95
+ <p className="text-[11.5px] font-semibold text-stone-400 tracking-widest uppercase mb-3 top-caption">
96
+ {eyebrow}
97
+ </p>
98
+ )}
99
+ </TextFade>
100
+
101
+ <TextFade direction="up" className="mt-0" delay={0.3}>
102
+ <h2 className="text-3d text-4xl md:text-5xl font-bold text-stone-900 dark:text-stone-50 leading-tight mb-5">
103
+ {headlineMain}
104
+ </h2>
105
+ </TextFade>
106
+
107
+ <TextFade direction="up" className="-mt-6" delay={0.3}>
108
+ <h2 className="text-3d text-4xl md:text-5xl font-bold text-stone-400 leading-tight mb-5">
109
+ {headlineAccent}
110
+ </h2>
111
+ </TextFade>
112
+
113
+ <TextFade direction="down" className="px-1" delay={0.5}>
114
+ {description && (
115
+ <p className="text-stone-700 dark:text-stone-300 text-base leading-relaxed">
116
+ {description}
117
+ </p>
118
+ )}
119
+ </TextFade>
120
+ </div>
121
+ {/* Services Grid */}
122
+ {serviceItems.length > 0 && (
123
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
124
+ {serviceItems.map((svc, i) => (
125
+ <SpotlightCard
126
+ key={svc.id ?? i}
127
+ className="group bg-[#19171ca4] backdrop-blur-md rounded-3xl shadow-md hover:shadow-2xl hover:-translate-y-1 transition-all duration-300"
128
+ >
129
+ <div className="bg-dots h-full w-full shadow-inner brightness-90 absolute p-6 md:p-8">
130
+ {/* Icon Row */}
131
+ <div className="flex items-start justify-between mb-6">
132
+ <div className="w-12 h-12 flex items-center justify-center rounded-xl bg-stone-800/10 bg-lines border border-stone-600 group-hover:bg-stone-700 group-hover:scale-110 transition-all duration-300">
133
+ <DynamicIcon
134
+ className="text-stone-300 group-hover:text-lime-400 transition-colors duration-300"
135
+ name={svc.iconName}
136
+ size={20}
137
+ />
138
+ </div>
139
+
140
+ {svc.number && (
141
+ <span className="text-xs font-mono text-stone-400 group-hover:text-lime-400 transition-colors duration-300">
142
+ {svc.number}
143
+ </span>
144
+ )}
145
+ </div>
146
+ {/* Title */}
147
+ <h3 className="text-lg font-semibold text-stone-900 dark:text-stone-50 mb-3 group-hover:text-lime-300 transition-colors duration-300">
148
+ {svc.title}
149
+ </h3>
150
+ {/* Description */}
151
+ <p className="text-md text-stone-400 leading-relaxed mb-5 group-hover:text-stone-300 transition-colors duration-300">
152
+ {svc.description}
153
+ </p>
154
+ {/* Tags */}
155
+ {svc.tags && svc.tags.length > 0 && (
156
+ <div className="flex flex-wrap gap-2">
157
+ {svc.tags.map((t, ti) => (
158
+ <span
159
+ key={t.id ?? ti}
160
+ className="text-xs px-2.5 py-1 rounded-full bg-stone-800/10 bg-lines text-stone-400 border border-stone-700 group-hover:border-lime-500/40 group-hover:text-lime-300 transition-all duration-300"
161
+ >
162
+ {t.tag}
163
+ </span>
164
+ ))}
165
+ </div>
166
+ )}
167
+ </div>
168
+ </SpotlightCard>
169
+ ))}
170
+ </div>
171
+ )}
172
+ </div>
173
+ </div>
174
+ </>
175
+ )
176
+ }