blocks-dusted 0.1.10 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +35 -0
  2. package/package.json +1 -1
  3. package/src/utils/paths.js +13 -13
  4. package/templates/blocks/DD-Accordion/Component.tsx +229 -0
  5. package/templates/blocks/DD-Accordion/README.md +3 -0
  6. package/templates/blocks/DD-Accordion/config.ts +73 -0
  7. package/templates/blocks/DD-Accordion/manifest.json +75 -0
  8. package/templates/blocks/DD-AnimText/Component.tsx +788 -0
  9. package/templates/blocks/DD-AnimText/README.md +3 -0
  10. package/templates/blocks/DD-AnimText/config.ts +295 -0
  11. package/templates/blocks/DD-AnimText/manifest.json +85 -0
  12. package/templates/blocks/DD-AsideNav/Component.tsx +321 -0
  13. package/templates/blocks/DD-AsideNav/config.ts +204 -0
  14. package/templates/blocks/DD-AsideNav/manifest.json +80 -0
  15. package/templates/blocks/DD-Banner/Component.tsx +51 -0
  16. package/templates/blocks/DD-Banner/config.ts +57 -0
  17. package/templates/blocks/DD-Banner/manifest.json +72 -0
  18. package/templates/blocks/DD-BgLightning/Component.tsx +44 -0
  19. package/templates/blocks/DD-BgLightning/README.md +3 -0
  20. package/templates/blocks/DD-BgLightning/config.ts +97 -0
  21. package/templates/blocks/DD-BgLightning/manifest.json +79 -0
  22. package/templates/blocks/DD-BookCall/Component.tsx +10 -11
  23. package/templates/blocks/DD-BookCall/config.ts +238 -238
  24. package/templates/blocks/DD-ButtonPop/Component.tsx +117 -0
  25. package/templates/blocks/DD-ButtonPop/config.ts +105 -0
  26. package/templates/blocks/DD-ButtonPop/manifest.json +88 -0
  27. package/templates/blocks/DD-ButtonPop/svg-icons/Dust2.tsx +167 -0
  28. package/templates/blocks/DD-Card/Component.tsx +251 -0
  29. package/templates/blocks/DD-Card/README.md +4 -0
  30. package/templates/blocks/DD-Card/config.ts +154 -0
  31. package/templates/blocks/DD-Card/manifest.json +82 -0
  32. package/templates/blocks/DD-CardTemplate01/Component.tsx +5 -6
  33. package/templates/blocks/DD-CardTemplate01/config.ts +106 -106
  34. package/templates/blocks/DD-Carousel-A/Component.tsx +249 -249
  35. package/templates/blocks/DD-Carousel-B/Component.tsx +403 -403
  36. package/templates/blocks/DD-Chip/Component.tsx +250 -0
  37. package/templates/blocks/DD-Chip/EXAMPLES.md +313 -0
  38. package/templates/blocks/DD-Chip/README.md +202 -0
  39. package/templates/blocks/DD-Chip/config.ts +369 -0
  40. package/templates/blocks/DD-Chip/manifest.json +74 -0
  41. package/templates/blocks/DD-ComparisonTable/Component.tsx +256 -256
  42. package/templates/blocks/DD-Contact/Component.tsx +434 -434
  43. package/templates/blocks/DD-Contact/config.ts +22 -22
  44. package/templates/blocks/DD-Counter/Component.tsx +190 -0
  45. package/templates/blocks/DD-Counter/README.md +4 -0
  46. package/templates/blocks/DD-Counter/config.ts +291 -0
  47. package/templates/blocks/DD-Counter/manifest.json +74 -0
  48. package/templates/blocks/DD-CustomCSS/Component.tsx +8 -0
  49. package/templates/blocks/DD-CustomCSS/README.md +4 -0
  50. package/templates/blocks/DD-CustomCSS/addToLayout.md +5 -0
  51. package/templates/blocks/DD-CustomCSS/config.ts +22 -0
  52. package/templates/blocks/DD-CustomCSS/manifest.json +72 -0
  53. package/templates/blocks/DD-CustomCSS/renderCustomCSS.md +7 -0
  54. package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
  55. package/templates/blocks/DD-FeatCat/config.ts +201 -201
  56. package/templates/blocks/DD-FeatStrip/Component.tsx +585 -585
  57. package/templates/blocks/DD-FeatStrip/config.ts +134 -134
  58. package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +628 -0
  59. package/templates/blocks/DD-FluidBtn/Component.tsx +43 -0
  60. package/templates/blocks/DD-FluidBtn/README.md +6 -0
  61. package/templates/blocks/DD-FluidBtn/config.ts +102 -0
  62. package/templates/blocks/DD-FluidBtn/manifest.json +86 -0
  63. package/templates/blocks/DD-GooeyInfo/Component.tsx +149 -0
  64. package/templates/blocks/DD-GooeyInfo/config.ts +135 -0
  65. package/templates/blocks/DD-GooeyInfo/manifest.json +72 -0
  66. package/templates/blocks/DD-Hero/Component.tsx +40 -17
  67. package/templates/blocks/DD-Hero/config.ts +267 -267
  68. package/templates/blocks/DD-HorizontalScroll/Component.tsx +4 -2
  69. package/templates/blocks/DD-HorizontalScroll/config.ts +231 -231
  70. package/templates/blocks/DD-Icon/Component.tsx +287 -0
  71. package/templates/blocks/DD-Icon/README.md +4 -0
  72. package/templates/blocks/DD-Icon/config.ts +258 -0
  73. package/templates/blocks/DD-Icon/hooks/revalidateBlockIds.ts +66 -0
  74. package/templates/blocks/DD-Icon/manifest.json +82 -0
  75. package/templates/blocks/DD-IconButton/Component.tsx +455 -0
  76. package/templates/blocks/DD-IconButton/README.md +326 -0
  77. package/templates/blocks/DD-IconButton/config.ts +239 -0
  78. package/templates/blocks/DD-IconButton/manifest.json +85 -0
  79. package/templates/blocks/DD-IdTag/Component.tsx +21 -0
  80. package/templates/blocks/DD-IdTag/README.md +4 -0
  81. package/templates/blocks/DD-IdTag/config.ts +41 -0
  82. package/templates/blocks/DD-IdTag/manifest.json +72 -0
  83. package/templates/blocks/DD-Iframe/Component.tsx +45 -0
  84. package/templates/blocks/DD-Iframe/README.md +4 -0
  85. package/templates/blocks/DD-Iframe/config.ts +47 -0
  86. package/templates/blocks/DD-Iframe/manifest.json +72 -0
  87. package/templates/blocks/DD-IframeMedia/Component.tsx +129 -0
  88. package/templates/blocks/DD-IframeMedia/README.md +4 -0
  89. package/templates/blocks/DD-IframeMedia/config.ts +79 -0
  90. package/templates/blocks/DD-IframeMedia/manifest.json +72 -0
  91. package/templates/blocks/DD-LineAnim/Component.tsx +36 -0
  92. package/templates/blocks/DD-LineAnim/README.md +4 -0
  93. package/templates/blocks/DD-LineAnim/config.ts +90 -0
  94. package/templates/blocks/DD-LineAnim/manifest.json +80 -0
  95. package/templates/blocks/DD-LinkGrid/Component.tsx +119 -0
  96. package/templates/blocks/DD-LinkGrid/README.md +4 -0
  97. package/templates/blocks/DD-LinkGrid/config.ts +51 -0
  98. package/templates/blocks/DD-LinkGrid/manifest.json +74 -0
  99. package/templates/blocks/DD-LogoMarq/Component.tsx +132 -0
  100. package/templates/blocks/DD-LogoMarq/README.md +4 -0
  101. package/templates/blocks/DD-LogoMarq/config.ts +264 -0
  102. package/templates/blocks/DD-LogoMarq/manifest.json +82 -0
  103. package/templates/blocks/DD-Lottie/Component.tsx +459 -0
  104. package/templates/blocks/DD-Lottie/README.md +4 -0
  105. package/templates/blocks/DD-Lottie/config.ts +379 -0
  106. package/templates/blocks/DD-Lottie/index.tsx +28 -0
  107. package/templates/blocks/DD-Lottie/manifest.json +83 -0
  108. package/templates/blocks/DD-MagicBento/Component.tsx +777 -0
  109. package/templates/blocks/DD-MagicBento/README.md +4 -0
  110. package/templates/blocks/DD-MagicBento/config.ts +264 -0
  111. package/templates/blocks/DD-MagicBento/manifest.json +74 -0
  112. package/templates/blocks/DD-MasonaryMedia/Component.tsx +547 -545
  113. package/templates/blocks/DD-MasonaryMedia/config.ts +2 -2
  114. package/templates/blocks/DD-MasonaryMedia/vendor.d.ts +2 -2
  115. package/templates/blocks/DD-Masonry/Component.tsx +645 -0
  116. package/templates/blocks/DD-Masonry/README.md +4 -0
  117. package/templates/blocks/DD-Masonry/config.ts +475 -0
  118. package/templates/blocks/DD-Masonry/manifest.json +82 -0
  119. package/templates/blocks/DD-Pricing/Component.tsx +372 -372
  120. package/templates/blocks/DD-Process/Component.tsx +149 -149
  121. package/templates/blocks/DD-Process/config.ts +150 -150
  122. package/templates/blocks/DD-Progress/Component.tsx +141 -0
  123. package/templates/blocks/DD-Progress/README.md +4 -0
  124. package/templates/blocks/DD-Progress/config.ts +175 -0
  125. package/templates/blocks/DD-Progress/manifest.json +74 -0
  126. package/templates/blocks/DD-ProjHeader/Component.tsx +91 -0
  127. package/templates/blocks/DD-ProjHeader/README.md +4 -0
  128. package/templates/blocks/DD-ProjHeader/config.ts +96 -0
  129. package/templates/blocks/DD-ProjHeader/manifest.json +72 -0
  130. package/templates/blocks/DD-ProjSlider/Component.tsx +123 -0
  131. package/templates/blocks/DD-ProjSlider/README.md +4 -0
  132. package/templates/blocks/DD-ProjSlider/config.ts +64 -0
  133. package/templates/blocks/DD-ProjSlider/manifest.json +72 -0
  134. package/templates/blocks/DD-ScrollText/Component.tsx +114 -0
  135. package/templates/blocks/DD-ScrollText/README.md +4 -0
  136. package/templates/blocks/DD-ScrollText/config.ts +58 -0
  137. package/templates/blocks/DD-ScrollText/manifest.json +72 -0
  138. package/templates/blocks/DD-Section/Component.tsx +266 -266
  139. package/templates/blocks/DD-Separator/Component.tsx +66 -0
  140. package/templates/blocks/DD-Separator/README.md +4 -0
  141. package/templates/blocks/DD-Separator/config.ts +58 -0
  142. package/templates/blocks/DD-Separator/manifest.json +72 -0
  143. package/templates/blocks/DD-Services/Component.tsx +176 -176
  144. package/templates/blocks/DD-Services/config.ts +149 -149
  145. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +307 -307
  146. package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
  147. package/templates/blocks/DD-StackCards/Component.tsx +473 -473
  148. package/templates/blocks/DD-StackCards/config.ts +399 -399
  149. package/templates/blocks/DD-Team/Component.tsx +247 -247
  150. package/templates/blocks/DD-Team/config.ts +173 -173
  151. package/templates/blocks/DD-TeamCards/Component.tsx +251 -0
  152. package/templates/blocks/DD-TeamCards/README.md +4 -0
  153. package/templates/blocks/DD-TeamCards/config.ts +302 -0
  154. package/templates/blocks/DD-TeamCards/manifest.json +74 -0
  155. package/templates/blocks/DD-TechStack/Component.tsx +168 -162
  156. package/templates/blocks/DD-TechStack/config.ts +153 -153
  157. package/templates/blocks/DD-TestMarq/Component.tsx +132 -0
  158. package/templates/blocks/DD-TestMarq/README.md +4 -0
  159. package/templates/blocks/DD-TestMarq/RowLabel.tsx +9 -0
  160. package/templates/blocks/DD-TestMarq/config.ts +88 -0
  161. package/templates/blocks/DD-TestMarq/manifest.json +72 -0
  162. package/templates/blocks/DD-Testim/Component.tsx +247 -0
  163. package/templates/blocks/DD-Testim/README.md +4 -0
  164. package/templates/blocks/DD-Testim/config.ts +101 -0
  165. package/templates/blocks/DD-Testim/manifest.json +75 -0
  166. package/templates/blocks/DD-Testim2/Component.tsx +256 -0
  167. package/templates/blocks/DD-Testim2/README.md +4 -0
  168. package/templates/blocks/DD-Testim2/config.ts +100 -0
  169. package/templates/blocks/DD-Testim2/manifest.json +74 -0
  170. package/templates/blocks/DD-Testimonials/Component.tsx +147 -147
  171. package/templates/blocks/DD-Testimonials/config.ts +120 -120
  172. package/templates/blocks/DD-TextMarq/Component.tsx +157 -0
  173. package/templates/blocks/DD-TextMarq/README.md +4 -0
  174. package/templates/blocks/DD-TextMarq/config.ts +237 -0
  175. package/templates/blocks/DD-TextMarq/manifest.json +82 -0
  176. package/templates/blocks/DD-Thanks/Component.tsx +500 -0
  177. package/templates/blocks/DD-Thanks/README.md +4 -0
  178. package/templates/blocks/DD-Thanks/config.ts +224 -0
  179. package/templates/blocks/DD-Thanks/manifest.json +72 -0
  180. package/templates/blocks/DD-VertMarq/Component.tsx +154 -0
  181. package/templates/blocks/DD-VertMarq/README.md +4 -0
  182. package/templates/blocks/DD-VertMarq/config.ts +236 -0
  183. package/templates/blocks/DD-VertMarq/dist/index.css +6 -0
  184. package/templates/blocks/DD-VertMarq/dist/index.min.css +1 -0
  185. package/templates/blocks/DD-VertMarq/manifest.json +74 -0
  186. package/templates/blocks/DD-Work/Component.tsx +318 -315
  187. package/templates/blocks/DD-Work/config.ts +165 -165
  188. package/templates/blocks/DD-Work-B/Component.tsx +294 -294
  189. package/templates/blocks/DDHG-CTASection2/Component.tsx +185 -0
  190. package/templates/blocks/DDHG-CTASection2/config.ts +140 -0
  191. package/templates/blocks/DDHG-CTASection2/manifest.json +45 -0
  192. package/templates/blocks/DDHG-HighlightArc/Component.tsx +243 -0
  193. package/templates/blocks/DDHG-HighlightArc/config.ts +85 -0
  194. package/templates/blocks/DDHG-HighlightArc/manifest.json +46 -0
  195. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +93 -0
  196. package/templates/blocks/DDHG-LogoMarquee/config.ts +66 -0
  197. package/templates/blocks/DDHG-LogoMarquee/manifest.json +39 -0
  198. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +213 -0
  199. package/templates/blocks/DDHG-NavCodeGrid/config.ts +64 -0
  200. package/templates/blocks/DDHG-NavCodeGrid/manifest.json +45 -0
  201. package/templates/blocks/DDHG-ProcessSec/Component.tsx +575 -0
  202. package/templates/blocks/DDHG-ProcessSec/config.ts +170 -0
  203. package/templates/blocks/DDHG-ProcessSec/manifest.json +56 -0
  204. package/templates/blocks/DDHG-ServiceDetails/Component.tsx +164 -0
  205. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +394 -0
  206. package/templates/blocks/DDHG-ServiceDetails/config.ts +77 -0
  207. package/templates/blocks/DDHG-ServiceDetails/manifest.json +50 -0
  208. package/templates/blocks/DDHG-ServiceHero/Component.tsx +234 -0
  209. package/templates/blocks/DDHG-ServiceHero/config.ts +49 -0
  210. package/templates/blocks/DDHG-ServiceHero/manifest.json +26 -0
  211. package/templates/blocks/DDHG-ServiceOverview/Component.tsx +277 -0
  212. package/templates/blocks/DDHG-ServiceOverview/config.ts +64 -0
  213. package/templates/blocks/DDHG-ServiceOverview/manifest.json +44 -0
  214. package/templates/blocks/DDHG-StatsFAQ/Component.tsx +389 -0
  215. package/templates/blocks/DDHG-StatsFAQ/config.ts +204 -0
  216. package/templates/blocks/DDHG-StatsFAQ/manifest.json +45 -0
  217. package/templates/blocks/DDHG-TeamCards/Component.tsx +131 -0
  218. package/templates/blocks/DDHG-TeamCards/config.ts +48 -0
  219. package/templates/blocks/DDHG-TeamCards/manifest.json +29 -0
  220. package/templates/shared/src/components/BgLightning/index.tsx +242 -0
  221. package/templates/shared/src/components/ui/ShareButton/index.tsx +80 -0
  222. package/templates/shared/src/components/ui/anims/FadeOnScroll.tsx +48 -0
  223. package/templates/shared/src/components/ui/anims/FadeUpOnInView.tsx +63 -0
  224. package/templates/shared/src/components/ui/anims/GlareHover.tsx +111 -0
  225. package/templates/shared/src/components/ui/anims/LineAnims.tsx +81 -0
  226. package/templates/shared/src/components/ui/anims/TitleAnims01.tsx +54 -0
  227. package/templates/shared/src/components/ui/anims/animation-container.tsx +29 -0
  228. package/templates/shared/src/components/ui/anims/blur-in.tsx +26 -0
  229. package/templates/shared/src/components/ui/anims/counter.tsx +53 -0
  230. package/templates/shared/src/components/ui/anims/fade.tsx +83 -0
  231. package/templates/shared/src/components/ui/anims/gradual-spacing.tsx +39 -0
  232. package/templates/shared/src/components/ui/anims/pull-up.tsx +93 -0
  233. package/templates/shared/src/components/ui/anims/reveal-text.tsx +76 -0
  234. package/templates/shared/src/components/ui/anims/rotate-words.tsx +43 -0
  235. package/templates/shared/src/components/ui/anims/shiny-text.tsx +58 -0
  236. package/templates/shared/src/components/ui/anims/shuffle-text.tsx +85 -0
  237. package/templates/shared/src/components/ui/anims/split-text.tsx +211 -0
  238. package/templates/shared/src/components/ui/anims/staggered-fade.tsx +133 -0
  239. package/templates/shared/src/components/ui/anims/text-scroll.tsx +111 -0
  240. package/templates/shared/src/components/ui/anims/typing-effect.tsx +112 -0
  241. package/templates/shared/src/components/ui/button-dusted-fluid/index.tsx +1521 -0
  242. package/templates/shared/src/components/ui/separator.tsx +85 -0
  243. package/templates/shared/src/hooks/useBodyScrollLock.ts +97 -0
  244. package/templates/shared/src/hooks/useClickOutside.ts +28 -0
  245. package/templates/shared/src/utilities/renderCustomCSS.ts +5 -0
  246. package/templates/shared/src/utilities/smoothScroll.ts +65 -0
