@opensite/ui 2.8.6 → 2.8.8

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 (143) 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-icon-sidebar.cjs +5 -4
  14. package/dist/carousel-icon-sidebar.js +5 -4
  15. package/dist/carousel-portfolio-hero.cjs +79 -25
  16. package/dist/carousel-portfolio-hero.d.cts +7 -2
  17. package/dist/carousel-portfolio-hero.d.ts +7 -2
  18. package/dist/carousel-portfolio-hero.js +79 -25
  19. package/dist/components.cjs +81 -42
  20. package/dist/components.d.cts +29 -2
  21. package/dist/components.d.ts +29 -2
  22. package/dist/components.js +81 -43
  23. package/dist/contact-map.cjs +46 -32
  24. package/dist/contact-map.js +46 -32
  25. package/dist/footer-accordion-social.cjs +17 -10
  26. package/dist/footer-accordion-social.js +17 -10
  27. package/dist/footer-animated-social.cjs +17 -10
  28. package/dist/footer-animated-social.js +17 -10
  29. package/dist/footer-brand-description.cjs +17 -10
  30. package/dist/footer-brand-description.js +17 -10
  31. package/dist/footer-brand-links-contact.cjs +17 -10
  32. package/dist/footer-brand-links-contact.js +17 -10
  33. package/dist/footer-comprehensive-links.cjs +17 -10
  34. package/dist/footer-comprehensive-links.js +17 -10
  35. package/dist/footer-contact-card.cjs +17 -10
  36. package/dist/footer-contact-card.js +17 -10
  37. package/dist/footer-cta-banner.cjs +17 -10
  38. package/dist/footer-cta-banner.js +17 -10
  39. package/dist/footer-cta-social.cjs +17 -10
  40. package/dist/footer-cta-social.js +17 -10
  41. package/dist/footer-info-cards-accordion.cjs +17 -10
  42. package/dist/footer-info-cards-accordion.js +17 -10
  43. package/dist/footer-nav-social.cjs +17 -10
  44. package/dist/footer-nav-social.js +17 -10
  45. package/dist/footer-newsletter-contact.cjs +17 -10
  46. package/dist/footer-newsletter-contact.js +17 -10
  47. package/dist/footer-newsletter-grid.cjs +17 -10
  48. package/dist/footer-newsletter-grid.js +17 -10
  49. package/dist/footer-newsletter-minimal.cjs +17 -10
  50. package/dist/footer-newsletter-minimal.js +17 -10
  51. package/dist/footer-social-apps.cjs +17 -10
  52. package/dist/footer-social-apps.js +17 -10
  53. package/dist/footer-social-newsletter.cjs +17 -10
  54. package/dist/footer-social-newsletter.js +17 -10
  55. package/dist/footer-split-image-accordion.cjs +17 -10
  56. package/dist/footer-split-image-accordion.js +17 -10
  57. package/dist/geo-map.cjs +46 -32
  58. package/dist/geo-map.js +46 -32
  59. package/dist/hero-coming-soon-countdown.cjs +17 -10
  60. package/dist/hero-coming-soon-countdown.js +17 -10
  61. package/dist/hero-video-background-dark.cjs +78 -16
  62. package/dist/hero-video-background-dark.d.cts +7 -2
  63. package/dist/hero-video-background-dark.d.ts +7 -2
  64. package/dist/hero-video-background-dark.js +78 -16
  65. package/dist/index.cjs +81 -42
  66. package/dist/index.d.cts +1 -0
  67. package/dist/index.d.ts +1 -0
  68. package/dist/index.js +81 -43
  69. package/dist/link-page-bento-layout.cjs +17 -10
  70. package/dist/link-page-bento-layout.js +17 -10
  71. package/dist/link-page-grid-cards.cjs +17 -10
  72. package/dist/link-page-grid-cards.js +17 -10
  73. package/dist/link-page-minimal-profile.cjs +17 -10
  74. package/dist/link-page-minimal-profile.js +17 -10
  75. package/dist/link-page-newsletter-social.cjs +17 -10
  76. package/dist/link-page-newsletter-social.js +17 -10
  77. package/dist/link-tree-block.cjs +17 -10
  78. package/dist/link-tree-block.js +17 -10
  79. package/dist/navbar-fullscreen-menu.cjs +17 -10
  80. package/dist/navbar-fullscreen-menu.js +17 -10
  81. package/dist/navbar-transparent-overlay.cjs +17 -10
  82. package/dist/navbar-transparent-overlay.js +17 -10
  83. package/dist/registry.cjs +967 -620
  84. package/dist/registry.js +967 -620
  85. package/dist/social-link-icon.cjs +17 -10
  86. package/dist/social-link-icon.d.cts +5 -0
  87. package/dist/social-link-icon.d.ts +5 -0
  88. package/dist/social-link-icon.js +17 -10
  89. package/dist/testimonials-bento-grid.cjs +1 -1
  90. package/dist/testimonials-bento-grid.js +1 -1
  91. package/dist/testimonials-carousel-image.cjs +16 -2
  92. package/dist/testimonials-carousel-image.d.cts +5 -1
  93. package/dist/testimonials-carousel-image.d.ts +5 -1
  94. package/dist/testimonials-carousel-image.js +16 -2
  95. package/dist/testimonials-centered-avatars.cjs +1 -1
  96. package/dist/testimonials-centered-avatars.js +1 -1
  97. package/dist/testimonials-grid-add-review.cjs +51 -29
  98. package/dist/testimonials-grid-add-review.js +51 -29
  99. package/dist/testimonials-images-helpful.cjs +181 -160
  100. package/dist/testimonials-images-helpful.d.cts +9 -1
  101. package/dist/testimonials-images-helpful.d.ts +9 -1
  102. package/dist/testimonials-images-helpful.js +181 -159
  103. package/dist/testimonials-large-quote.cjs +74 -43
  104. package/dist/testimonials-large-quote.d.cts +5 -1
  105. package/dist/testimonials-large-quote.d.ts +5 -1
  106. package/dist/testimonials-large-quote.js +74 -43
  107. package/dist/testimonials-list-verified.cjs +63 -44
  108. package/dist/testimonials-list-verified.d.cts +5 -1
  109. package/dist/testimonials-list-verified.d.ts +5 -1
  110. package/dist/testimonials-list-verified.js +64 -45
  111. package/dist/testimonials-logo-cards.cjs +55 -25
  112. package/dist/testimonials-logo-cards.d.cts +5 -1
  113. package/dist/testimonials-logo-cards.d.ts +5 -1
  114. package/dist/testimonials-logo-cards.js +55 -25
  115. package/dist/testimonials-marquee.cjs +440 -28
  116. package/dist/testimonials-marquee.js +441 -26
  117. package/dist/testimonials-masonry-grid.cjs +486 -69
  118. package/dist/testimonials-masonry-grid.d.cts +5 -1
  119. package/dist/testimonials-masonry-grid.d.ts +5 -1
  120. package/dist/testimonials-masonry-grid.js +483 -63
  121. package/dist/testimonials-mini-dividers.cjs +119 -83
  122. package/dist/testimonials-mini-dividers.d.cts +10 -6
  123. package/dist/testimonials-mini-dividers.d.ts +10 -6
  124. package/dist/testimonials-mini-dividers.js +119 -83
  125. package/dist/testimonials-minimal-numbered.cjs +9 -7
  126. package/dist/testimonials-minimal-numbered.d.cts +5 -1
  127. package/dist/testimonials-minimal-numbered.d.ts +5 -1
  128. package/dist/testimonials-minimal-numbered.js +9 -7
  129. package/dist/testimonials-parallax-number.cjs +14 -9
  130. package/dist/testimonials-parallax-number.js +14 -9
  131. package/dist/testimonials-scrolling-columns.cjs +100 -21
  132. package/dist/testimonials-scrolling-columns.js +100 -21
  133. package/dist/testimonials-simple-grid.cjs +22 -5
  134. package/dist/testimonials-simple-grid.js +22 -5
  135. package/dist/testimonials-slider-minimal.cjs +1 -1
  136. package/dist/testimonials-slider-minimal.js +1 -1
  137. package/dist/testimonials-stats-header.cjs +528 -87
  138. package/dist/testimonials-stats-header.d.cts +39 -3
  139. package/dist/testimonials-stats-header.d.ts +39 -3
  140. package/dist/testimonials-stats-header.js +523 -82
  141. package/dist/testimonials-twitter-cards.cjs +20 -12
  142. package/dist/testimonials-twitter-cards.js +20 -12
  143. package/package.json +11 -1
