@opensite/ui 0.7.9 → 0.8.0

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.
@@ -5,8 +5,8 @@ var React = require('react');
5
5
  var framerMotion = require('framer-motion');
6
6
  var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
8
- var classVarianceAuthority = require('class-variance-authority');
9
8
  var jsxRuntime = require('react/jsx-runtime');
9
+ var classVarianceAuthority = require('class-variance-authority');
10
10
  var img = require('@page-speed/img');
11
11
 
12
12
  function _interopNamespace(e) {
@@ -33,6 +33,111 @@ var React__namespace = /*#__PURE__*/_interopNamespace(React);
33
33
  function cn(...inputs) {
34
34
  return tailwindMerge.twMerge(clsx.clsx(inputs));
35
35
  }
36
+ var svgCache = /* @__PURE__ */ new Map();
37
+ function DynamicIcon({
38
+ name,
39
+ size = 28,
40
+ color,
41
+ className,
42
+ alt
43
+ }) {
44
+ const [svgContent, setSvgContent] = React__namespace.useState(null);
45
+ const [isLoading, setIsLoading] = React__namespace.useState(true);
46
+ const [error, setError] = React__namespace.useState(null);
47
+ const { url, iconName } = React__namespace.useMemo(() => {
48
+ const separator = name.includes("/") ? "/" : ":";
49
+ const [prefix, iconName2] = name.split(separator);
50
+ const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}`;
51
+ return {
52
+ url: baseUrl,
53
+ iconName: iconName2
54
+ };
55
+ }, [name, size]);
56
+ React__namespace.useEffect(() => {
57
+ let isMounted = true;
58
+ const fetchSvg = async () => {
59
+ const cached = svgCache.get(url);
60
+ if (cached) {
61
+ if (isMounted) {
62
+ setSvgContent(cached);
63
+ setIsLoading(false);
64
+ }
65
+ return;
66
+ }
67
+ try {
68
+ setIsLoading(true);
69
+ setError(null);
70
+ const response = await fetch(url);
71
+ if (!response.ok) {
72
+ throw new Error(`Failed to fetch icon: ${response.status}`);
73
+ }
74
+ let svg = await response.text();
75
+ svg = processSvgForCurrentColor(svg);
76
+ svgCache.set(url, svg);
77
+ if (isMounted) {
78
+ setSvgContent(svg);
79
+ setIsLoading(false);
80
+ }
81
+ } catch (err) {
82
+ if (isMounted) {
83
+ setError(err instanceof Error ? err.message : "Failed to load icon");
84
+ setIsLoading(false);
85
+ }
86
+ }
87
+ };
88
+ fetchSvg();
89
+ return () => {
90
+ isMounted = false;
91
+ };
92
+ }, [url]);
93
+ if (isLoading) {
94
+ return /* @__PURE__ */ jsxRuntime.jsx(
95
+ "span",
96
+ {
97
+ className: cn("inline-block", className),
98
+ style: { width: size, height: size },
99
+ "aria-hidden": "true"
100
+ }
101
+ );
102
+ }
103
+ if (error || !svgContent) {
104
+ return /* @__PURE__ */ jsxRuntime.jsx(
105
+ "span",
106
+ {
107
+ className: cn("inline-block", className),
108
+ style: { width: size, height: size },
109
+ role: "img",
110
+ "aria-label": alt || iconName
111
+ }
112
+ );
113
+ }
114
+ return /* @__PURE__ */ jsxRuntime.jsx(
115
+ "span",
116
+ {
117
+ className: cn("inline-flex items-center justify-center", className),
118
+ style: {
119
+ width: size,
120
+ height: size,
121
+ color: color || "inherit"
122
+ },
123
+ role: "img",
124
+ "aria-label": alt || iconName,
125
+ dangerouslySetInnerHTML: { __html: svgContent }
126
+ }
127
+ );
128
+ }
129
+ function processSvgForCurrentColor(svg) {
130
+ let processed = svg;
131
+ processed = processed.replace(
132
+ /stroke=["'](#000000|#000|black)["']/gi,
133
+ 'stroke="currentColor"'
134
+ );
135
+ processed = processed.replace(
136
+ /fill=["'](#000000|#000|black)["']/gi,
137
+ 'fill="currentColor"'
138
+ );
139
+ return processed;
140
+ }
36
141
  function normalizePhoneNumber(input) {
37
142
  const trimmed = input.trim();
38
143
  if (trimmed.toLowerCase().startsWith("tel:")) {
@@ -451,110 +556,49 @@ var Pressable = React__namespace.forwardRef(
451
556
  }
452
557
  );
453
558
  Pressable.displayName = "Pressable";
454
- var svgCache = /* @__PURE__ */ new Map();
455
- function DynamicIcon({
456
- name,
457
- size = 28,
458
- color,
559
+ function CarouselPagination({
560
+ onPrevious,
561
+ onNext,
562
+ canScrollPrevious = true,
563
+ canScrollNext = true,
564
+ iconSize = 24,
459
565
  className,
460
- alt
566
+ buttonClassName,
567
+ previousIcon = "lucide/arrow-left",
568
+ nextIcon = "lucide/arrow-right",
569
+ previousAriaLabel = "Previous",
570
+ nextAriaLabel = "Next"
461
571
  }) {
462
- const [svgContent, setSvgContent] = React__namespace.useState(null);
463
- const [isLoading, setIsLoading] = React__namespace.useState(true);
464
- const [error, setError] = React__namespace.useState(null);
465
- const { url, iconName } = React__namespace.useMemo(() => {
466
- const separator = name.includes("/") ? "/" : ":";
467
- const [prefix, iconName2] = name.split(separator);
468
- const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}`;
469
- return {
470
- url: baseUrl,
471
- iconName: iconName2
472
- };
473
- }, [name, size]);
474
- React__namespace.useEffect(() => {
475
- let isMounted = true;
476
- const fetchSvg = async () => {
477
- const cached = svgCache.get(url);
478
- if (cached) {
479
- if (isMounted) {
480
- setSvgContent(cached);
481
- setIsLoading(false);
482
- }
483
- return;
484
- }
485
- try {
486
- setIsLoading(true);
487
- setError(null);
488
- const response = await fetch(url);
489
- if (!response.ok) {
490
- throw new Error(`Failed to fetch icon: ${response.status}`);
491
- }
492
- let svg = await response.text();
493
- svg = processSvgForCurrentColor(svg);
494
- svgCache.set(url, svg);
495
- if (isMounted) {
496
- setSvgContent(svg);
497
- setIsLoading(false);
498
- }
499
- } catch (err) {
500
- if (isMounted) {
501
- setError(err instanceof Error ? err.message : "Failed to load icon");
502
- setIsLoading(false);
503
- }
504
- }
505
- };
506
- fetchSvg();
507
- return () => {
508
- isMounted = false;
509
- };
510
- }, [url]);
511
- if (isLoading) {
512
- return /* @__PURE__ */ jsxRuntime.jsx(
513
- "span",
572
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex justify-end gap-2", className), children: [
573
+ /* @__PURE__ */ jsxRuntime.jsx(
574
+ Pressable,
514
575
  {
515
- className: cn("inline-block", className),
516
- style: { width: size, height: size },
517
- "aria-hidden": "true"
576
+ onClick: onPrevious,
577
+ disabled: !canScrollPrevious,
578
+ "aria-label": previousAriaLabel,
579
+ asButton: true,
580
+ className: cn(
581
+ "relative z-40 flex h-10 w-10 items-center justify-center rounded-full disabled:opacity-50",
582
+ buttonClassName
583
+ ),
584
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: previousIcon, size: iconSize })
518
585
  }
519
- );
520
- }
521
- if (error || !svgContent) {
522
- return /* @__PURE__ */ jsxRuntime.jsx(
523
- "span",
586
+ ),
587
+ /* @__PURE__ */ jsxRuntime.jsx(
588
+ Pressable,
524
589
  {
525
- className: cn("inline-block", className),
526
- style: { width: size, height: size },
527
- role: "img",
528
- "aria-label": alt || iconName
590
+ onClick: onNext,
591
+ disabled: !canScrollNext,
592
+ "aria-label": nextAriaLabel,
593
+ asButton: true,
594
+ className: cn(
595
+ "relative z-40 flex h-10 w-10 items-center justify-center rounded-full disabled:opacity-50",
596
+ buttonClassName
597
+ ),
598
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: nextIcon, size: iconSize })
529
599
  }