@@ -0,0 +1,287 @@
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import { motion } from 'framer-motion'
5
+ import Link from 'next/link'
6
+ import { usePathname } from 'next/navigation'
7
+
8
+ import { cn } from '@/utilities/ui'
9
+ import { Media } from '@/components/Media'
10
+ import { smoothScrollTo, isAnchorLink } from '@/utilities/smoothScroll'
11
+
12
+ export type IconItem = {
13
+ icon?: any
14
+ iconSize?: string
15
+ customIconSize?: string
16
+ iconClassName?: string
17
+ label?: string
18
+ labelClassName?: string
19
+ flexDirection?: 'flex-col' | 'flex-col-reverse' | 'flex-row' | 'flex-row-reverse'
20
+ flexGap?: string
21
+ link?: string
22
+ imgCustomStyle?: Record<string, any>
23
+ }
24
+
25
+ export type CustomIconBlockProps = {
26
+ blockName?: string
27
+ blockType?: 'customIconBlock'
28
+ icons?: IconItem[]
29
+ className?: string
30
+ iconGridClass?: string
31
+ imgCustomStyle?: React.CSSProperties
32
+ priority?: boolean
33
+ }
34
+
35
+ const containerVariants = {
36
+ hidden: { opacity: 0 },
37
+ visible: {
38
+ opacity: 1,
39
+ transition: {
40
+ staggerChildren: 0.15,
41
+ delayChildren: 0.1
42
+ }
43
+ }
44
+ }
45
+
46
+ const itemVariants = {
47
+ hidden: { opacity: 0, y: 30, scale: 0.8 },
48
+ visible: {
49
+ opacity: 1,
50
+ y: 0,
51
+ scale: 1,
52
+ transition: {
53
+ duration: 0.4,
54
+ ease: [0.4, 0, 0.2, 1] as const
55
+ }
56
+ }
57
+ }
58
+
59
+ export const DDIcon: React.FC<CustomIconBlockProps> = ({
60
+ blockName,
61
+ icons = [],
62
+ iconGridClass,
63
+ imgCustomStyle: blockImgStyle,
64
+ priority = false
65
+ }) => {
66
+ const pathname = usePathname()
67
+ const hasCustomGridClass = iconGridClass && iconGridClass.trim().length > 0
68
+
69
+ if (!icons || !Array.isArray(icons) || icons.length === 0) {
70
+ return (
71
+ <div className="custom-icon-block-empty p-8 text-center">
72
+ <div className="text-sm text-gray-400">No icons to display</div>
73
+ {blockName && <div className="mt-1 text-xs text-gray-300">Block: {blockName}</div>}
74
+ </div>
75
+ )
76
+ }
77
+
78
+ const isExternalUrl = (url: string): boolean => {
79
+ return (
80
+ url.startsWith('http://') ||
81
+ url.startsWith('https://') ||
82
+ url.startsWith('mailto:') ||
83
+ url.startsWith('tel:')
84
+ )
85
+ }
86
+
87
+ const handleClick = (href: string, e: React.MouseEvent) => {
88
+ const hashIndex = href.indexOf('#')
89
+ const hasHash = hashIndex >= 0
90
+ const basePath = hasHash ? href.slice(0, hashIndex) : href
91
+ const hash = hasHash ? href.slice(hashIndex) : ''
92
+
93
+ const normalise = (p?: string) => (p ? p.replace(/\/$/, '') : '')
94
+
95
+ const isSamePageHash =
96
+ hasHash && (basePath === '' || normalise(basePath) === normalise(pathname))
97
+
98
+ if (isAnchorLink(href) || isSamePageHash) {
99
+ e.preventDefault()
100
+
101
+ const target = hash || href
102
+
103
+ smoothScrollTo(target, {
104
+ duration: 1,
105
+ offsetY: 80,
106
+ ease: 'power2.out'
107
+ })
108
+ }
109
+ }
110
+
111
+ const extractSizeFromClass = (sizeClass: string) => {
112
+ const widthMatch = sizeClass.match(/w-(\d+)/)
113
+ const heightMatch = sizeClass.match(/h-(\d+)/)
114
+
115
+ if (widthMatch && heightMatch) {
116
+ const width = parseInt(widthMatch[1], 10) * 4
117
+ const height = parseInt(heightMatch[1], 10) * 4
118
+
119
+ return {
120
+ width: `${width}px`,
121
+ height: `${height}px`,
122
+ maxWidth: `${width}px`,
123
+ maxHeight: `${height}px`
124
+ }
125
+ }
126
+
127
+ return {}
128
+ }
129
+
130
+ return (
131
+ <motion.div
132
+ initial="hidden"
133
+ whileInView="visible"
134
+ viewport={{ once: true, amount: 0.2 }}
135
+ variants={containerVariants}
136
+ data-block-type="customIconBlock"
137
+ data-block-name={blockName}
138
+ className={cn(
139
+ 'custom-icon-block',
140
+ !hasCustomGridClass && 'grid grid-cols-3 gap-6 text-center md:grid-cols-4 lg:grid-cols-5',
141
+ hasCustomGridClass && iconGridClass
142
+ )}
143
+ >
144
+ {icons.map((iconItem, index) => {
145
+ if (!iconItem) return null
146
+
147
+ const iconKey = `icon-${index}${iconItem.icon?.filename ? `-${iconItem.icon.filename}` : ''}`
148
+
149
+ if (!iconItem.icon) {
150
+ return (
151
+ <motion.div
152
+ key={iconKey}
153
+ variants={itemVariants}
154
+ className="flex flex-col items-center"
155
+ >
156
+ <div className="flex h-16 w-16 items-center justify-center border-2 border-gray-300 bg-gray-100 p-0 text-xs text-gray-400">
157
+ <span>No Icon</span>
158
+ </div>
159
+
160
+ {iconItem.label && (
161
+ <span className="custom-icon-label mt-2 text-center text-xs leading-tight text-gray-600">
162
+ {iconItem.label}
163
+ </span>
164
+ )}
165
+ </motion.div>
166
+ )
167
+ }
168
+
169
+ const iconSizeClass =
170
+ iconItem.iconSize === 'custom'
171
+ ? iconItem.customIconSize || 'h-16 w-16'
172
+ : iconItem.iconSize || 'h-16 w-16'
173
+
174
+ const sizeStyles = extractSizeFromClass(iconSizeClass)
175
+
176
+ const mediaContent = (
177
+ <Media
178
+ priority={priority ?? false}
179
+ resource={iconItem.icon}
180
+ className={cn(
181
+ 'custom-icon-media flex items-center justify-center p-0 transition-transform',
182
+ '[&>picture]:!mb-0 [&>picture]:!mt-0 [&>picture]:flex [&>picture]:h-full [&>picture]:w-full [&>picture]:items-center [&>picture]:justify-center',
183
+ '[&>picture>img]:block [&>picture>img]:h-full [&>picture>img]:w-full [&>picture>img]:object-contain',
184
+ '[&_img]:block [&_img]:h-full [&_img]:w-full [&_img]:object-contain',
185
+ iconSizeClass,
186
+ iconItem.iconClassName
187
+ )}
188
+ imgClassName={cn(
189
+ 'block h-full w-full object-contain transition-all duration-300',
190
+ iconItem.iconClassName
191
+ )}
192
+ />
193
+ )
194
+
195
+ const wrapperFlexDirection = iconItem.flexDirection || 'flex-col'
196
+ const wrapperGap = iconItem.flexGap || 'gap-2'
197
+
198
+ const defaultLabelClass = 'mt-2 max-w-20 text-center text-xs leading-tight text-gray-700'
199
+
200
+ const labelContent = iconItem.label ? (
201
+ <span className={cn('custom-icon-label', iconItem.labelClassName || defaultLabelClass)}>
202
+ {iconItem.label}
203
+ </span>
204
+ ) : null
205
+
206
+ const iconContent = (
207
+ <>
208
+ {mediaContent}
209
+ {labelContent}
210
+ </>
211
+ )
212
+
213
+ return (
214
+ <motion.div
215
+ key={iconKey}
216
+ variants={itemVariants}
217
+ className={cn(
218
+ 'custom-icon-item group flex items-center',
219
+ wrapperFlexDirection,
220
+ wrapperGap
221
+ )}
222
+ >
223
+ {iconItem.link && iconItem.link.trim() ? (
224
+ (() => {
225
+ const href = iconItem.link!.trim()
226
+ const hasHash = href.includes('#')
227
+ const base = hasHash ? href.split('#')[0] : href
228
+ const normalise = (p?: string) => (p ? p.replace(/\/$/, '') : '')
229
+
230
+ const onSamePage =
231
+ hasHash && (base === '' || normalise(base) === normalise(pathname))
232
+
233
+ if (isAnchorLink(href) || onSamePage) {
234
+ return (
235
+ <a
236
+ href={href}
237
+ className={cn(
238
+ 'custom-icon-block-link custom-icon-link flex cursor-pointer items-center brightness-100 transition-all duration-300 ease-in-out hover:brightness-110',
239
+ wrapperFlexDirection,
240
+ wrapperGap
241
+ )}
242
+ onClick={(e) => handleClick(href, e)}
243
+ aria-label={iconItem.label || 'Navigate to section'}
244
+ >
245
+ {iconContent}
246
+ </a>
247
+ )
248
+ }
249
+
250
+ const target = isExternalUrl(href) ? '_blank' : '_self'
251
+ const rel = isExternalUrl(href) ? 'noopener noreferrer' : undefined
252
+
253
+ return (
254
+ <Link
255
+ href={href}
256
+ className={cn(
257
+ 'custom-icon-block-link custom-icon-link flex items-center brightness-100 transition-all duration-300 ease-in-out hover:brightness-110',
258
+ wrapperFlexDirection,
259
+ wrapperGap
260
+ )}
261
+ target={target}
262
+ rel={rel}
263
+ aria-label={iconItem.label || 'Navigate to link'}
264
+ >
265
+ {iconContent}
266
+ </Link>
267
+ )
268
+ })()
269
+ ) : (
270
+ <div
271
+ className={cn(
272
+ 'custom-icon-wrapper flex items-center brightness-110 text-white/80 dark:text-stone-200/80',
273
+ wrapperFlexDirection,
274
+ wrapperGap
275
+ )}
276
+ >
277
+ {iconContent}
278
+ </div>
279
+ )}
280
+ </motion.div>
281
+ )
282
+ })}
283
+ </motion.div>
284
+ )
285
+ }
286
+
287
+ export default DDIcon
@@ -0,0 +1,4 @@
1
+ # DD-Icon
2
+
3
+ Portable first-pass conversion of `CustomIconBlock` 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
+
@@ -0,0 +1,258 @@
1
+ import { Block } from 'payload'
2
+
3
+ export const DDIcon: Block = {
4
+ slug: 'DD-Icon',
5
+ interfaceName: 'DDIcon',
6
+ labels: {
7
+ singular: 'Icon Block',
8
+ plural: 'Icon Blocks'
9
+ },
10
+ fields: [
11
+ // Block Settings
12
+ {
13
+ admin: {
14
+ initCollapsed: true,
15
+ hidden: true,
16
+ className: 'block-settings-group'
17
+ },
18
+ type: 'collapsible',
19
+ label: 'Block Settings',
20
+ fields: [
21
+ {
22
+ name: 'blockName',
23
+ type: 'text',
24
+ label: 'Block Name',
25
+ admin: {
26
+ description: 'Optional name for easier identification in admin',
27
+ placeholder: 'e.g., "Social Media Icons", "Technology Stack"'
28
+ }
29
+ }
30
+ ]
31
+ },
32
+
33
+ // Layout Settings
34
+ {
35
+ admin: {
36
+ initCollapsed: true,
37
+ className: 'layout-settings-group'
38
+ },
39
+ type: 'collapsible',
40
+ label: 'Layout Settings',
41
+ fields: [
42
+ {
43
+ name: 'iconGridClass',
44
+ type: 'text',
45
+ label: 'Grid Layout Classes',
46
+ admin: {
47
+ description:
48
+ 'Custom Tailwind grid classes. Default: grid grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-6 text-center',
49
+ placeholder: 'grid grid-cols-2 md:grid-cols-3 gap-8'
50
+ }
51
+ }
52
+ ]
53
+ },
54
+
55
+ // Icons Collection
56
+ {
57
+ name: 'icons',
58
+ type: 'array',
59
+ label: 'Icons',
60
+ minRows: 1,
61
+ maxRows: 20,
62
+ labels: {
63
+ singular: 'Icon',
64
+ plural: 'Icons'
65
+ },
66
+ fields: [
67
+ {
68
+ type: 'row',
69
+ fields: [
70
+ // Icon Upload
71
+ {
72
+ name: 'icon',
73
+ type: 'upload',
74
+ relationTo: 'media',
75
+ required: true,
76
+ label: 'Icon Image',
77
+ admin: {
78
+ width: '60%',
79
+ description: 'Upload or select from media library'
80
+ },
81
+ filterOptions: {
82
+ mimeType: { contains: 'image' }
83
+ }
84
+ },
85
+ // NEW: Priority Checkbox
86
+ {
87
+ name: 'priority',
88
+ type: 'checkbox',
89
+ label: 'Priority',
90
+ defaultValue: false,
91
+ admin: {
92
+ className: 'custom-checkbox-mt',
93
+ width: '35%',
94
+ description: 'Icon is above-the-fold.'
95
+ }
96
+ },
97
+ // Icon Size
98
+ {
99
+ name: 'iconSize',
100
+ type: 'select',
101
+ label: 'Icon Size',
102
+ defaultValue: 'w-16 h-16',
103
+ options: [
104
+ { label: 'Small (48px)', value: 'w-12 h-12' },
105
+ { label: 'Medium (64px)', value: 'w-16 h-16' },
106
+ { label: 'Large (80px)', value: 'w-20 h-20' },
107
+ { label: 'Extra Large (96px)', value: 'w-24 h-24' },
108
+ { label: 'Custom Size', value: 'custom' }
109
+ ],
110
+ admin: {
111
+ width: '100%',
112
+ description: 'Pre-defined sizes or choose manual input'
113
+ }
114
+ }
115
+ ]
116
+ },
117
+ // Icon Label
118
+ {
119
+ name: 'label',
120
+ type: 'text',
121
+ label: 'Icon Label'
122
+ // admin: {
123
+ // description: 'Text displayed below the icon'
124
+ // }
125
+ },
126
+
127
+ // Label & Layout Settings
128
+ {
129
+ admin: {
130
+ initCollapsed: true,
131
+ className: 'label-layout-group'
132
+ },
133
+ type: 'collapsible',
134
+ label: 'Label & Layout Settings (Optional)',
135
+ fields: [
136
+ {
137
+ name: 'labelClassName',
138
+ type: 'text',
139
+ label: 'Label CSS Classes',
140
+ admin: {
141
+ description:
142
+ 'Override default label classes: mt-2 text-xs text-center leading-tight max-w-20 text-gray-700',
143
+ placeholder: 'text-sm font-bold text-blue-500 mt-4'
144
+ }
145
+ },
146
+ {
147
+ type: 'row',
148
+ fields: [
149
+ {
150
+ name: 'flexDirection',
151
+ type: 'select',
152
+ label: 'Flex Direction',
153
+ defaultValue: 'flex-col',
154
+ options: [
155
+ { label: 'Column (Icon → Label)', value: 'flex-col' },
156
+ { label: 'Column Reverse (Label → Icon)', value: 'flex-col-reverse' },
157
+ { label: 'Row (Icon | Label)', value: 'flex-row' },
158
+ { label: 'Row Reverse (Label | Icon)', value: 'flex-row-reverse' }
159
+ ],
160
+ admin: {
161
+ width: '50%',
162
+ description: 'Direction of icon and label arrangement'
163
+ }
164
+ },
165
+ {
166
+ name: 'flexGap',
167
+ type: 'select',
168
+ label: 'Gap Distance',
169
+ defaultValue: 'gap-2',
170
+ options: [
171
+ { label: '0px', value: 'gap-0' },
172
+ { label: '4px', value: 'gap-1' },
173
+ { label: '8px (Default)', value: 'gap-2' },
174
+ { label: '12px', value: 'gap-3' },
175
+ { label: '16px', value: 'gap-4' },
176
+ { label: '20px', value: 'gap-5' },
177
+ { label: '24px', value: 'gap-6' },
178
+ { label: '32px', value: 'gap-8' }
179
+ ],
180
+ admin: {
181
+ width: '50%',
182
+ description: 'Space between icon and label'
183
+ }
184
+ }
185
+ ]
186
+ }
187
+ ]
188
+ },
189
+
190
+ // Icon Link
191
+ {
192
+ name: 'link',
193
+ type: 'text',
194
+ label: 'Link URL (Optional)',
195
+ admin: {
196
+ description:
197
+ 'Make this icon clickable. Supports internal (/about), external (https://example.com), and anchor (#section) links',
198
+ placeholder: '/services or https://example.com or #contact'
199
+ },
200
+ validate: (val: string | null | undefined) => {
201
+ if (!val) return true // Optional field
202
+
203
+ // Basic URL validation - allow internal paths, external URLs, and anchors
204
+ const urlPattern = /^(https?:\/\/|\/|#|mailto:|tel:)/
205
+ if (!urlPattern.test(val)) {
206
+ return 'Please enter a valid URL (starting with http://, https://, /, #, mailto:, or tel:)'
207
+ }
208
+ return true
209
+ }
210
+ },
211
+
212
+ // Icon Styling
213
+ {
214
+ admin: {
215
+ initCollapsed: true,
216
+ className: 'icon-styling-group'
217
+ },
218
+ type: 'collapsible',
219
+ label: 'Icon Styling (Optional)',
220
+ fields: [
221
+ {
222
+ name: 'customIconSize',
223
+ type: 'text',
224
+ label: 'Custom Size Classes',
225
+ admin: {
226
+ description: 'Custom Tailwind size classes (only when "Custom Size" is selected)',
227
+ placeholder: 'w-14 h-14 or w-32 h-32',
228
+ condition: (data, siblingData) => siblingData?.iconSize === 'custom'
229
+ }
230
+ },
231
+ {
232
+ name: 'iconClassName',
233
+ type: 'text',
234
+ label: 'Additional CSS Classes',
235
+ admin: {
236
+ description:
237
+ 'Extra Tailwind classes for styling. Other classes to override: custom-icon-wrapper custom-icon-link ',
238
+ placeholder: 'rounded-full shadow-lg border-2 border-lime-500'
239
+ }
240
+ },
241
+ {
242
+ name: 'imgCustomStyle',
243
+ type: 'json',
244
+ label: 'Custom Inline Styles',
245
+ admin: {
246
+ description:
247
+ 'Advanced: Custom CSS as JSON object (e.g., { "filter": "drop-shadow(0 0 10px rgba(0,0,0,0.5))", "borderRadius": "50%" })',
248
+ className: 'json-field'
249
+ }
250
+ }
251
+ ]
252
+ }
253
+
254
+ // ✅ Removed the iconId field entirely - React will use array index for keys
255
+ ]
256
+ }
257
+ ]
258
+ }
@@ -0,0 +1,66 @@
1
+ // NO LONGER NEEDED - Keeping for reference
2
+
3
+ // import type { FieldHook } from 'payload'
4
+
5
+ // /**
6
+ // * Hook to sanitize problematic blockId fields from CustomIconBlock components
7
+ // * This prevents conflicts with Payload's internal systems during save/publish
8
+ // * by regenerating safe blockId values without removing the fields
9
+ // */
10
+ // export const cleanConflictingBlockIds: FieldHook = ({ value, operation }) => {
11
+ // if (operation === 'create' || operation === 'update') {
12
+ // if (value?.root?.children) {
13
+ // return {
14
+ // ...value,
15
+ // root: {
16
+ // ...value.root,
17
+ // children: cleanLexicalContent(value.root.children),
18
+ // },
19
+ // }
20
+ // }
21
+ // }
22
+
23
+ // return value
24
+ // }
25
+
26
+ // function cleanLexicalContent(children: any[]): any[] {
27
+ // if (!Array.isArray(children)) return children
28
+
29
+ // return children.map((child: any) => {
30
+ // // Handle CustomIconBlock in Lexical format
31
+ // if (child.type === 'block' && child.fields?.blockType === 'customIconBlock') {
32
+ // const fields = { ...child.fields }
33
+
34
+ // // Sanitize blockId if it exists and might cause conflicts
35
+ // if (fields.blockId) {
36
+ // fields.blockId = `custom-icon-${Math.random().toString(36).substr(2, 12)}`
37
+ // }
38
+
39
+ // // Sanitize icons array if present
40
+ // if (fields.icons?.length > 0) {
41
+ // fields.icons = fields.icons.map((icon: any, index: number) => {
42
+ // const cleanIcon = { ...icon }
43
+ // if (cleanIcon.iconId) {
44
+ // cleanIcon.iconId = `icon-${index}-${Math.random().toString(36).substr(2, 9)}`
45
+ // }
46
+ // return cleanIcon
47
+ // })
48
+ // }
49
+
50
+ // return {
51
+ // ...child,
52
+ // fields,
53
+ // }
54
+ // }
55
+
56
+ // // Process children only one level deep to avoid infinite recursion
57
+ // if (child.children && Array.isArray(child.children)) {
58
+ // return {
59
+ // ...child,
60
+ // children: cleanLexicalContent(child.children),
61
+ // }
62
+ // }
63
+
64
+ // return child
65
+ // })
66
+ // }
@@ -0,0 +1,82 @@
1
+ {
2
+ "type": "block",
3
+ "name": "DD-Icon",
4
+ "label": "Custom Icon",
5
+ "sourceName": "CustomIconBlock",
6
+ "portableName": "DD-Icon",
7
+ "slug": "DD-Icon",
8
+ "version": "0.0.1",
9
+ "source": {
10
+ "repo": "Hadizainal/DesignsDustedv3",
11
+ "originalPath": "src/blocks/ROUND-TWO-CONVERSION/CustomIconBlock",
12
+ "originalSlug": "customIconBlock",
13
+ "originalConfigExport": "CustomIconBlock",
14
+ "originalComponentExport": "CustomIconBlock",
15
+ "originalInterfaceName": "DDIcon"
16
+ },
17
+ "files": [
18
+ {
19
+ "from": "Component.tsx",
20
+ "to": "src/blocks/DD-Icon/Component.tsx"
21
+ },
22
+ {
23
+ "from": "config.ts",
24
+ "to": "src/blocks/DD-Icon/config.ts"
25
+ }
26
+ ],
27
+ "dependencies": [
28
+ "framer-motion"
29
+ ],
30
+ "sharedFiles": [
31
+ {
32
+ "from": "templates/shared/src/utilities/smoothScroll.ts",
33
+ "to": "src/utilities/smoothScroll.ts",
34
+ "required": true,
35
+ "safeToCopy": true
36
+ }
37
+ ],
38
+ "requiredProjectFiles": [],
39
+ "optionalProjectFiles": [],
40
+ "collectionRegistration": {
41
+ "enabled": true,
42
+ "collections": [
43
+ "Pages",
44
+ "Posts"
45
+ ]
46
+ },
47
+ "renderBlocksRegistration": {
48
+ "enabled": true
49
+ },
50
+ "renderBlocksBehavior": {
51
+ "wrapperless": false
52
+ },
53
+ "pageRouteIntegration": {
54
+ "required": false,
55
+ "manualSteps": []
56
+ },
57
+ "manual": [
58
+ "Register DDIcon in Pages/Posts blocks arrays if automatic registration was skipped.",
59
+ "Register DD-Icon in RenderBlocks if automatic registration was skipped.",
60
+ "Run payload generate:types after schema changes."
61
+ ],
62
+ "manualSteps": [
63
+ "Register DDIcon in Pages/Posts blocks arrays if automatic registration was skipped.",
64
+ "Register DD-Icon in RenderBlocks if automatic registration was skipped.",
65
+ "Run payload generate:types after schema changes."
66
+ ],
67
+ "patchTargets": {
68
+ "blockConfigRegistration": {
69
+ "description": "Installer can add import { DDIcon } from '@/blocks/DD-Icon/config' and add DDIcon to clear Pages/Posts block arrays."
70
+ },
71
+ "renderBlocksRegistration": {
72
+ "description": "Installer can register DD-Icon in RenderBlocks."
73
+ }
74
+ },
75
+ "notes": [
76
+ "Converted from ROUND-TWO-CONVERSION as a portable Dusted template.",
77
+ "Project-specific imports were replaced with local, dependency-light rendering where required.",
78
+ "Manifest includes shared local support files required by original DesignsDusted source imports."
79
+ ],
80
+ "exportName": "DDIcon",
81
+ "interfaceName": "DDIcon"
82
+ }