@geomak/ui 6.18.0 → 6.20.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.
package/dist/index.cjs CHANGED
@@ -1774,7 +1774,25 @@ Card.Body = CardBody;
1774
1774
  Card.Footer = CardFooter;
1775
1775
  var Card_default = Card;
1776
1776
  var Arrow2 = ({ dir }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, "aria-hidden": "true", className: "h-5 w-5", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: dir === "left" ? "M15 19l-7-7 7-7" : "M9 5l7 7-7 7" }) });
1777
- function CardCarousel({
1777
+ var arrowBtn = "absolute top-1/2 -translate-y-1/2 z-30 flex h-9 w-9 items-center justify-center rounded-full border border-border bg-surface text-foreground-secondary shadow-md transition hover:text-foreground hover:bg-surface-raised disabled:opacity-0 disabled:pointer-events-none focus:outline-none focus-visible:ring-2 focus-visible:ring-accent";
1778
+ var Dots = ({ count, active, onSelect }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex items-center justify-center gap-1.5", children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx(
1779
+ "button",
1780
+ {
1781
+ type: "button",
1782
+ "aria-label": `Go to slide ${i + 1}`,
1783
+ "aria-current": i === active,
1784
+ onClick: () => onSelect(i),
1785
+ className: [
1786
+ "h-1.5 rounded-full transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
1787
+ i === active ? "w-5 bg-accent" : "w-1.5 bg-border hover:bg-foreground-muted"
1788
+ ].join(" ")
1789
+ },
1790
+ i
1791
+ )) });
1792
+ function CardCarousel(props) {
1793
+ return props.variant === "rotating" ? /* @__PURE__ */ jsxRuntime.jsx(RotatingCarousel, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(FlatCarousel, { ...props });
1794
+ }
1795
+ function FlatCarousel({
1778
1796
  children,
1779
1797
  itemWidth = 280,
1780
1798
  gap = 16,
@@ -1793,7 +1811,6 @@ function CardCarousel({
1793
1811
  const update = React24.useCallback(() => {
1794
1812
  const el = scrollerRef.current;
1795
1813
  if (!el) return;
1796
- el.clientWidth;
1797
1814
  setAtStart(el.scrollLeft <= 1);
1798
1815
  setAtEnd(el.scrollLeft + el.clientWidth >= el.scrollWidth - 1);
1799
1816
  const first = el.firstElementChild;
@@ -1811,49 +1828,109 @@ function CardCarousel({
1811
1828
  window.removeEventListener("resize", update);
1812
1829
  };
1813
1830
  }, [update]);
1814
- const scrollByDir = (dir) => {
1831
+ const slideStep = (dir) => {
1815
1832
  const el = scrollerRef.current;
1816
1833
  if (!el) return;
1817
1834
  const first = el.firstElementChild;
1818
1835
  const slideW = first ? first.getBoundingClientRect().width + gap : el.clientWidth;
1819
1836
  el.scrollBy({ left: dir * slideW, behavior: "smooth" });
1820
1837
  };
1821
- const scrollTo = (i) => {
1838
+ const scrollToIndex = (i) => {
1822
1839
  const el = scrollerRef.current;
1823
1840
  if (!el) return;
1824
1841
  const first = el.firstElementChild;
1825
1842
  const slideW = first ? first.getBoundingClientRect().width + gap : el.clientWidth;
1826
1843
  el.scrollTo({ left: i * slideW, behavior: "smooth" });
1827
1844
  };
1828
- const arrowBtn = "absolute top-1/2 -translate-y-1/2 z-10 flex h-9 w-9 items-center justify-center rounded-full border border-border bg-surface text-foreground-secondary shadow-md transition hover:text-foreground hover:bg-surface-raised disabled:opacity-0 disabled:pointer-events-none focus:outline-none focus-visible:ring-2 focus-visible:ring-accent";
1829
- return /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-label": ariaLabel, className: ["relative", className].filter(Boolean).join(" "), style, children: [
1830
- showArrows && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Previous", onClick: () => scrollByDir(-1), disabled: atStart, className: `${arrowBtn} left-1`, children: /* @__PURE__ */ jsxRuntime.jsx(Arrow2, { dir: "left" }) }),
1831
- /* @__PURE__ */ jsxRuntime.jsx(
1832
- "div",
1833
- {
1834
- ref: scrollerRef,
1835
- className: "flex overflow-x-auto snap-x snap-mandatory hidden-scrollbar scroll-smooth",
1836
- style: { gap },
1837
- children: slides.map((slide, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "snap-start flex-shrink-0", style: { width }, children: slide }, i))
1838
- }
1839
- ),
1840
- showArrows && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Next", onClick: () => scrollByDir(1), disabled: atEnd, className: `${arrowBtn} right-1`, children: /* @__PURE__ */ jsxRuntime.jsx(Arrow2, { dir: "right" }) }),
1841
- showDots && slides.length > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 flex items-center justify-center gap-1.5", children: slides.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
1842
- "button",
1843
- {
1844
- type: "button",
1845
- "aria-label": `Go to slide ${i + 1}`,
1846
- "aria-current": i === active,
1847
- onClick: () => scrollTo(i),
1848
- className: [
1849
- "h-1.5 rounded-full transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
1850
- i === active ? "w-5 bg-accent" : "w-1.5 bg-border hover:bg-foreground-muted"
1851
- ].join(" ")
1852
- },
1853
- i
1854
- )) })
1845
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { "aria-label": ariaLabel, "aria-roledescription": "carousel", className: ["relative", className].filter(Boolean).join(" "), style, children: [
1846
+ showArrows && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Previous", onClick: () => slideStep(-1), disabled: atStart, className: `${arrowBtn} left-1`, children: /* @__PURE__ */ jsxRuntime.jsx(Arrow2, { dir: "left" }) }),
1847
+ /* @__PURE__ */ jsxRuntime.jsx("div", { ref: scrollerRef, className: "flex overflow-x-auto snap-x snap-mandatory hidden-scrollbar scroll-smooth", style: { gap }, children: slides.map((slide, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "snap-start flex-shrink-0", style: { width }, children: slide }, i)) }),
1848
+ showArrows && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Next", onClick: () => slideStep(1), disabled: atEnd, className: `${arrowBtn} right-1`, children: /* @__PURE__ */ jsxRuntime.jsx(Arrow2, { dir: "right" }) }),
1849
+ showDots && slides.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(Dots, { count: slides.length, active, onSelect: scrollToIndex })
1855
1850
  ] });
1856
1851
  }
