@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,9 +1,10 @@
1
1
  "use client";
2
- import React, { useCallback, useMemo } from 'react';
2
+ import * as React3 from 'react';
3
+ import React3__default, { useCallback, useMemo } from 'react';
3
4
  import { clsx } from 'clsx';
4
5
  import { twMerge } from 'tailwind-merge';
5
6
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
6
- import { jsx, jsxs } from 'react/jsx-runtime';
7
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
8
  import { Slot } from '@radix-ui/react-slot';
8
9
  import { cva } from 'class-variance-authority';
9
10
 
@@ -11,36 +12,6 @@ import { cva } from 'class-variance-authority';
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
  function Avatar({
45
16
  className,
46
17
  ...props
@@ -127,7 +98,7 @@ var maxWidthStyles = {
127
98
  "4xl": "max-w-[1536px]",
128
99
  full: "max-w-full"
129
100
  };
130
- var Container = React.forwardRef(
101
+ var Container = React3__default.forwardRef(
131
102
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
132
103
  const Component = as;
133
104
  return /* @__PURE__ */ jsx(
@@ -433,7 +404,7 @@ var spacingStyles = {
433
404
  };
434
405
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl", "hero"];
435
406
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
436
- var Section = React.forwardRef(
407
+ var Section = React3__default.forwardRef(
437
408
  ({
438
409
  id,
439
410
  title,
@@ -494,6 +465,488 @@ var Section = React.forwardRef(
494
465
  }
495
466
  );
496
467
  Section.displayName = "Section";
468
+ var baseStyles = [
469
+ // Layout
470
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
471
+ // Typography - using CSS variables with sensible defaults
472
+ "font-[var(--button-font-family,inherit)]",
473
+ "font-[var(--button-font-weight,500)]",
474
+ "tracking-[var(--button-letter-spacing,0)]",
475
+ "leading-[var(--button-line-height,1.25)]",
476
+ "[text-transform:var(--button-text-transform,none)]",
477
+ "text-sm",
478
+ // Border radius
479
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
480
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
481
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
482
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
483
+ "[box-shadow:var(--button-shadow,none)]",
484
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
485
+ // Disabled state
486
+ "disabled:pointer-events-none disabled:opacity-50",
487
+ // SVG handling
488
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
489
+ // Focus styles
490
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
491
+ // Invalid state
492
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
493
+ ].join(" ");
494
+ var buttonVariants = cva(baseStyles, {
495
+ variants: {
496
+ variant: {
497
+ // Default (Primary) variant - full customization
498
+ default: [
499
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
500
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
501
+ "border-[length:var(--button-default-border-width,0px)]",
502
+ "border-[color:var(--button-default-border,transparent)]",
503
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
504
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
505
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
506
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
507
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
508
+ ].join(" "),
509
+ // Destructive variant - full customization
510
+ destructive: [
511
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
512
+ "text-[var(--button-destructive-fg,white)]",
513
+ "border-[length:var(--button-destructive-border-width,0px)]",
514
+ "border-[color:var(--button-destructive-border,transparent)]",
515
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
516
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
517
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
518
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
519
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
520
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
521
+ "dark:bg-destructive/60"
522
+ ].join(" "),
523
+ // Outline variant - full customization with proper border handling
524
+ outline: [
525
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
526
+ "text-[var(--button-outline-fg,inherit)]",
527
+ "border-[length:var(--button-outline-border-width,1px)]",
528
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
529
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
530
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
531
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
532
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
533
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
534
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
535
+ ].join(" "),
536
+ // Secondary variant - full customization
537
+ secondary: [
538
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
539
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
540
+ "border-[length:var(--button-secondary-border-width,0px)]",
541
+ "border-[color:var(--button-secondary-border,transparent)]",
542
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
543
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
544
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
545
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
546
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
547
+ ].join(" "),
548
+ // Ghost variant - full customization
549
+ ghost: [
550
+ "bg-[var(--button-ghost-bg,transparent)]",
551
+ "text-[var(--button-ghost-fg,inherit)]",
552
+ "border-[length:var(--button-ghost-border-width,0px)]",
553
+ "border-[color:var(--button-ghost-border,transparent)]",
554
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
555
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
556
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
557
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
558
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
559
+ "dark:hover:bg-accent/50"
560
+ ].join(" "),
561
+ // Link variant - full customization
562
+ link: [
563
+ "bg-[var(--button-link-bg,transparent)]",
564
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
565
+ "border-[length:var(--button-link-border-width,0px)]",
566
+ "border-[color:var(--button-link-border,transparent)]",
567
+ "[box-shadow:var(--button-link-shadow,none)]",
568
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
569
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
570
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
571
+ "underline-offset-4 hover:underline"
572
+ ].join(" ")
573
+ },
574
+ size: {
575
+ default: [
576
+ "h-[var(--button-height-md,2.25rem)]",
577
+ "px-[var(--button-padding-x-md,1rem)]",
578
+ "py-[var(--button-padding-y-md,0.5rem)]",
579
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
580
+ ].join(" "),
581
+ sm: [
582
+ "h-[var(--button-height-sm,2rem)]",
583
+ "px-[var(--button-padding-x-sm,0.75rem)]",
584
+ "py-[var(--button-padding-y-sm,0.25rem)]",
585
+ "gap-1.5",
586
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
587
+ ].join(" "),
588
+ md: [
589
+ "h-[var(--button-height-md,2.25rem)]",
590
+ "px-[var(--button-padding-x-md,1rem)]",
591
+ "py-[var(--button-padding-y-md,0.5rem)]",
592
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
593
+ ].join(" "),
594
+ lg: [
595
+ "h-[var(--button-height-lg,2.5rem)]",
596
+ "px-[var(--button-padding-x-lg,1.5rem)]",
597
+ "py-[var(--button-padding-y-lg,0.5rem)]",
598
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
599
+ ].join(" "),
600
+ icon: "size-[var(--button-height-md,2.25rem)]",
601
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
602
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
603
+ }
604
+ },
605
+ defaultVariants: {
606
+ variant: "default",
607
+ size: "default"
608
+ }
609
+ });
610
+ function normalizePhoneNumber(input) {
611
+ const trimmed = input.trim();
612
+ if (trimmed.toLowerCase().startsWith("tel:")) {
613
+ return trimmed;
614
+ }
615
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
616
+ if (match) {
617
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
618
+ const extension = match[3];
619
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
620
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
621
+ return `tel:${withExtension}`;
622
+ }
623
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
624
+ return `tel:${cleaned}`;
625
+ }
626
+ function normalizeEmail(input) {
627
+ const trimmed = input.trim();
628
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
629
+ return trimmed;
630
+ }
631
+ return `mailto:${trimmed}`;
632
+ }
633
+ function isEmail(input) {
634
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
635
+ return emailRegex.test(input.trim());
636
+ }
637
+ function isPhoneNumber(input) {
638
+ const trimmed = input.trim();
639
+ if (trimmed.toLowerCase().startsWith("tel:")) {
640
+ return true;
641
+ }
642
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
643
+ return phoneRegex.test(trimmed);
644
+ }
645
+ function isInternalUrl(href) {
646
+ if (typeof window === "undefined") {
647
+ return href.startsWith("/") && !href.startsWith("//");
648
+ }
649
+ const trimmed = href.trim();
650
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
651
+ return true;
652
+ }
653
+ try {
654
+ const url = new URL(trimmed, window.location.href);
655
+ const currentOrigin = window.location.origin;
656
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
657
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
658
+ } catch {
659
+ return false;
660
+ }
661
+ }
662
+ function toRelativePath(href) {
663
+ if (typeof window === "undefined") {
664
+ return href;
665
+ }
666
+ const trimmed = href.trim();
667
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
668
+ return trimmed;
669
+ }
670
+ try {
671
+ const url = new URL(trimmed, window.location.href);
672
+ const currentOrigin = window.location.origin;
673
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
674
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
675
+ return url.pathname + url.search + url.hash;
676
+ }
677
+ } catch {
678
+ }
679
+ return trimmed;
680
+ }
681
+ function useNavigation({
682
+ href,
683
+ onClick
684
+ } = {}) {
685
+ const linkType = React3.useMemo(() => {
686
+ if (!href || href.trim() === "") {
687
+ return onClick ? "none" : "none";
688
+ }
689
+ const trimmed = href.trim();
690
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
691
+ return "mailto";
692
+ }
693
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
694
+ return "tel";
695
+ }
696
+ if (isInternalUrl(trimmed)) {
697
+ return "internal";
698
+ }
699
+ try {
700
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
701
+ return "external";
702
+ } catch {
703
+ return "internal";
704
+ }
705
+ }, [href, onClick]);
706
+ const normalizedHref = React3.useMemo(() => {
707
+ if (!href || href.trim() === "") {
708
+ return void 0;
709
+ }
710
+ const trimmed = href.trim();
711
+ switch (linkType) {
712
+ case "tel":
713
+ return normalizePhoneNumber(trimmed);
714
+ case "mailto":
715
+ return normalizeEmail(trimmed);
716
+ case "internal":
717
+ return toRelativePath(trimmed);
718
+ case "external":
719
+ return trimmed;
720
+ default:
721
+ return trimmed;
722
+ }
723
+ }, [href, linkType]);
724
+ const target = React3.useMemo(() => {
725
+ switch (linkType) {
726
+ case "external":
727
+ return "_blank";
728
+ case "internal":
729
+ return "_self";
730
+ case "mailto":
731
+ case "tel":
732
+ return void 0;
733
+ default:
734
+ return void 0;
735
+ }
736
+ }, [linkType]);
737
+ const rel = React3.useMemo(() => {
738
+ if (linkType === "external") {
739
+ return "noopener noreferrer";
740
+ }
741
+ return void 0;
742
+ }, [linkType]);
743
+ const isExternal = linkType === "external";
744
+ const isInternal = linkType === "internal";
745
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
746
+ const handleClick = React3.useCallback(
747
+ (event) => {
748
+ if (onClick) {
749
+ try {
750
+ onClick(event);
751
+ } catch (error) {
752
+ console.error("Error in user onClick handler:", error);
753
+ }
754
+ }
755
+ if (event.defaultPrevented) {
756
+ return;
757
+ }
758
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
759
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
760
+ if (typeof window !== "undefined") {
761
+ const handler = window.__opensiteNavigationHandler;
762
+ if (typeof handler === "function") {
763
+ try {
764
+ const handled = handler(normalizedHref, event.nativeEvent || event);
765
+ if (handled !== false) {
766
+ event.preventDefault();
767
+ }
768
+ } catch (error) {
769
+ console.error("Error in navigation handler:", error);
770
+ }
771
+ }
772
+ }
773
+ }
774
+ },
775
+ [onClick, shouldUseRouter, normalizedHref]
776
+ );
777
+ return {
778
+ linkType,
779
+ normalizedHref,
780
+ target,
781
+ rel,
782
+ isExternal,
783
+ isInternal,
784
+ shouldUseRouter,
785
+ handleClick
786
+ };
787
+ }
788
+ var Pressable = React3.forwardRef(
789
+ ({
790
+ children,
791
+ className,
792
+ href,
793
+ onClick,
794
+ variant,
795
+ size,
796
+ asButton = false,
797
+ fallbackComponentType = "span",
798
+ componentType,
799
+ "aria-label": ariaLabel,
800
+ "aria-describedby": ariaDescribedby,
801
+ id,
802
+ ...props
803
+ }, ref) => {
804
+ const navigation = useNavigation({ href, onClick });
805
+ const {
806
+ normalizedHref,
807
+ target,
808
+ rel,
809
+ linkType,
810
+ isInternal,
811
+ handleClick
812
+ } = navigation;
813
+ const shouldRenderLink = normalizedHref && linkType !== "none";
814
+ const shouldRenderButton = !shouldRenderLink && onClick;
815
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
816
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
817
+ const shouldApplyButtonStyles = asButton || variant || size;
818
+ const combinedClassName = cn(
819
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
820
+ className
821
+ );
822
+ const dataProps = Object.fromEntries(
823
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
824
+ );
825
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
826
+ "data-slot": "button",
827
+ "data-variant": variant ?? "default",
828
+ "data-size": size ?? "default"
829
+ } : {};
830
+ const commonProps = {
831
+ className: combinedClassName,
832
+ onClick: handleClick,
833
+ "aria-label": ariaLabel,
834
+ "aria-describedby": ariaDescribedby,
835
+ id,
836
+ ...dataProps,
837
+ ...buttonDataAttributes
838
+ };
839
+ if (finalComponentType === "a" && shouldRenderLink) {
840
+ return /* @__PURE__ */ jsx(
841
+ "a",
842
+ {
843
+ ref,
844
+ href: normalizedHref,
845
+ target,
846
+ rel,
847
+ ...commonProps,
848
+ ...props,
849
+ children
850
+ }
851
+ );
852
+ }
853
+ if (finalComponentType === "button") {
854
+ return /* @__PURE__ */ jsx(
855
+ "button",
856
+ {
857
+ ref,
858
+ type: props.type || "button",
859
+ ...commonProps,
860
+ ...props,
861
+ children
862
+ }
863
+ );
864
+ }
865
+ if (finalComponentType === "div") {
866
+ return /* @__PURE__ */ jsx(
867
+ "div",
868
+ {
869
+ ref,
870
+ ...commonProps,
871
+ children
872
+ }
873
+ );
874
+ }
875
+ return /* @__PURE__ */ jsx(
876
+ "span",
877
+ {
878
+ ref,
879
+ ...commonProps,
880
+ children
881
+ }
882
+ );
883
+ }
884
+ );
885
+ Pressable.displayName = "Pressable";
886
+ var MOBILE_CLASSES = {
887
+ "fit-left": "items-start md:items-center",
888
+ "fit-center": "items-center",
889
+ "fit-right": "items-end md:items-center",
890
+ "full-left": "items-stretch md:items-center",
891
+ "full-center": "items-stretch md:items-center",
892
+ "full-right": "items-stretch md:items-center"
893
+ };
894
+ function BlockActions({
895
+ mobileConfig,
896
+ actionsClassName,
897
+ verticalSpacing = "mt-4 md:mt-8",
898
+ actions,
899
+ actionsSlot
900
+ }) {
901
+ const width = mobileConfig?.width ?? "full";
902
+ const position = mobileConfig?.position ?? "center";
903
+ const mobileLayoutClass = MOBILE_CLASSES[`${width}-${position}`];
904
+ if (actionsSlot) {
905
+ return /* @__PURE__ */ jsx("div", { children: actionsSlot });
906
+ } else if (actions && actions?.length > 0) {
907
+ return /* @__PURE__ */ jsx(
908
+ "div",
909
+ {
910
+ className: cn(
911
+ "flex flex-col md:flex-row flex-wrap gap-4",
912
+ mobileLayoutClass,
913
+ actionsClassName,
914
+ verticalSpacing
915
+ ),
916
+ children: actions.map((action, index) => /* @__PURE__ */ jsx(ActionComponent, { action }, index))
917
+ }
918
+ );
919
+ } else {
920
+ return null;
921
+ }
922
+ }
923
+ function ActionComponent({ action }) {
924
+ const {
925
+ label,
926
+ icon,
927
+ iconAfter,
928
+ children,
929
+ href,
930
+ onClick,
931
+ className: actionClassName,
932
+ ...pressableProps
933
+ } = action;
934
+ return /* @__PURE__ */ jsx(
935
+ Pressable,
936
+ {
937
+ href,
938
+ onClick,
939
+ asButton: action.asButton ?? true,
940
+ className: actionClassName,
941
+ ...pressableProps,
942
+ children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
943
+ icon,
944
+ label,
945
+ iconAfter
946
+ ] })
947
+ }
948
+ );
949
+ }
497
950
  function TestimonialsWallCompact({
498
951
  testimonials,
499
952
  testimonialsSlot,
@@ -509,9 +962,12 @@ function TestimonialsWallCompact({
509
962
  quoteClassName,
510
963
  background,
511
964
  containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
512
- spacing = "xl",
965
+ spacing = "lg",
513
966
  pattern,
514
- patternOpacity
967
+ patternOpacity,
968
+ actions,
969
+ actionsSlot,
970
+ actionsClassName
515
971
  }) {
516
972
  const getAuthorName = useCallback(
517
973
  (testimonial) => {
@@ -546,9 +1002,8 @@ function TestimonialsWallCompact({
546
1002
  "div",
547
1003
  {
548
1004
  className: cn(
1005
+ "bg-card text-card-foreground",
549
1006
  "rounded-lg border p-4 transition-shadow hover:shadow-md",
550
- getNestedCardBg(background, "card"),
551
- getNestedCardTextColor(background),
552
1007
  cardClassName
553
1008
  ),
554
1009
  children: [
@@ -567,14 +1022,23 @@ function TestimonialsWallCompact({
567
1022
  ] }),
568
1023
  /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
569
1024
  testimonial.author && (typeof testimonial.author === "string" ? /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: testimonial.author }) : testimonial.author),
570
- testimonial.handle && /* @__PURE__ */ jsx("p", { className: "truncate text-xs ", children: testimonial.handle })
1025
+ testimonial.handle && /* @__PURE__ */ jsx("p", { className: "truncate text-xs font-semibold opacity-75", children: testimonial.handle })
571
1026
  ] })
572
1027
  ] }),
