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,131 +1,140 @@
1
- 'use client'
2
-
3
- import { useEffect, useRef } from 'react'
4
- import Image from 'next/image'
5
- import Link from 'next/link'
6
- import { Globe, Briefcase, Camera } from 'lucide-react'
7
- import { cn } from '@/utilities/ui'
8
- import { getMediaUrl } from '@/utilities/getMediaUrl'
9
-
10
- type MediaDoc = {
11
- url?: string | null
12
- alt?: string | null
13
- }
14
-
15
- type TeamMember = {
16
- name?: string | null
17
- role?: string | null
18
- bio?: string | null
19
- image?: number | MediaDoc | null
20
- priority?: boolean | null
21
- facebookUrl?: string | null
22
- linkedinUrl?: string | null
23
- instagramUrl?: string | null
24
- id?: string | null
25
- }
26
-
27
- export type DDHGTeamCardsProps = {
28
- sectionId?: string | null
29
- customClassName?: string | null
30
- customCSS?: string | null
31
- members?: TeamMember[] | null
32
- }
33
-
34
- function getMediaDoc(img?: number | MediaDoc | null): MediaDoc | null {
35
- if (!img || typeof img === 'number') return null
36
- return img
37
- }
38
-
39
- export const DDHGTeamCards: React.FC<DDHGTeamCardsProps> = ({
40
- sectionId,
41
- customClassName,
42
- customCSS,
43
- members,
44
- }) => {
45
- const ref = useRef<HTMLDivElement>(null)
46
-
47
- useEffect(() => {
48
- const observer = new IntersectionObserver(
49
- (entries) => {
50
- entries.forEach((entry) => {
51
- if (entry.isIntersecting) {
52
- entry.target.querySelectorAll<HTMLElement>('.reveal').forEach((el, i) => {
53
- setTimeout(() => {
54
- el.style.opacity = '1'
55
- el.style.transform = 'translateY(0)'
56
- }, i * 100)
57
- })
58
- observer.unobserve(entry.target)
59
- }
60
- })
61
- },
62
- { threshold: 0.1 },
63
- )
64
- if (ref.current) observer.observe(ref.current)
65
- return () => observer.disconnect()
66
- }, [])
67
-
68
- return (
69
- <div id={sectionId ?? undefined} className={cn('px-5 md:px-8 py-8', customClassName)} ref={ref}>
70
- {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
71
- <div className="max-w-7xl mx-auto">
72
- {members && members.length > 0 && (
73
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6">
74
- {members.map((member, i) => {
75
- const imageDoc = getMediaDoc(member.image)
76
- const imageUrl = getMediaUrl(imageDoc?.url)
77
- const socials = [
78
- { Icon: Globe, url: member.facebookUrl, label: 'Facebook' },
79
- { Icon: Briefcase, url: member.linkedinUrl, label: 'LinkedIn' },
80
- { Icon: Camera, url: member.instagramUrl, label: 'Instagram' },
81
- ].filter((s) => !!s.url)
82
-
83
- return (
84
- <div
85
- key={member.id != null ? `id-${member.id}` : `idx-${i}`}
86
- className="reveal group relative overflow-hidden cursor-pointer bg-transparent h-[28rem] md:h-[32rem]"
87
- style={{ opacity: 0, transform: 'translateY(20px)', transition: `opacity 0.9s ease ${i * 0.1}s, transform 0.9s ease ${i * 0.1}s` }}
88
- >
89
- {imageDoc && imageUrl && (
90
- <div className="relative w-full h-full overflow-hidden brightness-100 group-hover:brightness-50">
91
- <Image
92
- src={imageUrl}
93
- alt={imageDoc.alt ?? member.name ?? ''}
94
- fill
95
- sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
96
- priority={Boolean(member.priority)}
97
- className="team-member object-cover object-top brightness-100 group-hover:brightness-50 group-hover:scale-110 transition-transform duration-500 ease-out"
98
- />
99
- </div>
100
- )}
101
- <div className="pattern-overlay absolute inset-0 opacity-[0.03] pointer-events-none" style={{ backgroundImage: 'repeating-linear-gradient(90deg, #000 0px, #000 1px, transparent 1px, transparent 20px)' }} />
102
- <div className="gradient-overlay absolute inset-0 bg-gradient-to-t from-black/80 via-black/80 to-transparent group-hover:from-black/90 transition-all duration-300 group-hover:backdrop-brightness-50" />
103
- <div className="content-card absolute inset-0 flex flex-col justify-end p-3 text-white">
104
- <div className="z-10 text-center mb-3">
105
- {member.name && <h3 className="text-xl md:text-2xl font-bold mb-1 text-white drop-shadow-lg" style={{ fontFamily: 'Outfit, sans-serif', fontWeight: 700 }}>{member.name}</h3>}
106
- {member.role && <p className="text-xs md:text-sm font-medium uppercase tracking-wider text-stone-200 dark:text-stone-200 drop-shadow-md" style={{ fontFamily: "'Inter', sans-serif", fontWeight: 500 }}>{member.role}</p>}
107
- </div>
108
- <div className="hover-panel transform translate-y-full group-hover:translate-y-0 transition-transform duration-300 ease-out p-4" style={{ background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(8px)' }}>
109
- {member.bio && <p className="text-[13px] text-white/70 leading-relaxed mb-4 opacity-0 group-hover:opacity-100 transition-opacity duration-300 delay-100">{member.bio}</p>}
110
- {socials.length > 0 && (
111
- <div className="socials-map flex gap-3 justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300 delay-200">
112
- {socials.map(({ Icon, url, label }) => (
113
- <Link key={label} href={url!} target="_blank" rel="noopener noreferrer" aria-label={`${member.name} on ${label}`} className="text-white/70 hover:text-white hover:scale-110 transition-all duration-200 p-2 rounded-full bg-white/10 hover:bg-white/20 backdrop-blur-sm">
114
- <Icon size={14} />
115
- </Link>
116
- ))}
117
- </div>
118
- )}
119
- </div>
120
- </div>
121
- </div>
122
- )
123
- })}
124
- </div>
125
- )}
126
- </div>
127
- </div>
128
- )
129
- }
130
-
131
- export default DDHGTeamCards
1
+ 'use client'
2
+
3
+ import Image from 'next/image'
4
+ import Link from 'next/link'
5
+ import { Globe, Briefcase, Camera } from 'lucide-react'
6
+ import { cn } from '@/utilities/ui'
7
+ import { getMediaUrl } from '@/utilities/getMediaUrl'
8
+
9
+ type MediaDoc = {
10
+ url?: string | null
11
+ alt?: string | null
12
+ }
13
+
14
+ type TeamMember = {
15
+ name?: string | null
16
+ role?: string | null
17
+ bio?: string | null
18
+ image?: number | MediaDoc | null
19
+ priority?: boolean | null
20
+ facebookUrl?: string | null
21
+ linkedinUrl?: string | null
22
+ instagramUrl?: string | null
23
+ id?: string | null
24
+ }
25
+
26
+ export type DDHGTeamCardsProps = {
27
+ sectionId?: string | null
28
+ customClassName?: string | null
29
+ customCSS?: string | null
30
+ members?: TeamMember[] | null
31
+ }
32
+
33
+ function getMediaDoc(img?: number | MediaDoc | null): MediaDoc | null {
34
+ if (!img || typeof img === 'number') return null
35
+ return img
36
+ }
37
+
38
+ export const DDHGTeamCards: React.FC<DDHGTeamCardsProps> = ({
39
+ sectionId,
40
+ customClassName,
41
+ customCSS,
42
+ members,
43
+ }) => {
44
+ return (
45
+ <div id={sectionId ?? undefined} className={cn('w-full px-5 py-8 md:px-8', customClassName)}>
46
+ {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
47
+ <div className="mx-auto w-full max-w-7xl">
48
+ {members && members.length > 0 && (
49
+ <div className="grid w-full grid-cols-1 gap-5 md:grid-cols-2 md:gap-6 lg:grid-cols-3">
50
+ {members.map((member, i) => {
51
+ const imageDoc = getMediaDoc(member.image)
52
+ const imageUrl = getMediaUrl(imageDoc?.url)
53
+ const socials = [
54
+ { Icon: Globe, url: member.facebookUrl, label: 'Facebook' },
55
+ { Icon: Briefcase, url: member.linkedinUrl, label: 'LinkedIn' },
56
+ { Icon: Camera, url: member.instagramUrl, label: 'Instagram' },
57
+ ].filter((s) => !!s.url)
58
+
59
+ return (
60
+ <div
61
+ key={member.id != null ? `id-${member.id}` : `idx-${i}`}
62
+ className="group relative overflow-hidden cursor-pointer rounded-md squircle border-none border-stone-300/0 bg-stone-100/50 transition-all duration-300 hover:shadow-2xl dark:border-stone-700/0 h-[28rem] dark:bg-stone-900/50 md:h-[32rem]"
63
+ >
64
+ {imageDoc && imageUrl && (
65
+ <div className="relative w-full h-full overflow-hidden brightness-100 group-hover:brightness-50 transition-all duration-300">
66
+ <Image
67
+ src={imageUrl}
68
+ alt={imageDoc.alt ?? member.name ?? ''}
69
+ fill
70
+ sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
71
+ priority={Boolean(member.priority)}
72
+ className="team-member object-cover object-top brightness-100 group-hover:brightness-50 group-hover:scale-110 transition-transform duration-500 ease-out"
73
+ />
74
+ </div>
75
+ )}
76
+ <div
77
+ className="pattern-overlay absolute inset-0 opacity-[0.03] pointer-events-none"
78
+ style={{
79
+ backgroundImage:
80
+ 'repeating-linear-gradient(90deg, #000 0px, #000 1px, transparent 1px, transparent 20px)',
81
+ }}
82
+ />
83
+ <div className="gradient-overlay absolute inset-0 bg-linear-to-t from-black/80 via-black/80 to-transparent group-hover:from-black/90 transition-all duration-300 group-hover:backdrop-brightness-50" />
84
+ <div className="content-card absolute inset-0 flex flex-col justify-end p-3 text-white">
85
+ <div className="z-10 text-center mb-3">
86
+ {member.name && (
87
+ <h3
88
+ className="text-xl md:text-2xl font-bold mb-1 text-white drop-shadow-lg"
89
+ style={{ fontFamily: 'Outfit, sans-serif', fontWeight: 700 }}
90
+ >
91
+ {member.name}
92
+ </h3>
93
+ )}
94
+ {member.role && (
95
+ <p
96
+ className="text-xs font-bold uppercase tracking-wider md:text-sm text-blue-600 dark:text-blue-500 drop-shadow-md"
97
+ style={{ fontFamily: "'Inter', sans-serif", fontWeight: 700 }}
98
+ >
99
+ {member.role}
100
+ </p>
101
+ )}
102
+ </div>
103
+ <div
104
+ className="hover-panel transform translate-y-full group-hover:translate-y-0 transition-transform duration-300 ease-out p-4"
105
+ style={{ background: 'rgba(0,0,0,0.4)', backdropFilter: 'blur(8px)' }}
106
+ >
107
+ {member.bio && (
108
+ <p className="text-sm text-white/70 leading-relaxed mb-4 opacity-0 group-hover:opacity-100 transition-all duration-300 delay-100">
109
+ {member.bio}
110
+ </p>
111
+ )}
112
+ {socials.length > 0 && (
113
+ <div className="socials-map flex gap-3 justify-center opacity-0 group-hover:opacity-100 transition-all duration-300 delay-200">
114
+ {socials.map(({ Icon, url, label }) => (
115
+ <Link
116
+ key={label}
117
+ href={url!}
118
+ target="_blank"
119
+ rel="noopener noreferrer"
120
+ aria-label={`${member.name} on ${label}`}
121
+ className="text-white/70 hover:text-white hover:scale-110 transition-all duration-200 p-2 rounded-full bg-white/10 hover:bg-white/20 backdrop-blur-sm"
122
+ >
123
+ <Icon size={14} />
124
+ </Link>
125
+ ))}
126
+ </div>
127
+ )}
128
+ </div>
129
+ </div>
130
+ </div>
131
+ )
132
+ })}
133
+ </div>
134
+ )}
135
+ </div>
136
+ </div>
137
+ )
138
+ }
139
+
140
+ export default DDHGTeamCards
@@ -0,0 +1,45 @@
1
+ import type { CheckboxField } from '@payloadcms/plugin-form-builder/types'
2
+ import type { FieldErrorsImpl, FieldValues, UseFormRegister } from 'react-hook-form'
3
+
4
+ import { useFormContext } from 'react-hook-form'
5
+
6
+ import { Checkbox as CheckboxUi } from '@/components/ui/checkbox'
7
+ import { Label } from '@/components/ui/label'
8
+ import React from 'react'
9
+
10
+ import { Error } from '../Error'
11
+ import { Width } from '../Width'
12
+
13
+ export const Checkbox: React.FC<
14
+ CheckboxField & {
15
+ errors: Partial<FieldErrorsImpl>
16
+ register: UseFormRegister<FieldValues>
17
+ }
18
+ > = ({ name, defaultValue, errors, label, register, required, width }) => {
19
+ const props = register(name, { required: required })
20
+ const { setValue } = useFormContext()
21
+
22
+ return (
23
+ <Width width={width}>
24
+ <div className="flex items-center gap-2">
25
+ <CheckboxUi
26
+ defaultChecked={defaultValue}
27
+ id={name}
28
+ {...props}
29
+ onCheckedChange={(checked) => {
30
+ setValue(props.name, checked)
31
+ }}
32
+ />
33
+ <Label htmlFor={name}>
34
+ {required && (
35
+ <span className="required">
36
+ * <span className="sr-only">(required)</span>
37
+ </span>
38
+ )}
39
+ {label}
40
+ </Label>
41
+ </div>
42
+ {errors[name] && <Error name={name} />}
43
+ </Width>
44
+ )
45
+ }
@@ -0,0 +1,163 @@
1
+ 'use client'
2
+ import type { FormFieldBlock, Form as FormType } from '@payloadcms/plugin-form-builder/types'
3
+
4
+ import { useRouter } from 'next/navigation'
5
+ import React, { useCallback, useState } from 'react'
6
+ import { useForm, FormProvider } from 'react-hook-form'
7
+ import RichText from '@/components/RichText'
8
+ import { Button } from '@/components/ui/button'
9
+ import type { DefaultTypedEditorState } from '@payloadcms/richtext-lexical'
10
+
11
+ import { fields } from './fields'
12
+ import { getClientSideURL } from '@/utilities/getURL'
13
+
14
+ export type FormBlockType = {
15
+ blockName?: string
16
+ blockType?: 'formBlock'
17
+ enableIntro: boolean
18
+ form: FormType
19
+ introContent?: DefaultTypedEditorState
20
+ }
21
+
22
+ export const FormBlock: React.FC<
23
+ {
24
+ id?: string
25
+ } & FormBlockType
26
+ > = (props) => {
27
+ const {
28
+ enableIntro,
29
+ form: formFromProps,
30
+ form: { id: formID, confirmationMessage, confirmationType, redirect, submitButtonLabel } = {},
31
+ introContent,
32
+ } = props
33
+
34
+ const formMethods = useForm({
35
+ defaultValues: formFromProps.fields,
36
+ })
37
+ const {
38
+ control,
39
+ formState: { errors },
40
+ handleSubmit,
41
+ register,
42
+ } = formMethods
43
+
44
+ const [isLoading, setIsLoading] = useState(false)
45
+ const [hasSubmitted, setHasSubmitted] = useState<boolean>()
46
+ const [error, setError] = useState<{ message: string; status?: string } | undefined>()
47
+ const router = useRouter()
48
+
49
+ const onSubmit = useCallback(
50
+ (data: FormFieldBlock[]) => {
51
+ let loadingTimerID: ReturnType<typeof setTimeout>
52
+ const submitForm = async () => {
53
+ setError(undefined)
54
+
55
+ const dataToSend = Object.entries(data).map(([name, value]) => ({
56
+ field: name,
57
+ value,
58
+ }))
59
+
60
+ // delay loading indicator by 1s
61
+ loadingTimerID = setTimeout(() => {
62
+ setIsLoading(true)
63
+ }, 1000)
64
+
65
+ try {
66
+ const req = await fetch(`${getClientSideURL()}/api/form-submissions`, {
67
+ body: JSON.stringify({
68
+ form: formID,
69
+ submissionData: dataToSend,
70
+ }),
71
+ headers: {
72
+ 'Content-Type': 'application/json',
73
+ },
74
+ method: 'POST',
75
+ })
76
+
77
+ const res = await req.json()
78
+
79
+ clearTimeout(loadingTimerID)
80
+
81
+ if (req.status >= 400) {
82
+ setIsLoading(false)
83
+
84
+ setError({
85
+ message: res.errors?.[0]?.message || 'Internal Server Error',
86
+ status: res.status,
87
+ })
88
+
89
+ return
90
+ }
91
+
92
+ setIsLoading(false)
93
+ setHasSubmitted(true)
94
+
95
+ if (confirmationType === 'redirect' && redirect) {
96
+ const { url } = redirect
97
+
98
+ const redirectUrl = url
99
+
100
+ if (redirectUrl) router.push(redirectUrl)
101
+ }
102
+ } catch (err) {
103
+ console.warn(err)
104
+ setIsLoading(false)
105
+ setError({
106
+ message: 'Something went wrong.',
107
+ })
108
+ }
109
+ }
110
+
111
+ void submitForm()
112
+ },
113
+ [router, formID, redirect, confirmationType],
114
+ )
115
+
116
+ return (
117
+ <div className="container lg:max-w-[48rem]">
118
+ {enableIntro && introContent && !hasSubmitted && (
119
+ <RichText className="mb-8 lg:mb-12" data={introContent} enableGutter={false} />
120
+ )}
121
+ <div className="p-4 lg:p-6 border border-border rounded-[0.8rem]">
122
+ <FormProvider {...formMethods}>
123
+ {!isLoading && hasSubmitted && confirmationType === 'message' && (
124
+ <RichText data={confirmationMessage} />
125
+ )}
126
+ {isLoading && !hasSubmitted && <p>Loading, please wait...</p>}
127
+ {error && <div>{`${error.status || '500'}: ${error.message || ''}`}</div>}
128
+ {!hasSubmitted && (
129
+ <form id={formID} onSubmit={handleSubmit(onSubmit)}>
130
+ <div className="mb-4 last:mb-0">
131
+ {formFromProps &&
132
+ formFromProps.fields &&
133
+ formFromProps.fields?.map((field, index) => {
134
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
135
+ const Field: React.FC<any> = fields?.[field.blockType as keyof typeof fields]
136
+ if (Field) {
137
+ return (
138
+ <div className="mb-6 last:mb-0" key={index}>
139
+ <Field
140
+ form={formFromProps}
141
+ {...field}
142
+ {...formMethods}
143
+ control={control}
144
+ errors={errors}
145
+ register={register}
146
+ />
147
+ </div>
148
+ )
149
+ }
150
+ return null
151
+ })}
152
+ </div>
153
+
154
+ <Button form={formID} type="submit" variant="default">
155
+ {submitButtonLabel}
156
+ </Button>
157
+ </form>
158
+ )}
159
+ </FormProvider>
160
+ </div>
161
+ </div>
162
+ )
163
+ }
@@ -0,0 +1,65 @@
1
+ import type { CountryField } from '@payloadcms/plugin-form-builder/types'
2
+ import type { Control, FieldErrorsImpl } from 'react-hook-form'
3
+
4
+ import { Label } from '@/components/ui/label'
5
+ import {
6
+ Select,
7
+ SelectContent,
8
+ SelectItem,
9
+ SelectTrigger,
10
+ SelectValue,
11
+ } from '@/components/ui/select'
12
+ import React from 'react'
13
+ import { Controller } from 'react-hook-form'
14
+
15
+ import { Error } from '../Error'
16
+ import { Width } from '../Width'
17
+ import { countryOptions } from './options'
18
+
19
+ export const Country: React.FC<
20
+ CountryField & {
21
+ control: Control
22
+ errors: Partial<FieldErrorsImpl>
23
+ }
24
+ > = ({ name, control, errors, label, required, width }) => {
25
+ return (
26
+ <Width width={width}>
27
+ <Label className="" htmlFor={name}>
28
+ {label}
29
+
30
+ {required && (
31
+ <span className="required">
32
+ * <span className="sr-only">(required)</span>
33
+ </span>
34
+ )}
35
+ </Label>
36
+ <Controller
37
+ control={control}
38
+ defaultValue=""
39
+ name={name}
40
+ render={({ field: { onChange, value } }) => {
41
+ const controlledValue = countryOptions.find((t) => t.value === value)
42
+
43
+ return (
44
+ <Select onValueChange={(val) => onChange(val)} value={controlledValue?.value}>
45
+ <SelectTrigger className="w-full" id={name}>
46
+ <SelectValue placeholder={label} />
47
+ </SelectTrigger>
48
+ <SelectContent>
49
+ {countryOptions.map(({ label, value }) => {
50
+ return (
51
+ <SelectItem key={value} value={value}>
52
+ {label}
53
+ </SelectItem>
54
+ )
55
+ })}
56
+ </SelectContent>
57
+ </Select>
58
+ )
59
+ }}
60
+ rules={{ required }}
61
+ />
62
+ {errors[name] && <Error name={name} />}
63
+ </Width>
64
+ )
65
+ }