@@ -1,48 +1,19 @@
1
1
  "use client";
2
- import * as React from 'react';
3
- import React__default, { useCallback, useMemo } from 'react';
2
+ import * as React4 from 'react';
3
+ import React4__default, { useCallback, useMemo } from 'react';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { Icon } from '@page-speed/icon';
7
7
  import { jsx, jsxs } from 'react/jsx-runtime';
8
8
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
9
+ import { cva } from 'class-variance-authority';
9
10
 
10
11
  // components/blocks/testimonials/testimonials-stats-header.tsx
11
12
  function cn(...inputs) {
12
13
  return twMerge(clsx(inputs));
13
14
  }
14
- function getNestedCardBg(parentBg, variant = "muted", options) {
15
- const isDark = parentBg === "dark" || parentBg === "secondary" || parentBg === "primary";
16
- if (isDark) {
17
- switch (variant) {
18
- case "muted":
19
- return "bg-background";
20
- case "card":
21
- return "bg-card";
22
- case "accent":
23
- return "bg-accent";
24
- case "subtle":
25
- return "bg-background/50";
26
- }
27
- } else {
28
- switch (variant) {
29
- case "muted":
30
- return "bg-muted";
31
- case "card":
32
- return "bg-card";
33
- case "accent":
34
- return "bg-accent";
35
- case "subtle":
36
- return "bg-muted/50";
37
- }
38
- }
39
- }
40
- function getNestedCardTextColor(parentBg, options) {
41
- const isDark = parentBg === "dark" || parentBg === "secondary" || parentBg === "primary";
42
- return isDark ? "text-foreground" : "";
43
- }
44
15
  var DEFAULT_ICON_API_KEY = "au382bi7fsh96w9h9xlrnat2jglx";
