@opensite/ui 2.9.0 → 2.9.2

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 (51) hide show
  1. package/dist/carousel-feature-badge.cjs +4 -3
  2. package/dist/carousel-feature-badge.d.cts +1 -1
  3. package/dist/carousel-feature-badge.d.ts +1 -1
  4. package/dist/carousel-feature-badge.js +4 -3
  5. package/dist/carousel-scrolling-feature-showcase.cjs +47 -38
  6. package/dist/carousel-scrolling-feature-showcase.js +47 -38
  7. package/dist/registry.cjs +454 -265
  8. package/dist/registry.js +454 -265
  9. package/dist/testimonials-grid-add-review.cjs +578 -39
  10. package/dist/testimonials-grid-add-review.d.cts +26 -26
  11. package/dist/testimonials-grid-add-review.d.ts +26 -26
  12. package/dist/testimonials-grid-add-review.js +577 -38
  13. package/dist/testimonials-images-helpful.cjs +85 -74
  14. package/dist/testimonials-images-helpful.js +85 -74
  15. package/dist/testimonials-list-verified.cjs +1 -0
  16. package/dist/testimonials-list-verified.js +1 -0
  17. package/dist/testimonials-logo-cards.cjs +8 -5
  18. package/dist/testimonials-logo-cards.js +8 -5
  19. package/dist/testimonials-masonry-grid.cjs +87 -11
  20. package/dist/testimonials-masonry-grid.d.cts +14 -1
  21. package/dist/testimonials-masonry-grid.d.ts +14 -1
  22. package/dist/testimonials-masonry-grid.js +88 -12
  23. package/dist/testimonials-mini-dividers.cjs +438 -26
  24. package/dist/testimonials-mini-dividers.js +434 -22
  25. package/dist/testimonials-minimal-numbered.cjs +1 -1
  26. package/dist/testimonials-minimal-numbered.js +1 -1
  27. package/dist/testimonials-parallax-number.cjs +1 -1
  28. package/dist/testimonials-parallax-number.js +1 -1
  29. package/dist/testimonials-quote-carousel.cjs +39 -37
  30. package/dist/testimonials-quote-carousel.d.cts +5 -1
  31. package/dist/testimonials-quote-carousel.d.ts +5 -1
  32. package/dist/testimonials-quote-carousel.js +39 -37
  33. package/dist/testimonials-scrolling-columns.cjs +438 -8
  34. package/dist/testimonials-scrolling-columns.js +436 -6
  35. package/dist/testimonials-simple-grid.cjs +82 -6
  36. package/dist/testimonials-simple-grid.d.cts +14 -1
  37. package/dist/testimonials-simple-grid.d.ts +14 -1
  38. package/dist/testimonials-simple-grid.js +83 -7
  39. package/dist/testimonials-stats-header.cjs +88 -8
  40. package/dist/testimonials-stats-header.d.cts +14 -1
  41. package/dist/testimonials-stats-header.d.ts +14 -1
  42. package/dist/testimonials-stats-header.js +89 -9
  43. package/dist/testimonials-twitter-cards.cjs +150 -25
  44. package/dist/testimonials-twitter-cards.d.cts +14 -1
  45. package/dist/testimonials-twitter-cards.d.ts +14 -1
  46. package/dist/testimonials-twitter-cards.js +151 -26
  47. package/dist/testimonials-wall-compact.cjs +529 -50
  48. package/dist/testimonials-wall-compact.d.cts +14 -1
  49. package/dist/testimonials-wall-compact.d.ts +14 -1
  50. package/dist/testimonials-wall-compact.js +526 -44
  51. package/package.json +1 -1
@@ -1,12 +1,13 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var React = require('react');
4
+ var React4 = require('react');
5
5
  var clsx = require('clsx');
6
6
  var tailwindMerge = require('tailwind-merge');
7
7
  var icon = require('@page-speed/icon');
8
8
  var jsxRuntime = require('react/jsx-runtime');
9
9
  var AvatarPrimitive = require('@radix-ui/react-avatar');
10
+ var classVarianceAuthority = require('class-variance-authority');
10
11
 