530
- );
531
- }
532
- return /* @__PURE__ */ jsxRuntime.jsx(
533
- "span",
534
- {
535
- className: cn("inline-flex items-center justify-center", className),
536
- style: {
537
- width: size,
538
- height: size,
539
- color: color || "inherit"
540
- },
541
- role: "img",
542
- "aria-label": alt || iconName,
543
- dangerouslySetInnerHTML: { __html: svgContent }
544
- }
545
- );
546
- }
547
- function processSvgForCurrentColor(svg) {
548
- let processed = svg;
549
- processed = processed.replace(
550
- /stroke=["'](#000000|#000|black)["']/gi,
551
- 'stroke="currentColor"'
552
- );
553
- processed = processed.replace(
554
- /fill=["'](#000000|#000|black)["']/gi,
555
- 'fill="currentColor"'
556
- );
557
- return processed;
600
+ )
601
+ ] });
558
602
  }
559
603
  var maxWidthStyles = {
560
604
  sm: "max-w-screen-sm",
@@ -953,28 +997,22 @@ function CarouselHorizontalCards({
953
997
  const carouselRef = React__namespace.useRef(null);
954
998
  const [isAtStart, setIsAtStart] = React__namespace.useState(true);
955
999
  const [isAtEnd, setIsAtEnd] = React__namespace.useState(false);
956
- const getCardWidth = React__namespace.useCallback(() => {
957
- if (typeof window === "undefined") return 320;
958
- if (window.innerWidth >= 1024) return 400;
959
- if (window.innerWidth >= 640) return 360;
960
- return 320;
961
- }, []);
962
- const scroll = (direction) => {
1000
+ const scrollLeft = () => {
1001
+ if (carouselRef.current) {
1002
+ carouselRef.current.scrollBy({ left: -300, behavior: "smooth" });
1003
+ }
1004
+ };
1005
+ const scrollRight = () => {
963
1006
  if (carouselRef.current) {
964
- const { scrollLeft } = carouselRef.current;
965
- const cardWidth = getCardWidth();
966
- const gap = 16;
967
- const scrollAmount = cardWidth + gap;
968
- const newScrollLeft = direction === "left" ? scrollLeft - scrollAmount : scrollLeft + scrollAmount;
969
- carouselRef.current.scrollTo({ left: newScrollLeft, behavior: "smooth" });
1007
+ carouselRef.current.scrollBy({ left: 300, behavior: "smooth" });
970
1008
  }
971
1009
  };
972
1010
  React__namespace.useEffect(() => {
973
1011
  const checkScrollPosition = () => {
974
1012
  if (carouselRef.current) {
975
- const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current;
976
- setIsAtStart(scrollLeft < 10);
977
- setIsAtEnd(scrollLeft + clientWidth >= scrollWidth - 10);
1013
+ const { scrollLeft: scrollLeft2, scrollWidth, clientWidth } = carouselRef.current;
1014
+ setIsAtStart(scrollLeft2 < 10);
1015
+ setIsAtEnd(scrollLeft2 + clientWidth >= scrollWidth - 10);
978
1016
  }
979
1017
  };
980
1018
  const currentRef = carouselRef.current;
@@ -990,41 +1028,6 @@ function CarouselHorizontalCards({
990
1028
  window.removeEventListener("resize", checkScrollPosition);
991
1029
  };
992
1030
  }, [items]);
993
- const renderItems = () => {
994
- if (itemsSlot) return itemsSlot;
995
- if (!items || items.length === 0) return null;
996
- return items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
997
- framerMotion.motion.div,
998
- {
999
- className: cn(
1000
- "group w-[320px] shrink-0 snap-start sm:w-[360px] lg:w-[400px]",
1001
- item.className
1002
- ),
1003
- initial: { opacity: 0, y: 20 },
1004
- animate: { opacity: 1, y: 0 },
1005
- transition: { duration: 0.5, delay: index * 0.1 },
1006
- children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-lg border bg-card text-card-foreground shadow-sm transition-shadow hover:shadow-md", children: [
1007
- /* @__PURE__ */ jsxRuntime.jsx(
1008
- img.Img,
1009
- {
1010
- alt: typeof item.title === "string" ? item.title : `Card ${index + 1}`,
1011
- className: cn("aspect-video w-full object-cover transition-transform group-hover:scale-105", item.imageClassName),
1012
- src: item.imageSrc,
1013
- optixFlowConfig
1014
- }
1015
- ),
1016
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
1017
- item.title && (typeof item.title === "string" ? /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-md font-semibold leading-tight text-card-foreground", children: item.title }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: item.title })),
1018
- (item.count !== void 0 || item.countLabel) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4", children: [
1019
- item.count !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-bold", children: item.count }),
1020
- item.countLabel && (typeof item.countLabel === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: item.countLabel }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: item.countLabel }))
1021
- ] })
1022
- ] })
1023
- ] })
1024
- },
1025
- item.id
1026
- ));
1027
- };
1028
1031
  return /* @__PURE__ */ jsxRuntime.jsx(
1029
1032
  Section,
1030
1033
  {
@@ -1035,74 +1038,143 @@ function CarouselHorizontalCards({
1035
1038
  patternOpacity,
1036
1039
  "aria-labelledby": "carousel-title",
1037
1040
  children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("container mx-auto px-4 md:px-6", containerClassName), children: [
1038
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mb-8 flex items-center justify-between gap-4", headerClassName), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1039
- heading && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: headingHref, className: "group inline-flex items-center", children: [
1040
- typeof heading === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1041
- "h2",
1042
- {
1043
- id: "carousel-title",
1044
- className: cn("text-2xl font-bold tracking-tight text-card-foreground md:text-3xl", headingClassName),
1045
- children: heading
1046
- }
1047
- ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: headingClassName, children: heading }),
1048
- /* @__PURE__ */ jsxRuntime.jsx(
1049
- DynamicIcon,
1050
- {
1051
- name: "lucide/chevron-right",
1052
- size: 24,
1053
- className: "ml-2 flex-shrink-0 self-center transition-transform group-hover:translate-x-1"
1054
- }
1055
- )
1056
- ] }),
1057
- subtitle && (typeof subtitle === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("mt-1 text-muted-foreground", subtitleClassName), children: subtitle }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: subtitleClassName, children: subtitle }))
1058
- ] }) }),
1059
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1041
+ /* @__PURE__ */ jsxRuntime.jsx(
1042
+ "div",
1043
+ {
1044
+ className: cn(
1045
+ "mb-8 flex items-center justify-between gap-4",
1046
+ headerClassName
1047
+ ),
1048
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1049
+ heading && /* @__PURE__ */ jsxRuntime.jsxs("a", { href: headingHref, className: "group inline-flex items-center", children: [
1050
+ typeof heading === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1051
+ "h2",
1052
+ {
1053
+ id: "carousel-title",
1054
+ className: cn(
1055
+ "text-2xl font-bold tracking-tight text-card-foreground md:text-3xl",
1056
+ headingClassName
1057
+ ),
1058
+ children: heading
1059
+ }
1060
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: headingClassName, children: heading }),
1061
+ /* @__PURE__ */ jsxRuntime.jsx(
1062
+ DynamicIcon,
1063
+ {
1064
+ name: "lucide/chevron-right",
1065
+ size: 24,
1066
+ className: "ml-2 shrink-0 self-center transition-transform group-hover:translate-x-1"
1067
+ }
1068
+ )
1069
+ ] }),
1070
+ subtitle && (typeof subtitle === "string" ? /* @__PURE__ */ jsxRuntime.jsx(
1071
+ "p",
1072
+ {
1073
+ className: cn(
1074
+ "mt-1 text-muted-foreground",
1075
+ subtitleClassName
1076
+ ),
1077
+ children: subtitle
1078
+ }
1079
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: subtitleClassName, children: subtitle }))
1080
+ ] })
1081
+ }
1082
+ ),
1083
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full", children: [
1060
1084
  /* @__PURE__ */ jsxRuntime.jsx(
1061
1085
  "div",
1062
1086
  {
1087
+ className: "flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-4 [scrollbar-width:none] md:py-6",
1063
1088
  ref: carouselRef,
1064
- className: cn(
1065
- "scrollbar-hide flex w-full space-x-4 overflow-x-auto pb-4",
1066
- "snap-x snap-mandatory scroll-pl-0",
1067
- carouselClassName
1068
- ),
1069
- children: renderItems()
1089
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1090
+ "div",
1091
+ {
1092
+ className: cn(
1093
+ "flex flex-row justify-start gap-4 pl-4",
1094
+ carouselClassName
1095
+ ),
1096
+ children: [
1097
+ items?.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
1098
+ framerMotion.motion.div,
1099
+ {
1100
+ className: cn(
1101
+ "rounded-lg last:pr-[5%] md:last:pr-[33%]"
1102
+ ),
1103
+ initial: { opacity: 0, y: 20 },
1104
+ animate: { opacity: 1, y: 0 },
1105
+ transition: { duration: 0.5, delay: 0.2 * index, ease: "easeOut" },
1106
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1107
+ "div",
1108
+ {
1109
+ className: cn(
1110
+ "group w-56 shrink-0 md:w-96",
1111
+ item.className
1112
+ ),
1113
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-lg border bg-card text-card-foreground shadow-sm transition-shadow hover:shadow-md", children: [
1114
+ /* @__PURE__ */ jsxRuntime.jsx(
1115
+ img.Img,
1116
+ {
1117
+ alt: typeof item.title === "string" ? item.title : `Card ${index + 1}`,
1118
+ className: cn(
1119
+ "aspect-video w-full object-cover transition-transform group-hover:scale-105",
1120
+ item.imageClassName
1121
+ ),
1122
+ src: item.imageSrc,
1123
+ optixFlowConfig
1124
+ }
1125
+ ),
1126
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
1127
+ item.title && (typeof item.title === "string" ? /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-md font-semibold leading-tight text-card-foreground", children: item.title }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: item.title })),
1128
+ (item.count !== void 0 || item.countLabel) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4", children: [
1129
+ item.count !== void 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xl font-bold", children: item.count }),
1130
+ item.countLabel && (typeof item.countLabel === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: item.countLabel }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: item.countLabel }))
1131
+ ] }),
1132
+ item.actions && item.actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex flex-wrap gap-2", children: item.actions.map((action, actionIndex) => {
1133
+ const {
1134
+ label,
1135
+ icon,
1136
+ iconAfter,
1137
+ children,
1138
+ className: actionClassName,
1139
+ asButton,
1140
+ ...pressableProps
1141
+ } = action;
1142
+ return /* @__PURE__ */ jsxRuntime.jsx(
1143
+ Pressable,
1144
+ {
1145
+ asButton,
1146
+ className: actionClassName,
1147
+ ...pressableProps,
1148
+ children: children ?? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1149
+ icon,
1150
+ label,
1151
+ iconAfter
1152
+ ] })
1153
+ },
1154
+ actionIndex
1155
+ );
1156
+ }) })
1157
+ ] })
1158
+ ] })
1159
+ }
1160
+ )
1161
+ },
1162
+ item.id
1163
+ )),
1164
+ itemsSlot
1165
+ ]
1166
+ }
1167
+ )
1070
1168
  }
