@particle-academy/react-fancy 4.11.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
@@ -7703,6 +7703,120 @@ var TimeGrid = react.forwardRef(
7703
7703
  }
7704
7704
  );
7705
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";
7706
7820
  var Tooltip = react.forwardRef(
7707
7821
  function Tooltip2({ children, content, placement = "top", delay = 200, offset = 8, className }, _ref) {
7708
7822
  const [open, setOpen] = react.useState(false);
@@ -14891,6 +15005,7 @@ exports.Input = Input;
14891
15005
  exports.InputTag = InputTag;
14892
15006
  exports.Kanban = Kanban;
14893
15007
  exports.MagicWand = MagicWand;
15008
+ exports.Marquee = Marquee;
14894
15009
  exports.MediaViewer = MediaViewer;
14895
15010
  exports.Menu = Menu2;
14896
15011
  exports.MobileMenu = MobileMenu;