11
12
  function _interopNamespace(e) {
12
13
  if (e && e.__esModule) return e;
@@ -26,19 +27,15 @@ function _interopNamespace(e) {
26
27
  return Object.freeze(n);
27
28
  }
28
29
 
29
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
30
+ var React4__namespace = /*#__PURE__*/_interopNamespace(React4);
30
31
  var AvatarPrimitive__namespace = /*#__PURE__*/_interopNamespace(AvatarPrimitive);
31
32
 
32
33
  // components/blocks/testimonials/testimonials-grid-add-review.tsx
33
34
  function cn(...inputs) {
34
35
  return tailwindMerge.twMerge(clsx.clsx(inputs));
35
36
  }
36
- function getNestedCardTextColor(parentBg, options) {
37
- const isDark = parentBg === "dark" || parentBg === "secondary" || parentBg === "primary";
38
- return isDark ? "text-foreground" : "";
39
- }
40
37
  var DEFAULT_ICON_API_KEY = "au382bi7fsh96w9h9xlrnat2jglx";
41
- var DynamicIcon = React__namespace.memo(function DynamicIcon2({
38
+ var DynamicIcon = React4__namespace.memo(function DynamicIcon2({
42
39
  apiKey,
43
40
  ...props
44
41
  }) {
@@ -139,7 +136,7 @@ var maxWidthStyles = {
139
136
  "4xl": "max-w-[1536px]",
140
137
  full: "max-w-full"
141
138
  };
142
- var Container = React__namespace.default.forwardRef(
139
+ var Container = React4__namespace.default.forwardRef(
143
140
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
144
141
  const Component = as;
145
142
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -445,7 +442,7 @@ var spacingStyles = {
445
442
  };
446
443
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl", "hero"];
447
444
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
448
- var Section = React__namespace.default.forwardRef(
445
+ var Section = React4__namespace.default.forwardRef(
449
446
  ({
450
447
  id,
451
448
  title,
@@ -506,6 +503,488 @@ var Section = React__namespace.default.forwardRef(
506
503
  }
507
504
  );
508
505
  Section.displayName = "Section";
506
+ var baseStyles = [
507
+ // Layout
508
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
509
+ // Typography - using CSS variables with sensible defaults
510
+ "font-[var(--button-font-family,inherit)]",
511
+ "font-[var(--button-font-weight,500)]",
512
+ "tracking-[var(--button-letter-spacing,0)]",
513
+ "leading-[var(--button-line-height,1.25)]",
514
+ "[text-transform:var(--button-text-transform,none)]",
515
+ "text-sm",
516
+ // Border radius
517
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
518
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
519
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
520
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
521
+ "[box-shadow:var(--button-shadow,none)]",
522
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
523
+ // Disabled state
524
+ "disabled:pointer-events-none disabled:opacity-50",
525
+ // SVG handling
526
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
527
+ // Focus styles
528
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
529
+ // Invalid state
530
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
531
+ ].join(" ");
532
+ var buttonVariants = classVarianceAuthority.cva(baseStyles, {
533
+ variants: {
534
+ variant: {
535
+ // Default (Primary) variant - full customization
536
+ default: [
537
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
538
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
539
+ "border-[length:var(--button-default-border-width,0px)]",
540
+ "border-[color:var(--button-default-border,transparent)]",
541
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
542
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
543
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
544
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
545
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
546
+ ].join(" "),
547
+ // Destructive variant - full customization
548
+ destructive: [
549
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
550
+ "text-[var(--button-destructive-fg,white)]",
551
+ "border-[length:var(--button-destructive-border-width,0px)]",
552
+ "border-[color:var(--button-destructive-border,transparent)]",
553
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
554
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
555
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
556
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
557
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
558
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
559
+ "dark:bg-destructive/60"
560
+ ].join(" "),
561
+ // Outline variant - full customization with proper border handling
562
+ outline: [
563
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
564
+ "text-[var(--button-outline-fg,inherit)]",
565
+ "border-[length:var(--button-outline-border-width,1px)]",
566
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
567
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
568
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
569
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
570
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
571
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
572
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
573
+ ].join(" "),
574
+ // Secondary variant - full customization
575
+ secondary: [
576
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
577
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
578
+ "border-[length:var(--button-secondary-border-width,0px)]",
579
+ "border-[color:var(--button-secondary-border,transparent)]",
580
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
581
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
582
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
583
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
584
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
585
+ ].join(" "),
586
+ // Ghost variant - full customization
587
+ ghost: [
588
+ "bg-[var(--button-ghost-bg,transparent)]",
589
+ "text-[var(--button-ghost-fg,inherit)]",
590
+ "border-[length:var(--button-ghost-border-width,0px)]",
591
+ "border-[color:var(--button-ghost-border,transparent)]",
592
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
593
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
594
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
595
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
596
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
597
+ "dark:hover:bg-accent/50"
598
+ ].join(" "),
599
+ // Link variant - full customization
600
+ link: [
601
+ "bg-[var(--button-link-bg,transparent)]",
602
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
603
+ "border-[length:var(--button-link-border-width,0px)]",
604
+ "border-[color:var(--button-link-border,transparent)]",
605
+ "[box-shadow:var(--button-link-shadow,none)]",
606
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
607
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
608
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
609
+ "underline-offset-4 hover:underline"
610
+ ].join(" ")
611
+ },
612
+ size: {
613
+ default: [
614
+ "h-[var(--button-height-md,2.25rem)]",
615
+ "px-[var(--button-padding-x-md,1rem)]",
616
+ "py-[var(--button-padding-y-md,0.5rem)]",
617
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
618
+ ].join(" "),
619
+ sm: [
620
+ "h-[var(--button-height-sm,2rem)]",
621
+ "px-[var(--button-padding-x-sm,0.75rem)]",
622
+ "py-[var(--button-padding-y-sm,0.25rem)]",
623
+ "gap-1.5",
624
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
625
+ ].join(" "),
626
+ md: [
627
+ "h-[var(--button-height-md,2.25rem)]",
628
+ "px-[var(--button-padding-x-md,1rem)]",
629
+ "py-[var(--button-padding-y-md,0.5rem)]",
630
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
631
+ ].join(" "),
632
+ lg: [
633
+ "h-[var(--button-height-lg,2.5rem)]",
634
+ "px-[var(--button-padding-x-lg,1.5rem)]",
635
+ "py-[var(--button-padding-y-lg,0.5rem)]",
636
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
637
+ ].join(" "),
638
+ icon: "size-[var(--button-height-md,2.25rem)]",
639
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
640
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
641
+ }
642
+ },
643
+ defaultVariants: {
644
+ variant: "default",
645
+ size: "default"
646
+ }
647
+ });
648
+ function normalizePhoneNumber(input) {
649
+ const trimmed = input.trim();
650
+ if (trimmed.toLowerCase().startsWith("tel:")) {
651
+ return trimmed;
652
+ }
653
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
654
+ if (match) {
655
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
656
+ const extension = match[3];
657
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
658
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
659
+ return `tel:${withExtension}`;
660
+ }
661
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
662
+ return `tel:${cleaned}`;
663
+ }
664
+ function normalizeEmail(input) {
665
+ const trimmed = input.trim();
666
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
667
+ return trimmed;
668
+ }
669
+ return `mailto:${trimmed}`;
670
+ }
671
+ function isEmail(input) {
672
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
673
+ return emailRegex.test(input.trim());
674
+ }
675
+ function isPhoneNumber(input) {
676
+ const trimmed = input.trim();
677
+ if (trimmed.toLowerCase().startsWith("tel:")) {
678
+ return true;
679
+ }
680
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
681
+ return phoneRegex.test(trimmed);
682
+ }
683
+ function isInternalUrl(href) {
684
+ if (typeof window === "undefined") {
685
+ return href.startsWith("/") && !href.startsWith("//");
686
+ }
687
+ const trimmed = href.trim();
688
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
689
+ return true;
690
+ }
691
+ try {
692
+ const url = new URL(trimmed, window.location.href);
693
+ const currentOrigin = window.location.origin;
694
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
695
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
696
+ } catch {
697
+ return false;
698
+ }
699
+ }
700
+ function toRelativePath(href) {
701
+ if (typeof window === "undefined") {
702
+ return href;
703
+ }
704
+ const trimmed = href.trim();
705
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
706
+ return trimmed;
707
+ }
708
+ try {
709
+ const url = new URL(trimmed, window.location.href);
710
+ const currentOrigin = window.location.origin;
711
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
712
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
713
+ return url.pathname + url.search + url.hash;
714
+ }
715
+ } catch {
716
+ }
717
+ return trimmed;
718
+ }
719
+ function useNavigation({
720
+ href,
721
+ onClick
722
+ } = {}) {
723
+ const linkType = React4__namespace.useMemo(() => {
724
+ if (!href || href.trim() === "") {
725
+ return onClick ? "none" : "none";
726
+ }
727
+ const trimmed = href.trim();
728
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
729
+ return "mailto";
730
+ }
731
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
732
+ return "tel";
733
+ }
734
+ if (isInternalUrl(trimmed)) {
735
+ return "internal";
736
+ }
737
+ try {
738
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
739
+ return "external";
740
+ } catch {
741
+ return "internal";
742
+ }
743
+ }, [href, onClick]);
744
+ const normalizedHref = React4__namespace.useMemo(() => {
745
+ if (!href || href.trim() === "") {
746
+ return void 0;
747
+ }
748
+ const trimmed = href.trim();
749
+ switch (linkType) {
750
+ case "tel":
751
+ return normalizePhoneNumber(trimmed);
752
+ case "mailto":
753
+ return normalizeEmail(trimmed);
754
+ case "internal":
755
+ return toRelativePath(trimmed);
756
+ case "external":
757
+ return trimmed;
758
+ default:
759
+ return trimmed;
760
+ }
761
+ }, [href, linkType]);
762
+ const target = React4__namespace.useMemo(() => {
763
+ switch (linkType) {
764
+ case "external":
765
+ return "_blank";
766
+ case "internal":
767
+ return "_self";
768
+ case "mailto":
769
+ case "tel":
770
+ return void 0;
771
+ default:
772
+ return void 0;
773
+ }
774
+ }, [linkType]);
775
+ const rel = React4__namespace.useMemo(() => {
776
+ if (linkType === "external") {
777
+ return "noopener noreferrer";
778
+ }
779
+ return void 0;
780
+ }, [linkType]);
781
+ const isExternal = linkType === "external";
782
+ const isInternal = linkType === "internal";
783
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
784
+ const handleClick = React4__namespace.useCallback(
785
+ (event) => {
786
+ if (onClick) {
787
+ try {
788
+ onClick(event);
789
+ } catch (error) {
790
+ console.error("Error in user onClick handler:", error);
791
+ }
792
+ }
793
+ if (event.defaultPrevented) {
794
+ return;
795
+ }
796
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
797
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
798
+ if (typeof window !== "undefined") {
799
+ const handler = window.__opensiteNavigationHandler;
800
+ if (typeof handler === "function") {
801
+ try {
802
+ const handled = handler(normalizedHref, event.nativeEvent || event);
803
+ if (handled !== false) {
804
+ event.preventDefault();
805
+ }
806
+ } catch (error) {
807
+ console.error("Error in navigation handler:", error);
808
+ }
809
+ }
810
+ }
811
+ }
812
+ },
813
+ [onClick, shouldUseRouter, normalizedHref]
814
+ );
815
+ return {
816
+ linkType,
817
+ normalizedHref,
818
+ target,
819
+ rel,
820
+ isExternal,
821
+ isInternal,
822
+ shouldUseRouter,
823
+ handleClick
824
+ };
825
+ }
826
+ var Pressable = React4__namespace.forwardRef(
827
+ ({
828
+ children,
829
+ className,
830
+ href,
831
+ onClick,
832
+ variant,
833
+ size,
834
+ asButton = false,
835
+ fallbackComponentType = "span",
836
+ componentType,
837
+ "aria-label": ariaLabel,
838
+ "aria-describedby": ariaDescribedby,
839
+ id,
840
+ ...props
841
+ }, ref) => {
842
+ const navigation = useNavigation({ href, onClick });
843
+ const {
844
+ normalizedHref,
845
+ target,
846
+ rel,
847
+ linkType,
848
+ isInternal,
849
+ handleClick
850
+ } = navigation;
851
+ const shouldRenderLink = normalizedHref && linkType !== "none";
852
+ const shouldRenderButton = !shouldRenderLink && onClick;
853
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
854
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
855
+ const shouldApplyButtonStyles = asButton || variant || size;
856
+ const combinedClassName = cn(
857
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
858
+ className
859
+ );
860
+ const dataProps = Object.fromEntries(
861
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
862
+ );
863
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
864
+ "data-slot": "button",
865
+ "data-variant": variant ?? "default",
866
+ "data-size": size ?? "default"
867
+ } : {};
868
+ const commonProps = {
869
+ className: combinedClassName,
870
+ onClick: handleClick,
871
+ "aria-label": ariaLabel,
872
+ "aria-describedby": ariaDescribedby,
873
+ id,
874
+ ...dataProps,
875
+ ...buttonDataAttributes
876
+ };
877
+ if (finalComponentType === "a" && shouldRenderLink) {
878
+ return /* @__PURE__ */ jsxRuntime.jsx(
879
+ "a",
880
+ {
881
+ ref,
882
+ href: normalizedHref,
883
+ target,
884
+ rel,
885
+ ...commonProps,
886
+ ...props,
887
+ children
888
+ }
889
+ );
890
+ }
891
+ if (finalComponentType === "button") {
892
+ return /* @__PURE__ */ jsxRuntime.jsx(
893
+ "button",
894
+ {
895
+ ref,
896
+ type: props.type || "button",
897
+ ...commonProps,
898
+ ...props,
899
+ children
900
+ }
901
+ );
902
+ }
903
+ if (finalComponentType === "div") {
904
+ return /* @__PURE__ */ jsxRuntime.jsx(
905
+ "div",
906
+ {
907
+ ref,
908
+ ...commonProps,
909
+ children
910
+ }
911
+ );
912
+ }
913
+ return /* @__PURE__ */ jsxRuntime.jsx(
914
+ "span",
915
+ {
916
+ ref,
917
+ ...commonProps,
918
+ children
919
+ }
920
+ );
921
+ }
922
+ );
923
+ Pressable.displayName = "Pressable";
924
+ var MOBILE_CLASSES = {
925
+ "fit-left": "items-start md:items-center",
926
+ "fit-center": "items-center",
927
+ "fit-right": "items-end md:items-center",
928
+ "full-left": "items-stretch md:items-center",
929
+ "full-center": "items-stretch md:items-center",
930
+ "full-right": "items-stretch md:items-center"
931
+ };
932
+ function BlockActions({
933
+ mobileConfig,
934
+ actionsClassName,
935
+ verticalSpacing = "mt-4 md:mt-8",
936
+ actions,
937
+ actionsSlot
938
+ }) {
939
+ const width = mobileConfig?.width ?? "full";
940
+ const position = mobileConfig?.position ?? "center";
941
+ const mobileLayoutClass = MOBILE_CLASSES[`${width}-${position}`];
942
+ if (actionsSlot) {
943
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: actionsSlot });
944
+ } else if (actions && actions?.length > 0) {
945
+ return /* @__PURE__ */ jsxRuntime.jsx(
946
+ "div",
947
+ {
948
+ className: cn(
949
+ "flex flex-col md:flex-row flex-wrap gap-4",
950
+ mobileLayoutClass,
951
+ actionsClassName,
952
+ verticalSpacing
953
+ ),
954
+ children: actions.map((action, index) => /* @__PURE__ */ jsxRuntime.jsx(ActionComponent, { action }, index))
955
+ }
956
+ );
957
+ } else {
958
+ return null;
959
+ }
960
+ }
961
+ function ActionComponent({ action }) {
962
+ const {
963
+ label,
964
+ icon,
965
+ iconAfter,
966
+ children,
967
+ href,
968
+ onClick,
969
+ className: actionClassName,
970
+ ...pressableProps
971
+ } = action;
972
+ return /* @__PURE__ */ jsxRuntime.jsx(
973
+ Pressable,
974
+ {
975
+ href,
976
+ onClick,
977
+ asButton: action.asButton ?? true,
978
+ className: actionClassName,
979
+ ...pressableProps,
980
+ children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
981
+ icon,
982
+ label,
983
+ iconAfter
984
+ ] })
985
+ }
986
+ );
987
+ }
509
988
  function TestimonialsGridAddReview({
510
989
  reviews,
511
990
  reviewsSlot,
@@ -520,21 +999,32 @@ function TestimonialsGridAddReview({
520
999
  descriptionClassName,
521
1000
  gridClassName,
522
1001
  cardClassName,
1002
+ quoteClassName,
523
1003
  addReviewCardClassName,
524
1004
  authorClassName,
525
1005
  background,
526
- spacing,
1006
+ spacing = "lg",
1007
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
527
1008
  pattern,
528
- patternOpacity
1009
+ patternOpacity,
1010
+ actions,
1011
+ actionsSlot,
1012
+ actionsClassName
529
1013
  }) {
530
- const getAuthorName = React.useCallback((review) => {
531
- if (typeof review.author === "string") return review.author;
1014
+ const getAuthorName = React4.useCallback((testimonial) => {
1015
+ if (typeof testimonial.author === "string") return testimonial.author;
532
1016
  return "";
533
1017
  }, []);
534
- const getInitials = React.useCallback((name) => {
1018
+ const getAvatarSrc = React4.useCallback(
1019
+ (testimonial) => {
1020
+ return testimonial.avatarSrc || testimonial.avatar?.src;
1021
+ },
1022
+ []
1023
+ );
1024
+ const getInitials = React4.useCallback((name) => {
535
1025
  return name.split(" ").map((n) => n[0]).join("");
536
1026
  }, []);
537
- const renderedReviews = React.useMemo(() => {
1027
+ const renderedReviews = React4.useMemo(() => {
538
1028
  if (reviewsSlot) return reviewsSlot;
539
1029
  return /* @__PURE__ */ jsxRuntime.jsxs(
540
1030
  "div",
@@ -548,17 +1038,14 @@ function TestimonialsGridAddReview({
548
1038
  Card,
549
1039
  {
550
1040
  className: cn(
551
- "flex cursor-pointer items-center justify-center border-2 border-dashed transition-colors hover:border-primary hover:bg-muted/50",
1041
+ "flex cursor-pointer items-center justify-center border-2 border-dashed transition-all duration-500 opacity-100 hover:border-primary hover:opacity-75",
552
1042
  addReviewCardClassName
553
1043
  ),
554
1044
  onClick: onAddReview,
555
1045
  children: /* @__PURE__ */ jsxRuntime.jsxs(
556
1046
  CardContent,
557
1047
  {
558
- className: cn(
559
- "flex flex-col items-center gap-3 py-12 text-center",
560
- getNestedCardTextColor(background)
561
- ),
1048
+ className: cn("flex flex-col items-center gap-3 py-12 text-center"),
562
1049
  children: [
563
1050
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex size-12 items-center justify-center rounded-full bg-primary/10", children: /* @__PURE__ */ jsxRuntime.jsx(
564
1051
  DynamicIcon,
@@ -575,19 +1062,58 @@ function TestimonialsGridAddReview({
575
1062
  )
576
1063
  }
577
1064
  ),
578
- reviews?.map((review, index) => {
579
- const authorName = getAuthorName(review);
580
- return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cardClassName, children: /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "space-y-4 p-6", children: [
581
- /* @__PURE__ */ jsxRuntime.jsx(StarRating, { rating: review.rating }),
582
- review.content && (typeof review.content === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm leading-relaxed", children: review.content }) : review.content),
583
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex items-center gap-3", authorClassName), children: [
584
- /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: "size-8", children: [
585
- /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: review.avatarSrc, alt: authorName }),
586
- /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { className: "text-xs", children: getInitials(authorName) })
587
- ] }),
588
- review.author && (typeof review.author === "string" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium", children: review.author }) : review.author)
589
- ] })
590
- ] }) }, index);
1065
+ reviews?.map((testimonial, index) => {
1066
+ const authorName = getAuthorName(testimonial);
1067
+ const avatarSrc = getAvatarSrc(testimonial);
1068
+ return /* @__PURE__ */ jsxRuntime.jsx(Card, { className: cardClassName, children: /* @__PURE__ */ jsxRuntime.jsx(CardContent, { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-12 justify-between", children: [
1069
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-4", children: [
1070
+ /* @__PURE__ */ jsxRuntime.jsx(StarRating, { rating: 5, size: 20 }),
1071
+ testimonial.quote && (typeof testimonial.quote === "string" ? /* @__PURE__ */ jsxRuntime.jsxs(
1072
+ "p",
1073
+ {
1074
+ className: cn(
1075
+ "mb-6 text-sm leading-relaxed",
1076
+ quoteClassName
1077
+ ),
1078
+ children: [
1079
+ "\u201C",
1080
+ testimonial.quote,
1081
+ "\u201D"
1082
+ ]
1083
+ }
1084
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mb-6", quoteClassName), children: testimonial.quote }))
1085
+ ] }),
1086
+ /* @__PURE__ */ jsxRuntime.jsxs(
1087
+ "div",
1088
+ {
1089
+ className: cn("flex items-center gap-4", authorClassName),
1090
+ children: [
1091
+ /* @__PURE__ */ jsxRuntime.jsxs(Avatar, { className: "size-14 ring-4 ring-primary shadow-lg", children: [
1092
+ /* @__PURE__ */ jsxRuntime.jsx(AvatarImage, { src: avatarSrc, alt: authorName }),
1093
+ /* @__PURE__ */ jsxRuntime.jsx(AvatarFallback, { children: getInitials(authorName) })
1094
+ ] }),
1095
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-1", children: [
1096
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0", children: [
1097
+ testimonial.author && (typeof testimonial.author === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base font-medium", children: testimonial.author }) : testimonial.author),
1098
+ testimonial.role && (typeof testimonial.role === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm opacity-75", children: testimonial.role }) : testimonial.role)
1099
+ ] }),
1100
+ testimonial.linkConfig?.href && /* @__PURE__ */ jsxRuntime.jsx(
1101
+ Pressable,
1102
+ {
1103
+ href: testimonial.linkConfig.href,
1104
+ className: cn(
1105
+ "text-sm transition-all duration-500",
1106
+ "hover:underline hover:underline-offset-4",
1107
+ testimonial.linkConfig.className
1108
+ ),
1109
+ children: testimonial.linkConfig.label
1110
+ }
1111
+ )
1112
+ ] })
1113
+ ]
1114
+ }
1115
+ )
1116
+ ] }) }) }, index);
591
1117
  })
592
1118
  ]
