blocks-dusted 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/package.json +5 -2
  2. package/src/cli.js +39 -3
  3. package/src/commands/shop.js +59 -0
  4. package/src/commands/warehouse.js +73 -0
  5. package/src/installers/patchRenderBlocks.js +1 -1
  6. package/src/registry/validateTemplateManifest.js +2 -1
  7. package/templates/blocks/BannerDD790432/Component.tsx +63 -0
  8. package/templates/blocks/BannerDD790432/config.ts +54 -0
  9. package/templates/blocks/BannerDD790432/manifest.json +80 -0
  10. package/templates/blocks/Button47/Component.tsx +124 -0
  11. package/templates/blocks/Button47/config.ts +75 -0
  12. package/templates/blocks/Button47/link.ts +22 -0
  13. package/templates/blocks/Button47/manifest.json +105 -0
  14. package/templates/blocks/Button47/styles.module.scss +75 -0
  15. package/templates/blocks/DD-Accordion/Component.tsx +55 -36
  16. package/templates/blocks/DD-Accordion/config.ts +71 -59
  17. package/templates/blocks/DD-AnimText/Component.tsx +50 -32
  18. package/templates/blocks/DD-AnimText/config.ts +296 -295
  19. package/templates/blocks/DD-AsideNav/Component.tsx +323 -319
  20. package/templates/blocks/DD-Banner/Component.tsx +54 -51
  21. package/templates/blocks/DD-BookCall/Component.tsx +10 -9
  22. package/templates/blocks/DD-ButtonPop/Component.tsx +118 -117
  23. package/templates/blocks/DD-Card/Component.tsx +4 -4
  24. package/templates/blocks/DD-CardTemplate01/Component.tsx +5 -5
  25. package/templates/blocks/DD-CardsWithVariantV3/Component.tsx +472 -0
  26. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/index.tsx +203 -0
  27. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/styles.ts +87 -0
  28. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/useInjectStyle.ts +39 -0
  29. package/templates/blocks/DD-CardsWithVariantV3/config.ts +326 -0
  30. package/templates/blocks/DD-CardsWithVariantV3/manifest.json +110 -0
  31. package/templates/blocks/DD-Carousel-A/Component.tsx +251 -249
  32. package/templates/blocks/DD-Carousel-B/Component.tsx +405 -403
  33. package/templates/blocks/DD-Chip/Component.tsx +14 -14
  34. package/templates/blocks/DD-ComparisonTable/Component.tsx +255 -256
  35. package/templates/blocks/DD-Contact/Component.tsx +433 -434
  36. package/templates/blocks/DD-Counter/Component.tsx +18 -13
  37. package/templates/blocks/DD-DeliCard/Component.tsx +303 -0
  38. package/templates/blocks/DD-DeliCard/config.ts +186 -0
  39. package/templates/blocks/DD-DeliCard/manifest.json +94 -0
  40. package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
  41. package/templates/blocks/DD-FeatProd/CircularGallery.tsx +1179 -0
  42. package/templates/blocks/DD-FeatProd/Component.tsx +484 -0
  43. package/templates/blocks/DD-FeatProd/config.ts +173 -0
  44. package/templates/blocks/DD-FeatProd/manifest.json +93 -0
  45. package/templates/blocks/DD-FeatServ/Component.tsx +607 -0
  46. package/templates/blocks/DD-FeatServ/config.ts +213 -0
  47. package/templates/blocks/DD-FeatServ/manifest.json +113 -0
  48. package/templates/blocks/DD-FeatStrip/Component.tsx +584 -585
  49. package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +627 -628
  50. package/templates/blocks/DD-FluidBtn/Component.tsx +46 -43
  51. package/templates/blocks/DD-GooeyInfo/Component.tsx +150 -149
  52. package/templates/blocks/DD-GooeyInfo/config.ts +136 -135
  53. package/templates/blocks/DD-Hero/Component.tsx +16 -40
  54. package/templates/blocks/DD-HeroPlas/Component.tsx +50 -0
  55. package/templates/blocks/DD-HeroPlas/ThreeScene.tsx +109 -0
  56. package/templates/blocks/DD-HeroPlas/config.ts +33 -0
  57. package/templates/blocks/DD-HeroPlas/manifest.json +88 -0
  58. package/templates/blocks/DD-HorizontalScroll/Component.tsx +4 -5
  59. package/templates/blocks/DD-Icon/Component.tsx +46 -81
  60. package/templates/blocks/DD-IconButton/Component.tsx +13 -20
  61. package/templates/blocks/DD-IdTag/Component.tsx +22 -21
  62. package/templates/blocks/DD-IframeMedia/Component.tsx +14 -10
  63. package/templates/blocks/DD-LineAnim/Component.tsx +42 -36
  64. package/templates/blocks/DD-LogoMarq/Component.tsx +38 -48
  65. package/templates/blocks/DD-Lottie/Component.tsx +11 -3
  66. package/templates/blocks/DD-MasonaryMedia/Component.tsx +544 -547
  67. package/templates/blocks/DD-MasonaryMedia/config.ts +389 -389
  68. package/templates/blocks/DD-Masonry/Component.tsx +654 -644
  69. package/templates/blocks/DD-Masonry/config.ts +475 -475
  70. package/templates/blocks/DD-MaterialSwatches/Component.tsx +85 -0
  71. package/templates/blocks/DD-MaterialSwatches/config.ts +232 -0
  72. package/templates/blocks/DD-MaterialSwatches/manifest.json +84 -0
  73. package/templates/blocks/DD-MaterialSwatches/material.ts +107 -0
  74. package/templates/blocks/DD-NineCanvas/Component.tsx +129 -0
  75. package/templates/blocks/DD-NineCanvas/RenderBlocks.tsx +244 -0
  76. package/templates/blocks/DD-NineCanvas/allowedCanvasBlocks.ts +6 -0
  77. package/templates/blocks/DD-NineCanvas/blockComponents.tsx +8 -0
  78. package/templates/blocks/DD-NineCanvas/canvas/CanvasGrid.tsx +316 -0
  79. package/templates/blocks/DD-NineCanvas/canvas/RadialNav.tsx +432 -0
  80. package/templates/blocks/DD-NineCanvas/canvas/canvas-panels.css +889 -0
  81. package/templates/blocks/DD-NineCanvas/canvas/icons/custom-icons.tsx +364 -0
  82. package/templates/blocks/DD-NineCanvas/config.ts +61 -0
  83. package/templates/blocks/DD-NineCanvas/manifest.json +165 -0
  84. package/templates/blocks/DD-NineCanvas/styles.scss +152 -0
  85. package/templates/blocks/DD-NineCanvas/tooltip/get-strict-context.tsx +26 -0
  86. package/templates/blocks/DD-NineCanvas/tooltip/primitives.tsx +178 -0
  87. package/templates/blocks/DD-NineCanvas/tooltip/tooltip.tsx +71 -0
  88. package/templates/blocks/DD-NineCanvas/tooltip/use-controlled-state.tsx +33 -0
  89. package/templates/blocks/DD-NineCanvas/utilities/FadeOnScroll.tsx +68 -0
  90. package/templates/blocks/DD-NineCanvas/utilities/ParallaxWrapper.tsx +157 -0
  91. package/templates/blocks/DD-NineCanvas/utilities/renderCustomCSS.ts +5 -0
  92. package/templates/blocks/DD-NineCanvas/utilities/webglCleanup.ts +27 -0
  93. package/templates/blocks/DD-PayloadAIAssistant/Component.tsx +78 -0
  94. package/templates/blocks/DD-PayloadAIAssistant/DustedBot.tsx +590 -0
  95. package/templates/blocks/DD-PayloadAIAssistant/config.ts +37 -0
  96. package/templates/blocks/DD-PayloadAIAssistant/demo.css +125 -0
  97. package/templates/blocks/DD-PayloadAIAssistant/manifest.json +93 -0
  98. package/templates/blocks/DD-PayloadAIAssistant/source-defaults.ts +17 -0
  99. package/templates/blocks/DD-PayloadAIAssistant/styles.ts +306 -0
  100. package/templates/blocks/DD-Pricing/Component.tsx +368 -372
  101. package/templates/blocks/DD-Pricing/config.ts +215 -215
  102. package/templates/blocks/DD-Process/Component.tsx +147 -149
  103. package/templates/blocks/DD-Progress/Component.tsx +156 -141
  104. package/templates/blocks/DD-ProjHeader/Component.tsx +93 -91
  105. package/templates/blocks/DD-ProjSlider/Component.tsx +1 -1
  106. package/templates/blocks/DD-ProjSlider/config.ts +2 -2
  107. package/templates/blocks/DD-Section/Component.tsx +265 -266
  108. package/templates/blocks/DD-Separator/Component.tsx +66 -66
  109. package/templates/blocks/DD-Services/Component.tsx +176 -176
  110. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +306 -307
  111. package/templates/blocks/DD-Slider-A/Component.tsx +237 -237
  112. package/templates/blocks/DD-StackCards/Component.tsx +474 -473
  113. package/templates/blocks/DD-Team/Component.tsx +247 -247
  114. package/templates/blocks/DD-TeamCards/Component.tsx +251 -251
  115. package/templates/blocks/DD-TechStack/Component.tsx +161 -168
  116. package/templates/blocks/DD-TestMarq/Component.tsx +158 -132
  117. package/templates/blocks/DD-Testim/Component.tsx +247 -247
  118. package/templates/blocks/DD-Testim2/Component.tsx +265 -256
  119. package/templates/blocks/DD-Testimonials/Component.tsx +139 -147
  120. package/templates/blocks/DD-TextMarq/Component.tsx +157 -157
  121. package/templates/blocks/DD-Thanks/Component.tsx +501 -500
  122. package/templates/blocks/DD-Work/Component.tsx +315 -315
  123. package/templates/blocks/DD-Work-B/Component.tsx +294 -294
  124. package/templates/blocks/DDHG-CTASection2/Component.tsx +183 -184
  125. package/templates/blocks/DDHG-HighlightArc/Component.tsx +246 -243
  126. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +180 -93
  127. package/templates/blocks/DDHG-LogoMarquee/config.ts +154 -66
  128. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +225 -213
  129. package/templates/blocks/DDHG-ProcessSec/Component.tsx +38 -39
  130. package/templates/blocks/DDHG-ServiceDetails/Component.tsx +11 -11
  131. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +30 -47
  132. package/templates/blocks/DDHG-ServiceHero/Component.tsx +10 -10
  133. package/templates/blocks/DDHG-ServiceOverview/Component.tsx +23 -23
  134. package/templates/blocks/DDHG-StatsFAQ/Component.tsx +40 -40
  135. package/templates/blocks/DDHG-TeamCards/Component.tsx +140 -131
  136. package/templates/blocks/Form/Checkbox/index.tsx +45 -0
  137. package/templates/blocks/Form/Component.tsx +163 -0
  138. package/templates/blocks/Form/Country/index.tsx +65 -0
  139. package/templates/blocks/Form/Country/options.ts +982 -0
  140. package/templates/blocks/Form/Email/index.tsx +38 -0
  141. package/templates/blocks/Form/Error/index.tsx +15 -0
  142. package/templates/blocks/Form/Message/index.tsx +13 -0
  143. package/templates/blocks/Form/Number/index.tsx +36 -0
  144. package/templates/blocks/Form/Select/index.tsx +63 -0
  145. package/templates/blocks/Form/State/index.tsx +64 -0
  146. package/templates/blocks/Form/State/options.ts +52 -0
  147. package/templates/blocks/Form/Text/index.tsx +32 -0
  148. package/templates/blocks/Form/Textarea/index.tsx +40 -0
  149. package/templates/blocks/Form/Width/index.tsx +13 -0
  150. package/templates/blocks/Form/config.ts +51 -0
  151. package/templates/blocks/Form/fields.tsx +21 -0
  152. package/templates/blocks/Form/manifest.json +168 -0
  153. package/templates/blocks/MediaBlock/Component.tsx +67 -0
  154. package/templates/blocks/MediaBlock/config.ts +14 -0
  155. package/templates/blocks/MediaBlock/manifest.json +92 -0
  156. package/templates/blocks/TransitionsDustedShowcase/Component.tsx +9 -0
  157. package/templates/blocks/TransitionsDustedShowcase/config.ts +13 -0
  158. package/templates/blocks/TransitionsDustedShowcase/manifest.json +80 -0
  159. package/templates/blocks/rawHtml/Component.tsx +25 -0
  160. package/templates/blocks/rawHtml/config.ts +56 -0
  161. package/templates/blocks/rawHtml/manifest.json +80 -0
