@opensite/ui 1.2.7 → 1.2.8

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.
@@ -689,63 +689,49 @@ function CarouselItem({ className, ...props }) {
689
689
  }
690
690
  );
691
691
  }
692
- function CarouselPrevious({
692
+ function CarouselPagination({
693
+ onPrevious,
694
+ onNext,
695
+ canScrollPrevious = true,
696
+ canScrollNext = true,
697
+ iconSize = 24,
693
698
  className,
694
- variant = "outline",
695
- size = "icon",
696
- ...props
699
+ buttonClassName,
700
+ previousIcon = "lucide/arrow-left",
701
+ nextIcon = "lucide/arrow-right",
702
+ previousAriaLabel = "Previous",
703
+ nextAriaLabel = "Next"
697
704
  }) {
698
- const { orientation, scrollPrev, canScrollPrev } = useCarousel();
699
- return /* @__PURE__ */ jsxRuntime.jsxs(
700
- Pressable,
701
- {
702
- "data-slot": "carousel-previous",
703
- variant,
704
- size,
705
- className: cn(
706
- "absolute size-8 rounded-full",
707
- orientation === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
708
- className
709
- ),
710
- disabled: !canScrollPrev,
711
- onClick: scrollPrev,
712
- asButton: true,
713
- ...props,
714
- children: [
715
- /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/arrow-left" }),
716
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Previous slide" })
717
- ]
718
- }
719
- );
720
- }
721
- function CarouselNext({
722
- className,
723
- variant = "default",
724
- size = "icon",
725
- ...props
726
- }) {
727
- const { orientation, scrollNext, canScrollNext } = useCarousel();
728
- return /* @__PURE__ */ jsxRuntime.jsxs(
729
- Pressable,
730
- {
731
- "data-slot": "carousel-next",
732
- variant,
733
- size,
734
- className: cn(
735
- "absolute size-8 rounded-full",
736
- orientation === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
737
- className
738
- ),
739
- disabled: !canScrollNext,
740
- onClick: scrollNext,
741
- asButton: true,
742
- ...props,
743
- children: [
744
- /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/arrow-right" }),
745
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Next slide" })
746
- ]
747
- }
748
- );
705
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex justify-end gap-2", className), children: [
706
+ /* @__PURE__ */ jsxRuntime.jsx(
707
+ Pressable,
708
+ {
709
+ onClick: onPrevious,
710
+ disabled: !canScrollPrevious,
711
+ "aria-label": previousAriaLabel,
712
+ asButton: true,
713
+ className: cn(
714
+ "relative z-40 flex h-10 w-10 items-center justify-center rounded-full disabled:opacity-50",
715
+ buttonClassName
716
+ ),
717
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: previousIcon, size: iconSize })
718
+ }
719
+ ),
720
+ /* @__PURE__ */ jsxRuntime.jsx(
721
+ Pressable,
722
+ {
723
+ onClick: onNext,
724
+ disabled: !canScrollNext,
725
+ "aria-label": nextAriaLabel,
726
+ asButton: true,
727
+ className: cn(
728
+ "relative z-40 flex h-10 w-10 items-center justify-center rounded-full disabled:opacity-50",
729
+ buttonClassName
730
+ ),
731
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: nextIcon, size: iconSize })
732
+ }
733
+ )
734
+ ] });
749
735
  }