593
1119
  }
@@ -613,36 +1139,49 @@ function TestimonialsGridAddReview({
613
1139
  pattern,
614
1140
  patternOpacity,
615
1141
  className,
1142
+ containerClassName,
616
1143
  children: [
617
1144
  /* @__PURE__ */ jsxRuntime.jsxs(
618
1145
  "div",
619
1146
  {
620
- className: cn("mx-auto mb-12 max-w-2xl text-center", headerClassName),
1147
+ className: cn(
1148
+ "mx-auto mb-12 max-w-full md:max-w-2xl text-center",
1149
+ headerClassName
1150
+ ),
621
1151
  children: [
622
1152
  heading && (typeof heading === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
623
1153
  "h2",
624
1154
  {
625
1155
  className: cn(
626
- "text-3xl font-semibold tracking-tight md:text-4xl",
1156
+ "text-3xl font-semibold tracking-tight md:text-4xl text-pretty",
627
1157
  headingClassName
628
1158
  ),
629
1159
  children: heading
630
1160
  }
631
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: headingClassName, children: heading })),
1161
+ ) : heading),
632
1162
  description && (typeof description === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
633
1163
  "p",
634
1164
  {
635
1165
  className: cn(
636
- "mt-4 text-lg text-muted-foreground",
1166
+ "mt-2 md:mt-4 text-lg text-balance",
637
1167
  descriptionClassName
638
1168
  ),
639
1169
  children: description
640
1170
  }
641
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mt-4", descriptionClassName), children: description }))
1171
+ ) : description)
642
1172
  ]
643
1173
  }
644
1174
  ),
645
- renderedReviews
1175
+ renderedReviews,
1176
+ /* @__PURE__ */ jsxRuntime.jsx(
1177
+ BlockActions,
1178
+ {
1179
+ actions,
1180
+ actionsSlot,
1181
+ actionsClassName: cn("mt-8 md:mt-12 justify-center", actionsClassName),
1182
+ mobileConfig: { width: "full", position: "center" }
1183
+ }
1184
+ )
646
1185
  ]
647
1186
  }
648
1187
  );