@opensite/ui 2.8.5 → 2.8.7

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 (141) hide show
  1. package/dist/about-developer-profile.cjs +17 -10
  2. package/dist/about-developer-profile.js +17 -10
  3. package/dist/article-chapters-author.cjs +17 -10
  4. package/dist/article-chapters-author.js +17 -10
  5. package/dist/carousel-animated-sections.cjs +79 -25
  6. package/dist/carousel-animated-sections.d.cts +7 -2
  7. package/dist/carousel-animated-sections.d.ts +7 -2
  8. package/dist/carousel-animated-sections.js +79 -25
  9. package/dist/carousel-gallery-thumbnails.cjs +79 -25
  10. package/dist/carousel-gallery-thumbnails.d.cts +7 -2
  11. package/dist/carousel-gallery-thumbnails.d.ts +7 -2
  12. package/dist/carousel-gallery-thumbnails.js +79 -25
  13. package/dist/carousel-portfolio-hero.cjs +79 -25
  14. package/dist/carousel-portfolio-hero.d.cts +7 -2
  15. package/dist/carousel-portfolio-hero.d.ts +7 -2
  16. package/dist/carousel-portfolio-hero.js +79 -25
  17. package/dist/components.cjs +35 -10
  18. package/dist/components.d.cts +29 -2
  19. package/dist/components.d.ts +29 -2
  20. package/dist/components.js +35 -11
  21. package/dist/footer-accordion-social.cjs +17 -10
  22. package/dist/footer-accordion-social.js +17 -10
  23. package/dist/footer-animated-social.cjs +17 -10
  24. package/dist/footer-animated-social.js +17 -10
  25. package/dist/footer-brand-description.cjs +17 -10
  26. package/dist/footer-brand-description.js +17 -10
  27. package/dist/footer-brand-links-contact.cjs +17 -10
  28. package/dist/footer-brand-links-contact.js +17 -10
  29. package/dist/footer-comprehensive-links.cjs +17 -10
  30. package/dist/footer-comprehensive-links.js +17 -10
  31. package/dist/footer-contact-card.cjs +17 -10
  32. package/dist/footer-contact-card.js +17 -10
  33. package/dist/footer-cta-banner.cjs +17 -10
  34. package/dist/footer-cta-banner.js +17 -10
  35. package/dist/footer-cta-social.cjs +17 -10
  36. package/dist/footer-cta-social.js +17 -10
  37. package/dist/footer-info-cards-accordion.cjs +17 -10
  38. package/dist/footer-info-cards-accordion.js +17 -10
  39. package/dist/footer-nav-social.cjs +17 -10
  40. package/dist/footer-nav-social.js +17 -10
  41. package/dist/footer-newsletter-contact.cjs +17 -10
  42. package/dist/footer-newsletter-contact.js +17 -10
  43. package/dist/footer-newsletter-grid.cjs +17 -10
  44. package/dist/footer-newsletter-grid.js +17 -10
  45. package/dist/footer-newsletter-minimal.cjs +17 -10
  46. package/dist/footer-newsletter-minimal.js +17 -10
  47. package/dist/footer-social-apps.cjs +17 -10
  48. package/dist/footer-social-apps.js +17 -10
  49. package/dist/footer-social-newsletter.cjs +17 -10
  50. package/dist/footer-social-newsletter.js +17 -10
  51. package/dist/footer-split-image-accordion.cjs +17 -10
  52. package/dist/footer-split-image-accordion.js +17 -10
  53. package/dist/hero-badge-shadow-overlay.cjs +1 -1
  54. package/dist/hero-badge-shadow-overlay.js +1 -1
  55. package/dist/hero-coming-soon-countdown.cjs +17 -10
  56. package/dist/hero-coming-soon-countdown.js +17 -10
  57. package/dist/hero-video-background-dark.cjs +78 -16
  58. package/dist/hero-video-background-dark.d.cts +7 -2
  59. package/dist/hero-video-background-dark.d.ts +7 -2
  60. package/dist/hero-video-background-dark.js +78 -16
  61. package/dist/index.cjs +35 -10
  62. package/dist/index.d.cts +1 -0
  63. package/dist/index.d.ts +1 -0
  64. package/dist/index.js +35 -11
  65. package/dist/link-page-bento-layout.cjs +17 -10
  66. package/dist/link-page-bento-layout.js +17 -10
  67. package/dist/link-page-grid-cards.cjs +17 -10
  68. package/dist/link-page-grid-cards.js +17 -10
  69. package/dist/link-page-minimal-profile.cjs +17 -10
  70. package/dist/link-page-minimal-profile.js +17 -10
  71. package/dist/link-page-newsletter-social.cjs +17 -10
  72. package/dist/link-page-newsletter-social.js +17 -10
  73. package/dist/link-tree-block.cjs +17 -10
  74. package/dist/link-tree-block.js +17 -10
  75. package/dist/navbar-fullscreen-menu.cjs +17 -10
  76. package/dist/navbar-fullscreen-menu.js +17 -10
  77. package/dist/navbar-transparent-overlay.cjs +17 -10
  78. package/dist/navbar-transparent-overlay.js +17 -10
  79. package/dist/registry.cjs +1398 -851
  80. package/dist/registry.js +1398 -851
  81. package/dist/social-link-icon.cjs +17 -10
  82. package/dist/social-link-icon.d.cts +5 -0
  83. package/dist/social-link-icon.d.ts +5 -0
  84. package/dist/social-link-icon.js +17 -10
  85. package/dist/testimonials-animated-split.cjs +5 -5
  86. package/dist/testimonials-animated-split.js +5 -5
  87. package/dist/testimonials-bento-grid.cjs +48 -56
  88. package/dist/testimonials-bento-grid.js +48 -56
  89. package/dist/testimonials-carousel-image.cjs +608 -96
  90. package/dist/testimonials-carousel-image.d.cts +26 -2
  91. package/dist/testimonials-carousel-image.d.ts +26 -2
  92. package/dist/testimonials-carousel-image.js +609 -97
  93. package/dist/testimonials-centered-avatars.cjs +89 -62
  94. package/dist/testimonials-centered-avatars.d.cts +5 -1
  95. package/dist/testimonials-centered-avatars.d.ts +5 -1
  96. package/dist/testimonials-centered-avatars.js +89 -62
  97. package/dist/testimonials-company-logo.cjs +6 -6
  98. package/dist/testimonials-company-logo.js +6 -6
  99. package/dist/testimonials-grid-add-review.cjs +51 -29
  100. package/dist/testimonials-grid-add-review.js +51 -29
  101. package/dist/testimonials-images-helpful.cjs +181 -160
  102. package/dist/testimonials-images-helpful.d.cts +9 -1
  103. package/dist/testimonials-images-helpful.d.ts +9 -1
  104. package/dist/testimonials-images-helpful.js +181 -159
  105. package/dist/testimonials-list-verified.cjs +63 -44
  106. package/dist/testimonials-list-verified.d.cts +5 -1
  107. package/dist/testimonials-list-verified.d.ts +5 -1
  108. package/dist/testimonials-list-verified.js +64 -45
  109. package/dist/testimonials-logo-cards.cjs +53 -29
  110. package/dist/testimonials-logo-cards.d.cts +5 -1
  111. package/dist/testimonials-logo-cards.d.ts +5 -1
  112. package/dist/testimonials-logo-cards.js +53 -29
  113. package/dist/testimonials-marquee.cjs +524 -102
  114. package/dist/testimonials-marquee.d.cts +5 -1
  115. package/dist/testimonials-marquee.d.ts +5 -1
  116. package/dist/testimonials-marquee.js +525 -100
  117. package/dist/testimonials-mini-dividers.cjs +120 -83
  118. package/dist/testimonials-mini-dividers.d.cts +10 -6
  119. package/dist/testimonials-mini-dividers.d.ts +10 -6
  120. package/dist/testimonials-mini-dividers.js +120 -83
  121. package/dist/testimonials-minimal-numbered.cjs +7 -6
  122. package/dist/testimonials-minimal-numbered.d.cts +5 -1
  123. package/dist/testimonials-minimal-numbered.d.ts +5 -1
  124. package/dist/testimonials-minimal-numbered.js +7 -6
  125. package/dist/testimonials-parallax-number.cjs +14 -8
  126. package/dist/testimonials-parallax-number.js +14 -8
  127. package/dist/testimonials-scrolling-columns.cjs +97 -13
  128. package/dist/testimonials-scrolling-columns.js +97 -13
  129. package/dist/testimonials-simple-grid.cjs +513 -68
  130. package/dist/testimonials-simple-grid.d.cts +9 -5
  131. package/dist/testimonials-simple-grid.d.ts +9 -5
  132. package/dist/testimonials-simple-grid.js +510 -62
  133. package/dist/testimonials-slider-minimal.cjs +604 -90
  134. package/dist/testimonials-slider-minimal.d.cts +17 -1
  135. package/dist/testimonials-slider-minimal.d.ts +17 -1
  136. package/dist/testimonials-slider-minimal.js +598 -81
  137. package/dist/testimonials-split-image.cjs +452 -17
  138. package/dist/testimonials-split-image.js +448 -13
  139. package/dist/testimonials-twitter-cards.cjs +26 -14
  140. package/dist/testimonials-twitter-cards.js +26 -14
  141. package/package.json +11 -1
