blocks-dusted 0.1.12 → 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 +1 -1
- 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 +535 -535
- 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 +139 -140
- 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-ComparisonTable/Component.tsx +255 -256
- package/templates/blocks/DD-Contact/Component.tsx +433 -435
- package/templates/blocks/DD-Counter/Component.tsx +11 -6
- 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 +296 -297
- 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 +937 -936
- 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-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 -545
- 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 +2 -3
- package/templates/blocks/DD-Pricing/config.ts +215 -215
- package/templates/blocks/DD-Process/Component.tsx +2 -2
- package/templates/blocks/DD-ProjHeader/Component.tsx +93 -91
- package/templates/blocks/DD-ProjSlider/Component.tsx +1 -1
- 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 +2 -2
- package/templates/blocks/DD-StackCards/Component.tsx +172 -171
- 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 +173 -174
- 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 -139
- package/templates/blocks/DD-Thanks/Component.tsx +1 -1
- package/templates/blocks/DD-Work/Component.tsx +318 -318
- 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 +6 -3
- package/templates/blocks/DDHG-LogoMarquee/Component.tsx +45 -8
- package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +1 -3
- package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +9 -22
- package/templates/blocks/DDHG-ServiceHero/Component.tsx +10 -10
- package/templates/blocks/DDHG-TeamCards/Component.tsx +6 -35
- 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
|
@@ -75,13 +75,13 @@ export const Counter: React.FC<CounterItem> = ({
|
|
|
75
75
|
}, [ref, inView, from, to, duration])
|
|
76
76
|
|
|
77
77
|
return (
|
|
78
|
-
<div className="counter-wrapper flex items-
|
|
78
|
+
<div className="counter-wrapper flex min-h-12 items-baseline justify-center gap-1 tabular-nums">
|
|
79
79
|
{prefix && (
|
|
80
80
|
<span className={prefixClass || 'text-xl font-bold sm:text-2xl md:text-4xl'}>{prefix}</span>
|
|
81
81
|
)}
|
|
82
82
|
<span ref={ref} className={className || 'text-xl font-bold sm:text-2xl md:text-4xl'} />
|
|
83
83
|
{suffix && (
|
|
84
|
-
<span className={suffixClass || 'text-
|
|
84
|
+
<span className={suffixClass || 'text-base font-medium sm:text-lg md:text-xl'}>
|
|
85
85
|
{suffix}
|
|
86
86
|
</span>
|
|
87
87
|
)}
|
|
@@ -166,13 +166,18 @@ export const DDCounter: React.FC<CounterBlockProps> = ({
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
return (
|
|
169
|
-
<div className={getLayoutClasses()} id={counterContId}>
|
|
169
|
+
<div className={`w-full ${getLayoutClasses()}`} id={counterContId}>
|
|
170
170
|
{counters.map((counter) => (
|
|
171
|
-
<div
|
|
171
|
+
<div
|
|
172
|
+
key={counter.id}
|
|
173
|
+
className="counter-item flex min-h-40 w-full flex-col items-center justify-center gap-3 rounded-2xl border border-border bg-card p-6 text-center shadow-sm"
|
|
174
|
+
id={counterId}
|
|
175
|
+
>
|
|
172
176
|
<RichText
|
|
173
177
|
data={counter.beforeCounter}
|
|
174
178
|
enableGutter={false}
|
|
175
|
-
|
|
179
|
+
enableProse={false}
|
|
180
|
+
className="counter_label-before text-sm font-medium text-muted-foreground [&_*]:m-0 [&_*]:text-current"
|
|
176
181
|
/>
|
|
177
182
|
|
|
178
183
|
<Counter {...counter} />
|
|
@@ -181,7 +186,7 @@ export const DDCounter: React.FC<CounterBlockProps> = ({
|
|
|
181
186
|
data={counter.label}
|
|
182
187
|
enableProse={false}
|
|
183
188
|
enableGutter={false}
|
|
184
|
-
className="counter_label-after
|
|
189
|
+
className="counter_label-after text-sm font-medium text-foreground [&_*]:m-0 [&_*]:text-current"
|
|
185
190
|
/>
|
|
186
191
|
</div>
|
|
187
192
|
))}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useMemo, useState } from 'react'
|
|
4
|
+
import Image from 'next/image'
|
|
5
|
+
import * as LucideIcons from 'lucide-react'
|
|
6
|
+
import { Truck } from 'lucide-react'
|
|
7
|
+
|
|
8
|
+
import { cn } from '@/utilities/ui'
|
|
9
|
+
import RichText from '@/components/RichText'
|
|
10
|
+
|
|
11
|
+
type MediaObj = {
|
|
12
|
+
url?: string | null
|
|
13
|
+
alt?: string | null
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type IconMedia = MediaObj | string | number | null | undefined
|
|
17
|
+
|
|
18
|
+
type Badge = {
|
|
19
|
+
icon?: string | null
|
|
20
|
+
customIcon?: IconMedia
|
|
21
|
+
title?: string | null
|
|
22
|
+
description?: string | null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type Feature = {
|
|
26
|
+
id?: string | null
|
|
27
|
+
icon?: string | null
|
|
28
|
+
customIcon?: IconMedia
|
|
29
|
+
title?: string | null
|
|
30
|
+
description?: string | null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type PricingItem = {
|
|
34
|
+
id?: string | null
|
|
35
|
+
price?: string | null
|
|
36
|
+
label?: string | null
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type DDDeliCardProps = {
|
|
40
|
+
blockType?: 'DD-DeliCard'
|
|
41
|
+
blockName?: string | null
|
|
42
|
+
sectionId?: string | null
|
|
43
|
+
customCSS?: string | null
|
|
44
|
+
image?: MediaObj | string | null
|
|
45
|
+
imageAlt?: string | null
|
|
46
|
+
badge?: Badge | null
|
|
47
|
+
heading?: string | null
|
|
48
|
+
description?: any | null
|
|
49
|
+
features?: Feature[] | null
|
|
50
|
+
pricingItems?: PricingItem[] | null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function getMediaUrl(media?: IconMedia): string | undefined {
|
|
54
|
+
if (!media) return undefined
|
|
55
|
+
if (typeof media === 'string') return media
|
|
56
|
+
if (typeof media === 'object' && 'url' in media && typeof media.url === 'string') return media.url
|
|
57
|
+
return undefined
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getMediaAlt(media?: MediaObj | string | null, fallback = ''): string {
|
|
61
|
+
if (!media || typeof media === 'string') return fallback
|
|
62
|
+
return media.alt ?? fallback
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function IconRenderer({
|
|
66
|
+
icon,
|
|
67
|
+
customIcon,
|
|
68
|
+
className,
|
|
69
|
+
size = 22,
|
|
70
|
+
}: {
|
|
71
|
+
icon?: string | null
|
|
72
|
+
customIcon?: IconMedia
|
|
73
|
+
className?: string
|
|
74
|
+
size?: number
|
|
75
|
+
}) {
|
|
76
|
+
const customIconUrl = getMediaUrl(customIcon)
|
|
77
|
+
|
|
78
|
+
if (customIconUrl) {
|
|
79
|
+
return (
|
|
80
|
+
<Image
|
|
81
|
+
src={customIconUrl}
|
|
82
|
+
alt=""
|
|
83
|
+
width={size}
|
|
84
|
+
height={size}
|
|
85
|
+
className={cn('h-auto w-auto object-contain', className)}
|
|
86
|
+
/>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const Icon = icon
|
|
91
|
+
? ((LucideIcons as Record<string, any>)[icon] as
|
|
92
|
+
| React.ComponentType<{ size?: number; strokeWidth?: number; className?: string }>
|
|
93
|
+
| undefined)
|
|
94
|
+
: undefined
|
|
95
|
+
|
|
96
|
+
if (Icon) return <Icon size={size} strokeWidth={2.2} className={className} />
|
|
97
|
+
|
|
98
|
+
return <Truck size={size} strokeWidth={2.2} className={className} />
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export const DDDeliCard: React.FC<DDDeliCardProps> = ({
|
|
102
|
+
sectionId,
|
|
103
|
+
customCSS,
|
|
104
|
+
image,
|
|
105
|
+
imageAlt,
|
|
106
|
+
badge,
|
|
107
|
+
heading = 'Reliable Delivery, Every Time',
|
|
108
|
+
description,
|
|
109
|
+
features = [],
|
|
110
|
+
pricingItems = [],
|
|
111
|
+
}) => {
|
|
112
|
+
const safeFeatures = useMemo(() => {
|
|
113
|
+
return features?.filter((feature) => feature?.title) ?? []
|
|
114
|
+
}, [features])
|
|
115
|
+
|
|
116
|
+
const safePricingItems = useMemo(() => {
|
|
117
|
+
return pricingItems?.filter((item) => item?.price || item?.label) ?? []
|
|
118
|
+
}, [pricingItems])
|
|
119
|
+
|
|
120
|
+
const [activeTab, setActiveTab] = useState(0)
|
|
121
|
+
|
|
122
|
+
const activeFeature = safeFeatures[activeTab] ?? safeFeatures[0]
|
|
123
|
+
const imageUrl = getMediaUrl(image)
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div
|
|
127
|
+
id={sectionId || undefined}
|
|
128
|
+
data-block-type="DD-DeliCard"
|
|
129
|
+
className="delivery-card-block mx-auto w-full max-w-7xl bg-stone-50/50 text-stone-900 dark:bg-stone-950 dark:text-stone-100 rounded-3xl squircle"
|
|
130
|
+
>
|
|
131
|
+
{customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
|
|
132
|
+
|
|
133
|
+
<div
|
|
134
|
+
data-container-type={`${sectionId}-container`}
|
|
135
|
+
className="overflow-hidden rounded-3xl border border-stone-200 dark:border-stone-800 squircle"
|
|
136
|
+
>
|
|
137
|
+
<div data-col-type={`${sectionId}-col`} className="grid grid-cols-1 lg:grid-cols-2">
|
|
138
|
+
<div className="relative min-h-[400px] overflow-hidden">
|
|
139
|
+
{imageUrl ? (
|
|
140
|
+
<>
|
|
141
|
+
<Image
|
|
142
|
+
data-image-type={`${sectionId}-img`}
|
|
143
|
+
src={imageUrl}
|
|
144
|
+
alt={imageAlt || getMediaAlt(image, 'BFS Plastics delivery truck')}
|
|
145
|
+
width={900}
|
|
146
|
+
height={600}
|
|
147
|
+
className="h-full min-h-[400px] w-full object-cover"
|
|
148
|
+
/>
|
|
149
|
+
<img
|
|
150
|
+
data-image-type={`${sectionId}-bgimg`}
|
|
151
|
+
src="/assets/svg/lines.svg"
|
|
152
|
+
alt=""
|
|
153
|
+
aria-hidden="true"
|
|
154
|
+
className="pointer-events-none absolute inset-0 h-full min-h-[400px] w-full object-cover"
|
|
155
|
+
/>
|
|
156
|
+
</>
|
|
157
|
+
) : (
|
|
158
|
+
<div className="flex h-full min-h-[400px] w-full items-center justify-center border border-dashed border-stone-300 dark:border-stone-700 text-sm text-stone-600 dark:text-stone-300">
|
|
159
|
+
Upload a delivery image
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
<div className="absolute inset-0 bg-gradient-to-r from-transparent to-stone-950/60 lg:bg-gradient-to-l lg:from-stone-950/60 lg:to-transparent" />
|
|
164
|
+
|
|
165
|
+
{(badge?.title || badge?.description) && (
|
|
166
|
+
<div
|
|
167
|
+
data-content-type={`${sectionId}-description`}
|
|
168
|
+
className="absolute bottom-6 left-6 rounded-xl bg-stone-50/95 dark:bg-stone-900/95 px-4 py-3 shadow-lg backdrop-blur-sm"
|
|
169
|
+
>
|
|
170
|
+
<div
|
|
171
|
+
data-container-type={`${sectionId}-container`}
|
|
172
|
+
className="flex items-center gap-2"
|
|
173
|
+
>
|
|
174
|
+
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-indigo-100 dark:bg-indigo-950/70">
|
|
175
|
+
<IconRenderer
|
|
176
|
+
icon={badge?.icon || 'Truck'}
|
|
177
|
+
customIcon={badge?.customIcon}
|
|
178
|
+
size={18}
|
|
179
|
+
className="text-indigo-700 dark:text-indigo-300"
|
|
180
|
+
/>
|
|
181
|
+
</span>
|
|
182
|
+
|
|
183
|
+
<div data-container-type={`${sectionId}-badge-desc`}>
|
|
184
|
+
{badge?.title && (
|
|
185
|
+
<div className="text-sm font-bold text-stone-900 dark:text-stone-100">
|
|
186
|
+
{badge.title}
|
|
187
|
+
</div>
|
|
188
|
+
)}
|
|
189
|
+
{badge?.description && (
|
|
190
|
+
<div className="text-xs text-stone-600 dark:text-stone-300">
|
|
191
|
+
{badge.description}
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
198
|
+
</div>
|
|
199
|
+
|
|
200
|
+
<div
|
|
201
|
+
data-container-type={`${sectionId}-heading`}
|
|
202
|
+
className="flex flex-col justify-center p-6 md:p-10 lg:p-12"
|
|
203
|
+
>
|
|
204
|
+
{heading && (
|
|
205
|
+
<h3 className="mb-0 text-2xl font-bold text-stone-900 dark:text-stone-100 md:text-3xl">
|
|
206
|
+
{heading}
|
|
207
|
+
</h3>
|
|
208
|
+
)}
|
|
209
|
+
|
|
210
|
+
{description && (
|
|
211
|
+
<RichText
|
|
212
|
+
data-content-type={`${sectionId}-richtext`}
|
|
213
|
+
data={description}
|
|
214
|
+
className="mb-8 max-w-none text-base leading-relaxed text-stone-600 dark:text-stone-300"
|
|
215
|
+
enableGutter={false}
|
|
216
|
+
enableProse={false}
|
|
217
|
+
/>
|
|
218
|
+
)}
|
|
219
|
+
|
|
220
|
+
{safeFeatures.length > 0 && (
|
|
221
|
+
<>
|
|
222
|
+
<div
|
|
223
|
+
data-container-type="delivery-location-tags"
|
|
224
|
+
className="mb-6 mt-6 flex flex-wrap gap-2"
|
|
225
|
+
>
|
|
226
|
+
{safeFeatures.map((feature, index) => (
|
|
227
|
+
<button
|
|
228
|
+
key={feature.id || feature.title || index}
|
|
229
|
+
type="button"
|
|
230
|
+
onClick={() => setActiveTab(index)}
|
|
231
|
+
className={cn(
|
|
232
|
+
'cursor-pointer whitespace-nowrap rounded-full px-4 py-2 text-sm font-medium transition-all duration-300 hover:duration-300',
|
|
233
|
+
activeTab === index
|
|
234
|
+
? 'border border-solid border-indigo-600 bg-indigo-600 text-white dark:border-indigo-400 dark:bg-indigo-500 dark:text-stone-950'
|
|
235
|
+
: 'border border-stone-200 bg-stone-100 text-stone-700 hover:bg-stone-200 hover:text-stone-950 dark:border-stone-700 dark:bg-stone-900 dark:text-stone-300 dark:hover:bg-stone-800 dark:hover:text-stone-100',
|
|
236
|
+
)}
|
|
237
|
+
>
|
|
238
|
+
{feature.title}
|
|
239
|
+
</button>
|
|
240
|
+
))}
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
{activeFeature && (
|
|
244
|
+
<div className="rounded-xl border border-stone-200 bg-stone-50 dark:border-stone-800 dark:bg-stone-900/80 p-5 md:p-6">
|
|
245
|
+
<div className="flex items-start gap-4">
|
|
246
|
+
<span className="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-indigo-100 dark:bg-indigo-950/70">
|
|
247
|
+
<IconRenderer
|
|
248
|
+
icon={activeFeature.icon || 'MapPin'}
|
|
249
|
+
customIcon={activeFeature.customIcon}
|
|
250
|
+
size={20}
|
|
251
|
+
className="text-indigo-700 dark:text-indigo-300"
|
|
252
|
+
/>
|
|
253
|
+
</span>
|
|
254
|
+
|
|
255
|
+
<div>
|
|
256
|
+
{activeFeature.title && (
|
|
257
|
+
<h4 className="mb-1 text-lg font-bold text-foreground">
|
|
258
|
+
{activeFeature.title}
|
|
259
|
+
</h4>
|
|
260
|
+
)}
|
|
261
|
+
|
|
262
|
+
{activeFeature.description && (
|
|
263
|
+
<p className="text-sm leading-relaxed text-stone-600 dark:text-stone-300">
|
|
264
|
+
{activeFeature.description}
|
|
265
|
+
</p>
|
|
266
|
+
)}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
)}
|
|
271
|
+
</>
|
|
272
|
+
)}
|
|
273
|
+
|
|
274
|
+
{safePricingItems.length > 0 && (
|
|
275
|
+
<div className="mt-6 grid grid-cols-1 gap-3 sm:grid-cols-3">
|
|
276
|
+
{safePricingItems.map((item, index) => (
|
|
277
|
+
<div
|
|
278
|
+
key={item.id || item.price || index}
|
|
279
|
+
className="rounded-xl border border-stone-200 bg-stone-50 dark:border-stone-800 dark:bg-stone-900/80 p-3 text-center"
|
|
280
|
+
>
|
|
281
|
+
{item.price && (
|
|
282
|
+
<div className="text-lg font-bold text-indigo-700 dark:text-indigo-300">
|
|
283
|
+
{item.price}
|
|
284
|
+
</div>
|
|
285
|
+
)}
|
|
286
|
+
|
|
287
|
+
{item.label && (
|
|
288
|
+
<div className="mt-0.5 text-xs text-stone-600 dark:text-stone-300">
|
|
289
|
+
{item.label}
|
|
290
|
+
</div>
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
))}
|
|
294
|
+
</div>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export default DDDeliCard
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import type { Block } from 'payload'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Author: Hadi Zainal | Designs Dusted
|
|
5
|
+
* Delivery Card Block - version 1.0 BFS variant
|
|
6
|
+
* Main delivery/freight card with editable image, badge, tabbed features,
|
|
7
|
+
* pricing summary, lucide icon picker defaults, and uploaded custom icon overrides.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { iconPicker } from '@/fields/lucide-icon-picker/field'
|
|
11
|
+
import { defaultLexical } from '@/fields/defaultLexical'
|
|
12
|
+
|
|
13
|
+
const iconFields = [
|
|
14
|
+
iconPicker({
|
|
15
|
+
name: 'icon',
|
|
16
|
+
description: 'Choose a Lucide icon. Overridden by custom icon upload if provided.',
|
|
17
|
+
}),
|
|
18
|
+
{
|
|
19
|
+
name: 'customIcon',
|
|
20
|
+
type: 'upload' as const,
|
|
21
|
+
relationTo: 'media' as const,
|
|
22
|
+
required: false,
|
|
23
|
+
label: 'Custom Icon (overrides Lucide)',
|
|
24
|
+
admin: {
|
|
25
|
+
description: 'Upload a custom icon image. If set, this takes priority over the Lucide icon.',
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
export const DDDeliCard: Block = {
|
|
31
|
+
slug: 'DD-DeliCard',
|
|
32
|
+
dbName: 'dd_deli_card',
|
|
33
|
+
interfaceName: 'DDDeliCard',
|
|
34
|
+
labels: {
|
|
35
|
+
singular: 'DD Deli Card',
|
|
36
|
+
plural: 'DD Deli Cards',
|
|
37
|
+
},
|
|
38
|
+
fields: [
|
|
39
|
+
{
|
|
40
|
+
type: 'tabs',
|
|
41
|
+
tabs: [
|
|
42
|
+
{
|
|
43
|
+
label: 'Content',
|
|
44
|
+
fields: [
|
|
45
|
+
{
|
|
46
|
+
name: 'image',
|
|
47
|
+
type: 'upload',
|
|
48
|
+
relationTo: 'media',
|
|
49
|
+
required: false,
|
|
50
|
+
label: 'Delivery Image',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: 'imageAlt',
|
|
54
|
+
type: 'text',
|
|
55
|
+
label: 'Image Alt Text',
|
|
56
|
+
defaultValue: 'BFS Plastics delivery truck',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: 'badge',
|
|
60
|
+
type: 'group',
|
|
61
|
+
label: 'Floating Badge',
|
|
62
|
+
fields: [
|
|
63
|
+
...iconFields,
|
|
64
|
+
{
|
|
65
|
+
name: 'title',
|
|
66
|
+
type: 'text',
|
|
67
|
+
defaultValue: 'Australia-wide',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'description',
|
|
71
|
+
type: 'text',
|
|
72
|
+
defaultValue: 'Delivery coverage',
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'heading',
|
|
78
|
+
type: 'text',
|
|
79
|
+
defaultValue: 'Reliable Delivery, Every Time',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'description',
|
|
83
|
+
type: 'richText',
|
|
84
|
+
label: 'Description',
|
|
85
|
+
editor: defaultLexical,
|
|
86
|
+
required: false,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'features',
|
|
90
|
+
type: 'array',
|
|
91
|
+
label: 'Delivery Features',
|
|
92
|
+
minRows: 1,
|
|
93
|
+
defaultValue: [
|
|
94
|
+
{
|
|
95
|
+
icon: 'MapPin',
|
|
96
|
+
title: 'Brisbane Metro',
|
|
97
|
+
description:
|
|
98
|
+
'From $35 + GST across all Brisbane metro areas. Fast, reliable local delivery.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
icon: 'Sun',
|
|
102
|
+
title: 'Gold & Sunshine Coast',
|
|
103
|
+
description:
|
|
104
|
+
'From $40 + GST covering Sunshine Coast and Gold Coast regional areas.',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
icon: 'Map',
|
|
108
|
+
title: 'Interstate Freight',
|
|
109
|
+
description:
|
|
110
|
+
'Interstate charges quoted as per request. We ship Australia-wide with trusted freight partners.',
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
fields: [
|
|
114
|
+
...iconFields,
|
|
115
|
+
{
|
|
116
|
+
name: 'title',
|
|
117
|
+
type: 'text',
|
|
118
|
+
required: true,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'description',
|
|
122
|
+
type: 'textarea',
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'pricingItems',
|
|
128
|
+
type: 'array',
|
|
129
|
+
label: 'Pricing Summary',
|
|
130
|
+
minRows: 1,
|
|
131
|
+
defaultValue: [
|
|
132
|
+
{
|
|
133
|
+
price: '$35',
|
|
134
|
+
label: 'Brisbane Metro',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
price: '$40',
|
|
138
|
+
label: 'Coast Regions',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
price: 'Quote',
|
|
142
|
+
label: 'Interstate',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
fields: [
|
|
146
|
+
{
|
|
147
|
+
name: 'price',
|
|
148
|
+
type: 'text',
|
|
149
|
+
required: true,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'label',
|
|
153
|
+
type: 'text',
|
|
154
|
+
required: true,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
label: 'Settings',
|
|
162
|
+
fields: [
|
|
163
|
+
{
|
|
164
|
+
name: 'sectionId',
|
|
165
|
+
type: 'text',
|
|
166
|
+
label: 'Section ID',
|
|
167
|
+
admin: {
|
|
168
|
+
description: 'Optional ID for anchor linking',
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'customCSS',
|
|
173
|
+
type: 'code',
|
|
174
|
+
label: 'Custom CSS',
|
|
175
|
+
admin: {
|
|
176
|
+
language: 'css',
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export default DDDeliCard
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "block",
|
|
3
|
+
"name": "DD-DeliCard",
|
|
4
|
+
"label": "Delivery Card",
|
|
5
|
+
"sourceName": "DD-DeliCard",
|
|
6
|
+
"portableName": "DD-DeliCard",
|
|
7
|
+
"slug": "DD-DeliCard",
|
|
8
|
+
"version": "0.0.1",
|
|
9
|
+
"source": {
|
|
10
|
+
"repo": "Hadizainal/Dusted-Payload-Blocks",
|
|
11
|
+
"originalPath": "CLI-TestGround/dusted-blocks-demo/src/blocks/DD-DeliCard",
|
|
12
|
+
"originalSlug": "DD-DeliCard",
|
|
13
|
+
"originalConfigExport": "DDDeliCard",
|
|
14
|
+
"originalComponentExport": "DDDeliCard",
|
|
15
|
+
"originalInterfaceName": "DDDeliCard"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
{
|
|
19
|
+
"from": "Component.tsx",
|
|
20
|
+
"to": "src/blocks/DD-DeliCard/Component.tsx"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"from": "config.ts",
|
|
24
|
+
"to": "src/blocks/DD-DeliCard/config.ts"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"dependencies": [
|
|
28
|
+
"lucide-react"
|
|
29
|
+
],
|
|
30
|
+
"sharedFiles": [],
|
|
31
|
+
"requiredProjectFiles": [
|
|
32
|
+
{
|
|
33
|
+
"path": "src/fields/defaultLexical.ts",
|
|
34
|
+
"reason": "Payload config uses the project Lexical editor configuration."
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"path": "src/fields/lucide-icon-picker/field.ts",
|
|
38
|
+
"reason": "Payload config uses the Lucide icon picker field."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"path": "src/components/RichText/index.tsx",
|
|
42
|
+
"reason": "Component renders Payload rich text."
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"path": "src/utilities/ui.ts",
|
|
46
|
+
"reason": "Component imports cn from @/utilities/ui."
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"optionalProjectFiles": [],
|
|
50
|
+
"collectionRegistration": {
|
|
51
|
+
"enabled": true,
|
|
52
|
+
"collections": [
|
|
53
|
+
"Pages",
|
|
54
|
+
"Posts"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"renderBlocksRegistration": {
|
|
58
|
+
"enabled": true
|
|
59
|
+
},
|
|
60
|
+
"renderBlocksBehavior": {
|
|
61
|
+
"wrapperless": false
|
|
62
|
+
},
|
|
63
|
+
"pageRouteIntegration": {
|
|
64
|
+
"required": false,
|
|
65
|
+
"manualSteps": []
|
|
66
|
+
},
|
|
67
|
+
"manual": [
|
|
68
|
+
"Register DDDeliCard in Pages/Posts block arrays if automatic registration was skipped.",
|
|
69
|
+
"Register DD-DeliCard in RenderBlocks if automatic registration was skipped.",
|
|
70
|
+
"Run payload generate:types after schema changes.",
|
|
71
|
+
"Run payload generate:importmap if admin/component imports changed."
|
|
72
|
+
],
|
|
73
|
+
"manualSteps": [
|
|
74
|
+
"Register DDDeliCard in Pages/Posts block arrays if automatic registration was skipped.",
|
|
75
|
+
"Register DD-DeliCard in RenderBlocks if automatic registration was skipped.",
|
|
76
|
+
"Run payload generate:types after schema changes.",
|
|
77
|
+
"Run payload generate:importmap if admin/component imports changed."
|
|
78
|
+
],
|
|
79
|
+
"patchTargets": {
|
|
80
|
+
"blockConfigRegistration": {
|
|
81
|
+
"description": "Installer can register DDDeliCard from @/blocks/DD-DeliCard/config."
|
|
82
|
+
},
|
|
83
|
+
"renderBlocksRegistration": {
|
|
84
|
+
"description": "Installer can register DD-DeliCard with component export DDDeliCard."
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
"notes": [
|
|
88
|
+
"Promoted from the verified Dusted Blocks demo implementation.",
|
|
89
|
+
"Installer preserves existing destination files and reports project requirements before writing."
|
|
90
|
+
],
|
|
91
|
+
"exportName": "DDDeliCard",
|
|
92
|
+
"componentExportName": "DDDeliCard",
|
|
93
|
+
"interfaceName": "DDDeliCard"
|
|
94
|
+
}
|