1071
1169
  ),
1072
- !isAtStart && /* @__PURE__ */ jsxRuntime.jsx(
1073
- Pressable,
1074
- {
1075
- onClick: () => scroll("left"),
1076
- className: cn(
1077
- "absolute left-4 top-1/2 z-10 -translate-y-1/2",
1078
- "flex h-12 w-12 items-center justify-center",
1079
- "rounded-full border border-border/50 bg-background shadow-lg",
1080
- "text-foreground transition-all duration-200",
1081
- "hover:bg-accent hover:shadow-xl hover:scale-105",
1082
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1083
- navigationClassName
1084
- ),
1085
- "aria-label": "Scroll left",
1086
- asButton: true,
1087
- children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/chevron-left", size: 24 })
1088
- }
1089
- ),
1090
- !isAtEnd && /* @__PURE__ */ jsxRuntime.jsx(
1091
- Pressable,
1170
+ /* @__PURE__ */ jsxRuntime.jsx(
1171
+ CarouselPagination,
1092
1172
  {
1093
- onClick: () => scroll("right"),
1094
- className: cn(
1095
- "absolute right-4 top-1/2 z-10 -translate-y-1/2",
1096
- "flex h-12 w-12 items-center justify-center",
1097
- "rounded-full border border-border/50 bg-background shadow-lg",
1098
- "text-foreground transition-all duration-200",
1099
- "hover:bg-accent hover:shadow-xl hover:scale-105",
1100
- "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
1101
- navigationClassName
1102
- ),
1103
- "aria-label": "Scroll right",
1104
- asButton: true,
1105
- children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/chevron-right", size: 24 })
1173
+ onPrevious: scrollLeft,
1174
+ onNext: scrollRight,
1175
+ canScrollPrevious: !isAtStart,
1176
+ canScrollNext: !isAtEnd,
1177
+ className: cn("mr-0 md:mr-10", navigationClassName)
1106
1178
  }
1107
1179
  )
1108
1180
  ] })
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { P as PatternName } from './pattern-background-a7gKHzHy.cjs';
3
3
  import { f as SectionBackground, g as SectionSpacing } from './community-initiatives-CES_LEJI.cjs';
