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,147 +1,139 @@
1
- "use client";
2
-
3
- import React from "react";
4
- import Image from "next/image";
5
- import { Star } from "lucide-react";
6
- import { motion, type Variants } from "framer-motion";
7
- export type DDTestimonialsProps = Record<string, any>;
8
- type Media = { url?: string | null };
9
- type Testimonial = {
10
- id?: string | null;
11
- title?: string | null;
12
- content?: unknown;
13
- designation?: unknown;
14
- companyLogo?: Media | string | null;
15
- fallbackAvatarUrl?: string | null;
16
- rating?: number | null;
17
- };
18
-
19
- function isLocalImageSrc(src?: string | null): src is string {
20
- return typeof src === "string" && src.startsWith("/api/media/file/");
21
- }
22
-
23
- const containerVariants: Variants = {
24
- hidden: {},
25
- show: { transition: { staggerChildren: 0.12, delayChildren: 0.05 } },
26
- };
27
-
28
- const cardVariants: Variants = {
29
- hidden: { opacity: 0, y: 24 },
30
- show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } },
31
- };
32
-
33
- export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
34
- eyebrow,
35
- headline,
36
- testimonials,
37
- sectionId,
38
- customCSS,
39
- }) => {
40
- const populated = ((testimonials ?? []) as unknown[]).filter(
41
- (t): t is Testimonial => typeof t === "object" && t !== null,
42
- );
43
-
44
- return (
45
- <>
46
- {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
47
-
48
- <div id={sectionId ?? "testimonials"} className="py-24">
49
- <div className="max-w-6xl lg:max-w-7xl mx-auto px-4 md:px-6">
50
- {/* Header */}
51
- <div className="text-center max-w-xl mx-auto mb-14">
52
- {eyebrow && (
53
- <p className="text-[11.5px] font-semibold text-stone-400 tracking-widest uppercase mb-3 top-caption">
54
- {eyebrow}
55
- </p>
56
- )}
57
- <h2 className="text-3xl font-semibold text-stone-900 dark:text-stone-50 leading-tight">
58
- {headline}
59
- </h2>
60
- </div>
61
- {/* Testimonials */}
62
- {populated.length > 0 && (
63
- <motion.div
64
- className="grid grid-cols-1 md:grid-cols-3 gap-6"
65
- variants={containerVariants}
66
- initial="hidden"
67
- whileInView="show"
68
- viewport={{ once: false, amount: 0.15 }}
69
- >
70
- {populated.map((t, i) => {
71
- const avatarUrl =
72
- typeof t.companyLogo === "object" &&
73
- t.companyLogo !== null &&
74
- "url" in t.companyLogo
75
- ? t.companyLogo.url
76
- : (t.fallbackAvatarUrl ?? null);
77
- const safeAvatarUrl = isLocalImageSrc(avatarUrl)
78
- ? avatarUrl
79
- : null;
80
-
81
- const stars = Math.min(5, Math.max(1, t.rating ?? 5));
82
-
83
- return (
84
- <motion.div
85
- key={t.id ?? i}
86
- className="rounded-2xl bg-stone-100 dark:bg-stone-900 border border-stone-100 dark:border-stone-800 p-8 flex flex-col"
87
- variants={cardVariants}
88
- style={{ cornerShape: "squircle" } as React.CSSProperties}
89
- >
90
- {/* Stars */}
91
- <div className="flex items-center gap-1 mb-5">
92
- {Array.from({ length: stars }).map((_, si) => (
93
- <Star
94
- key={si}
95
- size={14}
96
- className="text-amber-400"
97
- fill="currentColor"
98
- />
99
- ))}
100
- </div>
101
-
102
- {/* Quote */}
103
- {t.content ? (
104
- <div className="text-sm text-stone-400 leading-relaxed flex-1 mb-6">
105
- {/* TODO: Reconnect this project's RichText renderer for testimonial content. */}
106
- <span>
107
- Reconnect this project's RichText renderer for
108
- testimonial content.
109
- </span>
110
- </div>
111
- ) : null}
112
-
113
- {/* Author / Company */}
114
- <div className="flex items-center gap-3 pt-5 border-t border-stone-800">
115
- {safeAvatarUrl && (
116
- <div className="w-10 h-10 rounded-full overflow-hidden shrink-0 relative">
117
- <Image
118
- src={safeAvatarUrl}
119
- alt={t.title ?? ""}
120
- fill
121
- sizes="40px"
122
- className="object-cover object-top"
123
- />
124
- </div>
125
- )}
126
- <div>
127
- <div className="text-sm font-semibold text-stone-800 dark:text-stone-50">
128
- {t.title}
129
- </div>
130
- {t.designation ? (
131
- <div className="text-xs text-stone-300">
132
- {/* TODO: Reconnect RichText for designation if needed. */}
133
- <span>Reconnect RichText for designation</span>
134
- </div>
135
- ) : null}
136
- </div>
137
- </div>
138
- </motion.div>
139
- );
140
- })}
141
- </motion.div>
142
- )}
143
- </div>
144
- </div>
145
- </>
146
- );
147
- };
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import Image from 'next/image'
5
+ import { Star } from 'lucide-react'
6
+ import { motion, type Variants } from 'framer-motion'
7
+ export type DDTestimonialsProps = Record<string, any>
8
+ type Media = { url?: string | null }
9
+ type Testimonial = {
10
+ id?: string | null
11
+ title?: string | null
12
+ content?: unknown
13
+ designation?: unknown
14
+ companyLogo?: Media | string | null
15
+ fallbackAvatarUrl?: string | null
16
+ rating?: number | null
17
+ }
18
+
19
+ function isLocalImageSrc(src?: string | null): src is string {
20
+ return typeof src === 'string' && src.startsWith('/api/media/file/')
21
+ }
22
+
23
+ const containerVariants: Variants = {
24
+ hidden: {},
25
+ show: { transition: { staggerChildren: 0.12, delayChildren: 0.05 } },
26
+ }
27
+
28
+ const cardVariants: Variants = {
29
+ hidden: { opacity: 0, y: 24 },
30
+ show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: 'easeOut' } },
31
+ }
32
+
33
+ export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
34
+ eyebrow,
35
+ headline,
36
+ testimonials,
37
+ sectionId,
38
+ customCSS,
39
+ }) => {
40
+ const populated = ((testimonials ?? []) as unknown[]).filter(
41
+ (t): t is Testimonial => typeof t === 'object' && t !== null,
42
+ )
43
+
44
+ return (
45
+ <>
46
+ {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
47
+
48
+ <div id={sectionId ?? 'testimonials'} className="py-4">
49
+ <div className="max-w-6xl lg:max-w-7xl mx-auto px-4 md:px-6">
50
+ {/* Header */}
51
+ <div className="text-center max-w-xl mx-auto mb-14">
52
+ {eyebrow && (
53
+ <p className="text-[11.5px] font-semibold text-stone-400 tracking-widest uppercase mb-3 top-caption">
54
+ {eyebrow}
55
+ </p>
56
+ )}
57
+ <h2 className="text-3xl font-semibold text-stone-900 dark:text-stone-50 leading-tight">
58
+ {headline}
59
+ </h2>
60
+ </div>
61
+ {/* Testimonials */}
62
+ {populated.length > 0 && (
63
+ <motion.div
64
+ className="grid grid-cols-1 md:grid-cols-3 gap-6"
65
+ variants={containerVariants}
66
+ initial="hidden"
67
+ whileInView="show"
68
+ viewport={{ once: false, amount: 0.15 }}
69
+ >
70
+ {populated.map((t, i) => {
71
+ const avatarUrl =
72
+ typeof t.companyLogo === 'object' &&
73
+ t.companyLogo !== null &&
74
+ 'url' in t.companyLogo
75
+ ? t.companyLogo.url
76
+ : (t.fallbackAvatarUrl ?? null)
77
+ const safeAvatarUrl = isLocalImageSrc(avatarUrl) ? avatarUrl : null
78
+
79
+ const stars = Math.min(5, Math.max(1, t.rating ?? 5))
80
+
81
+ return (
82
+ <motion.div
83
+ key={t.id ?? i}
84
+ className="rounded-2xl bg-stone-100 dark:bg-stone-900 border border-stone-100 dark:border-stone-800 p-8 flex flex-col"
85
+ variants={cardVariants}
86
+ style={{ cornerShape: 'squircle' } as React.CSSProperties}
87
+ >
88
+ {/* Stars */}
89
+ <div className="flex items-center gap-1 mb-5">
90
+ {Array.from({ length: stars }).map((_, si) => (
91
+ <Star key={si} size={14} className="text-amber-400" fill="currentColor" />
92
+ ))}
93
+ </div>
94
+
95
+ {/* Quote */}
96
+ {t.content ? (
97
+ <div className="text-sm text-stone-400 leading-relaxed flex-1 mb-6">
98
+ {/* TODO: Reconnect this project's RichText renderer for testimonial content. */}
99
+ <span>
100
+ Reconnect this project's RichText renderer for testimonial content.
101
+ </span>
102
+ </div>
103
+ ) : null}
104
+
105
+ {/* Author / Company */}
106
+ <div className="flex items-center gap-3 pt-5 border-t border-stone-800">
107
+ {safeAvatarUrl && (
108
+ <div className="w-10 h-10 rounded-full overflow-hidden shrink-0 relative">
109
+ <Image
110
+ src={safeAvatarUrl}
111
+ alt={t.title ?? ''}
112
+ fill
113
+ sizes="40px"
114
+ className="object-cover object-top"
115
+ />
116
+ </div>
117
+ )}
118
+ <div>
119
+ <div className="text-sm font-semibold text-stone-800 dark:text-stone-50">
120
+ {t.title}
121
+ </div>
122
+ {t.designation ? (
123
+ <div className="text-xs text-stone-300">
124
+ {/* TODO: Reconnect RichText for designation if needed. */}
125
+ <span>Reconnect RichText for designation</span>
126
+ </div>
127
+ ) : null}
128
+ </div>
129
+ </div>
130
+ </motion.div>
131
+ )
132
+ })}
133
+ </motion.div>
134
+ )}
135
+ </div>
136
+ </div>
137
+ </>
138
+ )
139
+ }
@@ -1,157 +1,157 @@
1
- 'use client'
2
-
3
- import React from 'react'
4
- import { motion } from 'framer-motion'
5
- import { cn } from '@/utilities/ui'
6
- import { Separator } from '@/components/ui/separator'
7
-
8
- export interface TextMarqueeBlockProps {
9
- blockType: 'textMarquee'
10
- items?: Array<{
11
- text: string
12
- textClass?: string
13
- textStyle?: React.CSSProperties
14
- fontSize?: string
15
- fontWeight?: string
16
- color?: string
17
- }>
18
- speed?: number
19
- spacing?: number
20
- showTopDivider?: boolean
21
- showBottomDivider?: boolean
22
- containerClass?: string
23
- }
24
-
25
- export type TextMarqueeProps = {
26
- texts: {
27
- text: string
28
- textClass?: string
29
- textStyle?: React.CSSProperties
30
- fontSize?: string
31
- fontWeight?: string
32
- color?: string
33
- }[]
34
- speed?: number
35
- spacing?: number
36
- showTopDivider?: boolean
37
- showBottomDivider?: boolean
38
- containerClass?: string
39
- }
40
-
41
- export const TextMarqueeComponent: React.FC<TextMarqueeProps> = ({
42
- texts,
43
- speed = 20,
44
- spacing = 48,
45
- showTopDivider = false,
46
- showBottomDivider = false,
47
- containerClass = 'overflow-hidden py-8 bg-transparent'
48
- }) => {
49
- if (!texts || texts.length === 0) {
50
- return null
51
- }
52
-
53
- // Calculate total width - simple calculation, no memoization needed
54
- const totalWidth = texts.reduce((acc, text) => {
55
- const textWidth = text.text.length * 12 // ~12px per character
56
- return acc + textWidth + spacing
57
- }, 0)
58
-
59
- return (
60
- <>
61
- {showTopDivider && <Separator gradient={true} className="border-t" />}
62
- <div className={containerClass}>
63
- <motion.div
64
- className="flex items-center whitespace-nowrap"
65
- animate={{ x: [0, -totalWidth] }}
66
- transition={{
67
- repeat: Infinity,
68
- duration: speed,
69
- ease: 'linear',
70
- repeatType: 'loop'
71
- }}
72
- style={{ width: `${totalWidth * 2}px` }}
73
- >
74
- {/* First set of texts */}
75
- {texts.map((item, index) => (
76
- <div
77
- key={`text-${index}`}
78
- className="flex-shrink-0 flex items-center justify-center"
79
- style={{ marginRight: `${spacing}px` }}
80
- >
81
- <span
82
- className={cn('text-base text-center whitespace-nowrap', item.textClass)}
83
- style={{
84
- fontSize: item.fontSize || '1rem',
85
- fontWeight: item.fontWeight || 'normal',
86
- color: item.color || 'currentColor',
87
- ...item.textStyle
88
- }}
89
- >
90
- {item.text}
91
- </span>
92
- </div>
93
- ))}
94
-
95
- {/* Duplicate set for seamless loop */}
96
- {texts.map((item, index) => (
97
- <div
98
- key={`text-${index}-duplicate`}
99
- className="flex-shrink-0 flex items-center justify-center"
100
- style={{ marginRight: `${spacing}px` }}
101
- >
102
- <span
103
- className={cn('text-base text-center whitespace-nowrap', item.textClass)}
104
- style={{
105
- fontSize: item.fontSize || '1rem',
106
- fontWeight: item.fontWeight || 'normal',
107
- color: item.color || 'currentColor',
108
- ...item.textStyle
109
- }}
110
- >
111
- {item.text}
112
- </span>
113
- </div>
114
- ))}
115
- </motion.div>
116
- </div>
117
- {showBottomDivider && <Separator gradient={true} className="border-b" />}
118
- </>
119
- )
120
- }
121
-
122
- // Block component that transforms props and renders the TextMarquee
123
- export const DDTextMarq: React.FC<TextMarqueeBlockProps> = (props) => {
124
- const {
125
- items = [],
126
- speed = 20,
127
- spacing = 48,
128
- showTopDivider = false,
129
- showBottomDivider = false,
130
- containerClass = 'overflow-hidden py-8 bg-transparent'
131
- } = props
132
-
133
- // Transform items to match component expectations, using array index instead of id
134
- const texts = items.map((item, index) => ({
135
- text: item.text,
136
- textClass: item.textClass,
137
- textStyle: item.textStyle,
138
- fontSize: item.fontSize,
139
- fontWeight: item.fontWeight,
140
- color: item.color
141
- }))
142
-
143
- if (texts.length === 0) {
144
- return null
145
- }
146
-
147
- return (
148
- <TextMarqueeComponent
149
- texts={texts}
150
- speed={speed}
151
- spacing={spacing}
152
- showTopDivider={showTopDivider}
153
- showBottomDivider={showBottomDivider}
154
- containerClass={containerClass}
155
- />
156
- )
157
- }
1
+ 'use client'
2
+
3
+ import React from 'react'
4
+ import { motion } from 'framer-motion'
5
+ import { cn } from '@/utilities/ui'
6
+ import { Separator } from '@/components/ui/separator'
7
+
8
+ export interface TextMarqueeBlockProps {
9
+ blockType: 'textMarquee'
10
+ items?: Array<{
11
+ text: string
12
+ textClass?: string
13
+ textStyle?: React.CSSProperties
14
+ fontSize?: string
15
+ fontWeight?: string
16
+ color?: string
17
+ }>
18
+ speed?: number
19
+ spacing?: number
20
+ showTopDivider?: boolean
21
+ showBottomDivider?: boolean
22
+ containerClass?: string
23
+ }
24
+
25
+ export type TextMarqueeProps = {
26
+ texts: {
27
+ text: string
28
+ textClass?: string
29
+ textStyle?: React.CSSProperties
30
+ fontSize?: string
31
+ fontWeight?: string
32
+ color?: string
33
+ }[]
34
+ speed?: number
35
+ spacing?: number
36
+ showTopDivider?: boolean
37
+ showBottomDivider?: boolean
38
+ containerClass?: string
39
+ }
40
+
41
+ export const TextMarqueeComponent: React.FC<TextMarqueeProps> = ({
42
+ texts,
43
+ speed = 20,
44
+ spacing = 48,
45
+ showTopDivider = false,
46
+ showBottomDivider = false,
47
+ containerClass = 'overflow-hidden py-8 bg-transparent',
48
+ }) => {
49
+ if (!texts || texts.length === 0) {
50
+ return null
51
+ }
52
+
53
+ // Calculate total width - simple calculation, no memoization needed
54
+ const totalWidth = texts.reduce((acc, text) => {
55
+ const textWidth = text.text.length * 12 // ~12px per character
56
+ return acc + textWidth + spacing
57
+ }, 0)
58
+
59
+ return (
60
+ <>
61
+ {showTopDivider && <Separator gradient={true} className="border-t" />}
62
+ <div className={cn('overflow-clip relative top-24', containerClass)}>
63
+ <motion.div
64
+ className="flex items-center whitespace-nowrap"
65
+ animate={{ x: [0, -totalWidth] }}
66
+ transition={{
67
+ repeat: Infinity,
68
+ duration: speed,
69
+ ease: 'linear',
70
+ repeatType: 'loop',
71
+ }}
72
+ style={{ width: `${totalWidth * 2}px` }}
73
+ >
74
+ {/* First set of texts */}
75
+ {texts.map((item, index) => (
76
+ <div
77
+ key={`text-${index}`}
78
+ className="shrink-0 flex items-center justify-center"
79
+ style={{ marginRight: `${spacing}px` }}
80
+ >
81
+ <span
82
+ className={cn('text-center whitespace-nowrap uppercase', item.textClass)}
83
+ style={{
84
+ fontSize: item.fontSize || '4rem',
85
+ fontWeight: item.fontWeight || 'bold',
86
+ color: item.color || 'currentColor',
87
+ ...item.textStyle,
88
+ }}
89
+ >
90
+ {item.text}
91
+ </span>
92
+ </div>
93
+ ))}
94
+
95
+ {/* Duplicate set for seamless loop */}
96
+ {texts.map((item, index) => (
97
+ <div
98
+ key={`text-${index}-duplicate`}
99
+ className="shrink-0 flex items-center justify-center"
100
+ style={{ marginRight: `${spacing}px` }}
101
+ >
102
+ <span
103
+ className={cn('text-center whitespace-nowrap uppercase', item.textClass)}
104
+ style={{
105
+ fontSize: item.fontSize || '4rem',
106
+ fontWeight: item.fontWeight || 'bold',
107
+ color: item.color || 'currentColor',
108
+ ...item.textStyle,
109
+ }}
110
+ >
111
+ {item.text}
112
+ </span>
113
+ </div>
114
+ ))}
115
+ </motion.div>
116
+ </div>
117
+ {showBottomDivider && <Separator gradient={true} className="border-b" />}
118
+ </>
119
+ )
120
+ }
121
+
122
+ // Block component that transforms props and renders the TextMarquee
123
+ export const DDTextMarq: React.FC<TextMarqueeBlockProps> = (props) => {
124
+ const {
125
+ items = [],
126
+ speed = 20,
127
+ spacing = 48,
128
+ showTopDivider = false,
129
+ showBottomDivider = false,
130
+ containerClass = 'overflow-hidden py-8 bg-transparent',
131
+ } = props
132
+
133
+ // Transform items to match component expectations, using array index instead of id
134
+ const texts = items.map((item, index) => ({
135
+ text: item.text,
136
+ textClass: item.textClass,
137
+ textStyle: item.textStyle,
138
+ fontSize: item.fontSize,
139
+ fontWeight: item.fontWeight,
140
+ color: item.color,
141
+ }))
142
+
143
+ if (texts.length === 0) {
144
+ return null
145
+ }
146
+
147
+ return (
148
+ <TextMarqueeComponent
149
+ texts={texts}
150
+ speed={speed}
151
+ spacing={spacing}
152
+ showTopDivider={showTopDivider}
153
+ showBottomDivider={showBottomDivider}
154
+ containerClass={containerClass}
155
+ />
156
+ )
157
+ }