@@ -1,243 +1,246 @@
1
- 'use client'
2
-
3
- import { ArrowRight } from 'lucide-react'
4
- import Image from 'next/image'
5
- import Link from 'next/link'
6
- import React, { useState } from 'react'
7
-
8
- import { cn } from '@/utilities/ui'
9
-
10
- type PortableDoc = {
11
- slug?: string | null
12
- breadcrumbs?: Array<{ url?: string | null }> | null
13
- [key: string]: unknown
14
- }
15
-
16
- type Media = {
17
- url?: string | null
18
- alt?: string | null
19
- [key: string]: unknown
20
- }
21
-
22
- type LinkReference =
23
- | { relationTo: 'pages'; value: number | PortableDoc }
24
- | { relationTo: 'posts'; value: number | PortableDoc }
25
- | { relationTo: 'projects'; value: number | PortableDoc }
26
- | { relationTo: 'thoughts'; value: number | PortableDoc }
27
-
28
- type HighlightArchiveCardsLink = {
29
- type?: 'reference' | 'custom' | null
30
- reference?: LinkReference | null
31
- url?: string | null
32
- label?: string | null
33
- newTab?: boolean | null
34
- } | null
35
-
36
- type HighlightArchiveCardsItem = {
37
- id?: string | null
38
- code?: string | null
39
- title?: string | null
40
- type?: string | null
41
- bgImage?: number | Media | null
42
- priority?: boolean | null
43
- link?: HighlightArchiveCardsLink
44
- }
45
-
46
- export type DDHGHighlightArcProps = {
47
- gridColumns?: '1' | '2' | '3' | '4' | null
48
- highlights?: HighlightArchiveCardsItem[] | null
49
- }
50
-
51
- const getGridClassName = (gridColumns?: DDHGHighlightArcProps['gridColumns']) => {
52
- switch (gridColumns) {
53
- case '2':
54
- return 'grid-cols-1 md:grid-cols-2'
55
- case '3':
56
- return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-3'
57
- case '4':
58
- return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-4'
59
- case '1':
60
- default:
61
- return 'grid-cols-1'
62
- }
63
- }
64
-
65
- const getPageURLFromDoc = (doc: PortableDoc | number): string | null => {
66
- if (typeof doc === 'number') return null
67
-
68
- const crumbs = Array.isArray(doc?.breadcrumbs) ? doc.breadcrumbs : []
69
- if (crumbs.length) {
70
- const last = crumbs[crumbs.length - 1]
71
- if (typeof last?.url === 'string' && last.url) return last.url
72
- }
73
-
74
- const slug = doc?.slug
75
- if (!slug || slug === 'home') return '/'
76
- return slug.startsWith('/') ? slug : `/${slug}`
77
- }
78
-
79
- const getDocumentSlug = (doc: PortableDoc | number): string | null => {
80
- if (typeof doc === 'number') return null
81
- return doc?.slug || null
82
- }
83
-
84
- const getCollectionPath = (relationTo: LinkReference['relationTo']): string => {
85
- if (relationTo === 'posts') return 'thoughts'
86
- if (relationTo === 'thoughts') return 'thoughts'
87
- if (relationTo === 'projects') return 'archives'
88
- return ''
89
- }
90
-
91
- const resolveHref = (link: HighlightArchiveCardsLink): string | null => {
92
- if (!link) return null
93
- if (link.type === 'custom' && link.url) return link.url
94
-
95
- if (link.type === 'reference' && link.reference?.value) {
96
- const { relationTo, value } = link.reference
97
- if (relationTo === 'pages') return getPageURLFromDoc(value)
98
-
99
- const slug = getDocumentSlug(value)
100
- if (!slug) return null
101
- const basePath = getCollectionPath(relationTo)
102
- return basePath ? `/${basePath}/${slug}` : `/${slug}`
103
- }
104
-
105
- return null
106
- }
107
-
108
- const isExternalHref = (href: string): boolean =>
109
- href.startsWith('http://') ||
110
- href.startsWith('https://') ||
111
- href.startsWith('mailto:') ||
112
- href.startsWith('tel:')
113
-
114
- export const DDHGHighlightArc: React.FC<DDHGHighlightArcProps> = ({
115
- gridColumns = '1',
116
- highlights,
117
- }) => {
118
- const [hoveredIdx, setHoveredIdx] = useState<number | null>(null)
119
-
120
- if (!Array.isArray(highlights) || highlights.length === 0) return null
121
-
122
- return (
123
- <div className={cn('grid origin-top scale-100 !gap-0', getGridClassName(gridColumns))}>
124
- {highlights.map((h, i) => {
125
- const isHovered = hoveredIdx === i
126
- const bgImage = typeof h.bgImage === 'object' && h.bgImage !== null ? h.bgImage : null
127
- const href = resolveHref(h.link || null)
128
- const label = h.link?.label || 'View Archive'
129
- const target = h.link?.newTab ? '_blank' : undefined
130
- const rel = h.link?.newTab ? 'noopener noreferrer' : undefined
131
- const isExternal = href ? isExternalHref(href) : false
132
- const key = h.id != null ? `id-${h.id}` : `idx-${i}`
133
-
134
- const cardClassName = cn(
135
- 'group relative block h-[4rem] overflow-hidden rounded-[0px] border border-border px-5 py-5 !no-underline shadow-[0_18px_60px_rgba(31,31,30,0.12),inset_0_1px_0_rgba(255,255,255,0.62)] transition-all duration-500 ease-out hover:shadow-[0_24px_74px_rgba(31,31,30,0.16),inset_0_1px_0_rgba(255,255,255,0.72)]',
136
- 'before:pointer-events-none before:absolute before:inset-0 before:z-[1] --before:bg-[linear-gradient(90deg,rgba(255,255,255,0.62),rgba(255,255,255,0.22)_42%,rgba(255,255,255,0.06))] before:transition-opacity before:duration-500',
137
- 'after:pointer-events-none after:absolute after:inset-0 after:z-[2] after:opacity-[0.08] after:mix-blend-multiply --after:[background-image:radial-gradient(circle_at_20%_30%,rgba(31,31,30,0.5)_0_1px,transparent_1px),--radial-gradient(circle_at_80%_70%,rgba(255,255,255,0.8)_0_1px,transparent_1px)] after:[background-size:14px_14px,19px_19px]',
138
- href
139
- ? 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-500/40 focus-visible:ring-offset-2'
140
- : 'cursor-default',
141
- )
142
-
143
- const buttonClassName = cn(
144
- 'inline-flex items-center gap-2 whitespace-nowrap text-xs font-medium tracking-[0.04em] text-stone-700 transition-all duration-300',
145
- 'group-hover:text-[#1F1F1E]',
146
- isHovered ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-100',
147
- !href && 'opacity-40',
148
- )
149
-
150
- const cardContent = (
151
- <>
152
- <div className="HighCardsContent absolute inset-0 z-0 bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
153
-
154
- {bgImage?.url && (
155
- <div className="HighCardImg absolute inset-0 z-[1] overflow-hidden opacity-0 transition-opacity duration-500 ease-out group-hover:opacity-100">
156
- <div className="absolute left-0 top-0 h-[260%] w-full transition-transform duration-700 ease-out group-hover:-translate-y-[60%]">
157
- <Image
158
- src={bgImage.url}
159
- alt={bgImage.alt ?? h.title ?? ''}
160
- fill
161
- priority={Boolean(h.priority)}
162
- className="object-cover"
163
- sizes="(max-width: 768px) 100vw, 400px"
164
- />
165
- </div>
166
-
167
- {h.title && (
168
- <p className="HighCardTitle absolute left-0 top-0 z-10 px-4 py-4 text-[10px] font-semibold leading-snug text-stone-100 dark:text-stone-100 transition-colors duration-300">
169
- {h.title}
170
- </p>
171
- )}
172
-
173
- <div className="HighCardImgBg absolute inset-0 z-[2] bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
174
- <div className="absolute mix-blend-darken inset-0 z-[2] bg-gradient-to-r from-[#151515bf] via-[#20202029] to-[#00000000] opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
175
- </div>
176
- )}
177
-
178
- <div className="card-info relative z-[3] translate-y-0 transition-transform duration-500 group-hover:translate-y-[-7rem]">
179
- <div className="relative z-10 flex h-full items-start justify-between gap-6">
180
- <span className="relative w-full ml-auto max-w-fit right-0 -top-2 block origin-top-right scale-80">
181
- <span className={buttonClassName}>
182
- <span className="block">{label}</span>
183
- <ArrowRight
184
- size={14}
185
- className="transition-transform duration-300 group-hover:translate-x-1"
186
- />
187
- </span>
188
- </span>
189
- </div>
190
- </div>
191
- </>
192
- )
193
-
194
- if (!href) {
195
- return (
196
- <article
197
- key={key}
198
- className={cardClassName}
199
- onMouseEnter={() => setHoveredIdx(i)}
200
- onMouseLeave={() => setHoveredIdx(null)}
201
- >
202
- {cardContent}
203
- </article>
204
- )
205
- }
206
-
207
- if (isExternal) {
208
- return (
209
- <a
210
- key={key}
211
- href={href}
212
- target={target}
213
- rel={rel}
214
- aria-label={label}
215
- className={cardClassName}
216
- onMouseEnter={() => setHoveredIdx(i)}
217
- onMouseLeave={() => setHoveredIdx(null)}
218
- >
219
- {cardContent}
220
- </a>
221
- )
222
- }
223
-
224
- return (
225
- <Link
226
- key={key}
227
- href={href}
228
- target={target}
229
- rel={rel}
230
- aria-label={label}
231
- className={cardClassName}
232
- onMouseEnter={() => setHoveredIdx(i)}
233
- onMouseLeave={() => setHoveredIdx(null)}
234
- >
235
- {cardContent}
236
- </Link>
237
- )
238
- })}
239
- </div>
240
- )
241
- }
242
-
243
- export default DDHGHighlightArc
1
+ 'use client'
2
+
3
+ import { ArrowRight } from 'lucide-react'
4
+ import Image from 'next/image'
5
+ import Link from 'next/link'
6
+ import React, { useState } from 'react'
7
+
8
+ import { cn } from '@/utilities/ui'
9
+
10
+ type PortableDoc = {
11
+ slug?: string | null
12
+ breadcrumbs?: Array<{ url?: string | null }> | null
13
+ [key: string]: unknown
14
+ }
15
+
16
+ type Media = {
17
+ url?: string | null
18
+ alt?: string | null
19
+ [key: string]: unknown
20
+ }
21
+
22
+ type LinkReference =
23
+ | { relationTo: 'pages'; value: number | PortableDoc }
24
+ | { relationTo: 'projects'; value: number | PortableDoc }
25
+ | { relationTo: 'thoughts'; value: number | PortableDoc }
26
+
27
+ type HighlightArchiveCardsLink = {
28
+ type?: 'reference' | 'custom' | null
29
+ reference?: LinkReference | null
30
+ url?: string | null
31
+ label?: string | null
32
+ newTab?: boolean | null
33
+ } | null
34
+
35
+ type HighlightArchiveCardsItem = {
36
+ id?: string | null
37
+ code?: string | null
38
+ title?: string | null
39
+ type?: string | null
40
+ bgImage?: number | Media | null
41
+ priority?: boolean | null
42
+ link?: HighlightArchiveCardsLink
43
+ }
44
+
45
+ export type DDHGHighlightArcProps = {
46
+ gridColumns?: '1' | '2' | '3' | '4' | null
47
+ highlights?: HighlightArchiveCardsItem[] | null
48
+ }
49
+
50
+ const getGridClassName = (gridColumns?: DDHGHighlightArcProps['gridColumns']) => {
51
+ switch (gridColumns) {
52
+ case '2':
53
+ return 'grid-cols-1 md:grid-cols-2'
54
+ case '3':
55
+ return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-3'
56
+ case '4':
57
+ return 'grid-cols-1 md:grid-cols-2 xl:grid-cols-4'
58
+ case '1':
59
+ default:
60
+ return 'grid-cols-1'
61
+ }
62
+ }
63
+
64
+ const getPageURLFromDoc = (doc: PortableDoc | number): string | null => {
65
+ if (typeof doc === 'number') return null
66
+
67
+ const crumbs = Array.isArray(doc?.breadcrumbs) ? doc.breadcrumbs : []
68
+ if (crumbs.length) {
69
+ const last = crumbs[crumbs.length - 1]
70
+ if (typeof last?.url === 'string' && last.url) return last.url
71
+ }
72
+
73
+ const slug = doc?.slug
74
+ if (!slug || slug === 'home') return '/'
75
+ return slug.startsWith('/') ? slug : `/${slug}`
76
+ }
77
+
78
+ const getDocumentSlug = (doc: PortableDoc | number): string | null => {
79
+ if (typeof doc === 'number') return null
80
+ return doc?.slug || null
81
+ }
82
+
83
+ const getCollectionPath = (relationTo: LinkReference['relationTo']): string => {
84
+ if (relationTo === 'thoughts') return 'thoughts'
85
+ if (relationTo === 'projects') return 'archives'
86
+ return ''
87
+ }
88
+
89
+ const resolveHref = (link: HighlightArchiveCardsLink): string | null => {
90
+ if (!link) return null
91
+ if (link.type === 'custom' && link.url) return link.url
92
+
93
+ if (link.type === 'reference' && link.reference?.value) {
94
+ const { relationTo, value } = link.reference
95
+ if (relationTo === 'pages') return getPageURLFromDoc(value)
96
+
97
+ const slug = getDocumentSlug(value)
98
+ if (!slug) return null
99
+ const basePath = getCollectionPath(relationTo)
100
+ return basePath ? `/${basePath}/${slug}` : `/${slug}`
101
+ }
102
+
103
+ return null
104
+ }
105
+
106
+ const isExternalHref = (href: string): boolean =>
107
+ href.startsWith('http://') ||
108
+ href.startsWith('https://') ||
109
+ href.startsWith('mailto:') ||
110
+ href.startsWith('tel:')
111
+
112
+ export const DDHGHighlightArc: React.FC<DDHGHighlightArcProps> = ({
113
+ gridColumns = '1',
114
+ highlights,
115
+ }) => {
116
+ const [hoveredIdx, setHoveredIdx] = useState<number | null>(null)
117
+
118
+ if (!Array.isArray(highlights) || highlights.length === 0) return null
119
+
120
+ return (
121
+ <div
122
+ className={cn(
123
+ 'mx-auto grid w-full max-w-7xl origin-top scale-100 gap-0!',
124
+ getGridClassName(gridColumns),
125
+ )}
126
+ >
127
+ {highlights.map((h, i) => {
128
+ const isHovered = hoveredIdx === i
129
+ const bgImage = typeof h.bgImage === 'object' && h.bgImage !== null ? h.bgImage : null
130
+ const href = resolveHref(h.link || null)
131
+ const label = h.link?.label || 'View Archive'
132
+ const target = h.link?.newTab ? '_blank' : undefined
133
+ const rel = h.link?.newTab ? 'noopener noreferrer' : undefined
134
+ const isExternal = href ? isExternalHref(href) : false
135
+ const key = h.id != null ? `id-${h.id}` : `idx-${i}`
136
+
137
+ const cardClassName = cn(
138
+ 'group relative block h-[4rem] overflow-hidden rounded-md squircle border border-border bg-stone-100/50 px-5 py-5 !no-underline shadow-[0_18px_60px_rgba(31,31,30,0.12),inset_0_1px_0_rgba(255,255,255,0.62)] transition-all duration-500 ease-out hover:-translate-y-1 hover:shadow-[0_24px_74px_rgba(31,31,30,0.16),inset_0_1px_0_rgba(255,255,255,0.72)] dark:bg-stone-900/50',
139
+ 'before:pointer-events-none before:absolute before:inset-0 before:z-[1] --before:bg-[linear-gradient(90deg,rgba(255,255,255,0.62),rgba(255,255,255,0.22)_42%,rgba(255,255,255,0.06))] before:transition-opacity before:duration-500',
140
+ 'after:pointer-events-none after:absolute after:inset-0 after:z-2 after:opacity-[0.08] after:mix-blend-multiply --after:[background-image:radial-gradient(circle_at_20%_30%,rgba(31,31,30,0.5)_0_1px,transparent_1px),--radial-gradient(circle_at_80%_70%,rgba(255,255,255,0.8)_0_1px,transparent_1px)] after:[background-size:14px_14px,19px_19px]',
141
+ href
142
+ ? 'cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-500/40 focus-visible:ring-offset-2'
143
+ : 'cursor-default',
144
+ )
145
+
146
+ const buttonClassName = cn(
147
+ 'inline-flex items-center gap-2 whitespace-nowrap text-sm font-medium tracking-[0.04em] text-stone-700 transition-all duration-300',
148
+ 'group-hover:text-[#1F1F1E]',
149
+ isHovered ? 'translate-x-0 opacity-100' : 'translate-x-2 opacity-100',
150
+ !href && 'opacity-40',
151
+ )
152
+
153
+ const cardContent = (
154
+ <>
155
+ <div className="HighCardsContent absolute inset-0 z-0 bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
156
+
157
+ {bgImage?.url && (
158
+ <div className="HighCardImg absolute inset-0 z-1 overflow-hidden opacity-0 transition-opacity duration-500 ease-out group-hover:opacity-100">
159
+ <div className="absolute left-0 top-0 h-[260%] w-full transition-transform duration-700 ease-out group-hover:translate-y-[-60%]">
160
+ <Image
161
+ src={bgImage.url}
162
+ alt={bgImage.alt ?? h.title ?? ''}
163
+ fill
164
+ priority={Boolean(h.priority)}
165
+ className="object-cover"
166
+ sizes="(max-width: 768px) 100vw, 400px"
167
+ />
168
+ </div>
169
+
170
+ {h.title && (
171
+ <p className="HighCardTitle absolute left-0 top-0 z-10 px-4 py-4 text-sm font-semibold leading-snug text-stone-100 dark:text-stone-100 transition-colors duration-300">
172
+ {h.title}
173
+ </p>
174
+ )}
175
+
176
+ <div className="HighCardImgBg absolute inset-0 z-2 bg-transparent opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
177
+ <div className="absolute mix-blend-darken inset-0 z-2 bg-linear-to-r from-[#151515bf] via-[#20202029] to-[#00000000] opacity-0 transition-opacity duration-500 group-hover:opacity-70" />
178
+ </div>
179
+ )}
180
+
181
+ <div className="card-info relative z-3 translate-y-0 transition-transform duration-500 group-hover:-translate-y-28">
182
+ <div className="relative z-10 flex h-full items-start justify-between gap-6">
183
+ <span className="relative w-full ml-auto max-w-fit right-0 -top-2 block origin-top-right scale-95">
184
+ <span className={buttonClassName}>
185
+ <span className="block">{label}</span>
186
+ <ArrowRight
187
+ size={14}
188
+ className="transition-transform duration-300 group-hover:translate-x-1"
189
+ />
190
+ </span>
191
+ </span>
192
+ </div>
193
+ </div>
194
+ </>
195
+ )
196
+
197
+ if (!href) {
198
+ return (
199
+ <article
200
+ key={key}
201
+ className={cardClassName}
202
+ onMouseEnter={() => setHoveredIdx(i)}
203
+ onMouseLeave={() => setHoveredIdx(null)}
204
+ >
205
+ {cardContent}
206
+ </article>
207
+ )
208
+ }
209
+
210
+ if (isExternal) {
211
+ return (
212
+ <a
213
+ key={key}
214
+ href={href}
215
+ target={target}
216
+ rel={rel}
217
+ aria-label={label}
218
+ className={cardClassName}
219
+ onMouseEnter={() => setHoveredIdx(i)}
220
+ onMouseLeave={() => setHoveredIdx(null)}
221
+ >
222
+ {cardContent}
223
+ </a>
224
+ )
225
+ }
226
+
227
+ return (
228
+ <Link
229
+ key={key}
230
+ href={href}
231
+ target={target}
232
+ rel={rel}
233
+ aria-label={label}
234
+ className={cardClassName}
235
+ onMouseEnter={() => setHoveredIdx(i)}
236
+ onMouseLeave={() => setHoveredIdx(null)}
237
+ >
238
+ {cardContent}
239
+ </Link>
240
+ )
241
+ })}
242
+ </div>
243
+ )
244
+ }
245
+
246
+ export default DDHGHighlightArc