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.
- package/package.json +5 -2
- package/src/cli.js +39 -3
- package/src/commands/shop.js +59 -0
- package/src/commands/warehouse.js +73 -0
- package/src/installers/patchRenderBlocks.js +1 -1
- package/src/registry/validateTemplateManifest.js +2 -1
- package/templates/blocks/BannerDD790432/Component.tsx +63 -0
- package/templates/blocks/BannerDD790432/config.ts +54 -0
- package/templates/blocks/BannerDD790432/manifest.json +80 -0
- package/templates/blocks/Button47/Component.tsx +124 -0
- package/templates/blocks/Button47/config.ts +75 -0
- package/templates/blocks/Button47/link.ts +22 -0
- package/templates/blocks/Button47/manifest.json +105 -0
- package/templates/blocks/Button47/styles.module.scss +75 -0
- package/templates/blocks/DD-Accordion/Component.tsx +55 -36
- package/templates/blocks/DD-Accordion/config.ts +71 -59
- package/templates/blocks/DD-AnimText/Component.tsx +50 -32
- package/templates/blocks/DD-AnimText/config.ts +296 -295
- package/templates/blocks/DD-AsideNav/Component.tsx +323 -319
- package/templates/blocks/DD-Banner/Component.tsx +54 -51
- package/templates/blocks/DD-BookCall/Component.tsx +10 -9
- package/templates/blocks/DD-ButtonPop/Component.tsx +118 -117
- package/templates/blocks/DD-Card/Component.tsx +4 -4
- package/templates/blocks/DD-CardTemplate01/Component.tsx +5 -5
- package/templates/blocks/DD-CardsWithVariantV3/Component.tsx +472 -0
- package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/index.tsx +203 -0
- package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/styles.ts +87 -0
- package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/useInjectStyle.ts +39 -0
- package/templates/blocks/DD-CardsWithVariantV3/config.ts +326 -0
- package/templates/blocks/DD-CardsWithVariantV3/manifest.json +110 -0
- package/templates/blocks/DD-Carousel-A/Component.tsx +251 -249
- package/templates/blocks/DD-Carousel-B/Component.tsx +405 -403
- package/templates/blocks/DD-Chip/Component.tsx +14 -14
- package/templates/blocks/DD-ComparisonTable/Component.tsx +255 -256
- package/templates/blocks/DD-Contact/Component.tsx +433 -434
- package/templates/blocks/DD-Counter/Component.tsx +18 -13
- package/templates/blocks/DD-DeliCard/Component.tsx +303 -0
- package/templates/blocks/DD-DeliCard/config.ts +186 -0
- package/templates/blocks/DD-DeliCard/manifest.json +94 -0
- package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
- package/templates/blocks/DD-FeatProd/CircularGallery.tsx +1179 -0
- package/templates/blocks/DD-FeatProd/Component.tsx +484 -0
- package/templates/blocks/DD-FeatProd/config.ts +173 -0
- package/templates/blocks/DD-FeatProd/manifest.json +93 -0
- package/templates/blocks/DD-FeatServ/Component.tsx +607 -0
- package/templates/blocks/DD-FeatServ/config.ts +213 -0
- package/templates/blocks/DD-FeatServ/manifest.json +113 -0
- package/templates/blocks/DD-FeatStrip/Component.tsx +584 -585
- package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +627 -628
- package/templates/blocks/DD-FluidBtn/Component.tsx +46 -43
- package/templates/blocks/DD-GooeyInfo/Component.tsx +150 -149
- package/templates/blocks/DD-GooeyInfo/config.ts +136 -135
- package/templates/blocks/DD-Hero/Component.tsx +16 -40
- package/templates/blocks/DD-HeroPlas/Component.tsx +50 -0
- package/templates/blocks/DD-HeroPlas/ThreeScene.tsx +109 -0
- package/templates/blocks/DD-HeroPlas/config.ts +33 -0
- package/templates/blocks/DD-HeroPlas/manifest.json +88 -0
- package/templates/blocks/DD-HorizontalScroll/Component.tsx +4 -5
- package/templates/blocks/DD-Icon/Component.tsx +46 -81
- package/templates/blocks/DD-IconButton/Component.tsx +13 -20
- package/templates/blocks/DD-IdTag/Component.tsx +22 -21
- package/templates/blocks/DD-IframeMedia/Component.tsx +14 -10
- package/templates/blocks/DD-LineAnim/Component.tsx +42 -36
- package/templates/blocks/DD-LogoMarq/Component.tsx +38 -48
- package/templates/blocks/DD-Lottie/Component.tsx +11 -3
- package/templates/blocks/DD-MasonaryMedia/Component.tsx +544 -547
- package/templates/blocks/DD-MasonaryMedia/config.ts +389 -389
- package/templates/blocks/DD-Masonry/Component.tsx +654 -644
- package/templates/blocks/DD-Masonry/config.ts +475 -475
- package/templates/blocks/DD-MaterialSwatches/Component.tsx +85 -0
- package/templates/blocks/DD-MaterialSwatches/config.ts +232 -0
- package/templates/blocks/DD-MaterialSwatches/manifest.json +84 -0
- package/templates/blocks/DD-MaterialSwatches/material.ts +107 -0
- package/templates/blocks/DD-NineCanvas/Component.tsx +129 -0
- package/templates/blocks/DD-NineCanvas/RenderBlocks.tsx +244 -0
- package/templates/blocks/DD-NineCanvas/allowedCanvasBlocks.ts +6 -0
- package/templates/blocks/DD-NineCanvas/blockComponents.tsx +8 -0
- package/templates/blocks/DD-NineCanvas/canvas/CanvasGrid.tsx +316 -0
- package/templates/blocks/DD-NineCanvas/canvas/RadialNav.tsx +432 -0
- package/templates/blocks/DD-NineCanvas/canvas/canvas-panels.css +889 -0
- package/templates/blocks/DD-NineCanvas/canvas/icons/custom-icons.tsx +364 -0
- package/templates/blocks/DD-NineCanvas/config.ts +61 -0
- package/templates/blocks/DD-NineCanvas/manifest.json +165 -0
- package/templates/blocks/DD-NineCanvas/styles.scss +152 -0
- package/templates/blocks/DD-NineCanvas/tooltip/get-strict-context.tsx +26 -0
- package/templates/blocks/DD-NineCanvas/tooltip/primitives.tsx +178 -0
- package/templates/blocks/DD-NineCanvas/tooltip/tooltip.tsx +71 -0
- package/templates/blocks/DD-NineCanvas/tooltip/use-controlled-state.tsx +33 -0
- package/templates/blocks/DD-NineCanvas/utilities/FadeOnScroll.tsx +68 -0
- package/templates/blocks/DD-NineCanvas/utilities/ParallaxWrapper.tsx +157 -0
- package/templates/blocks/DD-NineCanvas/utilities/renderCustomCSS.ts +5 -0
- package/templates/blocks/DD-NineCanvas/utilities/webglCleanup.ts +27 -0
- package/templates/blocks/DD-PayloadAIAssistant/Component.tsx +78 -0
- package/templates/blocks/DD-PayloadAIAssistant/DustedBot.tsx +590 -0
- package/templates/blocks/DD-PayloadAIAssistant/config.ts +37 -0
- package/templates/blocks/DD-PayloadAIAssistant/demo.css +125 -0
- package/templates/blocks/DD-PayloadAIAssistant/manifest.json +93 -0
- package/templates/blocks/DD-PayloadAIAssistant/source-defaults.ts +17 -0
- package/templates/blocks/DD-PayloadAIAssistant/styles.ts +306 -0
- package/templates/blocks/DD-Pricing/Component.tsx +368 -372
- package/templates/blocks/DD-Pricing/config.ts +215 -215
- package/templates/blocks/DD-Process/Component.tsx +147 -149
- package/templates/blocks/DD-Progress/Component.tsx +156 -141
- package/templates/blocks/DD-ProjHeader/Component.tsx +93 -91
- package/templates/blocks/DD-ProjSlider/Component.tsx +1 -1
- package/templates/blocks/DD-ProjSlider/config.ts +2 -2
- package/templates/blocks/DD-Section/Component.tsx +265 -266
- package/templates/blocks/DD-Separator/Component.tsx +66 -66
- package/templates/blocks/DD-Services/Component.tsx +176 -176
- package/templates/blocks/DD-ShowcaseGrid/Component.tsx +306 -307
- package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
- package/templates/blocks/DD-StackCards/Component.tsx +474 -473
- package/templates/blocks/DD-Team/Component.tsx +247 -247
- package/templates/blocks/DD-TeamCards/Component.tsx +251 -251
- package/templates/blocks/DD-TechStack/Component.tsx +161 -168
- package/templates/blocks/DD-TestMarq/Component.tsx +158 -132
- package/templates/blocks/DD-Testim/Component.tsx +247 -247
- package/templates/blocks/DD-Testim2/Component.tsx +265 -256
- package/templates/blocks/DD-Testimonials/Component.tsx +139 -147
- package/templates/blocks/DD-TextMarq/Component.tsx +157 -157
- package/templates/blocks/DD-Thanks/Component.tsx +501 -500
- package/templates/blocks/DD-Work/Component.tsx +315 -315
- package/templates/blocks/DD-Work-B/Component.tsx +294 -294
- package/templates/blocks/DDHG-CTASection2/Component.tsx +183 -184
- package/templates/blocks/DDHG-HighlightArc/Component.tsx +246 -243
- package/templates/blocks/DDHG-LogoMarquee/Component.tsx +180 -93
- package/templates/blocks/DDHG-LogoMarquee/config.ts +154 -66
- package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +225 -213
- package/templates/blocks/DDHG-ProcessSec/Component.tsx +38 -39
- package/templates/blocks/DDHG-ServiceDetails/Component.tsx +11 -11
- package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +30 -47
- package/templates/blocks/DDHG-ServiceHero/Component.tsx +10 -10
- package/templates/blocks/DDHG-ServiceOverview/Component.tsx +23 -23
- package/templates/blocks/DDHG-StatsFAQ/Component.tsx +40 -40
- package/templates/blocks/DDHG-TeamCards/Component.tsx +140 -131
- package/templates/blocks/Form/Checkbox/index.tsx +45 -0
- package/templates/blocks/Form/Component.tsx +163 -0
- package/templates/blocks/Form/Country/index.tsx +65 -0
- package/templates/blocks/Form/Country/options.ts +982 -0
- package/templates/blocks/Form/Email/index.tsx +38 -0
- package/templates/blocks/Form/Error/index.tsx +15 -0
- package/templates/blocks/Form/Message/index.tsx +13 -0
- package/templates/blocks/Form/Number/index.tsx +36 -0
- package/templates/blocks/Form/Select/index.tsx +63 -0
- package/templates/blocks/Form/State/index.tsx +64 -0
- package/templates/blocks/Form/State/options.ts +52 -0
- package/templates/blocks/Form/Text/index.tsx +32 -0
- package/templates/blocks/Form/Textarea/index.tsx +40 -0
- package/templates/blocks/Form/Width/index.tsx +13 -0
- package/templates/blocks/Form/config.ts +51 -0
- package/templates/blocks/Form/fields.tsx +21 -0
- package/templates/blocks/Form/manifest.json +168 -0
- package/templates/blocks/MediaBlock/Component.tsx +67 -0
- package/templates/blocks/MediaBlock/config.ts +14 -0
- package/templates/blocks/MediaBlock/manifest.json +92 -0
- package/templates/blocks/TransitionsDustedShowcase/Component.tsx +9 -0
- package/templates/blocks/TransitionsDustedShowcase/config.ts +13 -0
- package/templates/blocks/TransitionsDustedShowcase/manifest.json +80 -0
- package/templates/blocks/rawHtml/Component.tsx +25 -0
- package/templates/blocks/rawHtml/config.ts +56 -0
- package/templates/blocks/rawHtml/manifest.json +80 -0
|
@@ -1,147 +1,139 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import React from
|
|
4
|
-
import Image from
|
|
5
|
-
import { Star } from
|
|
6
|
-
import { motion, type Variants } from
|
|
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 ===
|
|
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:
|
|
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 ===
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
return (
|
|
45
|
-
<>
|
|
46
|
-
{customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
|
|
47
|
-
|
|
48
|
-
<div id={sectionId ??
|
|
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 ===
|
|
73
|
-
t.companyLogo !== null &&
|
|
74
|
-
|
|
75
|
-
? t.companyLogo.url
|
|
76
|
-
: (t.fallbackAvatarUrl ?? null)
|
|
77
|
-
const safeAvatarUrl = isLocalImageSrc(avatarUrl)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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="
|
|
79
|
-
style={{ marginRight: `${spacing}px` }}
|
|
80
|
-
>
|
|
81
|
-
<span
|
|
82
|
-
className={cn('text-
|
|
83
|
-
style={{
|
|
84
|
-
fontSize: item.fontSize || '
|
|
85
|
-
fontWeight: item.fontWeight || '
|
|
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="
|
|
100
|
-
style={{ marginRight: `${spacing}px` }}
|
|
101
|
-
>
|
|
102
|
-
<span
|
|
103
|
-
className={cn('text-
|
|
104
|
-
style={{
|
|
105
|
-
fontSize: item.fontSize || '
|
|
106
|
-
fontWeight: item.fontWeight || '
|
|
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
|
+
}
|