blocks-dusted 0.1.11 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) 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/templates/blocks/DD-BookCall/Component.tsx +535 -534
  6. package/templates/blocks/DD-CardTemplate01/Component.tsx +140 -139
  7. package/templates/blocks/DD-Carousel-A/Component.tsx +1 -1
  8. package/templates/blocks/DD-Carousel-B/Component.tsx +1 -1
  9. package/templates/blocks/DD-Chip/Component.tsx +14 -14
  10. package/templates/blocks/DD-ComparisonTable/Component.tsx +1 -1
  11. package/templates/blocks/DD-Contact/Component.tsx +8 -7
  12. package/templates/blocks/DD-Counter/Component.tsx +10 -10
  13. package/templates/blocks/DD-FeatCat/Component.tsx +1 -1
  14. package/templates/blocks/DD-FeatStrip/Component.tsx +1 -1
  15. package/templates/blocks/DD-Hero/Component.tsx +297 -320
  16. package/templates/blocks/DD-HorizontalScroll/Component.tsx +936 -938
  17. package/templates/blocks/DD-IframeMedia/Component.tsx +14 -10
  18. package/templates/blocks/DD-MasonaryMedia/Component.tsx +8 -10
  19. package/templates/blocks/DD-Pricing/Component.tsx +369 -372
  20. package/templates/blocks/DD-Process/Component.tsx +147 -149
  21. package/templates/blocks/DD-Progress/Component.tsx +156 -141
  22. package/templates/blocks/DD-ProjSlider/config.ts +2 -2
  23. package/templates/blocks/DD-Section/Component.tsx +1 -1
  24. package/templates/blocks/DD-Services/Component.tsx +1 -1
  25. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +13 -13
  26. package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
  27. package/templates/blocks/DD-StackCards/Component.tsx +473 -473
  28. package/templates/blocks/DD-Team/Component.tsx +1 -1
  29. package/templates/blocks/DD-TechStack/Component.tsx +22 -28
  30. package/templates/blocks/DD-Testimonials/Component.tsx +35 -43
  31. package/templates/blocks/DD-TextMarq/Component.tsx +157 -157
  32. package/templates/blocks/DD-Thanks/Component.tsx +501 -500
  33. package/templates/blocks/DD-Work/Component.tsx +4 -4
  34. package/templates/blocks/DD-Work-B/Component.tsx +1 -1
  35. package/templates/blocks/DDHG-CTASection2/Component.tsx +6 -6
  36. package/templates/blocks/DDHG-HighlightArc/Component.tsx +243 -243
  37. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +143 -93
  38. package/templates/blocks/DDHG-LogoMarquee/config.ts +154 -66
  39. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +227 -213
  40. package/templates/blocks/DDHG-ProcessSec/Component.tsx +38 -39
  41. package/templates/blocks/DDHG-ServiceDetails/Component.tsx +11 -11
  42. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +24 -28
  43. package/templates/blocks/DDHG-ServiceHero/Component.tsx +1 -1
  44. package/templates/blocks/DDHG-ServiceOverview/Component.tsx +23 -23
  45. package/templates/blocks/DDHG-StatsFAQ/Component.tsx +40 -40
  46. package/templates/blocks/DDHG-TeamCards/Component.tsx +169 -131