750
736
  var maxWidthStyles = {
751
737
  sm: "max-w-screen-sm",
@@ -1150,19 +1136,33 @@ function CarouselScaleFocus({
1150
1136
  const [count, setCount] = React4.useState(0);
1151
1137
  const [lightboxOpen, setLightboxOpen] = React4.useState(false);
1152
1138
  const [lightboxIndex, setLightboxIndex] = React4.useState(0);
1139
+ const [canScrollPrevious, setCanScrollPrevious] = React4.useState(false);
1140
+ const [canScrollNext, setCanScrollNext] = React4.useState(false);
1153
1141
  React4.useEffect(() => {
1154
1142
  if (!api) {
1155
1143
  return;
1156
1144
  }
1157
- React4.startTransition(() => {
1158
- setCount(api.scrollSnapList().length);
1159
- setCurrent(api.selectedScrollSnap() + 1);
1160
- });
1161
- api.on("select", () => {
1145
+ const updateState = () => {
1162
1146
  React4.startTransition(() => {
1147
+ setCount(api.scrollSnapList().length);
1163
1148
  setCurrent(api.selectedScrollSnap() + 1);
1149
+ setCanScrollPrevious(api.canScrollPrev());
1150
+ setCanScrollNext(api.canScrollNext());
1164
1151
  });
1165
- });
1152
+ };
1153
+ updateState();
1154
+ api.on("select", updateState);
1155
+ api.on("reInit", updateState);
1156
+ return () => {
1157
+ api.off("select", updateState);
1158
+ api.off("reInit", updateState);
1159
+ };
1160
+ }, [api]);
1161
+ const scrollPrevious = React4.useCallback(() => {
1162
+ api?.scrollPrev();
1163
+ }, [api]);
1164
+ const scrollNext = React4.useCallback(() => {
1165
+ api?.scrollNext();
1166
1166
  }, [api]);
1167
1167
  const lightboxItems = React4.useMemo(() => {
1168
1168
  if (!images || images.length === 0) return [];
@@ -1263,63 +1263,37 @@ function CarouselScaleFocus({
1263
1263
  }
1264
1264
  ))
1265
1265
  ] }) : null,
1266
- /* @__PURE__ */ jsxRuntime.jsxs(
1267
- Carousel,
1268
- {
1269
- className: cn(
1270
- "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
1271
- carouselClassName
1272
- ),
1273
- setApi,
1274
- opts: {
1275
- startIndex
1276
- },
1277
- children: [
1278
- /* @__PURE__ */ jsxRuntime.jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent }),
1279
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("mt-4 hidden md:block", controlsClassName), children: [
1280
- /* @__PURE__ */ jsxRuntime.jsx(
1281
- CarouselPrevious,
1282
- {
1283
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-left-25 md:size-14 lg:-left-39.75 lg:size-14 [&>svg]:size-6!",
1284
- variant: "default"
1285
- }
1286
- ),
1287
- /* @__PURE__ */ jsxRuntime.jsx(
1288
- CarouselNext,
1289
- {
1290
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-right-25 md:size-14 lg:-right-39.75 lg:size-14 [&>svg]:size-6!",
1291
- variant: "default"
1292
- }
1293
- )
1294
- ] })
1295
- ]
1296
- }
1297
- ),
1298
- /* @__PURE__ */ jsxRuntime.jsxs(
1299
- "div",
1300
- {
1301
- className: cn(
1302
- "mt-10 flex items-center justify-center gap-4 md:hidden",
1303
- controlsClassName
1304
- ),
1305
- children: [
1306
- /* @__PURE__ */ jsxRuntime.jsx(
1307
- CarouselPrevious,
1308
- {
1309
- className: "static translate-x-0 translate-y-0 size-10",
1310
- variant: "default"
1311
- }
1266
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsx(
1268
+ Carousel,
1269
+ {
1270
+ className: cn(
1271
+ "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
1272
+ carouselClassName
1312
1273
  ),
1313
- /* @__PURE__ */ jsxRuntime.jsx(
1314
- CarouselNext,
1315
- {
1316
- className: "static translate-x-0 translate-y-0 size-10",
1317
- variant: "default"
1318
- }
1319
- )
1320
- ]
1321
- }
1322
- ),
1274
+ setApi,
1275
+ opts: {
1276
+ startIndex
1277
+ },
1278
+ children: /* @__PURE__ */ jsxRuntime.jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent })
1279
+ }
1280
+ ),
1281
+ /* @__PURE__ */ jsxRuntime.jsx(
1282
+ CarouselPagination,
1283
+ {
1284
+ onPrevious: scrollPrevious,
1285
+ onNext: scrollNext,
1286
+ canScrollPrevious,
1287
+ canScrollNext,
1288
+ iconSize: 24,
1289
+ className: cn(
1290
+ "mt-6 justify-center md:mt-0 md:absolute md:inset-x-0 md:top-1/2 md:-translate-y-1/2 md:pointer-events-none",
1291
+ controlsClassName
1292
+ ),
1293
+ buttonClassName: "size-10 md:size-14 bg-primary text-primary-foreground hover:bg-primary/90 pointer-events-auto md:absolute md:-left-25 lg:-left-39.75 [&:last-child]:md:left-auto [&:last-child]:md:-right-25 [&:last-child]:lg:-right-39.75"
1294
+ }
1295
+ )
1296
+ ] }),
1323
1297
  /* @__PURE__ */ jsxRuntime.jsx(
1324
1298
  "div",
1325
1299
  {
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import * as React4 from 'react';
3
- import React4__default, { useState, useEffect, startTransition, useMemo, useCallback } from 'react';
3
+ import React4__default, { useState, useEffect, useCallback, useMemo, startTransition } from 'react';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { Img } from '@page-speed/img';
@@ -665,63 +665,49 @@ function CarouselItem({ className, ...props }) {
665
665
  }
666
666
  );
667
667
  }