45
- var DynamicIcon = React.memo(function DynamicIcon2({
16
+ var DynamicIcon = React4.memo(function DynamicIcon2({
46
17
  apiKey,
47
18
  ...props
48
19
  }) {
@@ -126,7 +97,7 @@ var maxWidthStyles = {
126
97
  "4xl": "max-w-[1536px]",
127
98
  full: "max-w-full"
128
99
  };
129
- var Container = React__default.forwardRef(
100
+ var Container = React4__default.forwardRef(
130
101
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
131
102
  const Component = as;
132
103
  return /* @__PURE__ */ jsx(
@@ -432,7 +403,7 @@ var spacingStyles = {
432
403
  };
433
404
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl", "hero"];
434
405
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
435
- var Section = React__default.forwardRef(
406
+ var Section = React4__default.forwardRef(
436
407
  ({
437
408
  id,
438
409
  title,
@@ -493,6 +464,441 @@ var Section = React__default.forwardRef(
493
464
  }
494
465
  );
495
466
  Section.displayName = "Section";
467
+ function normalizePhoneNumber(input) {
468
+ const trimmed = input.trim();
469
+ if (trimmed.toLowerCase().startsWith("tel:")) {
470
+ return trimmed;
471
+ }
472
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
473
+ if (match) {
474
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
475
+ const extension = match[3];
476
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
477
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
478
+ return `tel:${withExtension}`;
479
+ }
480
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
481
+ return `tel:${cleaned}`;
482
+ }
483
+ function normalizeEmail(input) {
484
+ const trimmed = input.trim();
485
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
486
+ return trimmed;
487
+ }
488
+ return `mailto:${trimmed}`;
489
+ }
490
+ function isEmail(input) {
491
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
492
+ return emailRegex.test(input.trim());
493
+ }
494
+ function isPhoneNumber(input) {
495
+ const trimmed = input.trim();
496
+ if (trimmed.toLowerCase().startsWith("tel:")) {
497
+ return true;
498
+ }
499
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
500
+ return phoneRegex.test(trimmed);
501
+ }
502
+ function isInternalUrl(href) {
503
+ if (typeof window === "undefined") {
504
+ return href.startsWith("/") && !href.startsWith("//");
505
+ }
506
+ const trimmed = href.trim();
507
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
508
+ return true;
509
+ }
510
+ try {
511
+ const url = new URL(trimmed, window.location.href);
512
+ const currentOrigin = window.location.origin;
513
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
514
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
515
+ } catch {
516
+ return false;
517
+ }
518
+ }
519
+ function toRelativePath(href) {
520
+ if (typeof window === "undefined") {
521
+ return href;
522
+ }
523
+ const trimmed = href.trim();
524
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
525
+ return trimmed;
526
+ }
527
+ try {
528
+ const url = new URL(trimmed, window.location.href);
529
+ const currentOrigin = window.location.origin;
530
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
531
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
532
+ return url.pathname + url.search + url.hash;
533
+ }
534
+ } catch {
535
+ }
536
+ return trimmed;
537
+ }
538
+ function useNavigation({
539
+ href,
540
+ onClick
541
+ } = {}) {
542
+ const linkType = React4.useMemo(() => {
543
+ if (!href || href.trim() === "") {
544
+ return onClick ? "none" : "none";
545
+ }
546
+ const trimmed = href.trim();
547
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
548
+ return "mailto";
549
+ }
550
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
551
+ return "tel";
552
+ }
553
+ if (isInternalUrl(trimmed)) {
554
+ return "internal";
555
+ }
556
+ try {
557
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
558
+ return "external";
559
+ } catch {
560
+ return "internal";
561
+ }
562
+ }, [href, onClick]);
563
+ const normalizedHref = React4.useMemo(() => {
564
+ if (!href || href.trim() === "") {
565
+ return void 0;
566
+ }
567
+ const trimmed = href.trim();
568
+ switch (linkType) {
569
+ case "tel":
570
+ return normalizePhoneNumber(trimmed);
571
+ case "mailto":
572
+ return normalizeEmail(trimmed);
573
+ case "internal":
574
+ return toRelativePath(trimmed);
575
+ case "external":
576
+ return trimmed;
577
+ default:
578
+ return trimmed;
579
+ }
580
+ }, [href, linkType]);
581
+ const target = React4.useMemo(() => {
582
+ switch (linkType) {
583
+ case "external":
584
+ return "_blank";
585
+ case "internal":
586
+ return "_self";
587
+ case "mailto":
588
+ case "tel":
589
+ return void 0;
590
+ default:
591
+ return void 0;
592
+ }
593
+ }, [linkType]);
594
+ const rel = React4.useMemo(() => {
595
+ if (linkType === "external") {
596
+ return "noopener noreferrer";
597
+ }
598
+ return void 0;
599
+ }, [linkType]);
600
+ const isExternal = linkType === "external";
601
+ const isInternal = linkType === "internal";
602
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
603
+ const handleClick = React4.useCallback(
604
+ (event) => {
605
+ if (onClick) {
606
+ try {
607
+ onClick(event);
608
+ } catch (error) {
609
+ console.error("Error in user onClick handler:", error);
610
+ }
611
+ }
612
+ if (event.defaultPrevented) {
613
+ return;
614
+ }
615
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
616
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
617
+ if (typeof window !== "undefined") {
618
+ const handler = window.__opensiteNavigationHandler;
619
+ if (typeof handler === "function") {
620
+ try {
621
+ const handled = handler(normalizedHref, event.nativeEvent || event);
622
+ if (handled !== false) {
623
+ event.preventDefault();
624
+ }
625
+ } catch (error) {
626
+ console.error("Error in navigation handler:", error);
627
+ }
628
+ }
629
+ }
630
+ }
631
+ },
632
+ [onClick, shouldUseRouter, normalizedHref]
633
+ );
634
+ return {
635
+ linkType,
636
+ normalizedHref,
637
+ target,
638
+ rel,
639
+ isExternal,
640
+ isInternal,
641
+ shouldUseRouter,
642
+ handleClick
643
+ };
644
+ }
645
+ var baseStyles = [
646
+ // Layout
647
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
648
+ // Typography - using CSS variables with sensible defaults
649
+ "font-[var(--button-font-family,inherit)]",
650
+ "font-[var(--button-font-weight,500)]",
651
+ "tracking-[var(--button-letter-spacing,0)]",
652
+ "leading-[var(--button-line-height,1.25)]",
653
+ "[text-transform:var(--button-text-transform,none)]",
654
+ "text-sm",
655
+ // Border radius
656
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
657
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
658
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
659
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
660
+ "[box-shadow:var(--button-shadow,none)]",
661
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
662
+ // Disabled state
663
+ "disabled:pointer-events-none disabled:opacity-50",
664
+ // SVG handling
665
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
666
+ // Focus styles
667
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
668
+ // Invalid state
669
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
670
+ ].join(" ");
671
+ var buttonVariants = cva(baseStyles, {
672
+ variants: {
673
+ variant: {
674
+ // Default (Primary) variant - full customization
675
+ default: [
676
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
677
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
678
+ "border-[length:var(--button-default-border-width,0px)]",
679
+ "border-[color:var(--button-default-border,transparent)]",
680
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
681
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
682
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
683
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
684
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
685
+ ].join(" "),
686
+ // Destructive variant - full customization
687
+ destructive: [
688
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
689
+ "text-[var(--button-destructive-fg,white)]",
690
+ "border-[length:var(--button-destructive-border-width,0px)]",
691
+ "border-[color:var(--button-destructive-border,transparent)]",
692
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
693
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
694
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
695
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
696
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
697
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
698
+ "dark:bg-destructive/60"
699
+ ].join(" "),
700
+ // Outline variant - full customization with proper border handling
701
+ outline: [
702
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
703
+ "text-[var(--button-outline-fg,inherit)]",
704
+ "border-[length:var(--button-outline-border-width,1px)]",
705
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
706
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
707
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
708
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
709
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
710
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
711
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
712
+ ].join(" "),
713
+ // Secondary variant - full customization
714
+ secondary: [
715
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
716
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
717
+ "border-[length:var(--button-secondary-border-width,0px)]",
718
+ "border-[color:var(--button-secondary-border,transparent)]",
719
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
720
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
721
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
722
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
723
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
724
+ ].join(" "),
725
+ // Ghost variant - full customization
726
+ ghost: [
727
+ "bg-[var(--button-ghost-bg,transparent)]",
728
+ "text-[var(--button-ghost-fg,inherit)]",
729
+ "border-[length:var(--button-ghost-border-width,0px)]",
730
+ "border-[color:var(--button-ghost-border,transparent)]",
731
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
732
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
733
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
734
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
735
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
736
+ "dark:hover:bg-accent/50"
737
+ ].join(" "),
738
+ // Link variant - full customization
739
+ link: [
740
+ "bg-[var(--button-link-bg,transparent)]",
741
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
742
+ "border-[length:var(--button-link-border-width,0px)]",
743
+ "border-[color:var(--button-link-border,transparent)]",
744
+ "[box-shadow:var(--button-link-shadow,none)]",
745
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
746
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
747
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
748
+ "underline-offset-4 hover:underline"
749
+ ].join(" ")
750
+ },
751
+ size: {
752
+ default: [
753
+ "h-[var(--button-height-md,2.25rem)]",
754
+ "px-[var(--button-padding-x-md,1rem)]",
755
+ "py-[var(--button-padding-y-md,0.5rem)]",
756
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
757
+ ].join(" "),
758
+ sm: [
759
+ "h-[var(--button-height-sm,2rem)]",
760
+ "px-[var(--button-padding-x-sm,0.75rem)]",
761
+ "py-[var(--button-padding-y-sm,0.25rem)]",
762
+ "gap-1.5",
763
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
764
+ ].join(" "),
765
+ md: [
766
+ "h-[var(--button-height-md,2.25rem)]",
767
+ "px-[var(--button-padding-x-md,1rem)]",
768
+ "py-[var(--button-padding-y-md,0.5rem)]",
769
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
770
+ ].join(" "),
771
+ lg: [
772
+ "h-[var(--button-height-lg,2.5rem)]",
773
+ "px-[var(--button-padding-x-lg,1.5rem)]",
774
+ "py-[var(--button-padding-y-lg,0.5rem)]",
775
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
776
+ ].join(" "),
777
+ icon: "size-[var(--button-height-md,2.25rem)]",
778
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
779
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
780
+ }
781
+ },
782
+ defaultVariants: {
783
+ variant: "default",
784
+ size: "default"
785
+ }
786
+ });
787
+ var Pressable = React4.forwardRef(
788
+ ({
789
+ children,
790
+ className,
791
+ href,
792
+ onClick,
793
+ variant,
794
+ size,
795
+ asButton = false,
796
+ fallbackComponentType = "span",
797
+ componentType,
798
+ "aria-label": ariaLabel,
799
+ "aria-describedby": ariaDescribedby,
800
+ id,
801
+ ...props
802
+ }, ref) => {
803
+ const navigation = useNavigation({ href, onClick });
804
+ const {
805
+ normalizedHref,
806
+ target,
807
+ rel,
808
+ linkType,
809
+ isInternal,
810
+ handleClick
811
+ } = navigation;
812
+ const shouldRenderLink = normalizedHref && linkType !== "none";
813
+ const shouldRenderButton = !shouldRenderLink && onClick;
814
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
815
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
816
+ const shouldApplyButtonStyles = asButton || variant || size;
817
+ const combinedClassName = cn(
818
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
819
+ className
820
+ );
821
+ const dataProps = Object.fromEntries(
822
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
823
+ );
824
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
825
+ "data-slot": "button",
826
+ "data-variant": variant ?? "default",
827
+ "data-size": size ?? "default"
828
+ } : {};
829
+ const commonProps = {
830
+ className: combinedClassName,
831
+ onClick: handleClick,
832
+ "aria-label": ariaLabel,
833
+ "aria-describedby": ariaDescribedby,
834
+ id,
835
+ ...dataProps,
836
+ ...buttonDataAttributes
837
+ };
838
+ if (finalComponentType === "a" && shouldRenderLink) {
839
+ return /* @__PURE__ */ jsx(
840
+ "a",
841
+ {
842
+ ref,
843
+ href: normalizedHref,
844
+ target,
845
+ rel,
846
+ ...commonProps,
847
+ ...props,
848
+ children
849
+ }
850
+ );
851
+ }
852
+ if (finalComponentType === "button") {
853
+ return /* @__PURE__ */ jsx(
854
+ "button",
855
+ {
856
+ ref,
857
+ type: props.type || "button",
858
+ ...commonProps,
859
+ ...props,
860
+ children
861
+ }
862
+ );
863
+ }
864
+ if (finalComponentType === "div") {
865
+ return /* @__PURE__ */ jsx(
866
+ "div",
867
+ {
868
+ ref,
869
+ ...commonProps,
870
+ children
871
+ }
872
+ );
873
+ }
874
+ return /* @__PURE__ */ jsx(
875
+ "span",
876
+ {
877
+ ref,
878
+ ...commonProps,
879
+ children
880
+ }
881
+ );
882
+ }
883
+ );
884
+ Pressable.displayName = "Pressable";
885
+ function StarRating({
886
+ rating,
887
+ size = 18,
888
+ className
889
+ }) {
890
+ return /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-0.5", className), children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ jsx(
891
+ DynamicIcon,
892
+ {
893
+ name: "icon-park-solid/star",
894
+ size,
895
+ className: cn(
896
+ star <= rating ? "fill-primary text-primary" : "fill-muted text-muted"
897
+ )
898
+ },
899
+ star
900
+ )) });
901
+ }
496
902
  function TestimonialsStatsHeader({
497
903
  stats,
498
904
  statsSlot,
@@ -508,6 +914,7 @@ function TestimonialsStatsHeader({
508
914
  statItemClassName,
509
915
  testimonialsGridClassName,
510
916
  cardClassName,
917
+ statCardClassName,
511
918
  quoteClassName,
512
919
  authorClassName,
513
920
  background,
@@ -526,6 +933,11 @@ function TestimonialsStatsHeader({
526
933
  },
527
934
  []
528
935
  );
936
+ const renderStatIcon = useCallback((stat) => {
937
+ if (stat.iconSlot) return stat.iconSlot;
938
+ if (!stat.icon) return null;
939
+ return /* @__PURE__ */ jsx("div", { className: "mb-6", children: /* @__PURE__ */ jsx(DynamicIcon, { name: stat.icon, size: 32, className: stat.iconColor }) });
940
+ }, []);
529
941
  const getInitials = useCallback((name) => {
530
942
  return name.split(" ").map((n) => n[0]).join("");
531
943
  }, []);
@@ -536,24 +948,29 @@ function TestimonialsStatsHeader({
536
948
  "div",
537
949
  {
538
950
  className: cn(
539
- "mb-12 grid grid-cols-2 gap-4 md:grid-cols-4",
951
+ "mb-12 grid gap-6 md:grid-cols-2 lg:grid-cols-3",
540
952
  statsGridClassName
541
953
  ),
542
- children: stats.map((stat, index) => /* @__PURE__ */ jsxs(
543
- "div",
954
+ children: stats.map((stat) => /* @__PURE__ */ jsx(
955
+ Card,
544
956
  {
545
957
  className: cn(
546
- "rounded-lg p-6 text-center",
547
- getNestedCardBg(background),
548
- getNestedCardTextColor(background),
549
- statItemClassName
958
+ "overflow-hidden border p-0",
959
+ stat.className,
960
+ statCardClassName
550
961
  ),
551
- children: [
552
- typeof stat.value === "string" ? /* @__PURE__ */ jsx("p", { className: "text-3xl font-bold text-primary md:text-4xl", children: stat.value }) : stat.value,
553
- typeof stat.label === "string" ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: stat.label }) : stat.label
554
- ]
962
+ children: /* @__PURE__ */ jsxs(CardContent, { className: "p-6 md:p-8", children: [
963
+ renderStatIcon(stat),
964
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-end", children: [
965
+ stat.prefix && /* @__PURE__ */ jsx("span", { className: "mb-1 mr-1 text-2xl font-bold", children: stat.prefix }),
966
+ /* @__PURE__ */ jsx("h3", { className: "text-4xl font-bold tracking-tight leading-tight md:text-5xl", children: stat.value }),
967
+ stat.suffix && /* @__PURE__ */ jsx("span", { className: "mb-1 ml-1 text-2xl font-bold", children: stat.suffix })
968
+ ] }),
969
+ stat.label && /* @__PURE__ */ jsx("div", { className: "mb-4 text-xl font-semibold", children: stat.label }),
970
+ stat.description && (typeof stat.description === "string" ? /* @__PURE__ */ jsx("p", { className: "opacity-75", children: stat.description }) : stat.description)
971
+ ] })
555
972
  },
556
- index
973
+ stat.id
557
974
  ))
558
975
  }
559
976
  );
@@ -568,41 +985,65 @@ function TestimonialsStatsHeader({
568
985
  children: testimonials.map((testimonial, index) => {
569
986
  const authorName = getAuthorName(testimonial);
570
987
  const avatarSrc = getAvatarSrc(testimonial);
571
- return /* @__PURE__ */ jsx(Card, { className: cardClassName, children: /* @__PURE__ */ jsxs(CardContent, { className: "p-6", children: [
572
- /* @__PURE__ */ jsx("div", { className: "mb-4 flex gap-1", children: [...Array(5)].map((_, i) => /* @__PURE__ */ jsx(
573
- DynamicIcon,
574
- {
575
- name: "lucide/star",
576
- size: 16,
577
- className: "fill-primary text-primary"
578
- },
579
- i
580
- )) }),
581
- testimonial.quote && (typeof testimonial.quote === "string" ? /* @__PURE__ */ jsxs(
582
- "p",
583
- {
584
- className: cn(
585
- "mb-6 text-sm leading-relaxed",
586
- quoteClassName
587
- ),
588
- children: [
589
- "\u201C",
590
- testimonial.quote,
591
- "\u201D"
592
- ]
593
- }
594
- ) : /* @__PURE__ */ jsx("div", { className: cn("mb-6", quoteClassName), children: testimonial.quote })),
595
- /* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-3", authorClassName), children: [
596
- /* @__PURE__ */ jsxs(Avatar, { className: "size-10", children: [
597
- /* @__PURE__ */ jsx(AvatarImage, { src: avatarSrc, alt: authorName }),
598
- /* @__PURE__ */ jsx(AvatarFallback, { children: getInitials(authorName) })
599
- ] }),
600
- /* @__PURE__ */ jsxs("div", { children: [
601
- testimonial.author && (typeof testimonial.author === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: testimonial.author }) : testimonial.author),
602
- testimonial.role && (typeof testimonial.role === "string" ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: testimonial.role }) : testimonial.role)
603
- ] })
604
- ] })
605
- ] }) }, index);
988
+ return /* @__PURE__ */ jsx(
989
+ "div",
990
+ {
991
+ className: cn(
992
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-2xl border py-0 shadow-xl",
993
+ cardClassName
994
+ ),
995
+ children: /* @__PURE__ */ jsx(CardContent, { className: "p-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-12 justify-between", children: [
996
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start gap-4", children: [
997
+ /* @__PURE__ */ jsx(StarRating, { rating: 5, size: 20 }),
998
+ testimonial.quote && (typeof testimonial.quote === "string" ? /* @__PURE__ */ jsxs(
999
+ "p",
1000
+ {
1001
+ className: cn(
1002
+ "mb-6 text-sm leading-relaxed",
1003
+ quoteClassName
1004
+ ),
1005
+ children: [
1006
+ "\u201C",
1007
+ testimonial.quote,
1008
+ "\u201D"
1009
+ ]
1010
+ }
1011
+ ) : /* @__PURE__ */ jsx("div", { className: cn("mb-6", quoteClassName), children: testimonial.quote }))
1012
+ ] }),
1013
+ /* @__PURE__ */ jsxs(
1014
+ "div",
1015
+ {
1016
+ className: cn("flex items-center gap-3", authorClassName),
1017
+ children: [
1018
+ /* @__PURE__ */ jsxs(Avatar, { className: "size-10", children: [
1019
+ /* @__PURE__ */ jsx(AvatarImage, { src: avatarSrc, alt: authorName }),
1020
+ /* @__PURE__ */ jsx(AvatarFallback, { children: getInitials(authorName) })
1021
+ ] }),
1022
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1023
+ /* @__PURE__ */ jsxs("div", { children: [
1024
+ testimonial.author && (typeof testimonial.author === "string" ? /* @__PURE__ */ jsx("p", { className: "text-base font-medium", children: testimonial.author }) : testimonial.author),
1025
+ testimonial.role && (typeof testimonial.role === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm opacity-75", children: testimonial.role }) : testimonial.role)
1026
+ ] }),
1027
+ testimonial.linkConfig?.href && /* @__PURE__ */ jsx(
1028
+ Pressable,
1029
+ {
1030
+ href: testimonial.linkConfig.href,
1031
+ className: cn(
1032
+ "text-sm transition-all duration-300",
1033
+ "hover:underline hover:underline-offset-4",
1034
+ testimonial.linkConfig.className
1035
+ ),
1036
+ children: testimonial.linkConfig.label
1037
+ }
1038
+ )
1039
+ ] })
1040
+ ]
1041
+ }
1042
+ )
1043
+ ] }) })
1044
+ },
1045
+ index
1046
+ );
606
1047
  })
607
1048
  }
608
1049
  );