@particle-academy/react-fancy 4.10.0 → 4.12.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/README.md CHANGED
@@ -130,6 +130,7 @@ npx vite build # Build demo app (verifies imports work)
130
130
  | Card | Container with Header, Body, Footer compound slots | [docs](docs/Card.md) |
131
131
  | Callout | Alert/info box with icon, color, and dismissible support | [docs](docs/Callout.md) |
132
132
  | Timeline | Stacked, alternating, and horizontal timeline with data-driven or compound API | [docs](docs/Timeline.md) |
133
+ | Marquee | Auto-scrolling ticker strip with seamless wrap, px/s speed, fade edges, reduced-motion gate | [docs](docs/Marquee.md) |
133
134
 
134
135
  ### Overlay & Floating
135
136
 
package/dist/index.cjs CHANGED
@@ -2421,6 +2421,7 @@ var Button = react.forwardRef(
2421
2421
  loading = false,
2422
2422
  disabled,
2423
2423
  href,
2424
+ as: As,
2424
2425
  responsive,
2425
2426
  labelClassName,
2426
2427
  ...props
@@ -2583,11 +2584,12 @@ var Button = react.forwardRef(
2583
2584
  trailingElements
2584
2585
  ] });
2585
2586
  const safeHref = sanitizeHref(href);
