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.
Files changed (148) hide show
  1. package/package.json +1 -1
  2. package/src/installers/patchRenderBlocks.js +1 -1
  3. package/src/registry/validateTemplateManifest.js +2 -1
  4. package/templates/blocks/BannerDD790432/Component.tsx +63 -0
  5. package/templates/blocks/BannerDD790432/config.ts +54 -0
  6. package/templates/blocks/BannerDD790432/manifest.json +80 -0
  7. package/templates/blocks/Button47/Component.tsx +124 -0
  8. package/templates/blocks/Button47/config.ts +75 -0
  9. package/templates/blocks/Button47/link.ts +22 -0
  10. package/templates/blocks/Button47/manifest.json +105 -0
  11. package/templates/blocks/Button47/styles.module.scss +75 -0
  12. package/templates/blocks/DD-Accordion/Component.tsx +55 -36
  13. package/templates/blocks/DD-Accordion/config.ts +71 -59
  14. package/templates/blocks/DD-AnimText/Component.tsx +50 -32
  15. package/templates/blocks/DD-AnimText/config.ts +296 -295
  16. package/templates/blocks/DD-AsideNav/Component.tsx +323 -319
  17. package/templates/blocks/DD-Banner/Component.tsx +54 -51
  18. package/templates/blocks/DD-BookCall/Component.tsx +535 -535
  19. package/templates/blocks/DD-ButtonPop/Component.tsx +118 -117
  20. package/templates/blocks/DD-Card/Component.tsx +4 -4
  21. package/templates/blocks/DD-CardTemplate01/Component.tsx +139 -140
  22. package/templates/blocks/DD-CardsWithVariantV3/Component.tsx +472 -0
  23. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/index.tsx +203 -0
  24. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/styles.ts +87 -0
  25. package/templates/blocks/DD-CardsWithVariantV3/ElectricBorder/useInjectStyle.ts +39 -0
  26. package/templates/blocks/DD-CardsWithVariantV3/config.ts +326 -0
  27. package/templates/blocks/DD-CardsWithVariantV3/manifest.json +110 -0
  28. package/templates/blocks/DD-Carousel-A/Component.tsx +251 -249
  29. package/templates/blocks/DD-Carousel-B/Component.tsx +405 -403
  30. package/templates/blocks/DD-ComparisonTable/Component.tsx +255 -256
  31. package/templates/blocks/DD-Contact/Component.tsx +433 -435
  32. package/templates/blocks/DD-Counter/Component.tsx +11 -6
  33. package/templates/blocks/DD-DeliCard/Component.tsx +303 -0
  34. package/templates/blocks/DD-DeliCard/config.ts +186 -0
  35. package/templates/blocks/DD-DeliCard/manifest.json +94 -0
  36. package/templates/blocks/DD-FeatCat/Component.tsx +302 -302
  37. package/templates/blocks/DD-FeatProd/CircularGallery.tsx +1179 -0
  38. package/templates/blocks/DD-FeatProd/Component.tsx +484 -0
  39. package/templates/blocks/DD-FeatProd/config.ts +173 -0
  40. package/templates/blocks/DD-FeatProd/manifest.json +93 -0
  41. package/templates/blocks/DD-FeatServ/Component.tsx +607 -0
  42. package/templates/blocks/DD-FeatServ/config.ts +213 -0
  43. package/templates/blocks/DD-FeatServ/manifest.json +113 -0
  44. package/templates/blocks/DD-FeatStrip/Component.tsx +584 -585
  45. package/templates/blocks/DD-FluidBtn/ButtonsEyeCatching.tsx +627 -628
  46. package/templates/blocks/DD-FluidBtn/Component.tsx +46 -43
  47. package/templates/blocks/DD-GooeyInfo/Component.tsx +150 -149
  48. package/templates/blocks/DD-GooeyInfo/config.ts +136 -135
  49. package/templates/blocks/DD-Hero/Component.tsx +296 -297
  50. package/templates/blocks/DD-HeroPlas/Component.tsx +50 -0
  51. package/templates/blocks/DD-HeroPlas/ThreeScene.tsx +109 -0
  52. package/templates/blocks/DD-HeroPlas/config.ts +33 -0
  53. package/templates/blocks/DD-HeroPlas/manifest.json +88 -0
  54. package/templates/blocks/DD-HorizontalScroll/Component.tsx +937 -936
  55. package/templates/blocks/DD-Icon/Component.tsx +46 -81
  56. package/templates/blocks/DD-IconButton/Component.tsx +13 -20
  57. package/templates/blocks/DD-IdTag/Component.tsx +22 -21
  58. package/templates/blocks/DD-LineAnim/Component.tsx +42 -36
  59. package/templates/blocks/DD-LogoMarq/Component.tsx +38 -48
  60. package/templates/blocks/DD-Lottie/Component.tsx +11 -3
  61. package/templates/blocks/DD-MasonaryMedia/Component.tsx +544 -545
  62. package/templates/blocks/DD-MasonaryMedia/config.ts +389 -389
  63. package/templates/blocks/DD-Masonry/Component.tsx +654 -644
  64. package/templates/blocks/DD-Masonry/config.ts +475 -475
  65. package/templates/blocks/DD-MaterialSwatches/Component.tsx +85 -0
  66. package/templates/blocks/DD-MaterialSwatches/config.ts +232 -0
  67. package/templates/blocks/DD-MaterialSwatches/manifest.json +84 -0
  68. package/templates/blocks/DD-MaterialSwatches/material.ts +107 -0
  69. package/templates/blocks/DD-NineCanvas/Component.tsx +129 -0
  70. package/templates/blocks/DD-NineCanvas/RenderBlocks.tsx +244 -0
  71. package/templates/blocks/DD-NineCanvas/allowedCanvasBlocks.ts +6 -0
  72. package/templates/blocks/DD-NineCanvas/blockComponents.tsx +8 -0
  73. package/templates/blocks/DD-NineCanvas/canvas/CanvasGrid.tsx +316 -0
  74. package/templates/blocks/DD-NineCanvas/canvas/RadialNav.tsx +432 -0
  75. package/templates/blocks/DD-NineCanvas/canvas/canvas-panels.css +889 -0
  76. package/templates/blocks/DD-NineCanvas/canvas/icons/custom-icons.tsx +364 -0
  77. package/templates/blocks/DD-NineCanvas/config.ts +61 -0
  78. package/templates/blocks/DD-NineCanvas/manifest.json +165 -0
  79. package/templates/blocks/DD-NineCanvas/styles.scss +152 -0
  80. package/templates/blocks/DD-NineCanvas/tooltip/get-strict-context.tsx +26 -0
  81. package/templates/blocks/DD-NineCanvas/tooltip/primitives.tsx +178 -0
  82. package/templates/blocks/DD-NineCanvas/tooltip/tooltip.tsx +71 -0
  83. package/templates/blocks/DD-NineCanvas/tooltip/use-controlled-state.tsx +33 -0
  84. package/templates/blocks/DD-NineCanvas/utilities/FadeOnScroll.tsx +68 -0
  85. package/templates/blocks/DD-NineCanvas/utilities/ParallaxWrapper.tsx +157 -0
  86. package/templates/blocks/DD-NineCanvas/utilities/renderCustomCSS.ts +5 -0
  87. package/templates/blocks/DD-NineCanvas/utilities/webglCleanup.ts +27 -0
  88. package/templates/blocks/DD-PayloadAIAssistant/Component.tsx +78 -0
  89. package/templates/blocks/DD-PayloadAIAssistant/DustedBot.tsx +590 -0
  90. package/templates/blocks/DD-PayloadAIAssistant/config.ts +37 -0
  91. package/templates/blocks/DD-PayloadAIAssistant/demo.css +125 -0
  92. package/templates/blocks/DD-PayloadAIAssistant/manifest.json +93 -0
  93. package/templates/blocks/DD-PayloadAIAssistant/source-defaults.ts +17 -0
  94. package/templates/blocks/DD-PayloadAIAssistant/styles.ts +306 -0
  95. package/templates/blocks/DD-Pricing/Component.tsx +2 -3
  96. package/templates/blocks/DD-Pricing/config.ts +215 -215
  97. package/templates/blocks/DD-Process/Component.tsx +2 -2
  98. package/templates/blocks/DD-ProjHeader/Component.tsx +93 -91
  99. package/templates/blocks/DD-ProjSlider/Component.tsx +1 -1
  100. package/templates/blocks/DD-Section/Component.tsx +265 -266
  101. package/templates/blocks/DD-Separator/Component.tsx +66 -66
  102. package/templates/blocks/DD-Services/Component.tsx +176 -176
  103. package/templates/blocks/DD-ShowcaseGrid/Component.tsx +306 -307
  104. package/templates/blocks/DD-Slider-A/Component.tsx +2 -2
  105. package/templates/blocks/DD-StackCards/Component.tsx +172 -171
  106. package/templates/blocks/DD-Team/Component.tsx +247 -247
  107. package/templates/blocks/DD-TeamCards/Component.tsx +251 -251
  108. package/templates/blocks/DD-TechStack/Component.tsx +173 -174
  109. package/templates/blocks/DD-TestMarq/Component.tsx +158 -132
  110. package/templates/blocks/DD-Testim/Component.tsx +247 -247
  111. package/templates/blocks/DD-Testim2/Component.tsx +265 -256
  112. package/templates/blocks/DD-Testimonials/Component.tsx +139 -139
  113. package/templates/blocks/DD-Thanks/Component.tsx +1 -1
  114. package/templates/blocks/DD-Work/Component.tsx +318 -318
  115. package/templates/blocks/DD-Work-B/Component.tsx +294 -294
  116. package/templates/blocks/DDHG-CTASection2/Component.tsx +183 -184
  117. package/templates/blocks/DDHG-HighlightArc/Component.tsx +6 -3
  118. package/templates/blocks/DDHG-LogoMarquee/Component.tsx +45 -8
  119. package/templates/blocks/DDHG-NavCodeGrid/Component.tsx +1 -3
  120. package/templates/blocks/DDHG-ServiceDetails/ServiceDetailCard.tsx +9 -22
  121. package/templates/blocks/DDHG-ServiceHero/Component.tsx +10 -10
  122. package/templates/blocks/DDHG-TeamCards/Component.tsx +6 -35
  123. package/templates/blocks/Form/Checkbox/index.tsx +45 -0
  124. package/templates/blocks/Form/Component.tsx +163 -0
  125. package/templates/blocks/Form/Country/index.tsx +65 -0
  126. package/templates/blocks/Form/Country/options.ts +982 -0
  127. package/templates/blocks/Form/Email/index.tsx +38 -0
  128. package/templates/blocks/Form/Error/index.tsx +15 -0
  129. package/templates/blocks/Form/Message/index.tsx +13 -0
  130. package/templates/blocks/Form/Number/index.tsx +36 -0
  131. package/templates/blocks/Form/Select/index.tsx +63 -0
  132. package/templates/blocks/Form/State/index.tsx +64 -0
  133. package/templates/blocks/Form/State/options.ts +52 -0
  134. package/templates/blocks/Form/Text/index.tsx +32 -0
  135. package/templates/blocks/Form/Textarea/index.tsx +40 -0
  136. package/templates/blocks/Form/Width/index.tsx +13 -0
  137. package/templates/blocks/Form/config.ts +51 -0
  138. package/templates/blocks/Form/fields.tsx +21 -0
  139. package/templates/blocks/Form/manifest.json +168 -0
  140. package/templates/blocks/MediaBlock/Component.tsx +67 -0
  141. package/templates/blocks/MediaBlock/config.ts +14 -0
  142. package/templates/blocks/MediaBlock/manifest.json +92 -0
  143. package/templates/blocks/TransitionsDustedShowcase/Component.tsx +9 -0
  144. package/templates/blocks/TransitionsDustedShowcase/config.ts +13 -0
  145. package/templates/blocks/TransitionsDustedShowcase/manifest.json +80 -0
  146. package/templates/blocks/rawHtml/Component.tsx +25 -0
  147. package/templates/blocks/rawHtml/config.ts +56 -0
  148. package/templates/blocks/rawHtml/manifest.json +80 -0
