@particle-academy/react-fancy 4.11.0 → 4.12.1

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
 
@@ -231,9 +232,9 @@ Dark mode works via Tailwind's `dark:` class strategy. The library's `Portal` co
231
232
  ```
232
233
  src/
233
234
  ├── components/ # One directory per component
234
- │ ├── Action/
235
- │ │ ├── Action.tsx # Component implementation
236
- │ │ ├── Action.types.ts # Props interface
235
+ │ ├── Button/
236
+ │ │ ├── Button.tsx # Component implementation
237
+ │ │ ├── Button.types.ts # Props interface
237
238
  │ │ └── index.ts # Re-exports
238
239
  │ ├── Modal/
239
240
  │ │ ├── Modal.tsx # Root + Object.assign compound
@@ -256,7 +257,7 @@ src/
256
257
 
257
258
  - `Size` — `"xs" | "sm" | "md" | "lg" | "xl"`
258
259
  - `Color` — Full Tailwind color palette (17 colors)
259
- - `ButtonColor` — Subset of 10 standalone colors used by `Button` and friends (legacy alias: `ActionColor`)
260
+ - `ButtonColor` — Standalone colors used by `Button` and friends; alias of `Color` (full palette). Deprecated legacy alias: `ActionColor`
260
261
  - `Variant` — `"solid" | "outline" | "ghost" | "soft"`
261
262
  - `Placement` — `"top" | "bottom" | "left" | "right"` + start/end variants
262
263
 
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;