2587
+ const Anchor = As ?? "a";
2586
2588
  const buttonEl = safeHref && !disabled ? (
2587
2589
  // Anchor mode forwards the same rest props as the <button> branch so
2588
2590
  // onClick / target / rel / ref / ARIA / data-* reach the <a> (issue #7).
2589
2591
  /* @__PURE__ */ jsxRuntime.jsx(
2590
- "a",
2592
+ Anchor,
2591
2593
  {
2592
2594
  ref,
2593
2595
  href: safeHref,
@@ -7701,6 +7703,120 @@ var TimeGrid = react.forwardRef(
7701
7703
  }
7702
7704
  );
7703
7705
  TimeGrid.displayName = "TimeGrid";
7706
+ function toLength(value) {
7707
+ return typeof value === "number" ? `${value}px` : value;
7708
+ }
7709
+ var Marquee = react.forwardRef(
7710
+ ({
7711
+ children,
7712
+ items,
7713
+ speed = 40,
7714
+ duration,
7715
+ direction = "left",
7716
+ pauseOnHover = false,
7717
+ paused = false,
7718
+ gap = 40,
7719
+ fade = true,
7720
+ separator,
7721
+ angle = 0,
7722
+ decorative = true,
7723
+ className,
7724
+ style,
7725
+ ...rest
7726
+ }, ref) => {
7727
+ const rootRef = react.useRef(null);
7728
+ const unitRef = react.useRef(null);
7729
+ react.useImperativeHandle(ref, () => rootRef.current);
7730
+ const [copies, setCopies] = react.useState(1);
7731
+ const [measuredDuration, setMeasuredDuration] = react.useState(null);
7732
+ react.useEffect(() => {
7733
+ const root = rootRef.current;
7734
+ const unit = unitRef.current;
7735
+ if (!root || !unit || typeof ResizeObserver === "undefined") return;
7736
+ const update = () => {
7737
+ const unitWidth = unit.getBoundingClientRect().width;
7738
+ const containerWidth = root.clientWidth;
7739
+ if (unitWidth <= 0) return;
7740
+ const nextCopies = Math.max(1, Math.ceil(containerWidth / unitWidth));
7741
+ setCopies(nextCopies);
7742
+ if (duration === void 0) {
7743
+ setMeasuredDuration(unitWidth * nextCopies / Math.max(speed, 1));
7744
+ }
7745
+ };
7746
+ update();
7747
+ const observer = new ResizeObserver(update);
7748
+ observer.observe(root);
7749
+ observer.observe(unit);
7750
+ return () => observer.disconnect();
7751
+ }, [duration, speed]);
7752
+ const loopSeconds = duration ?? measuredDuration ?? 40;
7753
+ const itemNodes = items ?? react.Children.toArray(children);
7754
+ const hasSeparator = separator !== void 0 && separator !== null;
7755
+ const renderUnit = (duplicate, unitElRef) => /* @__PURE__ */ jsxRuntime.jsx(
7756
+ "div",
7757
+ {
7758
+ ref: unitElRef,
7759
+ className: "fancy-marquee__unit",
7760
+ "aria-hidden": duplicate || void 0,
7761
+ children: itemNodes.map((node, i) => /* @__PURE__ */ jsxRuntime.jsxs(react.Fragment, { children: [
7762
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "fancy-marquee__item", "data-react-fancy-marquee-item": "", "data-index": i, children: node }),
7763
+ hasSeparator && /* @__PURE__ */ jsxRuntime.jsx(
7764
+ "div",
7765
+ {
7766
+ className: "fancy-marquee__separator",
7767
+ "data-react-fancy-marquee-separator": "",
7768
+ "aria-hidden": "true",
7769
+ children: separator
7770
+ }
7771
+ )
7772
+ ] }, i))
7773
+ }
7774
+ );
7775
+ const renderGroup = (duplicate) => /* @__PURE__ */ jsxRuntime.jsx(
7776
+ "div",
7777
+ {
7778
+ className: "fancy-marquee__group",
7779
+ "data-react-fancy-marquee-group": "",
7780
+ "aria-hidden": duplicate || void 0,
7781
+ children: Array.from(
7782
+ { length: copies },
7783
+ (_, c) => /* @__PURE__ */ jsxRuntime.jsx(react.Fragment, { children: renderUnit(duplicate || c > 0, !duplicate && c === 0 ? unitRef : void 0) }, c)
7784
+ )
7785
+ }
7786
+ );
7787
+ const cssVars = {
7788
+ "--fancy-marquee-duration": `${loopSeconds}s`,
7789
+ "--fancy-marquee-gap": toLength(gap),
7790
+ ...fade !== false && {
7791
+ "--fancy-marquee-fade": fade === true ? "48px" : toLength(fade)
7792
+ }
7793
+ };
7794
+ const angleStyle = angle ? { transform: `rotate(${angle}deg)`, width: "102%", marginLeft: "-1%" } : {};
7795
+ return /* @__PURE__ */ jsxRuntime.jsx(
7796
+ "div",
7797
+ {
7798
+ ref: rootRef,
7799
+ "data-react-fancy-marquee": "",
7800
+ "data-direction": direction,
7801
+ "data-paused": paused ? "" : void 0,
7802
+ "aria-hidden": decorative || void 0,
7803
+ className: cn(
7804
+ "fancy-marquee",
7805
+ fade !== false && "fancy-marquee--fade",
7806
+ pauseOnHover && "fancy-marquee--pause-hover",
7807
+ className
7808
+ ),
7809
+ style: { ...cssVars, ...angleStyle, ...style },
7810
+ ...rest,
7811
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "fancy-marquee__track", "data-react-fancy-marquee-track": "", children: [
7812
+ renderGroup(false),
7813
+ renderGroup(true)
7814
+ ] })
7815
+ }
7816
+ );
7817
+ }
7818
+ );
7819
+ Marquee.displayName = "Marquee";
7704
7820
  var Tooltip = react.forwardRef(
7705
7821
  function Tooltip2({ children, content, placement = "top", delay = 200, offset = 8, className }, _ref) {
7706
7822
  const [open, setOpen] = react.useState(false);
@@ -14889,6 +15005,7 @@ exports.Input = Input;
14889
15005
  exports.InputTag = InputTag;
14890
15006
  exports.Kanban = Kanban;
14891
15007
  exports.MagicWand = MagicWand;
15008
+ exports.Marquee = Marquee;
14892
15009
  exports.MediaViewer = MediaViewer;
14893
15010
  exports.Menu = Menu2;
14894
15011
  exports.MobileMenu = MobileMenu;