668
- function CarouselPrevious({
668
+ function CarouselPagination({
669
+ onPrevious,
670
+ onNext,
671
+ canScrollPrevious = true,
672
+ canScrollNext = true,
673
+ iconSize = 24,
669
674
  className,
670
- variant = "outline",
671
- size = "icon",
672
- ...props
673
- }) {
674
- const { orientation, scrollPrev, canScrollPrev } = useCarousel();
675
- return /* @__PURE__ */ jsxs(
676
- Pressable,
677
- {
678
- "data-slot": "carousel-previous",
679
- variant,
680
- size,
681
- className: cn(
682
- "absolute size-8 rounded-full",
683
- orientation === "horizontal" ? "top-1/2 -left-12 -translate-y-1/2" : "-top-12 left-1/2 -translate-x-1/2 rotate-90",
684
- className
685
- ),
686
- disabled: !canScrollPrev,
687
- onClick: scrollPrev,
688
- asButton: true,
689
- ...props,
690
- children: [
691
- /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/arrow-left" }),
692
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Previous slide" })
693
- ]
694
- }
695
- );
696
- }
697
- function CarouselNext({
698
- className,
699
- variant = "default",
700
- size = "icon",
701
- ...props
675
+ buttonClassName,
676
+ previousIcon = "lucide/arrow-left",
677
+ nextIcon = "lucide/arrow-right",
678
+ previousAriaLabel = "Previous",
679
+ nextAriaLabel = "Next"
702
680
  }) {
703
- const { orientation, scrollNext, canScrollNext } = useCarousel();
704
- return /* @__PURE__ */ jsxs(
705
- Pressable,
706
- {
707
- "data-slot": "carousel-next",
708
- variant,
709
- size,
710
- className: cn(
711
- "absolute size-8 rounded-full",
712
- orientation === "horizontal" ? "top-1/2 -right-12 -translate-y-1/2" : "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
713
- className
714
- ),
715
- disabled: !canScrollNext,
716
- onClick: scrollNext,
717
- asButton: true,
718
- ...props,
719
- children: [
720
- /* @__PURE__ */ jsx(DynamicIcon, { name: "lucide/arrow-right" }),
721
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Next slide" })
722
- ]
723
- }
724
- );
681
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex justify-end gap-2", className), children: [
682
+ /* @__PURE__ */ jsx(
683
+ Pressable,
684
+ {
685
+ onClick: onPrevious,
686
+ disabled: !canScrollPrevious,
687
+ "aria-label": previousAriaLabel,
688
+ asButton: true,
689
+ className: cn(
690
+ "relative z-40 flex h-10 w-10 items-center justify-center rounded-full disabled:opacity-50",
691
+ buttonClassName
692
+ ),
693
+ children: /* @__PURE__ */ jsx(DynamicIcon, { name: previousIcon, size: iconSize })
694
+ }
695
+ ),
696
+ /* @__PURE__ */ jsx(
697
+ Pressable,
698
+ {
699
+ onClick: onNext,
700
+ disabled: !canScrollNext,
701
+ "aria-label": nextAriaLabel,
702
+ asButton: true,
703
+ className: cn(
704
+ "relative z-40 flex h-10 w-10 items-center justify-center rounded-full disabled:opacity-50",
705
+ buttonClassName
706
+ ),
707
+ children: /* @__PURE__ */ jsx(DynamicIcon, { name: nextIcon, size: iconSize })
708
+ }
709
+ )
710
+ ] });
725
711
  }