573
1028
  testimonial.badge && (typeof testimonial.badge === "string" ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "shrink-0 text-xs", children: testimonial.badge }) : testimonial.badge)
574
1029
  ]
575
1030
  }
576
1031
  ),
577
- testimonial.quote && (typeof testimonial.quote === "string" ? /* @__PURE__ */ jsx("p", { className: cn("text-sm leading-relaxed", quoteClassName), children: testimonial.quote }) : /* @__PURE__ */ jsx("div", { className: quoteClassName, children: testimonial.quote }))
1032
+ testimonial.quote && (typeof testimonial.quote === "string" ? /* @__PURE__ */ jsx(
1033
+ "p",
1034
+ {
1035
+ className: cn(
1036
+ "text-sm leading-relaxed pt-2",
1037
+ quoteClassName
1038
+ ),
1039
+ children: testimonial.quote
1040
+ }
1041
+ ) : testimonial.quote)
578
1042
  ]
579
1043
  },
580
1044
  index
@@ -606,23 +1070,41 @@ function TestimonialsWallCompact({
606
1070
  /* @__PURE__ */ jsxs(
607
1071
  "div",
608
1072
  {
609
- className: cn("mx-auto mb-12 max-w-2xl text-center", headerClassName),
1073
+ className: cn(
1074
+ "mx-auto mb-12 max-w-full md:max-w-md text-center",
1075
+ headerClassName
1076
+ ),
610
1077
  children: [
611
1078
  heading && (typeof heading === "string" ? /* @__PURE__ */ jsx(
612
1079
  "h2",
613
1080
  {
614
1081
  className: cn(
615
- "text-3xl font-semibold tracking-tight md:text-4xl",
1082
+ "text-3xl font-semibold tracking-tight md:text-4xl text-pretty",
616
1083
  headingClassName
617
1084
  ),
618
1085
  children: heading
619
1086
  }
620
1087
  ) : /* @__PURE__ */ jsx("div", { className: headingClassName, children: heading })),
621
- description && (typeof description === "string" ? /* @__PURE__ */ jsx("p", { className: cn("mt-4 text-lg", descriptionClassName), children: description }) : /* @__PURE__ */ jsx("div", { className: cn("mt-4", descriptionClassName), children: description }))
1088
+ description && (typeof description === "string" ? /* @__PURE__ */ jsx(
1089
+ "p",
1090
+ {
1091
+ className: cn("mt-4 text-lg text-balance", descriptionClassName),
1092
+ children: description
1093
+ }
1094
+ ) : description)
622
1095
  ]
623
1096
  }
624
1097
  ),
625
- renderedTestimonials
1098
+ renderedTestimonials,
1099
+ /* @__PURE__ */ jsx(
1100
+ BlockActions,
1101
+ {
1102
+ actions,
1103
+ actionsSlot,
1104
+ actionsClassName: cn("mt-8 md:mt-12 justify-center", actionsClassName),
1105
+ mobileConfig: { width: "full", position: "center" }
1106
+ }
1107
+ )
626
1108
  ]
627
1109
  }
628
1110
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensite/ui",
3
- "version": "2.9.0",
3
+ "version": "2.9.2",
4
4
  "description": "Foundational UI component library for OpenSite Semantic Site Builder with tree-shakable exports and abstract styling",
5
5
  "keywords": [
6
6
  "react",