@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.
- package/dist/carousel-feature-badge.cjs +4 -3
- package/dist/carousel-feature-badge.d.cts +1 -1
- package/dist/carousel-feature-badge.d.ts +1 -1
- package/dist/carousel-feature-badge.js +4 -3
- package/dist/carousel-scrolling-feature-showcase.cjs +47 -38
- package/dist/carousel-scrolling-feature-showcase.js +47 -38
- package/dist/registry.cjs +454 -265
- package/dist/registry.js +454 -265
- package/dist/testimonials-grid-add-review.cjs +578 -39
- package/dist/testimonials-grid-add-review.d.cts +26 -26
- package/dist/testimonials-grid-add-review.d.ts +26 -26
- package/dist/testimonials-grid-add-review.js +577 -38
- package/dist/testimonials-images-helpful.cjs +85 -74
- package/dist/testimonials-images-helpful.js +85 -74
- package/dist/testimonials-list-verified.cjs +1 -0
- package/dist/testimonials-list-verified.js +1 -0
- package/dist/testimonials-logo-cards.cjs +8 -5
- package/dist/testimonials-logo-cards.js +8 -5
- package/dist/testimonials-masonry-grid.cjs +87 -11
- package/dist/testimonials-masonry-grid.d.cts +14 -1
- package/dist/testimonials-masonry-grid.d.ts +14 -1
- package/dist/testimonials-masonry-grid.js +88 -12
- package/dist/testimonials-mini-dividers.cjs +438 -26
- package/dist/testimonials-mini-dividers.js +434 -22
- package/dist/testimonials-minimal-numbered.cjs +1 -1
- package/dist/testimonials-minimal-numbered.js +1 -1
- package/dist/testimonials-parallax-number.cjs +1 -1
- package/dist/testimonials-parallax-number.js +1 -1
- package/dist/testimonials-quote-carousel.cjs +39 -37
- package/dist/testimonials-quote-carousel.d.cts +5 -1
- package/dist/testimonials-quote-carousel.d.ts +5 -1
- package/dist/testimonials-quote-carousel.js +39 -37
- package/dist/testimonials-scrolling-columns.cjs +438 -8
- package/dist/testimonials-scrolling-columns.js +436 -6
- package/dist/testimonials-simple-grid.cjs +82 -6
- package/dist/testimonials-simple-grid.d.cts +14 -1
- package/dist/testimonials-simple-grid.d.ts +14 -1
- package/dist/testimonials-simple-grid.js +83 -7
- package/dist/testimonials-stats-header.cjs +88 -8
- package/dist/testimonials-stats-header.d.cts +14 -1
- package/dist/testimonials-stats-header.d.ts +14 -1
- package/dist/testimonials-stats-header.js +89 -9
- package/dist/testimonials-twitter-cards.cjs +150 -25
- package/dist/testimonials-twitter-cards.d.cts +14 -1
- package/dist/testimonials-twitter-cards.d.ts +14 -1
- package/dist/testimonials-twitter-cards.js +151 -26
- package/dist/testimonials-wall-compact.cjs +529 -50
- package/dist/testimonials-wall-compact.d.cts +14 -1
- package/dist/testimonials-wall-compact.d.ts +14 -1
- package/dist/testimonials-wall-compact.js +526 -44
- package/package.json +1 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
var
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
531
|
-
if (typeof
|
|
1014
|
+
const getAuthorName = React4.useCallback((testimonial) => {
|
|
1015
|
+
if (typeof testimonial.author === "string") return testimonial.author;
|
|
532
1016
|
return "";
|
|
533
1017
|
}, []);
|
|
534
|
-
const
|
|
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 =
|
|
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-
|
|
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((
|
|
579
|
-
const authorName = getAuthorName(
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
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(
|
|
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
|
-
) :
|
|
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-
|
|
1166
|
+
"mt-2 md:mt-4 text-lg text-balance",
|
|
637
1167
|
descriptionClassName
|
|
638
1168
|
),
|
|
639
1169
|
children: description
|
|
640
1170
|
}
|
|
641
|
-
) :
|
|
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
|
);
|