blocks-dusted 0.1.10 → 0.1.11
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/README.md +35 -0
- package/package.json +1 -1
- package/src/utils/paths.js +13 -13
- package/templates/blocks/DD-Accordion/Component.tsx +229 -0
- package/templates/blocks/DD-Accordion/README.md +3 -0
- package/templates/blocks/DD-Accordion/config.ts +73 -0
- package/templates/blocks/DD-Accordion/manifest.json +75 -0
- package/templates/blocks/DD-AnimText/Component.tsx +788 -0
- package/templates/blocks/DD-AnimText/README.md +3 -0
- package/templates/blocks/DD-AnimText/config.ts +295 -0
- package/templates/blocks/DD-AnimText/manifest.json +85 -0
- package/templates/blocks/DD-AsideNav/Component.tsx +321 -0
- package/templates/blocks/DD-AsideNav/config.ts +204 -0
- package/templates/blocks/DD-AsideNav/manifest.json +80 -0
- package/templates/blocks/DD-Banner/Component.tsx +51 -0
- package/templates/blocks/DD-Banner/config.ts +57 -0
- package/templates/blocks/DD-Banner/manifest.json +72 -0
- package/templates/blocks/DD-BgLightning/Component.tsx +44 -0
- package/templates/blocks/DD-BgLightning/README.md +3 -0
- package/templates/blocks/DD-BgLightning/config.ts +97 -0
- package/templates/blocks/DD-BgLightning/manifest.json +79 -0
- package/templates/blocks/DD-BookCall/Component.tsx +10 -11
- package/templates/blocks/DD-BookCall/config.ts +238 -238
- package/templates/blocks/DD-ButtonPop/Component.tsx +117 -0
- package/templates/blocks/DD-ButtonPop/config.ts +105 -0
- package/templates/blocks/DD-ButtonPop/manifest.json +88 -0
- package/templates/blocks/DD-ButtonPop/svg-icons/Dust2.tsx +167 -0
- package/templates/blocks/DD-Card/Component.tsx +251 -0
- package/templates/blocks/DD-Card/README.md +4 -0
- package/templates/blocks/DD-Card/config.ts +154 -0
- package/templates/blocks/DD-Card/manifest.json +82 -0
- package/templates/blocks/DD-CardTemplate01/Component.tsx +5 -6
- package/templates/blocks/DD-CardTemplate01/config.ts +106 -106
- package/templates/blocks/DD-Carousel-A/Component.tsx +249 -249
- package/templates/blocks/DD-Carousel-B/Component.tsx +403 -403
- package/templates/blocks/DD-Chip/Component.tsx +250 -0
- package/templates/blocks/DD-Chip/EXAMPLES.md +313 -0
- package/templates/blocks/DD-Chip/README.md +202 -0
- package/templates/blocks/DD-Chip/config.ts +369 -0
- package/templates/blocks/DD-Chip/manifest.json +74 -0
- package/templates/blocks/DD-ComparisonTable/Component.tsx +256 -256
- package/templates/blocks/DD-Contact/Component.tsx +434 -434
- package/templates/blocks/DD-Contact/config.ts +22 -22
- package/templates/blocks/DD-Counter/Component.tsx +190 -0
- package/templates/blocks/DD-Counter/README.md +4 -0
- package/templates/blocks/DD-Counter/config.ts +291 -0
- package/templates/blocks/DD-Counter/manifest.json +74 -0
- package/templates/blocks/DD-CustomCSS/Component.tsx +8 -0
- package/templates/blocks/DD-CustomCSS/README.md +4 -0
- package/templates/blocks/DD-CustomCSS/addToLayout.md +5 -0
- package/templates/blocks/DD-CustomCSS/config.ts +22 -0
- package/templates/blocks/DD-CustomCSS/manifest.json +72 -0
- package/templates/blocks/DD-CustomCSS/renderCustomCSS.md +7 -0
- package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
- package/templates/blocks/DD-FeatCat/config.ts +201 -201
- package/templates/blocks/DD-FeatStrip/Component.tsx +585 -585
- package/templates/blocks/DD-FeatStrip/config.ts +134 -134
- package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +628 -0
- package/templates/blocks/DD-FluidBtn/Component.tsx +43 -0
- package/templates/blocks/DD-FluidBtn/README.md +6 -0
- package/templates/blocks/DD-FluidBtn/config.ts +102 -0
- package/templates/blocks/DD-FluidBtn/manifest.json +86 -0
- package/templates/blocks/DD-GooeyInfo/Component.tsx +149 -0
- package/templates/blocks/DD-GooeyInfo/config.ts +135 -0
- package/templates/blocks/DD-GooeyInfo/manifest.json +72 -0
- package/templates/blocks/DD-Hero/Component.tsx +40 -17
- package/templates/blocks/DD-Hero/config.ts +267 -267
- package/templates/blocks/DD-HorizontalScroll/Component.tsx +4 -2
- package/templates/blocks/DD-HorizontalScroll/config.ts +231 -231
- package/templates/blocks/DD-Icon/Component.tsx +287 -0
- package/templates/blocks/DD-Icon/README.md +4 -0
- package/templates/blocks/DD-Icon/config.ts +258 -0
- package/templates/blocks/DD-Icon/hooks/revalidateBlockIds.ts +66 -0
- package/templates/blocks/DD-Icon/manifest.json +82 -0
- package/templates/blocks/DD-IconButton/Component.tsx +455 -0
- package/templates/blocks/DD-IconButton/README.md +326 -0
- package/templates/blocks/DD-IconButton/config.ts +239 -0
- package/templates/blocks/DD-IconButton/manifest.json +85 -0
- package/templates/blocks/DD-IdTag/Component.tsx +21 -0
- package/templates/blocks/DD-IdTag/README.md +4 -0
- package/templates/blocks/DD-IdTag/config.ts +41 -0
- package/templates/blocks/DD-IdTag/manifest.json +72 -0
- package/templates/blocks/DD-Iframe/Component.tsx +45 -0
- package/templates/blocks/DD-Iframe/README.md +4 -0
- package/templates/blocks/DD-Iframe/config.ts +47 -0
- package/templates/blocks/DD-Iframe/manifest.json +72 -0
- package/templates/blocks/DD-IframeMedia/Component.tsx +129 -0
- package/templates/blocks/DD-IframeMedia/README.md +4 -0
- package/templates/blocks/DD-IframeMedia/config.ts +79 -0
- package/templates/blocks/DD-IframeMedia/manifest.json +72 -0
- package/templates/blocks/DD-LineAnim/Component.tsx +36 -0
- package/templates/blocks/DD-LineAnim/README.md +4 -0
- package/templates/blocks/DD-LineAnim/config.ts +90 -0
- package/templates/blocks/DD-LineAnim/manifest.json +80 -0
- package/templates/blocks/DD-LinkGrid/Component.tsx +119 -0
- package/templates/blocks/DD-LinkGrid/README.md +4 -0
- package/templates/blocks/DD-LinkGrid/config.ts +51 -0
- package/templates/blocks/DD-LinkGrid/manifest.json +74 -0
- package/templates/blocks/DD-LogoMarq/Component.tsx +132 -0
- package/templates/blocks/DD-LogoMarq/README.md +4 -0
- package/templates/blocks/DD-LogoMarq/config.ts +264 -0
- package/templates/blocks/DD-LogoMarq/manifest.json +82 -0
- package/templates/blocks/DD-Lottie/Component.tsx +459 -0
- package/templates/blocks/DD-Lottie/README.md +4 -0
- package/templates/blocks/DD-Lottie/config.ts +379 -0
- package/templates/blocks/DD-Lottie/index.tsx +28 -0
- package/templates/blocks/DD-Lottie/manifest.json +83 -0
- package/templates/blocks/DD-MagicBento/Component.tsx +777 -0
- package/templates/blocks/DD-MagicBento/README.md +4 -0
- package/templates/blocks/DD-MagicBento/config.ts +264 -0
- package/templates/blocks/DD-MagicBento/manifest.json +74 -0
- package/templates/blocks/DD-MasonaryMedia/Component.tsx +547 -545
- package/templates/blocks/DD-MasonaryMedia/config.ts +2 -2
- package/templates/blocks/DD-MasonaryMedia/vendor.d.ts +2 -2
- package/templates/blocks/DD-Masonry/Component.tsx +645 -0
- package/templates/blocks/DD-Masonry/README.md +4 -0
- package/templates/blocks/DD-Masonry/config.ts +475 -0
- package/templates/blocks/DD-Masonry/manifest.json +82 -0
- package/templates/blocks/DD-Pricing/Component.tsx +372 -372
- package/templates/blocks/DD-Process/Component.tsx +149 -149
- package/templates/blocks/DD-Process/config.ts +150 -150
- package/templates/blocks/DD-Progress/Component.tsx +141 -0
- package/templates/blocks/DD-Progress/README.md +4 -0
- package/templates/blocks/DD-Progress/config.ts +175 -0
- package/templates/blocks/DD-Progress/manifest.json +74 -0
- package/templates/blocks/DD-ProjHeader/Component.tsx +91 -0
- package/templates/blocks/DD-ProjHeader/README.md +4 -0
- package/templates/blocks/DD-ProjHeader/config.ts +96 -0
- package/templates/blocks/DD-ProjHeader/manifest.json +72 -0
- package/templates/blocks/DD-ProjSlider/Component.tsx +123 -0
- package/templates/blocks/DD-ProjSlider/README.md +4 -0
- package/templates/blocks/DD-ProjSlider/config.ts +64 -0
- package/templates/blocks/DD-ProjSlider/manifest.json +72 -0
- package/templates/blocks/DD-ScrollText/Component.tsx +114 -0
- package/templates/blocks/DD-ScrollText/README.md +4 -0
- package/templates/blocks/DD-ScrollText/config.ts +58 -0
- package/templates/blocks/DD-ScrollText/manifest.json +72 -0
- package/templates/blocks/DD-Section/Component.tsx +266 -266
- package/templates/blocks/DD-Separator/Component.tsx +66 -0
- package/templates/blocks/DD-Separator/README.md +4 -0
- package/templates/blocks/DD-Separator/config.ts +58 -0
- package/templates/blocks/DD-Separator/manifest.json +72 -0
- package/templates/blocks/DD-Services/Component.tsx +176 -176
- package/templates/blocks/DD-Services/config.ts +149 -149
- package/templates/blocks/DD-ShowcaseGrid/Component.tsx +307 -307
- package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
- package/templates/blocks/DD-StackCards/Component.tsx +473 -473
- package/templates/blocks/DD-StackCards/config.ts +399 -399
- package/templates/blocks/DD-Team/Component.tsx +247 -247
- package/templates/blocks/DD-Team/config.ts +173 -173
- package/templates/blocks/DD-TeamCards/Component.tsx +251 -0
- package/templates/blocks/DD-TeamCards/README.md +4 -0
- package/templates/blocks/DD-TeamCards/config.ts +302 -0
- package/templates/blocks/DD-TeamCards/manifest.json +74 -0
- package/templates/blocks/DD-TechStack/Component.tsx +168 -162
- package/templates/blocks/DD-TechStack/config.ts +153 -153
- package/templates/blocks/DD-TestMarq/Component.tsx +132 -0
- package/templates/blocks/DD-TestMarq/README.md +4 -0
- package/templates/blocks/DD-TestMarq/RowLabel.tsx +9 -0
- package/templates/blocks/DD-TestMarq/config.ts +88 -0
- package/templates/blocks/DD-TestMarq/manifest.json +72 -0
- package/templates/blocks/DD-Testim/Component.tsx +247 -0
- package/templates/blocks/DD-Testim/README.md +4 -0
- package/templates/blocks/DD-Testim/config.ts +101 -0
- package/templates/blocks/DD-Testim/manifest.json +75 -0
- package/templates/blocks/DD-Testim2/Component.tsx +256 -0
- package/templates/blocks/DD-Testim2/README.md +4 -0
- package/templates/blocks/DD-Testim2/config.ts +100 -0
- package/templates/blocks/DD-Testim2/manifest.json +74 -0
- package/templates/blocks/DD-Testimonials/Component.tsx +147 -147
- package/templates/blocks/DD-Testimonials/config.ts +120 -120
- package/templates/blocks/DD-TextMarq/Component.tsx +157 -0
- package/templates/blocks/DD-TextMarq/README.md +4 -0
- package/templates/blocks/DD-TextMarq/config.ts +237 -0
- package/templates/blocks/DD-TextMarq/manifest.json +82 -0
- package/templates/blocks/DD-Thanks/Component.tsx +500 -0
- package/templates/blocks/DD-Thanks/README.md +4 -0
- package/templates/blocks/DD-Thanks/config.ts +224 -0
- package/templates/blocks/DD-Thanks/manifest.json +72 -0
- package/templates/blocks/DD-VertMarq/Component.tsx +154 -0
- package/templates/blocks/DD-VertMarq/README.md +4 -0
- package/templates/blocks/DD-VertMarq/config.ts +236 -0
- package/templates/blocks/DD-VertMarq/dist/index.css +6 -0
- package/templates/blocks/DD-VertMarq/dist/index.min.css +1 -0
- package/templates/blocks/DD-VertMarq/manifest.json +74 -0
- package/templates/blocks/DD-Work/Component.tsx +318 -315
- package/templates/blocks/DD-Work/config.ts +165 -165
- package/templates/blocks/DD-Work-B/Component.tsx +294 -294
- package/templates/blocks/DDHG-CTASection2/Component.tsx +185 -0
- package/templates/blocks/DDHG-CTASection2/config.ts +140 -0
- package/templates/blocks/DDHG-CTASection2/manifest.json +45 -0
- package/templates/blocks/DDHG-HighlightArc/Component.tsx +243 -0
- package/templates/blocks/DDHG-HighlightArc/config.ts +85 -0
- package/templates/blocks/DDHG-HighlightArc/manifest.json +46 -0
- package/templates/blocks/DDHG-LogoMarquee/Component.tsx +93 -0
- package/templates/blocks/DDHG-LogoMarquee/config.ts +66 -0
- package/templates/blocks/DDHG-LogoMarquee/manifest.json +39 -0
- package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +213 -0
- package/templates/blocks/DDHG-NavCodeGrid/config.ts +64 -0
- package/templates/blocks/DDHG-NavCodeGrid/manifest.json +45 -0
- package/templates/blocks/DDHG-ProcessSec/Component.tsx +575 -0
- package/templates/blocks/DDHG-ProcessSec/config.ts +170 -0
- package/templates/blocks/DDHG-ProcessSec/manifest.json +56 -0
- package/templates/blocks/DDHG-ServiceDetails/Component.tsx +164 -0
- package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +394 -0
- package/templates/blocks/DDHG-ServiceDetails/config.ts +77 -0
- package/templates/blocks/DDHG-ServiceDetails/manifest.json +50 -0
- package/templates/blocks/DDHG-ServiceHero/Component.tsx +234 -0
- package/templates/blocks/DDHG-ServiceHero/config.ts +49 -0
- package/templates/blocks/DDHG-ServiceHero/manifest.json +26 -0
- package/templates/blocks/DDHG-ServiceOverview/Component.tsx +277 -0
- package/templates/blocks/DDHG-ServiceOverview/config.ts +64 -0
- package/templates/blocks/DDHG-ServiceOverview/manifest.json +44 -0
- package/templates/blocks/DDHG-StatsFAQ/Component.tsx +389 -0
- package/templates/blocks/DDHG-StatsFAQ/config.ts +204 -0
- package/templates/blocks/DDHG-StatsFAQ/manifest.json +45 -0
- package/templates/blocks/DDHG-TeamCards/Component.tsx +131 -0
- package/templates/blocks/DDHG-TeamCards/config.ts +48 -0
- package/templates/blocks/DDHG-TeamCards/manifest.json +29 -0
- package/templates/shared/src/components/BgLightning/index.tsx +242 -0
- package/templates/shared/src/components/ui/ShareButton/index.tsx +80 -0
- package/templates/shared/src/components/ui/anims/FadeOnScroll.tsx +48 -0
- package/templates/shared/src/components/ui/anims/FadeUpOnInView.tsx +63 -0
- package/templates/shared/src/components/ui/anims/GlareHover.tsx +111 -0
- package/templates/shared/src/components/ui/anims/LineAnims.tsx +81 -0
- package/templates/shared/src/components/ui/anims/TitleAnims01.tsx +54 -0
- package/templates/shared/src/components/ui/anims/animation-container.tsx +29 -0
- package/templates/shared/src/components/ui/anims/blur-in.tsx +26 -0
- package/templates/shared/src/components/ui/anims/counter.tsx +53 -0
- package/templates/shared/src/components/ui/anims/fade.tsx +83 -0
- package/templates/shared/src/components/ui/anims/gradual-spacing.tsx +39 -0
- package/templates/shared/src/components/ui/anims/pull-up.tsx +93 -0
- package/templates/shared/src/components/ui/anims/reveal-text.tsx +76 -0
- package/templates/shared/src/components/ui/anims/rotate-words.tsx +43 -0
- package/templates/shared/src/components/ui/anims/shiny-text.tsx +58 -0
- package/templates/shared/src/components/ui/anims/shuffle-text.tsx +85 -0
- package/templates/shared/src/components/ui/anims/split-text.tsx +211 -0
- package/templates/shared/src/components/ui/anims/staggered-fade.tsx +133 -0
- package/templates/shared/src/components/ui/anims/text-scroll.tsx +111 -0
- package/templates/shared/src/components/ui/anims/typing-effect.tsx +112 -0
- package/templates/shared/src/components/ui/button-dusted-fluid/index.tsx +1521 -0
- package/templates/shared/src/components/ui/separator.tsx +85 -0
- package/templates/shared/src/hooks/useBodyScrollLock.ts +97 -0
- package/templates/shared/src/hooks/useClickOutside.ts +28 -0
- package/templates/shared/src/utilities/renderCustomCSS.ts +5 -0
- package/templates/shared/src/utilities/smoothScroll.ts +65 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { motion } from 'framer-motion'
|
|
4
|
+
import { ArrowDown } from 'lucide-react'
|
|
5
|
+
import React, { useEffect, useId, useRef, useState } from 'react'
|
|
6
|
+
import { cn } from '@/utilities/ui'
|
|
7
|
+
|
|
8
|
+
export type DDHGServiceHeroProps = {
|
|
9
|
+
sectionId?: string | null
|
|
10
|
+
customClassName?: string | null
|
|
11
|
+
customCSS?: string | null
|
|
12
|
+
blockId?: string | null
|
|
13
|
+
scrollSpeed?: string | number | null
|
|
14
|
+
eyebrowPrefix?: string | null
|
|
15
|
+
eyebrowEmphasis?: string | null
|
|
16
|
+
heading?: string | null
|
|
17
|
+
description?: string | null
|
|
18
|
+
minHeight?: 'screen' | 'large' | 'medium' | 'compact' | null
|
|
19
|
+
showDivider?: boolean | null
|
|
20
|
+
showScrollIndicator?: boolean | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const minHeightClassMap: Record<NonNullable<DDHGServiceHeroProps['minHeight']>, string> = {
|
|
24
|
+
screen: 'min-h-screen',
|
|
25
|
+
large: 'min-h-[70vh]',
|
|
26
|
+
medium: 'min-h-[56vh]',
|
|
27
|
+
compact: 'min-h-[42vh]'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let globalInstanceCounter = 0
|
|
31
|
+
|
|
32
|
+
function FadeUpOnInView({
|
|
33
|
+
children,
|
|
34
|
+
className = '',
|
|
35
|
+
delay = 0
|
|
36
|
+
}: {
|
|
37
|
+
children: React.ReactNode
|
|
38
|
+
className?: string
|
|
39
|
+
delay?: number
|
|
40
|
+
}) {
|
|
41
|
+
const ref = useRef<HTMLSpanElement | null>(null)
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const node = ref.current
|
|
44
|
+
if (!node) return
|
|
45
|
+
const observer = new IntersectionObserver(
|
|
46
|
+
([entry]) => {
|
|
47
|
+
if (entry?.isIntersecting) {
|
|
48
|
+
node.style.opacity = '1'
|
|
49
|
+
node.style.transform = 'translateY(0)'
|
|
50
|
+
node.style.transitionDelay = `${delay}s`
|
|
51
|
+
} else {
|
|
52
|
+
node.style.opacity = '0'
|
|
53
|
+
node.style.transform = 'translateY(24px)'
|
|
54
|
+
node.style.transitionDelay = '0s'
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ threshold: 0.25 }
|
|
58
|
+
)
|
|
59
|
+
observer.observe(node)
|
|
60
|
+
return () => observer.disconnect()
|
|
61
|
+
}, [delay])
|
|
62
|
+
return (
|
|
63
|
+
<span
|
|
64
|
+
ref={ref}
|
|
65
|
+
className={className}
|
|
66
|
+
style={{
|
|
67
|
+
display: 'inline-block',
|
|
68
|
+
opacity: 0,
|
|
69
|
+
transform: 'translateY(24px)',
|
|
70
|
+
transition: 'opacity 700ms ease, transform 700ms ease'
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
{children}
|
|
74
|
+
</span>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function RevealText({
|
|
79
|
+
text,
|
|
80
|
+
className = '',
|
|
81
|
+
instanceIndex
|
|
82
|
+
}: {
|
|
83
|
+
text: string[]
|
|
84
|
+
className?: string
|
|
85
|
+
instanceIndex?: number
|
|
86
|
+
}) {
|
|
87
|
+
const uniqueId = useId()
|
|
88
|
+
const uniqueInstanceId = useRef<number | undefined>(undefined)
|
|
89
|
+
const textRef = useRef<HTMLSpanElement | null>(null)
|
|
90
|
+
const [isVisible, setIsVisible] = useState(false)
|
|
91
|
+
const [animationKey, setAnimationKey] = useState(0)
|
|
92
|
+
if (uniqueInstanceId.current === undefined)
|
|
93
|
+
uniqueInstanceId.current = instanceIndex ?? globalInstanceCounter++
|
|
94
|
+
useEffect(() => {
|
|
95
|
+
const node = textRef.current
|
|
96
|
+
if (!node) return
|
|
97
|
+
const observer = new IntersectionObserver(
|
|
98
|
+
([entry]) => {
|
|
99
|
+
if (entry?.isIntersecting) {
|
|
100
|
+
setIsVisible(true)
|
|
101
|
+
setAnimationKey((key) => key + 1)
|
|
102
|
+
} else setIsVisible(false)
|
|
103
|
+
},
|
|
104
|
+
{ threshold: 0.3 }
|
|
105
|
+
)
|
|
106
|
+
observer.observe(node)
|
|
107
|
+
return () => observer.disconnect()
|
|
108
|
+
}, [uniqueId])
|
|
109
|
+
const value = text?.[0] || ''
|
|
110
|
+
const words = value.split(' ')
|
|
111
|
+
const baseDelay = (uniqueInstanceId.current ?? 0) * 0.05
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<style>{`@keyframes ddhgServiceHeroReveal { from { opacity: 0; transform: translateX(200px); } to { opacity: 1; transform: translateX(0%); } }`}</style>
|
|
115
|
+
<FadeUpOnInView delay={(uniqueInstanceId.current ?? 0) * 0.1}>
|
|
116
|
+
<span
|
|
117
|
+
ref={textRef}
|
|
118
|
+
className={cn(
|
|
119
|
+
'inline-block h-auto bg-transparent text-left font-bold uppercase leading-none tracking-[1px]',
|
|
120
|
+
className
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
123
|
+
{words.map((word, wordIndex) => {
|
|
124
|
+
const previousLength = words.slice(0, wordIndex).join(' ').length
|
|
125
|
+
const offset = wordIndex === 0 ? 0 : previousLength + 1
|
|
126
|
+
return (
|
|
127
|
+
<span
|
|
128
|
+
key={`${animationKey}-word-${wordIndex}`}
|
|
129
|
+
className="inline-block whitespace-nowrap"
|
|
130
|
+
>
|
|
131
|
+
{word.split('').map((char, charIndex) => {
|
|
132
|
+
const index = offset + charIndex
|
|
133
|
+
return (
|
|
134
|
+
<span
|
|
135
|
+
key={`${animationKey}-${index}-${char}`}
|
|
136
|
+
style={{
|
|
137
|
+
display: 'inline-block',
|
|
138
|
+
opacity: 0,
|
|
139
|
+
transform: isVisible ? undefined : 'translateX(200px)',
|
|
140
|
+
animationName: isVisible ? 'ddhgServiceHeroReveal' : 'none',
|
|
141
|
+
animationDuration: '1s',
|
|
142
|
+
animationFillMode: 'forwards',
|
|
143
|
+
animationDelay: `${baseDelay + index * 0.05}s`
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
{char}
|
|
147
|
+
</span>
|
|
148
|
+
)
|
|
149
|
+
})}
|
|
150
|
+
{wordIndex < words.length - 1 ? <span> </span> : null}
|
|
151
|
+
</span>
|
|
152
|
+
)
|
|
153
|
+
})}
|
|
154
|
+
</span>
|
|
155
|
+
</FadeUpOnInView>
|
|
156
|
+
</>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export const DDHGServiceHero: React.FC<DDHGServiceHeroProps> = ({
|
|
161
|
+
sectionId,
|
|
162
|
+
customClassName,
|
|
163
|
+
customCSS,
|
|
164
|
+
blockId,
|
|
165
|
+
scrollSpeed,
|
|
166
|
+
eyebrowPrefix = 'what we',
|
|
167
|
+
eyebrowEmphasis = 'do',
|
|
168
|
+
heading = 'SERVICES',
|
|
169
|
+
description = 'From initial vision to final handover, our services span the full spectrum of interior architecture. Each engagement is shaped by the same principle: spaces should serve the people inside them.',
|
|
170
|
+
minHeight = 'large',
|
|
171
|
+
showDivider = true,
|
|
172
|
+
showScrollIndicator = true
|
|
173
|
+
}) => {
|
|
174
|
+
const minHeightClass = minHeightClassMap[minHeight || 'large']
|
|
175
|
+
return (
|
|
176
|
+
<div
|
|
177
|
+
id={sectionId ?? undefined}
|
|
178
|
+
data-block-id={blockId ?? undefined}
|
|
179
|
+
data-scroll={scrollSpeed ? true : undefined}
|
|
180
|
+
data-scroll-speed={scrollSpeed ? String(scrollSpeed) : undefined}
|
|
181
|
+
className={cn('relative flex w-full overflow-hidden', minHeightClass, customClassName)}
|
|
182
|
+
>
|
|
183
|
+
{customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
|
|
184
|
+
<motion.div
|
|
185
|
+
className="ServiceHeroCard relative z-10 mx-auto max-w-4xl overflow-clip"
|
|
186
|
+
initial={{ opacity: 0, y: 20 }}
|
|
187
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
188
|
+
viewport={{ once: true, amount: 0.35 }}
|
|
189
|
+
transition={{ duration: 0.9, ease: [0.22, 1, 0.36, 1] }}
|
|
190
|
+
>
|
|
191
|
+
{(eyebrowPrefix || eyebrowEmphasis) && (
|
|
192
|
+
<p className="mb-2 origin-left !scale-80 text-xs uppercase tracking-[0.2em] text-stone-400 dark:text-stone-400">
|
|
193
|
+
{eyebrowPrefix && <span className="font-light">{eyebrowPrefix} </span>}
|
|
194
|
+
{eyebrowEmphasis && <span className="font-medium">{eyebrowEmphasis}</span>}
|
|
195
|
+
</p>
|
|
196
|
+
)}
|
|
197
|
+
{heading && (
|
|
198
|
+
<span className="mt-2 block leading-[0.82] tracking-[0.08em]">
|
|
199
|
+
<RevealText
|
|
200
|
+
text={[heading]}
|
|
201
|
+
instanceIndex={1}
|
|
202
|
+
className="text-4xl font-extrabold md:text-6xl lg:text-8xl"
|
|
203
|
+
/>
|
|
204
|
+
</span>
|
|
205
|
+
)}
|
|
206
|
+
{showDivider && <div className="mx-auto mb-6 h-px w-full bg-stone-400" />}
|
|
207
|
+
{description && (
|
|
208
|
+
<p className="mx-auto max-w-xl text-sm font-light leading-relaxed text-stone-700 dark:text-stone-200">
|
|
209
|
+
{description}
|
|
210
|
+
</p>
|
|
211
|
+
)}
|
|
212
|
+
</motion.div>
|
|
213
|
+
{showScrollIndicator && (
|
|
214
|
+
<motion.div
|
|
215
|
+
className="absolute bottom-8 left-1/2 flex -translate-x-1/2 flex-col items-center gap-2"
|
|
216
|
+
initial={{ opacity: 0, y: -8 }}
|
|
217
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
218
|
+
viewport={{ once: true, amount: 0.35 }}
|
|
219
|
+
transition={{ delay: 0.4, duration: 0.7 }}
|
|
220
|
+
aria-hidden="true"
|
|
221
|
+
>
|
|
222
|
+
<motion.div
|
|
223
|
+
className="h-10 w-px bg-stone-500"
|
|
224
|
+
animate={{ scaleY: [0.65, 1, 0.65], opacity: [0.45, 1, 0.45] }}
|
|
225
|
+
transition={{ duration: 1.8, repeat: Infinity, ease: 'easeInOut' }}
|
|
226
|
+
/>
|
|
227
|
+
<ArrowDown className="h-3.5 w-3.5 text-stone-600 dark:text-stone-300" strokeWidth={1.5} />
|
|
228
|
+
</motion.div>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export default DDHGServiceHero
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Block } from 'payload'
|
|
2
|
+
|
|
3
|
+
export const DDHGServiceHero: Block = {
|
|
4
|
+
slug: 'DDHG-ServiceHero',
|
|
5
|
+
interfaceName: 'DDHGServiceHero',
|
|
6
|
+
dbName: 'ddhg_svc_hero',
|
|
7
|
+
labels: { singular: 'Service Hero', plural: 'Service Heroes' },
|
|
8
|
+
fields: [
|
|
9
|
+
{
|
|
10
|
+
type: 'tabs',
|
|
11
|
+
tabs: [
|
|
12
|
+
{
|
|
13
|
+
label: 'Content',
|
|
14
|
+
fields: [
|
|
15
|
+
{ type: 'row', fields: [
|
|
16
|
+
{ name: 'eyebrowPrefix', type: 'text', label: 'Eyebrow Prefix', defaultValue: 'what we', admin: { width: '50%' } },
|
|
17
|
+
{ name: 'eyebrowEmphasis', type: 'text', label: 'Eyebrow Emphasis', defaultValue: 'do', admin: { width: '50%' } },
|
|
18
|
+
] },
|
|
19
|
+
{ name: 'heading', type: 'text', label: 'Heading', defaultValue: 'SERVICES', required: true },
|
|
20
|
+
{ name: 'description', type: 'textarea', label: 'Description', defaultValue: 'From initial vision to final handover, our services span the full spectrum of interior architecture. Each engagement is shaped by the same principle: spaces should serve the people inside them.' },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: 'Layout',
|
|
25
|
+
fields: [
|
|
26
|
+
{ name: 'minHeight', type: 'select', label: 'Minimum Height', defaultValue: 'large', options: [
|
|
27
|
+
{ label: 'Screen', value: 'screen' }, { label: 'Large', value: 'large' }, { label: 'Medium', value: 'medium' }, { label: 'Compact', value: 'compact' },
|
|
28
|
+
] },
|
|
29
|
+
{ type: 'row', fields: [
|
|
30
|
+
{ name: 'showDivider', type: 'checkbox', label: 'Show Divider', defaultValue: true, admin: { width: '50%' } },
|
|
31
|
+
{ name: 'showScrollIndicator', type: 'checkbox', label: 'Show Scroll Indicator', defaultValue: true, admin: { width: '50%' } },
|
|
32
|
+
] },
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: 'Advanced',
|
|
37
|
+
fields: [
|
|
38
|
+
{ type: 'row', fields: [
|
|
39
|
+
{ name: 'sectionId', type: 'text', label: 'Section ID', admin: { width: '50%' } },
|
|
40
|
+
{ name: 'customClassName', type: 'text', defaultValue: 'items-center justify-center', label: 'Custom CSS Classes', admin: { width: '50%' } },
|
|
41
|
+
] },
|
|
42
|
+
{ name: 'scrollSpeed', type: 'text', label: 'Scroll Speed', admin: { description: 'Optional data-scroll-speed value.' } },
|
|
43
|
+
{ name: 'customCSS', type: 'code', label: 'Custom CSS', admin: { language: 'css', description: 'Injected as a style tag. Use with caution.' } },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "block",
|
|
3
|
+
"name": "DDHG-ServiceHero",
|
|
4
|
+
"label": "Service Hero",
|
|
5
|
+
"sourceName": "ServiceHero",
|
|
6
|
+
"portableName": "DDHG-ServiceHero",
|
|
7
|
+
"slug": "DDHG-ServiceHero",
|
|
8
|
+
"version": "0.0.1",
|
|
9
|
+
"source": { "repo": "Hadizainal/HeatherGrey2025", "originalPath": "src/blocks/ServiceHero", "originalSlug": "serviceHero", "originalConfigExport": "ServiceHero", "originalComponentExport": "ServiceHeroBlock", "originalInterfaceName": "ServiceHeroBlockProps" },
|
|
10
|
+
"files": [
|
|
11
|
+
{ "from": "Component.tsx", "to": "src/blocks/DDHG-ServiceHero/Component.tsx" },
|
|
12
|
+
{ "from": "config.ts", "to": "src/blocks/DDHG-ServiceHero/config.ts" }
|
|
13
|
+
],
|
|
14
|
+
"dependencies": [{ "name": "framer-motion" }, { "name": "lucide-react" }],
|
|
15
|
+
"sharedFiles": [],
|
|
16
|
+
"requiredProjectFiles": [{ "path": "src/utilities/ui.ts", "reason": "Component uses the cn utility." }],
|
|
17
|
+
"optionalProjectFiles": [],
|
|
18
|
+
"collectionRegistration": { "enabled": true, "collections": ["Pages", "Posts"] },
|
|
19
|
+
"renderBlocksRegistration": { "enabled": true },
|
|
20
|
+
"renderBlocksBehavior": { "wrapperless": false },
|
|
21
|
+
"pageRouteIntegration": { "required": false, "manualSteps": [] },
|
|
22
|
+
"manualSteps": ["Run payload generate:types after schema changes."],
|
|
23
|
+
"notes": ["Converted from HeatherGrey2025 ServiceHero.", "dbName namespaced to ddhg_svc_hero.", "Installer must not overwrite existing target files."],
|
|
24
|
+
"exportName": "DDHGServiceHero",
|
|
25
|
+
"interfaceName": "DDHGServiceHero"
|
|
26
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { motion } from 'framer-motion'
|
|
4
|
+
import { CircleDot } from 'lucide-react'
|
|
5
|
+
import React, { useMemo, useState } from 'react'
|
|
6
|
+
import { cn } from '@/utilities/ui'
|
|
7
|
+
|
|
8
|
+
type ServiceOverviewItem = {
|
|
9
|
+
code?: string | null
|
|
10
|
+
title?: string | null
|
|
11
|
+
description?: string | null
|
|
12
|
+
details?: { label?: string | null; id?: string | null }[] | null
|
|
13
|
+
timeline?: string | null
|
|
14
|
+
engagement?: string | null
|
|
15
|
+
id?: string | null
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type DDHGServiceOverviewProps = {
|
|
19
|
+
sectionId?: string | null
|
|
20
|
+
customClassName?: string | null
|
|
21
|
+
customCSS?: string | null
|
|
22
|
+
blockId?: string | null
|
|
23
|
+
scrollSpeed?: string | number | null
|
|
24
|
+
eyebrowPrefix?: string | null
|
|
25
|
+
eyebrowEmphasis?: string | null
|
|
26
|
+
services?: ServiceOverviewItem[] | null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const defaultServices: ServiceOverviewItem[] = [
|
|
30
|
+
{
|
|
31
|
+
code: 'SVC-01',
|
|
32
|
+
title: 'Discovery',
|
|
33
|
+
description:
|
|
34
|
+
'We begin by understanding the brief, the existing space, operational needs, and the people who will use it every day.',
|
|
35
|
+
timeline: '8-12 weeks',
|
|
36
|
+
engagement: 'Fixed Fee'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
code: 'SVC-02',
|
|
40
|
+
title: 'Concept',
|
|
41
|
+
description:
|
|
42
|
+
'We translate business goals into a clear spatial direction, balancing brand, movement, function, and atmosphere.',
|
|
43
|
+
timeline: '12-16 weeks',
|
|
44
|
+
engagement: 'Project-based'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
code: 'SVC-03',
|
|
48
|
+
title: 'Design',
|
|
49
|
+
description:
|
|
50
|
+
'We develop the interior architecture, material language, lighting, furniture strategy, and practical user experience.',
|
|
51
|
+
timeline: '16-24 weeks',
|
|
52
|
+
engagement: 'Fixed Fee'
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
code: 'SVC-04',
|
|
56
|
+
title: 'Documentation',
|
|
57
|
+
description:
|
|
58
|
+
'We prepare the technical drawings, schedules, and coordination notes required for approval, pricing, and buildability.',
|
|
59
|
+
timeline: '8-12 weeks',
|
|
60
|
+
engagement: 'Project-based'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: 'SVC-05',
|
|
64
|
+
title: 'Procurement',
|
|
65
|
+
description:
|
|
66
|
+
'We support selection, specification, and sourcing so the design intent remains consistent through delivery.',
|
|
67
|
+
timeline: '12-16 weeks',
|
|
68
|
+
engagement: 'Fixed Fee'
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
code: 'SVC-06',
|
|
72
|
+
title: 'Handover',
|
|
73
|
+
description:
|
|
74
|
+
'We help close the project with practical review, refinements, and alignment between the completed space and the original intent.',
|
|
75
|
+
timeline: '16-24 weeks',
|
|
76
|
+
engagement: 'Project-based'
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
function getServices(services?: ServiceOverviewItem[] | null) {
|
|
81
|
+
return Array.isArray(services) && services.length > 0 ? services : defaultServices
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function getDeliverableCount(service: ServiceOverviewItem) {
|
|
85
|
+
return Array.isArray(service.details) && service.details.length > 0 ? service.details.length : 4
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function slugifyServiceTitle(value?: string | null) {
|
|
89
|
+
if (!value) return ''
|
|
90
|
+
return value
|
|
91
|
+
.trim()
|
|
92
|
+
.toLowerCase()
|
|
93
|
+
.replace(/&/g, 'and')
|
|
94
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
95
|
+
.replace(/^-+|-+$/g, '')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function scrollToServiceDetail(title?: string | null) {
|
|
99
|
+
const targetId = slugifyServiceTitle(title)
|
|
100
|
+
if (!targetId) return
|
|
101
|
+
const targetEl = document.getElementById(targetId)
|
|
102
|
+
if (targetEl) {
|
|
103
|
+
const targetTop = targetEl.getBoundingClientRect().top + window.scrollY - 120
|
|
104
|
+
window.history.pushState(null, '', `#${targetId}`)
|
|
105
|
+
window.scrollTo({ top: targetTop, behavior: 'smooth' })
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
window.location.hash = targetId
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const DDHGServiceOverview: React.FC<DDHGServiceOverviewProps> = ({
|
|
112
|
+
sectionId,
|
|
113
|
+
customClassName,
|
|
114
|
+
customCSS,
|
|
115
|
+
blockId,
|
|
116
|
+
scrollSpeed,
|
|
117
|
+
eyebrowPrefix = 'service',
|
|
118
|
+
eyebrowEmphasis = 'catalog',
|
|
119
|
+
services
|
|
120
|
+
}) => {
|
|
121
|
+
const serviceItems = useMemo(() => getServices(services), [services])
|
|
122
|
+
const [activeIndex, setActiveIndex] = useState(0)
|
|
123
|
+
const activeService = serviceItems[activeIndex] || serviceItems[0]
|
|
124
|
+
if (!activeService) return null
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<div
|
|
128
|
+
id={sectionId ?? undefined}
|
|
129
|
+
data-block-id={blockId ?? undefined}
|
|
130
|
+
data-scroll={scrollSpeed ? true : undefined}
|
|
131
|
+
data-scroll-speed={scrollSpeed ? String(scrollSpeed) : undefined}
|
|
132
|
+
className={cn('px-5 py-16 md:px-8', customClassName)}
|
|
133
|
+
>
|
|
134
|
+
{customCSS && <style dangerouslySetInnerHTML={{ __html: customCSS }} />}
|
|
135
|
+
<div className="ServiceOverview01 mx-auto max-w-7xl">
|
|
136
|
+
<motion.div
|
|
137
|
+
className="ServiceOverview02 mb-10"
|
|
138
|
+
initial={{ opacity: 0, y: 20 }}
|
|
139
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
140
|
+
viewport={{ once: true, amount: 0.25 }}
|
|
141
|
+
transition={{ duration: 0.9, ease: [0.22, 1, 0.36, 1] }}
|
|
142
|
+
>
|
|
143
|
+
{(eyebrowPrefix || eyebrowEmphasis) && (
|
|
144
|
+
<p className="ServiceOverview03 mb-2 text-[10px] uppercase tracking-[0.2em] text-stone-600 dark:text-stone-300">
|
|
145
|
+
{eyebrowPrefix && <span className="font-light">{eyebrowPrefix} </span>}
|
|
146
|
+
{eyebrowEmphasis && <span className="font-medium">{eyebrowEmphasis}</span>}
|
|
147
|
+
</p>
|
|
148
|
+
)}
|
|
149
|
+
</motion.div>
|
|
150
|
+
<div className="ServiceOverview04 relative">
|
|
151
|
+
<div className="ServiceOverview05 absolute left-0 right-0 top-6 hidden h-px bg-[#444444] md:block" />
|
|
152
|
+
<div className="ServiceOverview06 grid grid-cols-2 gap-4 md:grid-cols-3 lg:grid-cols-6">
|
|
153
|
+
{serviceItems.map((service, i) => {
|
|
154
|
+
const isActive = activeIndex === i
|
|
155
|
+
return (
|
|
156
|
+
<motion.button
|
|
157
|
+
key={service.id || service.code || `service-${i}`}
|
|
158
|
+
type="button"
|
|
159
|
+
className="ServiceOverview07 group relative cursor-pointer text-left"
|
|
160
|
+
initial={{ opacity: 0, y: 20 }}
|
|
161
|
+
whileInView={{ opacity: 1, y: 0 }}
|
|
162
|
+
viewport={{ once: true, amount: 0.2 }}
|
|
163
|
+
transition={{ duration: 0.9, delay: i * 0.08, ease: [0.22, 1, 0.36, 1] }}
|
|
164
|
+
onMouseEnter={() => setActiveIndex(i)}
|
|
165
|
+
onFocus={() => setActiveIndex(i)}
|
|
166
|
+
onClick={() => scrollToServiceDetail(service.title)}
|
|
167
|
+
>
|
|
168
|
+
<div className="ServiceOverview08 flex flex-col items-center text-center">
|
|
169
|
+
<div
|
|
170
|
+
className={cn(
|
|
171
|
+
'ServiceOverview09 mb-3 flex h-12 w-12 items-center justify-center border transition-all duration-500',
|
|
172
|
+
isActive
|
|
173
|
+
? 'border-[#444444] bg-[#444444]'
|
|
174
|
+
: 'border-[#444444] bg-[#e5e7eb] group-hover:border-[#A8A29E]'
|
|
175
|
+
)}
|
|
176
|
+
style={{ borderRadius: '2px' }}
|
|
177
|
+
>
|
|
178
|
+
<span
|
|
179
|
+
className={cn(
|
|
180
|
+
'ServiceOverview10 text-xs font-semibold transition-colors duration-500',
|
|
181
|
+
isActive ? 'text-[#F5F3EF]' : 'text-stone-600 dark:text-stone-300'
|
|
182
|
+
)}
|
|
183
|
+
>
|
|
184
|
+
{String(i + 1).padStart(2, '0')}
|
|
185
|
+
</span>
|
|
186
|
+
</div>
|
|
187
|
+
<span
|
|
188
|
+
className={cn(
|
|
189
|
+
'ServiceOverview11 text-[13px] font-medium uppercase tracking-[0.1em] transition-colors duration-500',
|
|
190
|
+
isActive
|
|
191
|
+
? 'text-stone-700 dark:text-stone-200'
|
|
192
|
+
: 'text-stone-600 dark:text-stone-300 group-hover:text-stone-700'
|
|
193
|
+
)}
|
|
194
|
+
>
|
|
195
|
+
{service.title}
|
|
196
|
+
</span>
|
|
197
|
+
{service.code && (
|
|
198
|
+
<span className="ServiceOverview12 mt-1 font-mono text-[10px] text-stone-600 dark:text-stone-300">
|
|
199
|
+
{service.code}
|
|
200
|
+
</span>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
</motion.button>
|
|
204
|
+
)
|
|
205
|
+
})}
|
|
206
|
+
</div>
|
|
207
|
+
</div>
|
|
208
|
+
<motion.div
|
|
209
|
+
key={`service-overview-card-${activeIndex}`}
|
|
210
|
+
className="ServiceOverview13 mt-10"
|
|
211
|
+
initial={{ opacity: 0, x: 20 }}
|
|
212
|
+
animate={{ opacity: 1, x: 0 }}
|
|
213
|
+
transition={{ duration: 0.2, delay: 0.05, ease: [0.22, 1, 0.36, 1] }}
|
|
214
|
+
>
|
|
215
|
+
<motion.div
|
|
216
|
+
className="ServiceOverview14 z-plane-1 p-6 md:p-8"
|
|
217
|
+
initial={{ x: -40, filter: 'blur(20px)', opacity: 0 }}
|
|
218
|
+
animate={{ x: 0, filter: 'blur(0px)', opacity: 1 }}
|
|
219
|
+
transition={{ duration: 0.2, delay: 0.08, ease: [0.22, 1, 0.36, 1] }}
|
|
220
|
+
>
|
|
221
|
+
<div className="flex flex-col gap-6 md:flex-row md:gap-10">
|
|
222
|
+
<div className="flex-shrink-0">
|
|
223
|
+
{activeService.code && (
|
|
224
|
+
<span className="mb-2 block font-mono text-[10px] uppercase tracking-[0.15em] text-stone-600 dark:text-stone-300">
|
|
225
|
+
{activeService.code}
|
|
226
|
+
</span>
|
|
227
|
+
)}
|
|
228
|
+
{activeService.title && (
|
|
229
|
+
<h3 className="mb-2 text-xl font-semibold text-purple-700 dark:text-purple-500">
|
|
230
|
+
{activeService.title}
|
|
231
|
+
</h3>
|
|
232
|
+
)}
|
|
233
|
+
<div className="mb-3 h-px w-8 bg-stone-500" />
|
|
234
|
+
{activeService.description && (
|
|
235
|
+
<p className="max-w-md text-[14px] font-light leading-relaxed text-stone-700 dark:text-stone-200">
|
|
236
|
+
{activeService.description}
|
|
237
|
+
</p>
|
|
238
|
+
)}
|
|
239
|
+
</div>
|
|
240
|
+
<div className="flex flex-grow flex-col gap-5 md:flex-row md:items-center md:gap-8">
|
|
241
|
+
<div className="text-center">
|
|
242
|
+
<p className="mb-1 text-[10px] uppercase tracking-[0.15em] text-stone-600 dark:text-stone-300">
|
|
243
|
+
Typical Timeline
|
|
244
|
+
</p>
|
|
245
|
+
<p className="text-md font-semibold text-stone-700 dark:text-stone-200">
|
|
246
|
+
{activeService.timeline || '8-12 weeks'}
|
|
247
|
+
</p>
|
|
248
|
+
</div>
|
|
249
|
+
<div className="hidden h-10 w-px bg-stone-500 md:block" />
|
|
250
|
+
<div className="text-center">
|
|
251
|
+
<p className="mb-1 text-[10px] uppercase tracking-[0.15em] text-stone-600 dark:text-stone-300">
|
|
252
|
+
Deliverables
|
|
253
|
+
</p>
|
|
254
|
+
<p className="inline-flex items-center gap-2 text-md font-semibold text-stone-700 dark:text-stone-200">
|
|
255
|
+
<CircleDot className="h-3.5 w-3.5" strokeWidth={1.7} />
|
|
256
|
+
{getDeliverableCount(activeService)}
|
|
257
|
+
</p>
|
|
258
|
+
</div>
|
|
259
|
+
<div className="hidden h-10 w-px bg-stone-500 md:block" />
|
|
260
|
+
<div className="text-center">
|
|
261
|
+
<p className="mb-1 text-[10px] uppercase tracking-[0.15em] text-stone-600 dark:text-stone-300">
|
|
262
|
+
Engagement
|
|
263
|
+
</p>
|
|
264
|
+
<p className="text-md font-semibold text-stone-700 dark:text-stone-200">
|
|
265
|
+
{activeService.engagement || 'Fixed Fee'}
|
|
266
|
+
</p>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
</motion.div>
|
|
271
|
+
</motion.div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export default DDHGServiceOverview
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { Block } from 'payload'
|
|
2
|
+
|
|
3
|
+
export const DDHGServiceOverview: Block = {
|
|
4
|
+
slug: 'DDHG-ServiceOverview',
|
|
5
|
+
interfaceName: 'DDHGServiceOverview',
|
|
6
|
+
dbName: 'ddhg_svc_overview',
|
|
7
|
+
labels: {
|
|
8
|
+
singular: 'Service Overview',
|
|
9
|
+
plural: 'Service Overviews',
|
|
10
|
+
},
|
|
11
|
+
fields: [
|
|
12
|
+
{
|
|
13
|
+
type: 'tabs',
|
|
14
|
+
tabs: [
|
|
15
|
+
{
|
|
16
|
+
label: 'Content',
|
|
17
|
+
fields: [
|
|
18
|
+
{
|
|
19
|
+
type: 'row',
|
|
20
|
+
fields: [
|
|
21
|
+
{ name: 'eyebrowPrefix', type: 'text', label: 'Eyebrow Prefix', defaultValue: 'service', admin: { width: '50%' } },
|
|
22
|
+
{ name: 'eyebrowEmphasis', type: 'text', label: 'Eyebrow Emphasis', defaultValue: 'catalog', admin: { width: '50%' } },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'services', type: 'array', label: 'Services', minRows: 1, maxRows: 8,
|
|
27
|
+
labels: { singular: 'Service', plural: 'Services' },
|
|
28
|
+
fields: [
|
|
29
|
+
{
|
|
30
|
+
type: 'row', fields: [
|
|
31
|
+
{ name: 'code', type: 'text', label: 'Code', defaultValue: 'SVC-01', admin: { width: '33.33%' } },
|
|
32
|
+
{ name: 'title', type: 'text', label: 'Title', required: true, admin: { width: '33.33%' } },
|
|
33
|
+
{ name: 'timeline', type: 'text', label: 'Typical Timeline', defaultValue: '8-12 weeks', admin: { width: '33.33%' } },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
{ name: 'description', type: 'textarea', label: 'Description' },
|
|
37
|
+
{
|
|
38
|
+
type: 'row', fields: [
|
|
39
|
+
{ name: 'engagement', type: 'text', label: 'Engagement', defaultValue: 'Fixed Fee', admin: { width: '50%' } },
|
|
40
|
+
{
|
|
41
|
+
name: 'details', type: 'array', label: 'Deliverables', admin: { width: '50%' },
|
|
42
|
+
fields: [{ name: 'label', type: 'text', label: 'Deliverable', required: true }],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
label: 'Advanced',
|
|
52
|
+
fields: [
|
|
53
|
+
{ type: 'row', fields: [
|
|
54
|
+
{ name: 'sectionId', type: 'text', label: 'Section ID', admin: { width: '50%' } },
|
|
55
|
+
{ name: 'customClassName', type: 'text', label: 'Custom CSS Classes', admin: { width: '50%' } },
|
|
56
|
+
] },
|
|
57
|
+
{ name: 'scrollSpeed', type: 'text', label: 'Scroll Speed', admin: { description: 'Optional data-scroll-speed value.' } },
|
|
58
|
+
{ name: 'customCSS', type: 'code', label: 'Custom CSS', admin: { language: 'css', description: 'Injected as a style tag. Use with caution.' } },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}
|