@@ -1,14 +1,444 @@
1
1
  "use client";
2
- import React, { useState, useCallback, useEffect, useMemo } from 'react';
2
+ import * as React2 from 'react';
3
+ import React2__default, { useState, useCallback, useEffect, useMemo } from 'react';
4
+ import { AnimatePresence, motion } from 'framer-motion';
3
5
  import { clsx } from 'clsx';
4
6
  import { twMerge } from 'tailwind-merge';
5
- import * as AvatarPrimitive from '@radix-ui/react-avatar';
7
+ import { Icon } from '@page-speed/icon';
6
8
  import { jsx, jsxs } from 'react/jsx-runtime';
9
+ import { cva } from 'class-variance-authority';
10
+ import * as AvatarPrimitive from '@radix-ui/react-avatar';
7
11
 
8
12
  // components/blocks/testimonials/testimonials-slider-minimal.tsx
9
13
  function cn(...inputs) {
10
14
  return twMerge(clsx(inputs));
11
15
  }
16
+ var DEFAULT_ICON_API_KEY = "au382bi7fsh96w9h9xlrnat2jglx";
17
+ var DynamicIcon = React2.memo(function DynamicIcon2({
18
+ apiKey,
19
+ ...props
20
+ }) {
21
+ return /* @__PURE__ */ jsx(Icon, { ...props, apiKey: apiKey ?? DEFAULT_ICON_API_KEY });
22
+ });
23
+ DynamicIcon.displayName = "DynamicIcon";
24
+ function normalizePhoneNumber(input) {
25
+ const trimmed = input.trim();
26
+ if (trimmed.toLowerCase().startsWith("tel:")) {
27
+ return trimmed;
28
+ }
29
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
30
+ if (match) {
31
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
32
+ const extension = match[3];
33
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
34
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
35
+ return `tel:${withExtension}`;
36
+ }
37
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
38
+ return `tel:${cleaned}`;
39
+ }
40
+ function normalizeEmail(input) {
41
+ const trimmed = input.trim();
42
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
43
+ return trimmed;
44
+ }
45
+ return `mailto:${trimmed}`;
46
+ }
47
+ function isEmail(input) {
48
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
49
+ return emailRegex.test(input.trim());
50
+ }
51
+ function isPhoneNumber(input) {
52
+ const trimmed = input.trim();
53
+ if (trimmed.toLowerCase().startsWith("tel:")) {
54
+ return true;
55
+ }
56
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
57
+ return phoneRegex.test(trimmed);
58
+ }
59
+ function isInternalUrl(href) {
60
+ if (typeof window === "undefined") {
61
+ return href.startsWith("/") && !href.startsWith("//");
62
+ }
63
+ const trimmed = href.trim();
64
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
65
+ return true;
66
+ }
67
+ try {
68
+ const url = new URL(trimmed, window.location.href);
69
+ const currentOrigin = window.location.origin;
70
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
71
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ function toRelativePath(href) {
77
+ if (typeof window === "undefined") {
78
+ return href;
79
+ }
80
+ const trimmed = href.trim();
81
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
82
+ return trimmed;
83
+ }
84
+ try {
85
+ const url = new URL(trimmed, window.location.href);
86
+ const currentOrigin = window.location.origin;
87
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
88
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
89
+ return url.pathname + url.search + url.hash;
90
+ }
91
+ } catch {
92
+ }
93
+ return trimmed;
94
+ }
95
+ function useNavigation({
96
+ href,
97
+ onClick
98
+ } = {}) {
99
+ const linkType = React2.useMemo(() => {
100
+ if (!href || href.trim() === "") {
101
+ return onClick ? "none" : "none";
102
+ }
103
+ const trimmed = href.trim();
104
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
105
+ return "mailto";
106
+ }
107
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
108
+ return "tel";
109
+ }
110
+ if (isInternalUrl(trimmed)) {
111
+ return "internal";
112
+ }
113
+ try {
114
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
115
+ return "external";
116
+ } catch {
117
+ return "internal";
118
+ }
119
+ }, [href, onClick]);
120
+ const normalizedHref = React2.useMemo(() => {
121
+ if (!href || href.trim() === "") {
122
+ return void 0;
123
+ }
124
+ const trimmed = href.trim();
125
+ switch (linkType) {
126
+ case "tel":
127
+ return normalizePhoneNumber(trimmed);
128
+ case "mailto":
129
+ return normalizeEmail(trimmed);
130
+ case "internal":
131
+ return toRelativePath(trimmed);
132
+ case "external":
133
+ return trimmed;
134
+ default:
135
+ return trimmed;
136
+ }
137
+ }, [href, linkType]);
138
+ const target = React2.useMemo(() => {
139
+ switch (linkType) {
140
+ case "external":
141
+ return "_blank";
142
+ case "internal":
143
+ return "_self";
144
+ case "mailto":
145
+ case "tel":
146
+ return void 0;
147
+ default:
148
+ return void 0;
149
+ }
150
+ }, [linkType]);
151
+ const rel = React2.useMemo(() => {
152
+ if (linkType === "external") {
153
+ return "noopener noreferrer";
154
+ }
155
+ return void 0;
156
+ }, [linkType]);
157
+ const isExternal = linkType === "external";
158
+ const isInternal = linkType === "internal";
159
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
160
+ const handleClick = React2.useCallback(
161
+ (event) => {
162
+ if (onClick) {
163
+ try {
164
+ onClick(event);
165
+ } catch (error) {
166
+ console.error("Error in user onClick handler:", error);
167
+ }
168
+ }
169
+ if (event.defaultPrevented) {
170
+ return;
171
+ }
172
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
173
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
174
+ if (typeof window !== "undefined") {
175
+ const handler = window.__opensiteNavigationHandler;
176
+ if (typeof handler === "function") {
177
+ try {
178
+ const handled = handler(normalizedHref, event.nativeEvent || event);
179
+ if (handled !== false) {
180
+ event.preventDefault();
181
+ }
182
+ } catch (error) {
183
+ console.error("Error in navigation handler:", error);
184
+ }
185
+ }
186
+ }
187
+ }
188
+ },
189
+ [onClick, shouldUseRouter, normalizedHref]
190
+ );
191
+ return {
192
+ linkType,
193
+ normalizedHref,
194
+ target,
195
+ rel,
196
+ isExternal,
197
+ isInternal,
198
+ shouldUseRouter,
199
+ handleClick
200
+ };
201
+ }
202
+ var baseStyles = [
203
+ // Layout
204
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
205
+ // Typography - using CSS variables with sensible defaults
206
+ "font-[var(--button-font-family,inherit)]",
207
+ "font-[var(--button-font-weight,500)]",
208
+ "tracking-[var(--button-letter-spacing,0)]",
209
+ "leading-[var(--button-line-height,1.25)]",
210
+ "[text-transform:var(--button-text-transform,none)]",
211
+ "text-sm",
212
+ // Border radius
213
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
214
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
215
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
216
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
217
+ "[box-shadow:var(--button-shadow,none)]",
218
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
219
+ // Disabled state
220
+ "disabled:pointer-events-none disabled:opacity-50",
221
+ // SVG handling
222
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
223
+ // Focus styles
224
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
225
+ // Invalid state
226
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
227
+ ].join(" ");
228
+ var buttonVariants = cva(baseStyles, {
229
+ variants: {
230
+ variant: {
231
+ // Default (Primary) variant - full customization
232
+ default: [
233
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
234
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
235
+ "border-[length:var(--button-default-border-width,0px)]",
236
+ "border-[color:var(--button-default-border,transparent)]",
237
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
238
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
239
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
240
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
241
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
242
+ ].join(" "),
243
+ // Destructive variant - full customization
244
+ destructive: [
245
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
246
+ "text-[var(--button-destructive-fg,white)]",
247
+ "border-[length:var(--button-destructive-border-width,0px)]",
248
+ "border-[color:var(--button-destructive-border,transparent)]",
249
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
250
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
251
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
252
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
253
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
254
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
255
+ "dark:bg-destructive/60"
256
+ ].join(" "),
257
+ // Outline variant - full customization with proper border handling
258
+ outline: [
259
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
260
+ "text-[var(--button-outline-fg,inherit)]",
261
+ "border-[length:var(--button-outline-border-width,1px)]",
262
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
263
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
264
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
265
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
266
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
267
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
268
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
269
+ ].join(" "),
270
+ // Secondary variant - full customization
271
+ secondary: [
272
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
273
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
274
+ "border-[length:var(--button-secondary-border-width,0px)]",
275
+ "border-[color:var(--button-secondary-border,transparent)]",
276
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
277
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
278
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
279
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
280
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
281
+ ].join(" "),
282
+ // Ghost variant - full customization
283
+ ghost: [
284
+ "bg-[var(--button-ghost-bg,transparent)]",
285
+ "text-[var(--button-ghost-fg,inherit)]",
286
+ "border-[length:var(--button-ghost-border-width,0px)]",
287
+ "border-[color:var(--button-ghost-border,transparent)]",
288
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
289
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
290
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
291
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
292
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
293
+ "dark:hover:bg-accent/50"
294
+ ].join(" "),
295
+ // Link variant - full customization
296
+ link: [
297
+ "bg-[var(--button-link-bg,transparent)]",
298
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
299
+ "border-[length:var(--button-link-border-width,0px)]",
300
+ "border-[color:var(--button-link-border,transparent)]",
301
+ "[box-shadow:var(--button-link-shadow,none)]",
302
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
303
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
304
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
305
+ "underline-offset-4 hover:underline"
306
+ ].join(" ")
307
+ },
308
+ size: {
309
+ default: [
310
+ "h-[var(--button-height-md,2.25rem)]",
311
+ "px-[var(--button-padding-x-md,1rem)]",
312
+ "py-[var(--button-padding-y-md,0.5rem)]",
313
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
314
+ ].join(" "),
315
+ sm: [
316
+ "h-[var(--button-height-sm,2rem)]",
317
+ "px-[var(--button-padding-x-sm,0.75rem)]",
318
+ "py-[var(--button-padding-y-sm,0.25rem)]",
319
+ "gap-1.5",
320
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
321
+ ].join(" "),
322
+ md: [
323
+ "h-[var(--button-height-md,2.25rem)]",
324
+ "px-[var(--button-padding-x-md,1rem)]",
325
+ "py-[var(--button-padding-y-md,0.5rem)]",
326
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
327
+ ].join(" "),
328
+ lg: [
329
+ "h-[var(--button-height-lg,2.5rem)]",
330
+ "px-[var(--button-padding-x-lg,1.5rem)]",
331
+ "py-[var(--button-padding-y-lg,0.5rem)]",
332
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
333
+ ].join(" "),
334
+ icon: "size-[var(--button-height-md,2.25rem)]",
335
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
336
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
337
+ }
338
+ },
339
+ defaultVariants: {
340
+ variant: "default",
341
+ size: "default"
342
+ }
343
+ });
344
+ var Pressable = React2.forwardRef(
345
+ ({
346
+ children,
347
+ className,
348
+ href,
349
+ onClick,
350
+ variant,
351
+ size,
352
+ asButton = false,
353
+ fallbackComponentType = "span",
354
+ componentType,
355
+ "aria-label": ariaLabel,
356
+ "aria-describedby": ariaDescribedby,
357
+ id,
358
+ ...props
359
+ }, ref) => {
360
+ const navigation = useNavigation({ href, onClick });
361
+ const {
362
+ normalizedHref,
363
+ target,
364
+ rel,
365
+ linkType,
366
+ isInternal,
367
+ handleClick
368
+ } = navigation;
369
+ const shouldRenderLink = normalizedHref && linkType !== "none";
370
+ const shouldRenderButton = !shouldRenderLink && onClick;
371
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
372
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
373
+ const shouldApplyButtonStyles = asButton || variant || size;
374
+ const combinedClassName = cn(
375
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
376
+ className
377
+ );
378
+ const dataProps = Object.fromEntries(
379
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
380
+ );
381
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
382
+ "data-slot": "button",
383
+ "data-variant": variant ?? "default",
384
+ "data-size": size ?? "default"
385
+ } : {};
386
+ const commonProps = {
387
+ className: combinedClassName,
388
+ onClick: handleClick,
389
+ "aria-label": ariaLabel,
390
+ "aria-describedby": ariaDescribedby,
391
+ id,
392
+ ...dataProps,
393
+ ...buttonDataAttributes
394
+ };
395
+ if (finalComponentType === "a" && shouldRenderLink) {
396
+ return /* @__PURE__ */ jsx(
397
+ "a",
398
+ {
399
+ ref,
400
+ href: normalizedHref,
401
+ target,
402
+ rel,
403
+ ...commonProps,
404
+ ...props,
405
+ children
406
+ }
407
+ );
408
+ }
409
+ if (finalComponentType === "button") {
410
+ return /* @__PURE__ */ jsx(
411
+ "button",
412
+ {
413
+ ref,
414
+ type: props.type || "button",
415
+ ...commonProps,
416
+ ...props,
417
+ children
418
+ }
419
+ );
420
+ }
421
+ if (finalComponentType === "div") {
422
+ return /* @__PURE__ */ jsx(
423
+ "div",
424
+ {
425
+ ref,
426
+ ...commonProps,
427
+ children
428
+ }
429
+ );
430
+ }
431
+ return /* @__PURE__ */ jsx(
432
+ "span",
433
+ {
434
+ ref,
435
+ ...commonProps,
436
+ children
437
+ }
438
+ );
439
+ }
440
+ );
441
+ Pressable.displayName = "Pressable";
12
442
  function Avatar({
13
443
  className,
14
444
  ...props
@@ -63,7 +493,7 @@ var maxWidthStyles = {
63
493
  "4xl": "max-w-[1536px]",
64
494
  full: "max-w-full"
65
495
  };
66
- var Container = React.forwardRef(
496
+ var Container = React2__default.forwardRef(
67
497
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
68
498
  const Component = as;
69
499
  return /* @__PURE__ */ jsx(
@@ -369,7 +799,7 @@ var spacingStyles = {
369
799
  };
370
800
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl", "hero"];
371
801
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
372
- var Section = React.forwardRef(
802
+ var Section = React2__default.forwardRef(
373
803
  ({
374
804
  id,
375
805
  title,
@@ -439,36 +869,58 @@ function TestimonialsSliderMinimal({
439
869
  quoteClassName,
440
870
  authorClassName,
441
871
  avatarClassName,
872
+ navButtonClassName,
442
873
  dotsClassName,
443
874
  background,
444
875
  containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
445
- spacing = "xl",
876
+ spacing = "none",
446
877
  pattern,
447
- patternOpacity
878
+ patternOpacity,
879
+ previousButtonAriaLabel,
880
+ nextButtonAriaLabel,
881
+ navigationClassName
448
882
  }) {
449
883
  const [currentIndex, setCurrentIndex] = useState(0);
450
- const [isTransitioning, setIsTransitioning] = useState(false);
451
884
  const totalTestimonials = testimonials?.length ?? 0;
452
- const effectiveAutoPlayInterval = autoPlayInterval ?? 5e3;
453
- const goToSlide = useCallback(
885
+ const autoPlayTimerRef = React2.useRef(
886
+ null
887
+ );
888
+ const resetAutoPlay = useCallback(() => {
889
+ if (autoPlayTimerRef.current) {
890
+ clearInterval(autoPlayTimerRef.current);
891
+ autoPlayTimerRef.current = null;
892
+ }
893
+ if (!autoPlayInterval || autoPlayInterval <= 0 || totalTestimonials === 0)
894
+ return;
895
+ autoPlayTimerRef.current = setInterval(() => {
896
+ setCurrentIndex((prev) => (prev + 1) % totalTestimonials);
897
+ }, autoPlayInterval);
898
+ }, [autoPlayInterval, totalTestimonials]);
899
+ const goToNext = useCallback(() => {
900
+ if (totalTestimonials === 0) return;
901
+ setCurrentIndex((prev) => prev === totalTestimonials - 1 ? 0 : prev + 1);
902
+ resetAutoPlay();
903
+ }, [totalTestimonials, resetAutoPlay]);
904
+ const goToPrevious = useCallback(() => {
905
+ if (totalTestimonials === 0) return;
906
+ setCurrentIndex((prev) => prev === 0 ? totalTestimonials - 1 : prev - 1);
907
+ resetAutoPlay();
908
+ }, [totalTestimonials, resetAutoPlay]);
909
+ const goToIndex = useCallback(
454
910
  (index) => {
455
- if (index === currentIndex) return;
456
- setIsTransitioning(true);
457
- setTimeout(() => {
458
- setCurrentIndex(index);
459
- setIsTransitioning(false);
460
- }, 300);
911
+ setCurrentIndex(index);
912
+ resetAutoPlay();
461
913
  },
462
- [currentIndex]
914
+ [resetAutoPlay]
463
915
  );
464
916
  useEffect(() => {
465
- if (effectiveAutoPlayInterval <= 0 || totalTestimonials === 0) return;
466
- const interval = setInterval(() => {
467
- const nextIndex = (currentIndex + 1) % totalTestimonials;
468
- goToSlide(nextIndex);
469
- }, effectiveAutoPlayInterval);
470
- return () => clearInterval(interval);
471
- }, [currentIndex, effectiveAutoPlayInterval, totalTestimonials, goToSlide]);
917
+ resetAutoPlay();
918
+ return () => {
919
+ if (autoPlayTimerRef.current) {
920
+ clearInterval(autoPlayTimerRef.current);
921
+ }
922
+ };
923
+ }, [resetAutoPlay]);
472
924
  const current = testimonials?.[currentIndex];
473
925
  const getAuthorName = useCallback((testimonial) => {
474
926
  if (typeof testimonial.author === "string") return testimonial.author;
@@ -488,53 +940,64 @@ function TestimonialsSliderMinimal({
488
940
  if (!current) return null;
489
941
  const authorName = getAuthorName(current);
490
942
  const avatarSrc = getAvatarSrc(current);
491
- return /* @__PURE__ */ jsxs(
492
- "div",
493
- {
494
- className: cn(
495
- "transition-opacity duration-300",
496
- isTransitioning ? "opacity-0" : "opacity-100"
497
- ),
498
- children: [
499
- current.quote && (typeof current.quote === "string" ? /* @__PURE__ */ jsxs(
500
- "blockquote",
501
- {
502
- className: cn(
503
- "text-xl font-medium leading-relaxed md:text-2xl lg:text-3xl",
504
- quoteClassName
505
- ),
506
- children: [
507
- "\u201C",
508
- current.quote,
509
- "\u201D"
510
- ]
511
- }
512
- ) : /* @__PURE__ */ jsx("div", { className: quoteClassName, children: current.quote })),
513
- /* @__PURE__ */ jsxs(
514
- "div",
515
- {
516
- className: cn(
517
- "mt-8 flex flex-col items-center gap-4",
518
- authorClassName
519
- ),
520
- children: [
521
- /* @__PURE__ */ jsxs(Avatar, { className: cn("size-14", avatarClassName), children: [
943
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-12 md:space-y-24", children: [
944
+ current.quote && (typeof current.quote === "string" ? /* @__PURE__ */ jsxs(
945
+ "blockquote",
946
+ {
947
+ className: cn(
948
+ "text-xl font-thin leading-relaxed md:text-2xl text-balance line-clamp-6",
949
+ quoteClassName
950
+ ),
951
+ children: [
952
+ "\u201C",
953
+ current.quote,
954
+ "\u201D"
955
+ ]
956
+ }
957
+ ) : current.quote),
958
+ /* @__PURE__ */ jsxs(
959
+ "div",
960
+ {
961
+ className: cn(
962
+ "mt-8 flex flex-col items-center gap-6 md:gap-12",
963
+ authorClassName
964
+ ),
965
+ children: [
966
+ /* @__PURE__ */ jsxs(
967
+ Avatar,
968
+ {
969
+ className: cn(
970
+ "relative flex shrink-0 overflow-hidden rounded-full ring-4 ring-primary shadow-xl size-12",
971
+ avatarClassName
972
+ ),
973
+ children: [
522
974
  /* @__PURE__ */ jsx(AvatarImage, { src: avatarSrc, alt: authorName }),
523
975
  /* @__PURE__ */ jsx(AvatarFallback, { children: getInitials(authorName) })
524
- ] }),
525
- /* @__PURE__ */ jsxs("div", { children: [
526
- current.author && (typeof current.author === "string" ? /* @__PURE__ */ jsx("p", { className: "font-semibold", children: current.author }) : current.author),
527
- current.role && (typeof current.role === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm ", children: current.role }) : current.role)
528
- ] })
529
- ]
530
- }
531
- )
532
- ]
533
- }
534
- );
976
+ ]
977
+ }
978
+ ),
979
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-0", children: [
980
+ current.author && (typeof current.author === "string" ? /* @__PURE__ */ jsx("p", { className: "font-semibold", children: current.author }) : current.author),
981
+ current.role && (typeof current.role === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm ", children: current.role }) : current.role)
982
+ ] }),
983
+ current.linkConfig?.href && /* @__PURE__ */ jsx(
984
+ Pressable,
985
+ {
986
+ href: current.linkConfig.href,
987
+ className: cn(
988
+ current.linkConfig.className,
989
+ "text-sm font-bold tracking-wide uppercase",
990
+ "hover:underline hover:underline-offset-2"
991
+ ),
992
+ children: current.linkConfig.label
993
+ }
994
+ )
995
+ ]
996
+ }
997
+ )
998
+ ] });
535
999
  }, [
536
1000
  testimonialsSlot,
537
- isTransitioning,
538
1001
  current,
539
1002
  quoteClassName,
540
1003
  authorClassName,
@@ -552,21 +1015,75 @@ function TestimonialsSliderMinimal({
552
1015
  patternOpacity,
553
1016
  className,
554
1017
  containerClassName,
555
- children: /* @__PURE__ */ jsxs("div", { className: cn("mx-auto max-w-3xl text-center", contentClassName), children: [
556
- renderedTestimonial,
557
- testimonials && testimonials.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("mt-8 flex justify-center gap-2", dotsClassName), children: testimonials.map((_, index) => /* @__PURE__ */ jsx(
558
- "button",
559
- {
560
- onClick: () => goToSlide(index),
561
- className: cn(
562
- "size-2 rounded-full transition-all",
563
- index === currentIndex ? "w-6 bg-primary text-primary-foreground" : ""
564
- ),
565
- "aria-label": `Go to testimonial ${index + 1}`
566
- },
567
- index
568
- )) })
569
- ] })
1018
+ children: /* @__PURE__ */ jsxs(
1019
+ "div",
1020
+ {
1021
+ className: cn(
1022
+ "mx-auto max-w-3xl text-center min-h-[700px] flex flex-col items-center justify-center",
1023
+ contentClassName
1024
+ ),
1025
+ children: [
1026
+ /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsx(
1027
+ motion.div,
1028
+ {
1029
+ initial: { opacity: 0, y: 10 },
1030
+ animate: { opacity: 1, y: 0 },
1031
+ exit: { opacity: 0, y: -10 },
1032
+ transition: { duration: 0.4, ease: "easeInOut" },
1033
+ children: renderedTestimonial
1034
+ },
1035
+ currentIndex
1036
+ ) }),
1037
+ testimonials && testimonials.length > 1 && /* @__PURE__ */ jsxs(
1038
+ "div",
1039
+ {
1040
+ className: cn(
1041
+ "mt-8 flex items-center justify-center gap-4",
1042
+ navigationClassName
1043
+ ),
1044
+ children: [
1045
+ /* @__PURE__ */ jsx(
1046
+ Pressable,
1047
+ {
1048
+ asButton: true,
1049
+ variant: "default",
1050
+ size: "icon",
1051
+ className: cn("size-10 rounded-full", navButtonClassName),
1052
+ onClick: goToPrevious,
1053
+ "aria-label": previousButtonAriaLabel ?? "Previous testimonial",
1054
+ children: /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/chevron-left", size: 24 })
1055
+ }
1056
+ ),
1057
+ /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2", dotsClassName), children: testimonials.map((_, index) => /* @__PURE__ */ jsx(
1058
+ "button",
1059
+ {
1060
+ onClick: () => goToIndex(index),
1061
+ className: cn(
1062
+ "size-2 rounded-full transition-all",
1063
+ index === currentIndex ? "w-6 bg-primary text-primary-foreground" : "bg-card text-card-foreground"
1064
+ ),
1065
+ "aria-label": `Go to testimonial ${index + 1}`
1066
+ },
1067
+ index
1068
+ )) }),
1069
+ /* @__PURE__ */ jsx(
1070
+ Pressable,
1071
+ {
1072
+ asButton: true,
1073
+ variant: "default",
1074
+ size: "icon",
1075
+ className: cn("size-10 rounded-full", navButtonClassName),
1076
+ onClick: goToNext,
1077
+ "aria-label": nextButtonAriaLabel ?? "Next testimonial",
1078
+ children: /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/chevron-right", size: 24 })
1079
+ }
1080
+ )
1081
+ ]
1082
+ }
1083
+ )
1084
+ ]
1085
+ }
1086
+ )
570
1087
  }
571
1088
  );
572
1089
  }