1852
+ function RotatingCarousel({
1853
+ children,
1854
+ itemWidth = 280,
1855
+ showArrows = true,
1856
+ showDots = false,
1857
+ "aria-label": ariaLabel = "Carousel",
1858
+ className = "",
1859
+ style
1860
+ }) {
1861
+ const slides = React24__default.default.Children.toArray(children);
1862
+ const count = slides.length;
1863
+ const [active, setActive] = React24.useState(0);
1864
+ const reduced = framerMotion.useReducedMotion();
1865
+ const wrap = (n) => count > 0 ? (n % count + count) % count : 0;
1866
+ const idx = wrap(active);
1867
+ const step = (dir) => setActive((a) => wrap(a + dir));
1868
+ const w = typeof itemWidth === "number" ? itemWidth : parseInt(String(itemWidth), 10) || 320;
1869
+ const widthCss = typeof itemWidth === "number" ? `${itemWidth}px` : itemWidth;
1870
+ const SPACING = w * 0.6;
1871
+ const SIDE_SCALE = 0.82;
1872
+ const SIDE_OPACITY = 0.5;
1873
+ const transition = reduced ? { duration: 0 } : { type: "spring", stiffness: 280, damping: 32, mass: 0.9 };
1874
+ const onKeyDown = (e) => {
1875
+ if (e.key === "ArrowLeft") {
1876
+ e.preventDefault();
1877
+ step(-1);
1878
+ } else if (e.key === "ArrowRight") {
1879
+ e.preventDefault();
1880
+ step(1);
1881
+ }
1882
+ };
1883
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1884
+ "section",
1885
+ {
1886
+ "aria-label": ariaLabel,
1887
+ "aria-roledescription": "carousel",
1888
+ className: ["relative", className].filter(Boolean).join(" "),
1889
+ style,
1890
+ onKeyDown,
1891
+ children: [
1892
+ showArrows && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Previous", onClick: () => step(-1), className: `${arrowBtn} left-1`, children: /* @__PURE__ */ jsxRuntime.jsx(Arrow2, { dir: "left" }) }),
1893
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative mx-auto overflow-hidden py-6", children: [
1894
+ count > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "invisible mx-auto", style: { width: widthCss }, "aria-hidden": "true", children: slides[idx] }),
1895
+ slides.map((slide, i) => {
1896
+ let offset = wrap(i - idx);
1897
+ if (offset > count / 2) offset -= count;
1898
+ const abs = Math.abs(offset);
1899
+ if (abs > 2) return null;
1900
+ const isCenter = offset === 0;
1901
+ const isPeek = abs === 1;
1902
+ return /* @__PURE__ */ jsxRuntime.jsx(
1903
+ "div",
1904
+ {
1905
+ className: "absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2",
1906
+ style: { width: widthCss, zIndex: 30 - abs * 10, pointerEvents: isCenter || isPeek ? "auto" : "none" },
1907
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1908
+ framerMotion.motion.div,
1909
+ {
1910
+ initial: false,
1911
+ animate: {
1912
+ x: offset * (abs <= 1 ? SPACING : SPACING * 1.7),
1913
+ scale: isCenter ? 1 : SIDE_SCALE,
1914
+ opacity: abs <= 1 ? isCenter ? 1 : SIDE_OPACITY : 0
1915
+ },
1916
+ transition,
1917
+ className: isCenter ? void 0 : "cursor-pointer",
1918
+ onClick: isCenter ? void 0 : () => setActive(i),
1919
+ "aria-hidden": isCenter ? void 0 : true,
1920
+ children: slide
1921
+ }
1922
+ )
1923
+ },
1924
+ i
1925
+ );
1926
+ })
1927
+ ] }),
1928
+ showArrows && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", "aria-label": "Next", onClick: () => step(1), className: `${arrowBtn} right-1`, children: /* @__PURE__ */ jsxRuntime.jsx(Arrow2, { dir: "right" }) }),
1929
+ showDots && count > 1 && /* @__PURE__ */ jsxRuntime.jsx(Dots, { count, active: idx, onSelect: setActive })
1930
+ ]
1931
+ }
1932
+ );
1933
+ }
1857
1934
  var VALUE_SIZE = {
1858
1935
  sm: "text-xl",
1859
1936
  md: "text-3xl",