@@ -94,7 +94,7 @@ export const DDTeam: React.FC<DDTeamProps> = ({
94
94
 
95
95
  <div id={sectionId ?? 'team'} className="py-24">
96
96
  <div className="max-w-6xl lg:max-w-7xl mx-auto px-4 md:px-6">
97
- {/* Header */}
97
+ {/* Header */}#BlockName-DDTeam
98
98
  <div className="text-center max-w-2xl mx-auto mb-16">
99
99
  {eyebrow && (
100
100
  <p className="text-[11.5px] font-semibold text-stone-400 tracking-widest uppercase mb-3 top-caption">
@@ -7,16 +7,14 @@ export type DDTechStackProps = Record<string, any>
7
7
 
8
8
  // ─── Types ────────────────────────────────────────────────────────────────────
9
9
  type MediaObj = { url?: string | null; alt?: string | null }
10
- type TechStackItem = {
11
- image?: string | MediaObj | null
12
- icon?: string | null
13
- iconName?: string | null
10
+ type TechStackItem = {
11
+ image?: string | MediaObj | null
12
+ icon?: string | null
14
13
  label?: string | null
15
14
  showLabel?: boolean | null
16
15
  }
17
- type TechStackRow = {
18
- duration?: number | null
19
- speed?: number | null
16
+ type TechStackRow = {
17
+ duration?: number | null
20
18
  direction?: 'left' | 'right' | null
21
19
  items?: TechStackItem[] | null
22
20
  }
@@ -34,7 +32,7 @@ function getMediaAlt(media: string | MediaObj | null | undefined): string | null
34
32
 
35
33
  // ─── Dynamic Lucide Icon ──────────────────────────────────────────────────────
36
34
  function isLocalImageSrc(src?: string | null): src is string {
37
- return typeof src === 'string' && (src.startsWith('/api/media/file/') || src.startsWith('/assets/') || src.startsWith('/logo') || src.startsWith('data:'))
35
+ return typeof src === 'string' && src.startsWith('/api/media/file/')
38
36
  }
39
37
 
40
38
  function DynamicIcon({
@@ -66,7 +64,7 @@ function TechItem({
66
64
 
67
65
  return (
68
66
  <div className="relative group/tech text-center shrink-0">
69
- <div className="w-20 h-20 bg-linear-to-b from-[#1a1a1f] to-[#0f0f12] rounded-2xl p-4 transition-all duration-300 hover:scale-105 hover:shadow-[0_0_30px_rgba(140,138,132,0.15)] flex items-center justify-center border border-[#2A2A30] ">
67
+ <div className="w-20 h-20 bg-linear-to-b from-[#1a1a1f] to-[#0f0f12] rounded-2xl p-4 transition-all duration-300 hover:scale-105 hover:shadow-[0_0_30px_rgba(140,138,132,0.15)] flex items-center justify-center border border-[#2A2A30] invert-[1] dark:invert-[0]">
70
68
  {safeImageUrl ? (
71
69
  <Image
72
70
  src={safeImageUrl}
@@ -88,18 +86,14 @@ function TechItem({
88
86
  }
89
87
 
90
88
  // ─── Component ────────────────────────────────────────────────────────────────
91
- export const DDTechStack: React.FC<DDTechStackProps> = ({
92
- eyebrow,
93
- heading,
94
- headlineMain,
95
- headlineAccent,
96
- techRows,
97
- rows: legacyRows,
98
- sectionId,
99
- customCSS,
100
- }) => {
101
- const resolvedHeading = heading ?? [headlineMain, headlineAccent].filter(Boolean).join(' ')
102
- const rows = (techRows ?? legacyRows ?? []) as TechStackRow[]
89
+ export const DDTechStack: React.FC<DDTechStackProps> = ({
90
+ eyebrow,
91
+ heading,
92
+ techRows,
93
+ sectionId,
94
+ customCSS,
95
+ }) => {
96
+ const rows = (techRows ?? []) as TechStackRow[]
103
97
  if (!rows.length) return null
104
98
 
105
99
  const rowMaskStyle: React.CSSProperties = {
@@ -111,22 +105,22 @@ export const DDTechStack: React.FC<DDTechStackProps> = ({
111
105
  return (
112
106
  <section
113
107
  id={sectionId ?? undefined}
114
- className="relative mx-auto w-full max-w-6xl bg-transparent px-4 py-10 md:px-6"
108
+ className="relative w-full bg-transparent py-12 px-4 md:px-6 max-w-6xl mx-auto"
115
109
  >
116
110
  {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
117
111
 
118
- {(eyebrow || resolvedHeading) && (
112
+ {(eyebrow || heading) && (
119
113
  <div className="mb-12">
120
-
114
+ #BlockName-DDTechStack
121
115
  {eyebrow && (
122
116
  <p className="text-sm uppercase tracking-widest text-[#9A9790] mb-2">{eyebrow}</p>
123
117
  )}
124
- {resolvedHeading && (
118
+ {heading && (
125
119
  <h2
126
120
  className="text-3xl font-bold tracking-tighter sm:text-4xl text-stone-900 dark:text-stone-50"
127
121
  style={{ fontFamily: "'Space Grotesk', sans-serif" }}
128
122
  >
129
- {resolvedHeading}
123
+ {heading}
130
124
  </h2>
131
125
  )}
132
126
  </div>
@@ -134,7 +128,7 @@ export const DDTechStack: React.FC<DDTechStackProps> = ({
134
128
 
135
129
  <div className="relative">
136
130
  {rows.map((row, rowIndex) => {
137
- const duration = row.duration ?? row.speed ?? 40
131
+ const duration = row.duration ?? 40
138
132
  const direction = row.direction ?? 'left'
139
133
  const items = row.items ?? []
140
134
 
@@ -164,7 +158,7 @@ export const DDTechStack: React.FC<DDTechStackProps> = ({
164
158
  key={idx}
165
159
  imageUrl={imgUrl}
166
160
  imageAlt={imgAlt}
167
- icon={item.icon ?? item.iconName}
161
+ icon={item.icon}
168
162
  label={item.label}
169
163
  showLabel={item.showLabel}
170
164
  />
@@ -1,34 +1,34 @@
1
- "use client";
1
+ 'use client'
2
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 };
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
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
- };
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
18
 
19
19
  function isLocalImageSrc(src?: string | null): src is string {
20
- return typeof src === "string" && src.startsWith("/api/media/file/");
20
+ return typeof src === 'string' && src.startsWith('/api/media/file/')
21
21
  }
22
22
 
23
23
  const containerVariants: Variants = {
24
24
  hidden: {},
25
25
  show: { transition: { staggerChildren: 0.12, delayChildren: 0.05 } },
26
- };
26
+ }
27
27
 
28
28
  const cardVariants: Variants = {
29
29
  hidden: { opacity: 0, y: 24 },
30
- show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } },
31
- };
30
+ show: { opacity: 1, y: 0, transition: { duration: 0.5, ease: 'easeOut' } },
31
+ }
32
32
 
33
33
  export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
34
34
  eyebrow,
@@ -38,16 +38,16 @@ export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
38
38
  customCSS,
39
39
  }) => {
40
40
  const populated = ((testimonials ?? []) as unknown[]).filter(
41
- (t): t is Testimonial => typeof t === "object" && t !== null,
42
- );
41
+ (t): t is Testimonial => typeof t === 'object' && t !== null,
42
+ )
43
43
 
44
44
  return (
45
45
  <>
46
46
  {customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
47
47
 
48
- <div id={sectionId ?? "testimonials"} className="py-24">
48
+ <div id={sectionId ?? 'testimonials'} className="py-24">
49
49
  <div className="max-w-6xl lg:max-w-7xl mx-auto px-4 md:px-6">
50
- {/* Header */}
50
+ {/* Header */}#BlockName-DDTestimonials
51
51
  <div className="text-center max-w-xl mx-auto mb-14">
52
52
  {eyebrow && (
53
53
  <p className="text-[11.5px] font-semibold text-stone-400 tracking-widest uppercase mb-3 top-caption">
@@ -69,33 +69,26 @@ export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
69
69
  >
70
70
  {populated.map((t, i) => {
71
71
  const avatarUrl =
72
- typeof t.companyLogo === "object" &&
72
+ typeof t.companyLogo === 'object' &&
73
73
  t.companyLogo !== null &&
74
- "url" in t.companyLogo
74
+ 'url' in t.companyLogo
75
75
  ? t.companyLogo.url
76
- : (t.fallbackAvatarUrl ?? null);
77
- const safeAvatarUrl = isLocalImageSrc(avatarUrl)
78
- ? avatarUrl
79
- : null;
76
+ : (t.fallbackAvatarUrl ?? null)
77
+ const safeAvatarUrl = isLocalImageSrc(avatarUrl) ? avatarUrl : null
80
78
 
81
- const stars = Math.min(5, Math.max(1, t.rating ?? 5));
79
+ const stars = Math.min(5, Math.max(1, t.rating ?? 5))
82
80
 
83
81
  return (
84
82
  <motion.div
85
83
  key={t.id ?? i}
86
84
  className="rounded-2xl bg-stone-100 dark:bg-stone-900 border border-stone-100 dark:border-stone-800 p-8 flex flex-col"
87
85
  variants={cardVariants}
88
- style={{ cornerShape: "squircle" } as React.CSSProperties}
86
+ style={{ cornerShape: 'squircle' } as React.CSSProperties}
89
87
  >
90
88
  {/* Stars */}
91
89
  <div className="flex items-center gap-1 mb-5">
92
90
  {Array.from({ length: stars }).map((_, si) => (
93
- <Star
94
- key={si}
95
- size={14}
96
- className="text-amber-400"
97
- fill="currentColor"
98
- />
91
+ <Star key={si} size={14} className="text-amber-400" fill="currentColor" />
99
92
  ))}
100
93
  </div>
101
94
 
@@ -104,8 +97,7 @@ export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
104
97
  <div className="text-sm text-stone-400 leading-relaxed flex-1 mb-6">
105
98
  {/* TODO: Reconnect this project's RichText renderer for testimonial content. */}
106
99
  <span>
107
- Reconnect this project's RichText renderer for
108
- testimonial content.
100
+ Reconnect this project's RichText renderer for testimonial content.
109
101
  </span>
110
102
  </div>
111
103
  ) : null}
@@ -116,7 +108,7 @@ export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
116
108
  <div className="w-10 h-10 rounded-full overflow-hidden shrink-0 relative">
117
109
  <Image
118
110
  src={safeAvatarUrl}
119
- alt={t.title ?? ""}
111
+ alt={t.title ?? ''}
120
112
  fill
121
113
  sizes="40px"
122
114
  className="object-cover object-top"
@@ -136,12 +128,12 @@ export const DDTestimonials: React.FC<DDTestimonialsProps> = ({
136
128
  </div>
137
129
  </div>
138
130
  </motion.div>
139
- );
131
+ )
140
132
  })}
141
133
  </motion.div>
142
134
  )}
143
135
  </div>
144
136
  </div>
145
137
  </>
146
- );
147
- };
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
+ }