4
- import { O as OptixFlowConfig } from './blocks-tmd_MRJv.cjs';
4
+ import { A as ActionConfig, O as OptixFlowConfig } from './blocks-tmd_MRJv.cjs';
5
5
  import 'react/jsx-runtime';
6
6
  import 'class-variance-authority';
7
7
  import './button-variants-8mtEHxev.cjs';
@@ -42,6 +42,10 @@ interface CardItem {
42
42
  * Label for the count
43
43
  */
44
44
  countLabel?: React.ReactNode;
45
+ /**
46
+ * Array of action configurations for buttons/links on the card
47
+ */
48
+ actions?: ActionConfig[];
45
49
  /**
46
50
  * Additional CSS classes for the card
47
51
  */
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { P as PatternName } from './pattern-background-a7gKHzHy.js';
3
3
  import { f as SectionBackground, g as SectionSpacing } from './community-initiatives-BRz60ABw.js';
4
- import { O as OptixFlowConfig } from './blocks-DRE-f6cS.js';
4
+ import { A as ActionConfig, O as OptixFlowConfig } from './blocks-DRE-f6cS.js';
5
5
  import 'react/jsx-runtime';
6
6
  import 'class-variance-authority';
7
7
  import './button-variants-8mtEHxev.js';
@@ -42,6 +42,10 @@ interface CardItem {
42
42
  * Label for the count
43
43
  */
44
44
  countLabel?: React.ReactNode;
45
+ /**
46
+ * Array of action configurations for buttons/links on the card
47
+ */
48
+ actions?: ActionConfig[];
45
49
  /**
46
50
  * Additional CSS classes for the card
47
51
  */