@@ -1,628 +1,627 @@
1
- 'use client'
2
-
3
- import * as React from 'react'
4
- import Link from 'next/link'
5
- import { usePathname, useRouter } from 'next/navigation'
6
- import { cn } from '@/utilities/ui'
7
- import { Button, type ButtonProps } from '@/components/ui/button'
8
- import type { Page, Post } from '@/payload-types'
9
- import * as LucideIcons from 'lucide-react'
10
- const useDrawerClose = () => undefined
11
-
12
- // import './index.scss'
13
-
14
-
15
- const useDrawerCloseIfAvailable = () => {
16
- try {
17
- return useDrawerClose()
18
- } catch {
19
- return undefined
20
- }
21
- }
22
-
23
- export type ButtonsEyeCatchingProps = {
24
- variant?: 'v1' | 'v2' | 'v3' | 'v4' | 'v5' | 'v6' | 'v7'
25
- label?: string
26
- displayMode?: 'iconWithLabel' | 'iconOnly' | 'labelOnly'
27
- iconPosition?: 'before' | 'after'
28
- iconName?: string
29
- type?: 'custom' | 'reference' | null
30
- reference?: {
31
- relationTo: 'pages' | 'posts'
32
- value: Page | Post | string | number
33
- } | null
34
- url?: string | null
35
- newTab?: boolean
36
- size?: ButtonProps['size']
37
- className?: string
38
- wrapperClassName?: string
39
- fullWidth?: boolean
40
- disabled?: ButtonProps['disabled']
41
- closeDrawerOnClick?: boolean
42
- onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
43
- onMouseEnter?: (e: React.MouseEvent<HTMLButtonElement>) => void
44
- onSubmit?: (e: React.FormEvent<HTMLButtonElement>) => void
45
- form?: string
46
- htmlType?: 'submit' | 'button' | 'reset'
47
- }
48
-
49
- const getCollectionPath = (relationTo: string) => {
50
- return relationTo === 'posts' ? 'posts' : relationTo
51
- }
52
-
53
- const getHref = (props: ButtonsEyeCatchingProps): string | null => {
54
- const { type, reference, url } = props
55
-
56
- if (type === 'reference' && reference?.value) {
57
- const slug =
58
- typeof reference.value === 'object' && 'slug' in reference.value
59
- ? reference.value.slug
60
- : typeof reference.value === 'string'
61
- ? reference.value
62
- : null
63
-
64
- if (!slug) return null
65
-
66
- const basePath =
67
- reference.relationTo === 'pages' ? '' : `/${getCollectionPath(reference.relationTo)}`
68
-
69
- return `${basePath}/${slug}`
70
- }
71
-
72
- return url || null
73
- }
74
-
75
- const LinkWrapper: React.FC<
76
- React.PropsWithChildren<{
77
- href: string
78
- newTab?: boolean
79
- onClick?: () => void
80
- className?: string
81
- closeDrawerOnClick?: boolean
82
- }>
83
- > = ({ href, newTab, onClick, className, children, closeDrawerOnClick = true }) => {
84
- const closeDrawer = useDrawerClose?.() || (() => {})
85
- const pathname = usePathname()
86
-
87
- const hashIndex = href.indexOf('#')
88
- const hasHash = hashIndex >= 0
89
- const basePath = hasHash ? href.slice(0, hashIndex) : href
90
- const hash = hasHash ? href.slice(hashIndex) : ''
91
-
92
- const normalise = (p?: string) => (p ? p.replace(/\/$/, '') : '')
93
- const isSamePageHash = hasHash && (basePath === '' || normalise(basePath) === normalise(pathname))
94
-
95
- const handleClick = (e: React.MouseEvent) => {
96
- onClick?.()
97
-
98
- if (isSamePageHash && hash) {
99
- e.preventDefault()
100
-
101
- const targetId = hash.slice(1)
102
- const el = document.getElementById(targetId)
103
-
104
- if (el) {
105
- el.scrollIntoView({ behavior: 'smooth', block: 'start' })
106
- window.history.pushState(null, '', href)
107
- }
108
-
109
- if (closeDrawerOnClick) {
110
- closeDrawer()
111
- }
112
-
113
- return
114
- }
115
-
116
- if (closeDrawerOnClick && !newTab) {
117
- closeDrawer()
118
- }
119
- }
120
-
121
- const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
122
-
123
- return (
124
- <Link href={href} {...newTabProps} onClick={handleClick} className={className} prefetch>
125
- {children}
126
- </Link>
127
- )
128
- }
129
- function GlassChip() {
130
- return (
131
- <div className="absolute w-full h-full group mx-auto left-0 right-0 rounded-2xl squircle overflow-hidden">
132
- <div
133
- className="
134
- absolute inset-0 mx-auto
135
- flex items-center justify-center
136
- overflow-hidden
137
- rounded-full
138
- border-t border-b border-white/10
139
- bg-white/[0.05]
140
- text-white
141
- text-[18px]
142
- font-bold
143
- tracking-[1px]
144
- backdrop-blur-[15px]
145
- shadow-[0_15px_35px_rgba(255,255,255,0.15)]
146
- transition-all duration-500
147
- hover:tracking-[2px]
148
-
149
- before:absolute
150
- before:left-0
151
- before:top-0
152
- before:h-full
153
- before:w-1/2
154
- before:skew-x-[45deg]
155
- before:bg-[linear-gradient(to_left,rgba(255,255,255,0.22),transparent)]
156
- before:transition-transform
157
- before:duration-700
158
- hover:before:translate-x-[220%]
159
-
160
- after:absolute
161
- after:inset-[1px]
162
- after:rounded-full
163
- after:border
164
- after:border-white/10
165
- after:shadow-[inset_0_1px_0_rgba(255,255,255,0.45),inset_0_-8px_18px_rgba(0,0,0,0.25)]
166
- "
167
- ></div>
168
-
169
- <div
170
- className="
171
- absolute left-1/2 -translate-x-1/2
172
- -top-[3px]
173
- h-[8px] w-[30px]
174
- rounded-full
175
- bg-[#ba55d3]
176
- shadow-[0_0_5px_#663399,0_0_15px_#ba55d3,0_0_30px_#663399,0_0_60px_#ff00ff]
177
- transition-all duration-500
178
- group-hover:top-0
179
- group-hover:bg-[#ba55d355]
180
- group-hover:h-full
181
- group-hover:w-[80%]
182
- group-hover:rounded-full
183
- group-hover:delay-300
184
- "
185
- />
186
-
187
- <div
188
- className="
189
- absolute left-1/2 -translate-x-1/2
190
- -bottom-[3px]
191
- h-[8px] w-[30px]
192
- rounded-full
193
- bg-[#ba55d3]
194
- shadow-[0_0_5px_#663399,0_0_15px_#ba55d3,0_0_30px_#663399,0_0_60px_#ff00ff]
195
- transition-all duration-500
196
- group-hover:bottom-0
197
- group-hover:bg-[#ba55d399]
198
- group-hover:h-full
199
- group-hover:brightness-150
200
- group-hover:w-[80%]
201
- group-hover:rounded-full
202
- group-hover:delay-300
203
- "
204
- />
205
- </div>
206
- )
207
- }
208
- function EyeCatchingButton_v1(props: ButtonProps & { wrapperClassName?: string }) {
209
- const { wrapperClassName, size = 'default', children, ...buttonProps } = props
210
-
211
- const sizeClasses = {
212
- sm: 'h-8 w-fit',
213
- default: 'h-10 w-40',
214
- lg: 'h-12 w-48',
215
- icon: 'h-10 w-10',
216
- clear: ''
217
- }
218
-
219
- return (
220
- <div
221
- className={cn(
222
- sizeClasses[size || 'default'],
223
- wrapperClassName,
224
- 'relative min-w-fit overflow-hidden rounded-2xl p-0.5 font-semibold group',
225
- 'border-1 border-stone-800/20',
226
- '-bg-card !dark:bg-zinc-900',
227
- 'shadow',
228
- 'drop-shadow-[2px_4px_6px_rgba(0,0,0,0.25)]',
229
- 'transition-all duration-300 hover:duration-300',
230
- 'hover:-translate-y-0.5 hover:scale-[1]',
231
- 'hover:!bg-[rgba(102,51,153,1)] hover:border-0 hover:border-[#985fd023] hover:drop-shadow-[inset_0px_0px_6px_rgba(102,51,153,1)]'
232
- )}
233
- >
234
- <GlassChip />
235
-
236
- <span className="absolute inset-[-1000%] animate-[spin_5s_linear_infinite] dark:bg-[conic-gradient(from_90deg_at_50%_50%,#fff_0%,#4a4a4a_7%)] bg-[conic-gradient(from_90deg_at_50%_50%,#000_0%,#fff_5%)] group-hover:bg-none rounded-2xl overflow-hidden blur-[4px] brightness-[1.2]" />
237
-
238
- <Button
239
- {...buttonProps}
240
- className={cn(
241
- buttonProps.className,
242
- 'h-full w-full rounded-full text-zinc-800 dark:text-zinc-200 backdrop-blur-xl -bg-card dark:!bg-zinc-900 flex items-center justify-center gap-2',
243
- 'border-2 border-ridge border-[#938eab8c]',
244
- 'group-hover:shadow-[inset_0_1px_6px_rgba(255,255,255,0.28),inset_0_-8px_18px_rgba(0,0,0,0.22)]',
245
- 'group-active:translate-y-[1px] group-active:scale-[0.985]',
246
- // 'group-active:bg-[rgba(74,35,120,0.95)]',
247
- 'group-active:shadow-[inset_0_3px_10px_rgba(0,0,0,0.45),inset_0_1px_2px_rgba(255,255,255,0.18)]',
248
- 'group-active:duration-75'
249
- )}
250
- >
251
- {children}
252
- </Button>
253
- </div>
254
- )
255
- }
256
-
257
- function EyeCatchingButton_v2(props: ButtonProps & { wrapperClassName?: string }) {
258
- const { wrapperClassName, size = 'default', children, ...buttonProps } = props
259
-
260
- const sizeClasses = {
261
- sm: 'h-8',
262
- default: 'h-10',
263
- lg: 'h-12',
264
- icon: 'h-10 w-10',
265
- clear: ''
266
- }
267
-
268
- return (
269
- <div
270
- className={cn(
271
- 'eyecatchingbtn-2',
272
- sizeClasses[size || 'default'],
273
- wrapperClassName,
274
- 'relative overflow-hidden rounded-2xl dark:bg-card-purple bg-violet-600 shadow border dark:border-stone-900/50 group border-stone-400 p-0.5 min-w-fit hover:bg-violet-600 hover:brightness-110 duration-300 hover:duration-300 transition-all font-semibold',
275
- 'transition-all duration-300 ease-out',
276
- 'hover:-translate-y-0.5 hover:scale-[1]',
277
- 'hover:border-[#985fd066] hover:shadow-[0_0_22px_rgba(152,95,208,0.32)] hover:drop-shadow-[0_8px_18px_rgba(102,51,153,0.35)]',
278
- 'active:translate-y-0 active:scale-[0.975]',
279
- 'active:shadow-[inset_0_2px_10px_rgba(0,0,0,0.35),0_0_10px_rgba(152,95,208,0.25)]',
280
- 'active:duration-75'
281
- )}
282
- >
283
- <GlassChip />
284
- <span className="absolute inset-[-1000%] animate-[spin_5s_linear_infinite] dark:bg-[conic-gradient(from_90deg_at_50%_50%,#fff_0%,#4a4a4a_7%)] bg-[conic-gradient(from_90deg_at_50%_50%,#000_0%,#fff_5%)] group-hover:bg-none rounded-2xl overflow-hidden blur-[4px] brightness-[1.2]" />
285
- <Button
286
- {...buttonProps}
287
- className={cn(
288
- buttonProps.className,
289
- 'h-full w-full rounded-full text-zinc-800 dark:text-zinc-200 backdrop-blur-xl bg-card-purple dark:bg-zinc-900 flex items-center justify-center gap-2'
290
- )}
291
- >
292
- {children}
293
- </Button>
294
- </div>
295
- )
296
- }
297
-
298
- function EyeCatchingButton_v3(props: ButtonProps & { wrapperClassName?: string }) {
299
- const { wrapperClassName, size = 'default', children, ...buttonProps } = props
300
-
301
- const sizeClasses = {
302
- sm: 'h-8 w-fit',
303
- default: 'h-10 w-40',
304
- lg: 'h-12 w-48',
305
- icon: 'h-10 w-10',
306
- clear: ''
307
- }
308
-
309
- return (
310
- <div
311
- className={cn(
312
- sizeClasses[size || 'default'],
313
- wrapperClassName,
314
- 'relative overflow-hidden rounded-2xl dark:bg-card-purple bg-violet-600 shadow border dark:border-[rgba(132,43,222,0.5)] group border-[rgba(132,43,222,0.5)] p-0.5 min-w-fit hover:bg-violet-600 hover:brightness-110 duration-300 hover:duration-300 transition-all font-semibold drop-shadow-xl'
315
- // 'relative min-w-fit overflow-hidden rounded-2xl p-0.5 font-semibold group',
316
- // 'border-2 border-[rgba(132,43,222,0.915)]',
317
- // '!bg-[rgba(102,51,153,0.551)]',
318
- // 'drop-shadow-[2px_4px_6px_rgba(0,0,0,0.25)]',
319
- // 'transition-all duration-300 hover:duration-300 ease-in-out',
320
- // 'hover:!bg-[rgba(102,51,153,0.842)] hover:!mix-blend-normal hover:brightness-110'
321
- )}
322
- >
323
- <span className="absolute inset-[-1000%] animate-[spin_5s_linear_infinite_reverse] dark:bg-[conic-gradient(from_90deg_at_50%_50%,#fff_0%,#4a4a4a_7%)] bg-[conic-gradient(from_90deg_at_50%_50%,#000_0%,#fff_5%)] group-hover:bg-none blur-[4px] brightness-[1.2]" />
324
- <Button
325
- {...buttonProps}
326
- className={cn(
327
- buttonProps.className,
328
- 'h-full w-full rounded-full text-zinc-800 dark:text-zinc-800 backdrop-blur-xl bg-[radial-gradient(circle_at_center,_#e4e0e0,_#9b9898)] dark:bg-[radial-gradient(circle_at_center,_#e4e0e0,_#9b9898)] --bg-zinc-300 --dark:bg-zinc-300 flex items-center justify-center gap-2 drop-shadow-md'
329
- )}
330
- >
331
- {children}
332
- </Button>
333
- </div>
334
- )
335
- }
336
-
337
- function EyeCatchingButton_v4(props: ButtonProps & { wrapperClassName?: string }) {
338
- const { wrapperClassName, size = 'default', children, ...buttonProps } = props
339
-
340
- const sizeClasses = {
341
- sm: 'h-8',
342
- default: 'h-10',
343
- lg: 'h-12',
344
- icon: 'h-10 w-10',
345
- clear: ''
346
- }
347
-
348
- return (
349
- <div
350
- className={cn(
351
- 'relative !rounded-full min-w-fit inline-flex items-center gap-1',
352
- sizeClasses[size || 'default'],
353
- wrapperClassName
354
- )}
355
- >
356
- <Button
357
- {...buttonProps}
358
- className={cn(
359
- 'w-full animate-bg-shine border-[1px] rounded-full bg-card shadow-xl bg-[length:200%_100%] tracking-wide flex items-center justify-center gap-2',
360
- 'dark:bg-[linear-gradient(110deg,#09090B,45%,#27272A,55%,#09090B)] dark:text-zinc-200 dark:border-stone-800 font-semibold',
361
- 'bg-[linear-gradient(110deg,#FFF,45%,#E4E4E7,55%,#FFF)] text-zinc-800 border-stone-300',
362
- 'hover:border-violet-500 hover:outline hover:outline-2 hover:outline-offset-1 hover:outline-violet-600',
363
- buttonProps.className
364
- )}
365
- >
366
- {children}
367
- </Button>
368
- </div>
369
- )
370
- }
371
-
372
- function EyeCatchingButton_v5(props: ButtonProps & { wrapperClassName?: string }) {
373
- const { wrapperClassName, size = 'default', children, ...buttonProps } = props
374
-
375
- const sizeClasses = {
376
- sm: 'h-8',
377
- default: 'h-10',
378
- lg: 'h-12',
379
- icon: 'h-10 w-10',
380
- clear: ''
381
- }
382
-
383
- return (
384
- <div
385
- className={cn(
386
- 'relative !rounded-full min-w-fit inline-flex items-center gap-1',
387
- sizeClasses[size || 'default'],
388
- wrapperClassName
389
- )}
390
- >
391
- <Button
392
- {...buttonProps}
393
- className={cn(
394
- 'w-full border border-stone-800 rounded-full shadow-xl tracking-wide flex items-center justify-center gap-2',
395
- 'bg-[radial-gradient(circle,_rgba(255,_255,_255,_1),_rgba(230,_230,_235,_0.95))] bg-[length:200%_100%] animate-bg-shine',
396
- '!text-stone-950 hover:text-stone-950 font-semibold',
397
- 'hover:border-violet-500 hover:outline hover:outline-2 hover:outline-offset-1 hover:outline-violet-600',
398
- buttonProps.className
399
- )}
400
- >
401
- {children}
402
- </Button>
403
- </div>
404
- )
405
- }
406
-
407
- function EyeCatchingButton_v6(props: ButtonProps & { wrapperClassName?: string }) {
408
- const { wrapperClassName, size = 'default', children, ...buttonProps } = props
409
-
410
- const sizeClasses = {
411
- sm: 'h-8',
412
- default: 'h-10',
413
- lg: 'h-12',
414
- icon: 'h-10 w-10',
415
- clear: ''
416
- }
417
-
418
- return (
419
- <div
420
- className={cn(
421
- 'relative !rounded-full min-w-fit inline-flex items-center gap-1',
422
- sizeClasses[size || 'default'],
423
- wrapperClassName
424
- )}
425
- >
426
- <Button
427
- {...buttonProps}
428
- className={cn(
429
- 'w-full border border-stone-800 rounded-full shadow-xl tracking-wide flex items-center justify-center gap-2',
430
- 'bg-[radial-gradient(circle,_rgba(190,_242,_100,_1),_rgba(132,_204,_22,_0.95))] bg-[length:200%_100%] animate-bg-shine',
431
- '!text-stone-950 hover:text-stone-950 font-semibold',
432
- 'hover:border-violet-500 hover:outline hover:outline-2 hover:outline-offset-1 hover:outline-violet-600',
433
- buttonProps.className
434
- )}
435
- >
436
- {children}
437
- </Button>
438
- </div>
439
- )
440
- }
441
-
442
- function EyeCatchingButton_v7(props: ButtonProps & { wrapperClassName?: string }) {
443
- const { wrapperClassName, size = 'default', children, ...buttonProps } = props
444
-
445
- const sizeClasses = {
446
- sm: 'text-sm',
447
- default: 'text-base',
448
- lg: 'text-lg',
449
- icon: 'text-base',
450
- clear: ''
451
- }
452
-
453
- return (
454
- <div className={cn('relative inline-flex items-center min-w-fit', wrapperClassName)}>
455
- <Button
456
- {...buttonProps}
457
- variant="ghost"
458
- className={cn(
459
- 'p-0 h-auto bg-transparent shadow-none border-0 rounded-none',
460
- 'tracking-wide underline-none hover:brightness-110',
461
- 'text-zinc-800 dark:text-zinc-200 font-semibold',
462
- 'hover:text-violet-300 transition-colors duration-200',
463
- sizeClasses[size || 'default'],
464
- buttonProps.className
465
- )}
466
- >
467
- {children}
468
- </Button>
469
- </div>
470
- )
471
- }
472
-
473
- function renderVariant(
474
- variant: NonNullable<ButtonsEyeCatchingProps['variant']>,
475
- props: ButtonProps & { wrapperClassName?: string }
476
- ) {
477
- switch (variant) {
478
- case 'v1':
479
- return <EyeCatchingButton_v1 {...props} />
480
- case 'v2':
481
- return <EyeCatchingButton_v2 {...props} />
482
- case 'v3':
483
- return <EyeCatchingButton_v3 {...props} />
484
- case 'v4':
485
- return <EyeCatchingButton_v4 {...props} />
486
- case 'v5':
487
- return <EyeCatchingButton_v5 {...props} />
488
- case 'v6':
489
- return <EyeCatchingButton_v6 {...props} />
490
- case 'v7':
491
- return <EyeCatchingButton_v7 {...props} />
492
- default:
493
- return <EyeCatchingButton_v1 {...props} />
494
- }
495
- }
496
-
497
- export const ButtonsEyeCatching = ({
498
- variant = 'v1',
499
- label,
500
- displayMode = 'iconWithLabel',
501
- iconPosition = 'before',
502
- iconName,
503
- type: linkType,
504
- reference,
505
- url,
506
- newTab,
507
- size,
508
- className,
509
- wrapperClassName,
510
- fullWidth,
511
- disabled,
512
- closeDrawerOnClick = true,
513
- onClick,
514
- onMouseEnter,
515
- onSubmit,
516
- form,
517
- htmlType
518
- }: ButtonsEyeCatchingProps) => {
519
- const closeDrawer = useDrawerClose?.() || (() => {})
520
- const router = useRouter()
521
- const pathname = usePathname()
522
-
523
- const href = getHref({ type: linkType, reference, url, label })
524
-
525
- const IconComponent =
526
- iconName && LucideIcons[iconName as keyof typeof LucideIcons]
527
- ? (LucideIcons[iconName as keyof typeof LucideIcons] as React.ComponentType<{
528
- className?: string
529
- }>)
530
- : null
531
-
532
- const hashIndex = href?.indexOf('#') ?? -1
533
- const hasHash = hashIndex >= 0
534
- const basePath = hasHash && href ? href.slice(0, hashIndex) : href
535
- const hash = hasHash && href ? href.slice(hashIndex) : ''
536
-
537
- const normalise = (p?: string) => (p ? p.replace(/\/$/, '') : '')
538
- const isSamePageHash =
539
- hasHash && (basePath === '' || normalise(basePath ?? undefined) === normalise(pathname))
540
-
541
- const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
542
- if (onClick) {
543
- onClick(e)
544
- if (e.defaultPrevented) return
545
- }
546
-
547
- if (isSamePageHash && hash) {
548
- e.preventDefault()
549
-
550
- const targetId = hash.slice(1)
551
- const el = document.getElementById(targetId)
552
-
553
- if (el) {
554
- el.scrollIntoView({ behavior: 'smooth', block: 'start' })
555
-
556
- if (href) {
557
- window.history.pushState(null, '', href)
558
- }
559
- }
560
-
561
- if (closeDrawerOnClick) {
562
- closeDrawer()
563
- }
564
-
565
- return
566
- }
567
-
568
- if (closeDrawerOnClick && !newTab && href) {
569
- closeDrawer()
570
- }
571
-
572
- if (href) {
573
- e.preventDefault()
574
- router.push(href)
575
- }
576
- }
577
-
578
- const iconElement =
579
- IconComponent && displayMode !== 'labelOnly' ? (
580
- <IconComponent
581
- className={cn('h-4 w-4', size === 'sm' && 'h-3 w-3', size === 'lg' && 'h-5 w-5')}
582
- />
583
- ) : null
584
-
585
- let buttonChildren: React.ReactNode
586
-
587
- if (displayMode === 'iconOnly') {
588
- buttonChildren = iconElement
589
- } else if (displayMode === 'labelOnly') {
590
- buttonChildren = label
591
- } else {
592
- buttonChildren =
593
- iconPosition === 'before' ? (
594
- <>
595
- {iconElement}
596
- {label}
597
- </>
598
- ) : (
599
- <>
600
- {label}
601
- {iconElement}
602
- </>
603
- )
604
- }
605
-
606
- const btn = renderVariant(variant, {
607
- size,
608
- disabled,
609
- className: cn(fullWidth ? 'w-full' : undefined, className),
610
- wrapperClassName,
611
- children: buttonChildren,
612
- onClick: handleClick,
613
- onMouseEnter,
614
- onSubmit,
615
- form,
616
- type: htmlType
617
- })
618
-
619
- if (href && newTab) {
620
- return (
621
- <LinkWrapper href={href} newTab={newTab} closeDrawerOnClick={closeDrawerOnClick}>
622
- {btn}
623
- </LinkWrapper>
624
- )
625
- }
626
-
627
- return btn
628
- }
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import Link from 'next/link'
5
+ import { usePathname, useRouter } from 'next/navigation'
6
+ import { cn } from '@/utilities/ui'
7
+ import { Button, type ButtonProps } from '@/components/ui/button'
8
+ import type { Page } from '@/payload-types'
9
+ import * as LucideIcons from 'lucide-react'
10
+ const useDrawerClose = () => undefined
11
+
12
+ // import './index.scss'
13
+
14
+ const useDrawerCloseIfAvailable = () => {
15
+ try {
16
+ return useDrawerClose()
17
+ } catch {
18
+ return undefined
19
+ }
20
+ }
21
+
22
+ export type ButtonsEyeCatchingProps = {
23
+ variant?: 'v1' | 'v2' | 'v3' | 'v4' | 'v5' | 'v6' | 'v7'
24
+ label?: string
25
+ displayMode?: 'iconWithLabel' | 'iconOnly' | 'labelOnly'
26
+ iconPosition?: 'before' | 'after'
27
+ iconName?: string
28
+ type?: 'custom' | 'reference' | null
29
+ reference?: {
30
+ relationTo: 'pages'
31
+ value: Page | string | number
32
+ } | null
33
+ url?: string | null
34
+ newTab?: boolean
35
+ size?: ButtonProps['size']
36
+ className?: string
37
+ wrapperClassName?: string
38
+ fullWidth?: boolean
39
+ disabled?: ButtonProps['disabled']
40
+ closeDrawerOnClick?: boolean
41
+ onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
42
+ onMouseEnter?: (e: React.MouseEvent<HTMLButtonElement>) => void
43
+ onSubmit?: (e: React.FormEvent<HTMLButtonElement>) => void
44
+ form?: string
45
+ htmlType?: 'submit' | 'button' | 'reset'
46
+ }
47
+
48
+ const getCollectionPath = (relationTo: string) => {
49
+ return relationTo
50
+ }
51
+
52
+ const getHref = (props: ButtonsEyeCatchingProps): string | null => {
53
+ const { type, reference, url } = props
54
+
55
+ if (type === 'reference' && reference?.value) {
56
+ const slug =
57
+ typeof reference.value === 'object' && 'slug' in reference.value
58
+ ? reference.value.slug
59
+ : typeof reference.value === 'string'
60
+ ? reference.value
61
+ : null
62
+
63
+ if (!slug) return null
64
+
65
+ const basePath =
66
+ reference.relationTo === 'pages' ? '' : `/${getCollectionPath(reference.relationTo)}`
67
+
68
+ return `${basePath}/${slug}`
69
+ }
70
+
71
+ return url || null
72
+ }
73
+
74
+ const LinkWrapper: React.FC<
75
+ React.PropsWithChildren<{
76
+ href: string
77
+ newTab?: boolean
78
+ onClick?: () => void
79
+ className?: string
80
+ closeDrawerOnClick?: boolean
81
+ }>
82
+ > = ({ href, newTab, onClick, className, children, closeDrawerOnClick = true }) => {
83
+ const closeDrawer = useDrawerClose?.() || (() => {})
84
+ const pathname = usePathname()
85
+
86
+ const hashIndex = href.indexOf('#')
87
+ const hasHash = hashIndex >= 0
88
+ const basePath = hasHash ? href.slice(0, hashIndex) : href
89
+ const hash = hasHash ? href.slice(hashIndex) : ''
90
+
91
+ const normalise = (p?: string) => (p ? p.replace(/\/$/, '') : '')
92
+ const isSamePageHash = hasHash && (basePath === '' || normalise(basePath) === normalise(pathname))
93
+
94
+ const handleClick = (e: React.MouseEvent) => {
95
+ onClick?.()
96
+
97
+ if (isSamePageHash && hash) {
98
+ e.preventDefault()
99
+
100
+ const targetId = hash.slice(1)
101
+ const el = document.getElementById(targetId)
102
+
103
+ if (el) {
104
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' })
105
+ window.history.pushState(null, '', href)
106
+ }
107
+
108
+ if (closeDrawerOnClick) {
109
+ closeDrawer()
110
+ }
111
+
112
+ return
113
+ }
114
+
115
+ if (closeDrawerOnClick && !newTab) {
116
+ closeDrawer()
117
+ }
118
+ }
119
+
120
+ const newTabProps = newTab ? { rel: 'noopener noreferrer', target: '_blank' } : {}
121
+
122
+ return (
123
+ <Link href={href} {...newTabProps} onClick={handleClick} className={className} prefetch>
124
+ {children}
125
+ </Link>
126
+ )
127
+ }
128
+ function GlassChip() {
129
+ return (
130
+ <div className="absolute w-full h-full group mx-auto left-0 right-0 rounded-full squircle overflow-hidden">
131
+ <div
132
+ className="
133
+ absolute inset-0 mx-auto
134
+ flex items-center justify-center
135
+ overflow-hidden
136
+ rounded-full
137
+ border-t border-b border-white/10
138
+ bg-white/[0.05]
139
+ text-white
140
+ text-[18px]
141
+ font-bold
142
+ tracking-[1px]
143
+ backdrop-blur-[15px]
144
+ shadow-[0_15px_35px_rgba(255,255,255,0.15)]
145
+ transition-all duration-500
146
+ hover:tracking-[2px]
147
+
148
+ before:absolute
149
+ before:left-0
150
+ before:top-0
151
+ before:h-full
152
+ before:w-1/2
153
+ before:skew-x-[45deg]
154
+ before:bg-[linear-gradient(to_left,rgba(255,255,255,0.22),transparent)]
155
+ before:transition-transform
156
+ before:duration-700
157
+ hover:before:translate-x-[220%]
158
+
159
+ after:absolute
160
+ after:inset-[1px]
161
+ after:rounded-full
162
+ after:border
163
+ after:border-white/10
164
+ after:shadow-[inset_0_1px_0_rgba(255,255,255,0.45),inset_0_-8px_18px_rgba(0,0,0,0.25)]
165
+ "
166
+ ></div>
167
+
168
+ <div
169
+ className="
170
+ absolute left-1/2 -translate-x-1/2
171
+ -top-[3px]
172
+ h-[8px] w-[30px]
173
+ rounded-full
174
+ bg-[#ba55d3]
175
+ shadow-[0_0_5px_#663399,0_0_15px_#ba55d3,0_0_30px_#663399,0_0_60px_#ff00ff]
176
+ transition-all duration-500
177
+ group-hover:top-0
178
+ group-hover:bg-[#ba55d355]
179
+ group-hover:h-full
180
+ group-hover:w-[80%]
181
+ group-hover:rounded-full
182
+ group-hover:delay-300
183
+ "
184
+ />
185
+
186
+ <div
187
+ className="
188
+ absolute left-1/2 -translate-x-1/2
189
+ -bottom-[3px]
190
+ h-[8px] w-[30px]
191
+ rounded-full
192
+ bg-[#ba55d3]
193
+ shadow-[0_0_5px_#663399,0_0_15px_#ba55d3,0_0_30px_#663399,0_0_60px_#ff00ff]
194
+ transition-all duration-500
195
+ group-hover:bottom-0
196
+ group-hover:bg-[#ba55d399]
197
+ group-hover:h-full
198
+ group-hover:brightness-150
199
+ group-hover:w-[80%]
200
+ group-hover:rounded-full
201
+ group-hover:delay-300
202
+ "
203
+ />
204
+ </div>
205
+ )
206
+ }
207
+ function EyeCatchingButton_v1(props: ButtonProps & { wrapperClassName?: string }) {
208
+ const { wrapperClassName, size = 'default', children, ...buttonProps } = props
209
+
210
+ const sizeClasses = {
211
+ sm: 'h-8 w-fit',
212
+ default: 'h-10 w-40',
213
+ lg: 'h-12 w-48',
214
+ icon: 'h-10 w-10',
215
+ clear: '',
216
+ }
217
+
218
+ return (
219
+ <div
220
+ className={cn(
221
+ sizeClasses[size || 'default'],
222
+ wrapperClassName,
223
+ 'relative min-w-fit overflow-hidden rounded-full p-0.5 font-semibold group',
224
+ 'border-1 border-stone-800/20',
225
+ 'bg-white dark:!bg-zinc-900',
226
+ 'shadow',
227
+ 'drop-shadow-[2px_4px_6px_rgba(0,0,0,0.25)]',
228
+ 'transition-all duration-300 hover:duration-300',
229
+ 'hover:-translate-y-0.5 hover:scale-[1]',
230
+ 'hover:!bg-[rgba(102,51,153,1)] hover:border-0 hover:border-[#985fd023] hover:drop-shadow-[inset_0px_0px_6px_rgba(102,51,153,1)]',
231
+ )}
232
+ >
233
+ <GlassChip />
234
+
235
+ <span className="absolute inset-[-1000%] animate-[spin_5s_linear_infinite] dark:bg-[conic-gradient(from_90deg_at_50%_50%,#fff_0%,#4a4a4a_7%)] bg-[conic-gradient(from_90deg_at_50%_50%,#000_0%,#fff_5%)] group-hover:bg-none rounded-full overflow-hidden blur-[4px] brightness-[1.2]" />
236
+
237
+ <Button
238
+ {...buttonProps}
239
+ className={cn(
240
+ buttonProps.className,
241
+ 'h-full w-full rounded-full !bg-white hover:!bg-white !text-zinc-950 dark:!bg-zinc-900 dark:hover:!bg-zinc-900 dark:!text-white backdrop-blur-xl flex items-center justify-center gap-2',
242
+ 'border-2 border-ridge border-[#938eab8c]',
243
+ 'group-hover:shadow-[inset_0_1px_6px_rgba(255,255,255,0.28),inset_0_-8px_18px_rgba(0,0,0,0.22)]',
244
+ 'group-active:translate-y-[1px] group-active:scale-[0.985]',
245
+ // 'group-active:bg-[rgba(74,35,120,0.95)]',
246
+ 'group-active:shadow-[inset_0_3px_10px_rgba(0,0,0,0.45),inset_0_1px_2px_rgba(255,255,255,0.18)]',
247
+ 'group-active:duration-75',
248
+ )}
249
+ >
250
+ {children}
251
+ </Button>
252
+ </div>
253
+ )
254
+ }
255
+
256
+ function EyeCatchingButton_v2(props: ButtonProps & { wrapperClassName?: string }) {
257
+ const { wrapperClassName, size = 'default', children, ...buttonProps } = props
258
+
259
+ const sizeClasses = {
260
+ sm: 'h-8',
261
+ default: 'h-10',
262
+ lg: 'h-12',
263
+ icon: 'h-10 w-10',
264
+ clear: '',
265
+ }
266
+
267
+ return (
268
+ <div
269
+ className={cn(
270
+ 'eyecatchingbtn-2',
271
+ sizeClasses[size || 'default'],
272
+ wrapperClassName,
273
+ 'relative overflow-hidden rounded-full dark:bg-card-purple bg-violet-600 shadow border dark:border-stone-900/50 group border-stone-400 p-0.5 min-w-fit hover:bg-violet-600 hover:brightness-110 duration-300 hover:duration-300 transition-all font-semibold',
274
+ 'transition-all duration-300 ease-out',
275
+ 'hover:-translate-y-0.5 hover:scale-[1]',
276
+ 'hover:border-[#985fd066] hover:shadow-[0_0_22px_rgba(152,95,208,0.32)] hover:drop-shadow-[0_8px_18px_rgba(102,51,153,0.35)]',
277
+ 'active:translate-y-0 active:scale-[0.975]',
278
+ 'active:shadow-[inset_0_2px_10px_rgba(0,0,0,0.35),0_0_10px_rgba(152,95,208,0.25)]',
279
+ 'active:duration-75',
280
+ )}
281
+ >
282
+ <GlassChip />
283
+ <span className="absolute inset-[-1000%] animate-[spin_5s_linear_infinite] dark:bg-[conic-gradient(from_90deg_at_50%_50%,#fff_0%,#4a4a4a_7%)] bg-[conic-gradient(from_90deg_at_50%_50%,#000_0%,#fff_5%)] group-hover:bg-none rounded-full overflow-hidden blur-[4px] brightness-[1.2]" />
284
+ <Button
285
+ {...buttonProps}
286
+ className={cn(
287
+ buttonProps.className,
288
+ 'h-full w-full rounded-full !bg-violet-700 hover:!bg-violet-700 !text-white backdrop-blur-xl dark:!bg-zinc-900 dark:hover:!bg-zinc-900 dark:!text-white flex items-center justify-center gap-2',
289
+ )}
290
+ >
291
+ {children}
292
+ </Button>
293
+ </div>
294
+ )
295
+ }
296
+
297
+ function EyeCatchingButton_v3(props: ButtonProps & { wrapperClassName?: string }) {
298
+ const { wrapperClassName, size = 'default', children, ...buttonProps } = props
299
+
300
+ const sizeClasses = {
301
+ sm: 'h-8 w-fit',
302
+ default: 'h-10 w-40',
303
+ lg: 'h-12 w-48',
304
+ icon: 'h-10 w-10',
305
+ clear: '',
306
+ }
307
+
308
+ return (
309
+ <div
310
+ className={cn(
311
+ sizeClasses[size || 'default'],
312
+ wrapperClassName,
313
+ 'relative overflow-hidden rounded-full dark:bg-card-purple bg-violet-600 shadow border dark:border-[rgba(132,43,222,0.5)] group border-[rgba(132,43,222,0.5)] p-0.5 min-w-fit hover:bg-violet-600 hover:brightness-110 duration-300 hover:duration-300 transition-all font-semibold drop-shadow-xl',
314
+ // 'relative min-w-fit overflow-hidden rounded-full p-0.5 font-semibold group',
315
+ // 'border-2 border-[rgba(132,43,222,0.915)]',
316
+ // '!bg-[rgba(102,51,153,0.551)]',
317
+ // 'drop-shadow-[2px_4px_6px_rgba(0,0,0,0.25)]',
318
+ // 'transition-all duration-300 hover:duration-300 ease-in-out',
319
+ // 'hover:!bg-[rgba(102,51,153,0.842)] hover:!mix-blend-normal hover:brightness-110'
320
+ )}
321
+ >
322
+ <span className="absolute inset-[-1000%] animate-[spin_5s_linear_infinite_reverse] dark:bg-[conic-gradient(from_90deg_at_50%_50%,#fff_0%,#4a4a4a_7%)] bg-[conic-gradient(from_90deg_at_50%_50%,#000_0%,#fff_5%)] group-hover:bg-none blur-[4px] brightness-[1.2]" />
323
+ <Button
324
+ {...buttonProps}
325
+ className={cn(
326
+ buttonProps.className,
327
+ 'h-full w-full rounded-full !bg-zinc-200 hover:!bg-zinc-300 !text-zinc-950 dark:!bg-zinc-200 dark:hover:!bg-zinc-300 dark:!text-zinc-950 backdrop-blur-xl flex items-center justify-center gap-2 drop-shadow-md',
328
+ )}
329
+ >
330
+ {children}
331
+ </Button>
332
+ </div>
333
+ )
334
+ }
335
+
336
+ function EyeCatchingButton_v4(props: ButtonProps & { wrapperClassName?: string }) {
337
+ const { wrapperClassName, size = 'default', children, ...buttonProps } = props
338
+
339
+ const sizeClasses = {
340
+ sm: 'h-8',
341
+ default: 'h-10',
342
+ lg: 'h-12',
343
+ icon: 'h-10 w-10',
344
+ clear: '',
345
+ }
346
+
347
+ return (
348
+ <div
349
+ className={cn(
350
+ 'relative !rounded-full min-w-fit inline-flex items-center gap-1',
351
+ sizeClasses[size || 'default'],
352
+ wrapperClassName,
353
+ )}
354
+ >
355
+ <Button
356
+ {...buttonProps}
357
+ className={cn(
358
+ 'w-full animate-bg-shine border-[1px] rounded-full !bg-white hover:!bg-zinc-100 shadow-xl bg-[length:200%_100%] tracking-wide flex items-center justify-center gap-2',
359
+ 'dark:!bg-zinc-900 dark:hover:!bg-zinc-800 dark:!text-zinc-100 dark:border-stone-800 font-semibold',
360
+ '!text-zinc-900 border-stone-300',
361
+ 'hover:border-violet-500 hover:outline hover:outline-2 hover:outline-offset-1 hover:outline-violet-600',
362
+ buttonProps.className,
363
+ )}
364
+ >
365
+ {children}
366
+ </Button>
367
+ </div>
368
+ )
369
+ }
370
+
371
+ function EyeCatchingButton_v5(props: ButtonProps & { wrapperClassName?: string }) {
372
+ const { wrapperClassName, size = 'default', children, ...buttonProps } = props
373
+
374
+ const sizeClasses = {
375
+ sm: 'h-8',
376
+ default: 'h-10',
377
+ lg: 'h-12',
378
+ icon: 'h-10 w-10',
379
+ clear: '',
380
+ }
381
+
382
+ return (
383
+ <div
384
+ className={cn(
385
+ 'relative !rounded-full min-w-fit inline-flex items-center gap-1',
386
+ sizeClasses[size || 'default'],
387
+ wrapperClassName,
388
+ )}
389
+ >
390
+ <Button
391
+ {...buttonProps}
392
+ className={cn(
393
+ 'w-full border border-stone-800 rounded-full shadow-xl tracking-wide flex items-center justify-center gap-2',
394
+ '!bg-white hover:!bg-zinc-100 bg-[length:200%_100%] animate-bg-shine dark:!bg-white dark:hover:!bg-zinc-100',
395
+ '!text-stone-900 hover:!text-stone-900 font-semibold',
396
+ 'hover:border-violet-500 hover:outline hover:outline-2 hover:outline-offset-1 hover:outline-violet-600',
397
+ buttonProps.className,
398
+ )}
399
+ >
400
+ {children}
401
+ </Button>
402
+ </div>
403
+ )
404
+ }
405
+
406
+ function EyeCatchingButton_v6(props: ButtonProps & { wrapperClassName?: string }) {
407
+ const { wrapperClassName, size = 'default', children, ...buttonProps } = props
408
+
409
+ const sizeClasses = {
410
+ sm: 'h-8',
411
+ default: 'h-10',
412
+ lg: 'h-12',
413
+ icon: 'h-10 w-10',
414
+ clear: '',
415
+ }
416
+
417
+ return (
418
+ <div
419
+ className={cn(
420
+ 'relative !rounded-full min-w-fit inline-flex items-center gap-1',
421
+ sizeClasses[size || 'default'],
422
+ wrapperClassName,
423
+ )}
424
+ >
425
+ <Button
426
+ {...buttonProps}
427
+ className={cn(
428
+ 'w-full border border-stone-800 rounded-full shadow-xl tracking-wide flex items-center justify-center gap-2',
429
+ 'bg-[radial-gradient(circle,_rgba(190,_242,_100,_1),_rgba(132,_204,_22,_0.95))] bg-[length:200%_100%] animate-bg-shine',
430
+ '!text-stone-900 hover:!text-stone-900 font-semibold',
431
+ 'hover:border-violet-500 hover:outline hover:outline-2 hover:outline-offset-1 hover:outline-violet-600',
432
+ buttonProps.className,
433
+ )}
434
+ >
435
+ {children}
436
+ </Button>
437
+ </div>
438
+ )
439
+ }
440
+
441
+ function EyeCatchingButton_v7(props: ButtonProps & { wrapperClassName?: string }) {
442
+ const { wrapperClassName, size = 'default', children, ...buttonProps } = props
443
+
444
+ const sizeClasses = {
445
+ sm: 'text-sm',
446
+ default: 'text-base',
447
+ lg: 'text-lg',
448
+ icon: 'text-base',
449
+ clear: '',
450
+ }
451
+
452
+ return (
453
+ <div className={cn('relative inline-flex items-center min-w-fit', wrapperClassName)}>
454
+ <Button
455
+ {...buttonProps}
456
+ variant="ghost"
457
+ className={cn(
458
+ 'p-0 h-auto bg-transparent shadow-none border-0 rounded-none',
459
+ 'tracking-wide underline-none hover:brightness-110',
460
+ 'text-zinc-800 dark:text-zinc-200 font-semibold',
461
+ 'hover:text-violet-700 dark:hover:text-violet-300 transition-colors duration-200',
462
+ sizeClasses[size || 'default'],
463
+ buttonProps.className,
464
+ )}
465
+ >
466
+ {children}
467
+ </Button>
468
+ </div>
469
+ )
470
+ }
471
+
472
+ function renderVariant(
473
+ variant: NonNullable<ButtonsEyeCatchingProps['variant']>,
474
+ props: ButtonProps & { wrapperClassName?: string },
475
+ ) {
476
+ switch (variant) {
477
+ case 'v1':
478
+ return <EyeCatchingButton_v1 {...props} />
479
+ case 'v2':
480
+ return <EyeCatchingButton_v2 {...props} />
481
+ case 'v3':
482
+ return <EyeCatchingButton_v3 {...props} />
483
+ case 'v4':
484
+ return <EyeCatchingButton_v4 {...props} />
485
+ case 'v5':
486
+ return <EyeCatchingButton_v5 {...props} />
487
+ case 'v6':
488
+ return <EyeCatchingButton_v6 {...props} />
489
+ case 'v7':
490
+ return <EyeCatchingButton_v7 {...props} />
491
+ default:
492
+ return <EyeCatchingButton_v1 {...props} />
493
+ }
494
+ }
495
+
496
+ export const ButtonsEyeCatching = ({
497
+ variant = 'v1',
498
+ label,
499
+ displayMode = 'iconWithLabel',
500
+ iconPosition = 'before',
501
+ iconName,
502
+ type: linkType,
503
+ reference,
504
+ url,
505
+ newTab,
506
+ size,
507
+ className,
508
+ wrapperClassName,
509
+ fullWidth,
510
+ disabled,
511
+ closeDrawerOnClick = true,
512
+ onClick,
513
+ onMouseEnter,
514
+ onSubmit,
515
+ form,
516
+ htmlType,
517
+ }: ButtonsEyeCatchingProps) => {
518
+ const closeDrawer = useDrawerClose?.() || (() => {})
519
+ const router = useRouter()
520
+ const pathname = usePathname()
521
+
522
+ const href = getHref({ type: linkType, reference, url, label })
523
+
524
+ const IconComponent =
525
+ iconName && LucideIcons[iconName as keyof typeof LucideIcons]
526
+ ? (LucideIcons[iconName as keyof typeof LucideIcons] as React.ComponentType<{
527
+ className?: string
528
+ }>)
529
+ : null
530
+
531
+ const hashIndex = href?.indexOf('#') ?? -1
532
+ const hasHash = hashIndex >= 0
533
+ const basePath = hasHash && href ? href.slice(0, hashIndex) : href
534
+ const hash = hasHash && href ? href.slice(hashIndex) : ''
535
+
536
+ const normalise = (p?: string) => (p ? p.replace(/\/$/, '') : '')
537
+ const isSamePageHash =
538
+ hasHash && (basePath === '' || normalise(basePath ?? undefined) === normalise(pathname))
539
+
540
+ const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
541
+ if (onClick) {
542
+ onClick(e)
543
+ if (e.defaultPrevented) return
544
+ }
545
+
546
+ if (isSamePageHash && hash) {
547
+ e.preventDefault()
548
+
549
+ const targetId = hash.slice(1)
550
+ const el = document.getElementById(targetId)
551
+
552
+ if (el) {
553
+ el.scrollIntoView({ behavior: 'smooth', block: 'start' })
554
+
555
+ if (href) {
556
+ window.history.pushState(null, '', href)
557
+ }
558
+ }
559
+
560
+ if (closeDrawerOnClick) {
561
+ closeDrawer()
562
+ }
563
+
564
+ return
565
+ }
566
+
567
+ if (closeDrawerOnClick && !newTab && href) {
568
+ closeDrawer()
569
+ }
570
+
571
+ if (href) {
572
+ e.preventDefault()
573
+ router.push(href)
574
+ }
575
+ }
576
+
577
+ const iconElement =
578
+ IconComponent && displayMode !== 'labelOnly' ? (
579
+ <IconComponent
580
+ className={cn('h-4 w-4', size === 'sm' && 'h-3 w-3', size === 'lg' && 'h-5 w-5')}
581
+ />
582
+ ) : null
583
+
584
+ let buttonChildren: React.ReactNode
585
+
586
+ if (displayMode === 'iconOnly') {
587
+ buttonChildren = iconElement
588
+ } else if (displayMode === 'labelOnly') {
589
+ buttonChildren = label
590
+ } else {
591
+ buttonChildren =
592
+ iconPosition === 'before' ? (
593
+ <>
594
+ {iconElement}
595
+ {label}
596
+ </>
597
+ ) : (
598
+ <>
599
+ {label}
600
+ {iconElement}
601
+ </>
602
+ )
603
+ }
604
+
605
+ const btn = renderVariant(variant, {
606
+ size,
607
+ disabled,
608
+ className: cn(fullWidth ? 'w-full' : undefined, className),
609
+ wrapperClassName,
610
+ children: buttonChildren,
611
+ onClick: handleClick,
612
+ onMouseEnter,
613
+ onSubmit,
614
+ form,
615
+ type: htmlType,
616
+ })
617
+
618
+ if (href && newTab) {
619
+ return (
620
+ <LinkWrapper href={href} newTab={newTab} closeDrawerOnClick={closeDrawerOnClick}>
621
+ {btn}
622
+ </LinkWrapper>
623
+ )
624
+ }
625
+
626
+ return btn
627
+ }