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