726
712
  var maxWidthStyles = {
727
713
  sm: "max-w-screen-sm",
@@ -1126,19 +1112,33 @@ function CarouselScaleFocus({
1126
1112
  const [count, setCount] = useState(0);
1127
1113
  const [lightboxOpen, setLightboxOpen] = useState(false);
1128
1114
  const [lightboxIndex, setLightboxIndex] = useState(0);
1115
+ const [canScrollPrevious, setCanScrollPrevious] = useState(false);
1116
+ const [canScrollNext, setCanScrollNext] = useState(false);
1129
1117
  useEffect(() => {
1130
1118
  if (!api) {
1131
1119
  return;
1132
1120
  }
1133
- startTransition(() => {
1134
- setCount(api.scrollSnapList().length);
1135
- setCurrent(api.selectedScrollSnap() + 1);
1136
- });
1137
- api.on("select", () => {
1121
+ const updateState = () => {
1138
1122
  startTransition(() => {
1123
+ setCount(api.scrollSnapList().length);
1139
1124
  setCurrent(api.selectedScrollSnap() + 1);
1125
+ setCanScrollPrevious(api.canScrollPrev());
1126
+ setCanScrollNext(api.canScrollNext());
1140
1127
  });
1141
- });
1128
+ };
1129
+ updateState();
1130
+ api.on("select", updateState);
1131
+ api.on("reInit", updateState);
1132
+ return () => {
1133
+ api.off("select", updateState);
1134
+ api.off("reInit", updateState);
1135
+ };
1136
+ }, [api]);
1137
+ const scrollPrevious = useCallback(() => {
1138
+ api?.scrollPrev();
1139
+ }, [api]);
1140
+ const scrollNext = useCallback(() => {
1141
+ api?.scrollNext();
1142
1142
  }, [api]);
1143
1143
  const lightboxItems = useMemo(() => {
1144
1144
  if (!images || images.length === 0) return [];
@@ -1239,63 +1239,37 @@ function CarouselScaleFocus({
1239
1239
  }
1240
1240
  ))
1241
1241
  ] }) : null,
1242
- /* @__PURE__ */ jsxs(
1243
- Carousel,
1244
- {
1245
- className: cn(
1246
- "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
1247
- carouselClassName
1248
- ),
1249
- setApi,
1250
- opts: {
1251
- startIndex
1252
- },
1253
- children: [
1254
- /* @__PURE__ */ jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent }),
1255
- /* @__PURE__ */ jsxs("div", { className: cn("mt-4 hidden md:block", controlsClassName), children: [
1256
- /* @__PURE__ */ jsx(
1257
- CarouselPrevious,
1258
- {
1259
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-left-25 md:size-14 lg:-left-39.75 lg:size-14 [&>svg]:size-6!",
1260
- variant: "default"
1261
- }
1262
- ),
1263
- /* @__PURE__ */ jsx(
1264
- CarouselNext,
1265
- {
1266
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-right-25 md:size-14 lg:-right-39.75 lg:size-14 [&>svg]:size-6!",
1267
- variant: "default"
1268
- }
1269
- )
1270
- ] })
1271
- ]
1272
- }
1273
- ),
1274
- /* @__PURE__ */ jsxs(
1275
- "div",
1276
- {
1277
- className: cn(
1278
- "mt-10 flex items-center justify-center gap-4 md:hidden",
1279
- controlsClassName
1280
- ),
1281
- children: [
1282
- /* @__PURE__ */ jsx(
1283
- CarouselPrevious,
1284
- {
1285
- className: "static translate-x-0 translate-y-0 size-10",
1286
- variant: "default"
1287
- }
1242
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1243
+ /* @__PURE__ */ jsx(
1244
+ Carousel,
1245
+ {
1246
+ className: cn(
1247
+ "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
1248
+ carouselClassName
1288
1249
  ),
1289
- /* @__PURE__ */ jsx(
1290
- CarouselNext,
1291
- {
1292
- className: "static translate-x-0 translate-y-0 size-10",
1293
- variant: "default"
1294
- }
1295
- )
1296
- ]
1297
- }
1298
- ),
1250
+ setApi,
1251
+ opts: {
1252
+ startIndex
1253
+ },
1254
+ children: /* @__PURE__ */ jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent })
1255
+ }
1256
+ ),
1257
+ /* @__PURE__ */ jsx(
1258
+ CarouselPagination,
1259
+ {
1260
+ onPrevious: scrollPrevious,
1261
+ onNext: scrollNext,
1262
+ canScrollPrevious,
1263
+ canScrollNext,
1264
+ iconSize: 24,
1265
+ className: cn(
1266
+ "mt-6 justify-center md:mt-0 md:absolute md:inset-x-0 md:top-1/2 md:-translate-y-1/2 md:pointer-events-none",
1267
+ controlsClassName
1268
+ ),
1269
+ buttonClassName: "size-10 md:size-14 bg-primary text-primary-foreground hover:bg-primary/90 pointer-events-auto md:absolute md:-left-25 lg:-left-39.75 [&:last-child]:md:left-auto [&:last-child]:md:-right-25 [&:last-child]:lg:-right-39.75"
1270
+ }
1271
+ )
1272
+ ] }),
1299
1273
  /* @__PURE__ */ jsx(
1300
1274
  "div",
1301
1275
  {
package/dist/registry.cjs CHANGED
@@ -34533,19 +34533,33 @@ function CarouselScaleFocus({
34533
34533
  const [count, setCount] = React52.useState(0);
34534
34534
  const [lightboxOpen, setLightboxOpen] = React52.useState(false);
34535
34535
  const [lightboxIndex, setLightboxIndex] = React52.useState(0);
34536
+ const [canScrollPrevious, setCanScrollPrevious] = React52.useState(false);
34537
+ const [canScrollNext, setCanScrollNext] = React52.useState(false);
34536
34538
  React52.useEffect(() => {
34537
34539
  if (!api) {
34538
34540
  return;
34539
34541
  }
34540
- React52.startTransition(() => {
34541
- setCount(api.scrollSnapList().length);
34542
- setCurrent(api.selectedScrollSnap() + 1);
34543
- });
34544
- api.on("select", () => {
34542
+ const updateState = () => {
34545
34543
  React52.startTransition(() => {
34544
+ setCount(api.scrollSnapList().length);
34546
34545
  setCurrent(api.selectedScrollSnap() + 1);
34546
+ setCanScrollPrevious(api.canScrollPrev());
34547
+ setCanScrollNext(api.canScrollNext());
34547
34548
  });
34548
- });
34549
+ };
34550
+ updateState();
34551
+ api.on("select", updateState);
34552
+ api.on("reInit", updateState);
34553
+ return () => {
34554
+ api.off("select", updateState);
34555
+ api.off("reInit", updateState);
34556
+ };
34557
+ }, [api]);
34558
+ const scrollPrevious = React52.useCallback(() => {
34559
+ api?.scrollPrev();
34560
+ }, [api]);
34561
+ const scrollNext = React52.useCallback(() => {
34562
+ api?.scrollNext();
34549
34563
  }, [api]);
34550
34564
  const lightboxItems = React52.useMemo(() => {
34551
34565
  if (!images || images.length === 0) return [];
@@ -34646,63 +34660,37 @@ function CarouselScaleFocus({
34646
34660
  }
34647
34661
  ))
34648
34662
  ] }) : null,
34649
- /* @__PURE__ */ jsxRuntime.jsxs(
34650
- Carousel,
34651
- {
34652
- className: cn(
34653
- "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
34654
- carouselClassName
34655
- ),
34656
- setApi,
34657
- opts: {
34658
- startIndex
34659
- },
34660
- children: [
34661
- /* @__PURE__ */ jsxRuntime.jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent }),
34662
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("mt-4 hidden md:block", controlsClassName), children: [
34663
- /* @__PURE__ */ jsxRuntime.jsx(
34664
- CarouselPrevious,
34665
- {
34666
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-left-25 md:size-14 lg:-left-39.75 lg:size-14 [&>svg]:size-6!",
34667
- variant: "default"
34668
- }
34669
- ),
34670
- /* @__PURE__ */ jsxRuntime.jsx(
34671
- CarouselNext,
34672
- {
34673
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-right-25 md:size-14 lg:-right-39.75 lg:size-14 [&>svg]:size-6!",
34674
- variant: "default"
34675
- }
34676
- )
34677
- ] })
34678
- ]
34679
- }
34680
- ),
34681
- /* @__PURE__ */ jsxRuntime.jsxs(
34682
- "div",
34683
- {
34684
- className: cn(
34685
- "mt-10 flex items-center justify-center gap-4 md:hidden",
34686
- controlsClassName
34687
- ),
34688
- children: [
34689
- /* @__PURE__ */ jsxRuntime.jsx(
34690
- CarouselPrevious,
34691
- {
34692
- className: "static translate-x-0 translate-y-0 size-10",
34693
- variant: "default"
34694
- }
34663
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
34664
+ /* @__PURE__ */ jsxRuntime.jsx(
34665
+ Carousel,
34666
+ {
34667
+ className: cn(
34668
+ "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
34669
+ carouselClassName
34695
34670
  ),
34696
- /* @__PURE__ */ jsxRuntime.jsx(
34697
- CarouselNext,
34698
- {
34699
- className: "static translate-x-0 translate-y-0 size-10",
34700
- variant: "default"
34701
- }
34702
- )
34703
- ]
34704
- }
34705
- ),
34671
+ setApi,
34672
+ opts: {
34673
+ startIndex
34674
+ },
34675
+ children: /* @__PURE__ */ jsxRuntime.jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent })
34676
+ }
34677
+ ),
34678
+ /* @__PURE__ */ jsxRuntime.jsx(
34679
+ CarouselPagination,
34680
+ {
34681
+ onPrevious: scrollPrevious,
34682
+ onNext: scrollNext,
34683
+ canScrollPrevious,
34684
+ canScrollNext,
34685
+ iconSize: 24,
34686
+ className: cn(
34687
+ "mt-6 justify-center md:mt-0 md:absolute md:inset-x-0 md:top-1/2 md:-translate-y-1/2 md:pointer-events-none",
34688
+ controlsClassName
34689
+ ),
34690
+ buttonClassName: "size-10 md:size-14 bg-primary text-primary-foreground hover:bg-primary/90 pointer-events-auto md:absolute md:-left-25 lg:-left-39.75 [&:last-child]:md:left-auto [&:last-child]:md:-right-25 [&:last-child]:lg:-right-39.75"
34691
+ }
34692
+ )
34693
+ ] }),
34706
34694
  /* @__PURE__ */ jsxRuntime.jsx(
34707
34695
  "div",
34708
34696
  {
package/dist/registry.js CHANGED
@@ -34493,19 +34493,33 @@ function CarouselScaleFocus({
34493
34493
  const [count, setCount] = useState(0);
34494
34494
  const [lightboxOpen, setLightboxOpen] = useState(false);
34495
34495
  const [lightboxIndex, setLightboxIndex] = useState(0);
34496
+ const [canScrollPrevious, setCanScrollPrevious] = useState(false);
34497
+ const [canScrollNext, setCanScrollNext] = useState(false);
34496
34498
  useEffect(() => {
34497
34499
  if (!api) {
34498
34500
  return;
34499
34501
  }
34500
- startTransition(() => {
34501
- setCount(api.scrollSnapList().length);
34502
- setCurrent(api.selectedScrollSnap() + 1);
34503
- });
34504
- api.on("select", () => {
34502
+ const updateState = () => {
34505
34503
  startTransition(() => {
34504
+ setCount(api.scrollSnapList().length);
34506
34505
  setCurrent(api.selectedScrollSnap() + 1);
34506
+ setCanScrollPrevious(api.canScrollPrev());
34507
+ setCanScrollNext(api.canScrollNext());
34507
34508
  });
34508
- });
34509
+ };
34510
+ updateState();
34511
+ api.on("select", updateState);
34512
+ api.on("reInit", updateState);
34513
+ return () => {
34514
+ api.off("select", updateState);
34515
+ api.off("reInit", updateState);
34516
+ };
34517
+ }, [api]);
34518
+ const scrollPrevious = useCallback(() => {
34519
+ api?.scrollPrev();
34520
+ }, [api]);
34521
+ const scrollNext = useCallback(() => {
34522
+ api?.scrollNext();
34509
34523
  }, [api]);
34510
34524
  const lightboxItems = useMemo$1(() => {
34511
34525
  if (!images || images.length === 0) return [];
@@ -34606,63 +34620,37 @@ function CarouselScaleFocus({
34606
34620
  }
34607
34621
  ))
34608
34622
  ] }) : null,
34609
- /* @__PURE__ */ jsxs(
34610
- Carousel,
34611
- {
34612
- className: cn(
34613
- "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
34614
- carouselClassName
34615
- ),
34616
- setApi,
34617
- opts: {
34618
- startIndex
34619
- },
34620
- children: [
34621
- /* @__PURE__ */ jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent }),
34622
- /* @__PURE__ */ jsxs("div", { className: cn("mt-4 hidden md:block", controlsClassName), children: [
34623
- /* @__PURE__ */ jsx(
34624
- CarouselPrevious,
34625
- {
34626
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-left-25 md:size-14 lg:-left-39.75 lg:size-14 [&>svg]:size-6!",
34627
- variant: "default"
34628
- }
34629
- ),
34630
- /* @__PURE__ */ jsx(
34631
- CarouselNext,
34632
- {
34633
- className: "size-10 max-[767px]:static max-[767px]:translate-y-0 md:-right-25 md:size-14 lg:-right-39.75 lg:size-14 [&>svg]:size-6!",
34634
- variant: "default"
34635
- }
34636
- )
34637
- ] })
34638
- ]
34639
- }
34640
- ),
34641
- /* @__PURE__ */ jsxs(
34642
- "div",
34643
- {
34644
- className: cn(
34645
- "mt-10 flex items-center justify-center gap-4 md:hidden",
34646
- controlsClassName
34647
- ),
34648
- children: [
34649
- /* @__PURE__ */ jsx(
34650
- CarouselPrevious,
34651
- {
34652
- className: "static translate-x-0 translate-y-0 size-10",
34653
- variant: "default"
34654
- }
34623
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
34624
+ /* @__PURE__ */ jsx(
34625
+ Carousel,
34626
+ {
34627
+ className: cn(
34628
+ "mx-auto w-full max-w-200 [&>div:nth-child(1)]:md:overflow-visible",
34629
+ carouselClassName
34655
34630
  ),
34656
- /* @__PURE__ */ jsx(
34657
- CarouselNext,
34658
- {
34659
- className: "static translate-x-0 translate-y-0 size-10",
34660
- variant: "default"
34661
- }
34662
- )
34663
- ]
34664
- }
34665
- ),
34631
+ setApi,
34632
+ opts: {
34633
+ startIndex
34634
+ },
34635
+ children: /* @__PURE__ */ jsx(CarouselContent, { className: carouselContentClassName, children: imagesContent })
34636
+ }
34637
+ ),
34638
+ /* @__PURE__ */ jsx(
34639
+ CarouselPagination,
34640
+ {
34641
+ onPrevious: scrollPrevious,
34642
+ onNext: scrollNext,
34643
+ canScrollPrevious,
34644
+ canScrollNext,
34645
+ iconSize: 24,
34646
+ className: cn(
34647
+ "mt-6 justify-center md:mt-0 md:absolute md:inset-x-0 md:top-1/2 md:-translate-y-1/2 md:pointer-events-none",
34648
+ controlsClassName
34649
+ ),
34650
+ buttonClassName: "size-10 md:size-14 bg-primary text-primary-foreground hover:bg-primary/90 pointer-events-auto md:absolute md:-left-25 lg:-left-39.75 [&:last-child]:md:left-auto [&:last-child]:md:-right-25 [&:last-child]:lg:-right-39.75"
34651
+ }
34652
+ )
34653
+ ] }),
34666
34654
  /* @__PURE__ */ jsx(
34667
34655
  "div",
34668
34656
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensite/ui",
3
- "version": "1.2.7",
3
+ "version": "1.2.8",
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",
@@ -3307,6 +3307,7 @@
3307
3307
  "dev": "tsup --watch",
3308
3308
  "test": "vitest",
3309
3309
  "test:ci": "./scripts/test-ci-silent.sh",
3310
+ "test:f": "./scripts/test-failures-only.sh",
3310
3311
  "type-check": "tsc --noEmit",
3311
3312
  "size": "bash -c 'size-limit --silent 2>/dev/null'",
3312
3313
  "generate:exports": "bash scripts/generate-all-exports.sh && node scripts/merge-exports.js && node scripts/create-organized-exports.js",