gsap-react-marquee 0.3.2 → 0.4.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
@@ -1,6 +1,6 @@
1
1
  # GSAP React Marquee
2
2
 
3
- `gsap-react-marquee` is a React marquee component powered by GSAP. It supports horizontal and vertical scrolling, seamless looping, optional fill mode, pause-on-hover, scroll-follow speed changes, draggable interaction, gradient overlays, and TypeScript types.
3
+ `gsap-react-marquee` is a React marquee component powered by GSAP. It supports horizontal and vertical scrolling, seamless looping, optional fill mode, pause-on-hover, scroll-follow speed changes, draggable interaction, reduced-motion preferences, accessible visual clones, gradient overlays, and TypeScript types.
4
4
 
5
5
  ## Installation
6
6
 
@@ -16,7 +16,18 @@ yarn add gsap-react-marquee gsap @gsap/react
16
16
  pnpm add gsap-react-marquee gsap @gsap/react
17
17
  ```
18
18
 
19
- `react`, `react-dom`, `gsap`, and `@gsap/react` are peer dependencies and must be installed by the consuming app.
19
+ `react`, `gsap`, and `@gsap/react` are peer dependencies and must be installed by the consuming app. Your React application may still use `react-dom`, but this package does not require it directly.
20
+
21
+ ### Compatibility
22
+
23
+ | Environment | Supported versions | Release verification |
24
+ | --- | --- | --- |
25
+ | React | 18 and 19 | Packed ESM, CommonJS, types, SSR, CSS, and Chromium fixtures |
26
+ | GSAP | 3.12 and 3.13 | Packed fixtures use 3.12.5 and 3.13.0 |
27
+ | `@gsap/react` | 2.1 or newer | Packed fixtures use 2.1.2 |
28
+ | Node.js | 20 and 22 | Typecheck, lint, unit, build, package, and audit release matrix |
29
+
30
+ Node.js versions older than 20 are not part of the `0.4.0` support contract.
20
31
 
21
32
  ## Basic Usage
22
33
 
@@ -48,16 +59,29 @@ Use `fill` when a short piece of content should repeat enough times to cover the
48
59
  </Marquee>
49
60
  ```
50
61
 
62
+ `fill` is independent from `dir`. It uses the same coverage calculation for
63
+ left, right, up, and down movement. Without `fill`, every direction renders one
64
+ semantic original and one visual clone.
65
+
51
66
  ### Vertical Marquee
52
67
 
53
68
  ```tsx
54
- <Marquee dir="up" speed={80} spacing={12}>
69
+ <Marquee
70
+ dir="up"
71
+ fill
72
+ speed={80}
73
+ spacing={12}
74
+ containerStyle={{ height: 320 }}
75
+ >
55
76
  <div>Item 1</div>
56
77
  <div>Item 2</div>
57
78
  <div>Item 3</div>
58
79
  </Marquee>
59
80
  ```
60
81
 
82
+ Use `containerStyle` or `containerClassName` when the surrounding layout does
83
+ not already provide a constrained height.
84
+
61
85
  ### Gradient Overlay
62
86
 
63
87
  When `gradient` is enabled, the component detects the nearest non-transparent background color and uses it for the edge fade. You can override the color with `gradientColor`.
@@ -76,6 +100,49 @@ When `gradient` is enabled, the component detects the nearest non-transparent ba
76
100
  </Marquee>
77
101
  ```
78
102
 
103
+ `pauseOnHover` also pauses while keyboard focus is inside the marquee. Leaving
104
+ with either pointer or focus cannot override a controlled `paused={true}`.
105
+
106
+ ### Controlled Pause
107
+
108
+ `paused` is controlled React state, not only an initial setting. Changing it to
109
+ `true` pauses the current direction; changing it to `false` resumes that
110
+ direction at the base speed. Hover/focus leave, drag release, scroll response,
111
+ reverse delay, and late plugin loading cannot resume `paused={true}`. Draggable
112
+ still initializes while controlled-paused so it becomes usable when resumed.
113
+
114
+ Applications showing motion for more than five seconds should expose a visible
115
+ pause control when their accessibility requirements call for one. The component
116
+ provides controlled state; the consumer owns control placement and labeling:
117
+
118
+ ```tsx
119
+ import { useState } from "react";
120
+ import Marquee from "gsap-react-marquee";
121
+
122
+ export function ControlledMarquee() {
123
+ const [isPaused, setIsPaused] = useState(false);
124
+
125
+ return (
126
+ <section aria-label="Partner announcements">
127
+ <button
128
+ type="button"
129
+ aria-pressed={isPaused}
130
+ onClick={() => setIsPaused((current) => !current)}
131
+ >
132
+ {isPaused ? "Play marquee" : "Pause marquee"}
133
+ </button>
134
+ <Marquee paused={isPaused} draggable>
135
+ <span>Controlled animation</span>
136
+ </Marquee>
137
+ </section>
138
+ );
139
+ }
140
+ ```
141
+
142
+ `respectReducedMotion` handles user motion preference. It does not remove the
143
+ consumer's responsibility to provide pause/stop controls required by content,
144
+ duration, and applicable WCAG policy.
145
+
79
146
  ### Scroll-Follow
80
147
 
81
148
  `scrollFollow` changes the marquee timeline speed and direction based on vertical wheel movement.
@@ -88,7 +155,7 @@ When `gradient` is enabled, the component detects the nearest non-transparent ba
88
155
 
89
156
  ### Draggable
90
157
 
91
- `draggable` lets users drag the marquee track manually. Momentum throwing uses GSAP's `InertiaPlugin`. The package imports the plugin from `gsap/all.js`; if your GSAP setup does not include access to InertiaPlugin, dragging still initializes but momentum behavior may be limited by GSAP availability.
158
+ `draggable` lets users drag the marquee track manually. The package loads `Draggable` only when this prop is enabled. Momentum throwing is enabled when your GSAP setup has already registered `InertiaPlugin`; otherwise direct dragging still works without the inertia throw. This feature detection keeps the package compatible with GSAP 3.12 distributions, which do not all expose `InertiaPlugin` at the same package path.
92
159
 
93
160
  ```tsx
94
161
  <Marquee draggable pauseOnHover>
@@ -117,18 +184,69 @@ export function Example() {
117
184
  }
118
185
  ```
119
186
 
187
+ ### Reduced Motion
188
+
189
+ The component respects `prefers-reduced-motion: reduce` by default. It renders
190
+ one static original and creates no GSAP timeline, Observer, or Draggable
191
+ instance. Preference changes are applied without reloading the page.
192
+
193
+ Use `respectReducedMotion={false}` only when motion remains appropriate for the
194
+ content and audience:
195
+
196
+ ```tsx
197
+ <Marquee respectReducedMotion={false}>
198
+ <span>Animation remains enabled</span>
199
+ </Marquee>
200
+ ```
201
+
202
+ ### Accessibility and Child Content
203
+
204
+ Version `0.4.0` supports presentational children such as text, images, and logo
205
+ groups. Interactive controls, links, stable IDs, and ID-reference relationships
206
+ inside repeated children are not supported. A future major version may add an
207
+ explicit render-item API for interactive content.
208
+
209
+ SSR output contains only the semantic original. After client measurement,
210
+ visual clones receive `aria-hidden="true"`, native `inert` where available, and
211
+ disabled pointer interaction. The Safari 14.1 fallback removes clone tab stops,
212
+ form names, IDs, and ID-reference attributes and prevents programmatic focus
213
+ from remaining inside a clone. Development builds warn once when unsupported
214
+ child content is detected.
215
+
216
+ Clone sanitization means ID-based CSS, fragment links, and SVG references inside
217
+ repeated children may not survive. Keep those relationships outside marquee
218
+ children.
219
+
220
+ Use `containerProps` for root semantics and event handlers:
221
+
222
+ ```tsx
223
+ <Marquee
224
+ containerProps={{
225
+ "aria-label": "Technology partners",
226
+ role: "region",
227
+ }}
228
+ >
229
+ <span>Presentational partner logos</span>
230
+ </Marquee>
231
+ ```
232
+
120
233
  ## Props
121
234
 
122
235
  | Prop | Type | Default | Description |
123
236
  | --- | --- | --- | --- |
124
237
  | `children` | `ReactNode` | Required | Content rendered inside each marquee item. |
125
238
  | `className` | `string` | `undefined` | Class applied to each `.gsap-react-marquee-content` element. |
239
+ | `containerClassName` | `string` | `undefined` | Class applied only to the root viewport. |
240
+ | `containerStyle` | `CSSProperties` | `undefined` | Inline styles applied to the root viewport. |
241
+ | `containerProps` | root `div` attributes | `undefined` | ARIA, `data-*`, and event props applied to the root viewport. |
126
242
  | `dir` | `"left" \| "right" \| "up" \| "down"` | `"left"` | Direction of movement. |
127
243
  | `loop` | `number` | `-1` | Number of timeline repeats. `-1` means infinite. |
128
- | `paused` | `boolean` | `false` | Starts the timeline paused. |
244
+ | `paused` | `boolean` | `false` | Controls whether the current timeline is paused. |
245
+ | `respectReducedMotion` | `boolean` | `true` | Shows one static original when reduced motion is requested. |
129
246
  | `delay` | `number` | `0` | Delay in seconds before the timeline starts. |
130
247
  | `speed` | `number` | `100` | Animation speed in pixels per second. |
131
248
  | `fill` | `boolean` | `false` | Repeats content enough times to cover the measured marquee area. |
249
+ | `maxDuplicates` | `number` | `100` | Maximum additional clones in fill mode, capped internally at `250`. |
132
250
  | `pauseOnHover` | `boolean` | `false` | Pauses on pointer hover and resumes on leave. |
133
251
  | `gradient` | `boolean` | `false` | Enables edge gradient overlays. |
134
252
  | `gradientColor` | `string` | `undefined` | Explicit gradient color. Overrides automatic background detection. |
@@ -137,15 +255,33 @@ export function Example() {
137
255
  | `scrollFollow` | `boolean` | `false` | Adjusts timeline speed from wheel/scroll direction. |
138
256
  | `scrollSpeed` | `number` | `2.5` | Scroll-follow multiplier. Clamped between `1.1` and `4`. |
139
257
 
258
+ ### Numeric Normalization
259
+
260
+ Numeric props are normalized before layout or GSAP receives them. Invalid
261
+ values never create negative, `NaN`, or infinite durations.
262
+
263
+ | Prop | Accepted values | Invalid-value behavior |
264
+ | --- | --- | --- |
265
+ | `speed` | finite number greater than `0` | Uses `100` |
266
+ | `spacing` | finite number at least `0` | Uses `16` |
267
+ | `delay` | finite number at least `0` | Uses `0` |
268
+ | `loop` | `-1` or integer at least `0` | Uses `-1` |
269
+ | `scrollSpeed` | finite number | Clamped to `1.1`–`4`; non-finite uses `2.5` |
270
+ | `maxDuplicates` | positive integer | Uses `100`, then caps at hard ceiling `250` |
271
+
140
272
  ## How Sizing Works
141
273
 
142
- The component measures the root container and first content item after mount. It then creates enough cloned marquee items for the selected mode and starts a GSAP timeline.
274
+ The component renders one semantic original during SSR, then measures the root container and first content item after client mount. It creates enough inaccessible visual clones for the selected mode and starts a GSAP timeline unless reduced motion is active.
143
275
 
144
- In normal mode (`fill={false}`), the component renders one original item plus one clone. This is suitable when your content is already large enough to create a continuous loop.
276
+ In normal mode (`fill={false}`), the component renders one original item plus
277
+ one clone. Each repeated wrapper automatically spans at least the active
278
+ viewport axis and grows when its natural content is larger. Short vertical
279
+ content therefore re-enters from the far edge instead of the middle; `fill` is
280
+ not required for correct entry geometry.
145
281
 
146
- In fill mode (`fill={true}`), the component calculates how many clones are required to cover the measured target size. The duplicate count is capped to prevent excessive DOM growth, and the component re-measures when the container, the first content item, child content, or unloaded images change size.
282
+ In fill mode (`fill={true}`), the component calculates how many clones are required to cover the measured target size plus one seamless wrap segment. The calculation includes `spacing` and behaves identically for horizontal and vertical directions. `maxDuplicates` defaults to `100` and has a hard internal ceiling of `250`. When a configured ceiling prevents full coverage, development builds emit one warning and keep the rendered clone count finite.
147
283
 
148
- If the container has no reliable defined size, the component falls back to the viewport width or height as its measurement target. This avoids recursive expansion when the marquee is placed inside content-sized layouts.
284
+ Clone calculation uses the root container's measured width or height. The root automatically fills the parent's width in horizontal mode and the parent's width and height in vertical mode. Page layout defines the available viewport; no width or height prop is required on `Marquee`. Clone-generated size changes are excluded from subsequent viewport measurements.
149
285
 
150
286
  ## Styling
151
287
 
@@ -174,14 +310,66 @@ Use `className` to style the repeated content wrapper:
174
310
  </Marquee>
175
311
  ```
176
312
 
177
- For predictable measurement, give the marquee or its parent a stable width for horizontal marquees and a stable height for vertical marquees.
313
+ Use `containerClassName` or `containerStyle` only when the automatic viewport
314
+ needs an explicit visual or sizing override:
315
+
316
+ ```tsx
317
+ <Marquee
318
+ dir="up"
319
+ fill
320
+ containerClassName="vertical-marquee"
321
+ containerStyle={{ height: 320, backgroundColor: "#111827" }}
322
+ >
323
+ <span>Measured vertical content</span>
324
+ </Marquee>
325
+ ```
326
+
327
+ Required root layout styles remain controlled by the component. The root fills
328
+ the available parent viewport automatically. Width, height, color, background,
329
+ and other non-critical styles can still be overridden through `containerStyle`.
330
+
331
+ ## Public Utilities
332
+
333
+ Version `0.4.0` keeps the historical utility exports as supported public API.
334
+ Aliases below are identical function references, not wrappers.
335
+
336
+ | Utility | Contract |
337
+ | --- | --- |
338
+ | `normalizeMarqueeOptions` | Returns finite normalized numeric options using documented fallbacks and caps. |
339
+ | `hasUsableMeasurement` | Returns `true` only when every supplied number is finite and greater than zero. |
340
+ | `hasDefinedWidth` | Checks whether `offsetWidth` is finite and positive; it no longer infers CSS width semantics. |
341
+ | `getTargetSize` / `getTargetWidth` | Returns root `offsetHeight` or `offsetWidth`; no viewport fallback is used. |
342
+ | `calculateDuplicateCountResult` | Returns bounded clone count, required count, and limit state. Fill includes spacing and one wrap segment. |
343
+ | `calculateDuplicateCount` / `calculateDuplicates` | Returns only the bounded additional-clone count from the same calculation. |
344
+ | `getMinSize` / `getMinWidth` | Returns active-axis wrapper minimum size for normal or fill layout. |
345
+ | `setupContainerStyles` | Applies direction, spacing, flex sizing, and overflow to root, track, and content elements. |
346
+ | `resumeTimeline` | Restores forward/reverse playback or controlled pause from explicit options. |
347
+ | `createMarqueeAnimation` / `coreAnimation` | Builds segments and optional drag behavior, honoring loop, reverse, pause, and cleanup contracts. |
348
+ | `getEffectiveBackgroundColor` | Returns the first non-transparent computed ancestor background, or `transparent`. |
349
+ | `cn` | Combines `clsx` inputs and resolves Tailwind class conflicts. |
350
+
351
+ These `0.4.0` behavior changes are intentional. Future removal or narrowing of
352
+ historical helpers requires deprecation before a major release.
178
353
 
179
354
  ## Runtime Notes
180
355
 
181
- - The component uses `useLayoutEffect`, `ResizeObserver`, `requestAnimationFrame`, and DOM measurements, so it is intended for client-side rendering.
356
+ - SSR safely renders one static original. Client measurement adds visual clones after hydration.
357
+ - The animated lifecycle uses an isomorphic layout effect, `ResizeObserver`, `requestAnimationFrame`, and DOM measurements.
182
358
  - In SSR frameworks such as Next.js, render it from a client component. Add `"use client"` to the file that imports and renders the marquee.
183
359
  - Images that are not complete at mount are watched and trigger a re-measure after `load` or `error`.
184
- - Changing animation props such as `dir`, `speed`, `delay`, `fill`, `draggable`, `spacing`, `loop`, or `paused` re-initializes the GSAP timeline.
360
+ - Observer and Draggable load independently. A failed optional chunk disables only that feature; the base marquee still starts. Toggling the feature off and on requests a retry.
361
+ - Changing animation props such as `dir`, `speed`, `delay`, `fill`, `maxDuplicates`, `draggable`, `spacing`, `loop`, or `paused` re-initializes the GSAP timeline.
362
+ - ESM and CommonJS bundles contain the same minified runtime and inject one minified base-style element in browsers. Importing either entrypoint during SSR does not access `document`.
363
+ - Published JavaScript and CSS are minified, while supported development diagnostics remain intact. Source maps are intentionally not published for `0.4.0`; repository TypeScript remains the review/debug source.
364
+
365
+ ## Browser Support
366
+
367
+ Supported targets are current and previous-major Chromium and Firefox releases,
368
+ plus Safari and iOS Safari 14.1 or newer. Native `inert` is feature-detected.
369
+ Safari versions without it use the clone-specific focus, form, and pointer
370
+ fallback described above. Playwright Chromium, Firefox, and WebKit are the automated
371
+ compatibility signals; WebKit does not exactly emulate every older Safari
372
+ release.
185
373
 
186
374
  ## Troubleshooting
187
375
 
@@ -191,11 +379,14 @@ Use `fill={true}` for short content, increase `spacing` only as much as needed,
191
379
 
192
380
  ### Vertical marquee does not move correctly
193
381
 
194
- Give the container or one of its parents a real height. Vertical mode measures height, not width.
382
+ Ensure the surrounding page layout exposes usable vertical space. The marquee
383
+ automatically fills that parent space and measures its own root; do not copy the
384
+ parent height onto `Marquee`.
195
385
 
196
386
  ### The marquee expands the page
197
387
 
198
- Place it in a container with an explicit width or max width. In fill mode, the component falls back to viewport measurement when it detects content-sized containers, but explicit layout constraints are still more predictable.
388
+ Constrain the surrounding page region with normal CSS layout. The marquee root
389
+ automatically consumes that region instead of expanding to the cloned track.
199
390
 
200
391
  ### Dragging has no momentum
201
392
 
@@ -203,28 +394,39 @@ Momentum depends on GSAP `InertiaPlugin` availability. If the plugin is not avai
203
394
 
204
395
  ## Development
205
396
 
206
- Install dependencies:
397
+ Use Node.js 20 or 22 and the pinned pnpm version from `packageManager`.
398
+
399
+ Install the exact lockfile:
400
+
401
+ ```bash
402
+ pnpm install --frozen-lockfile
403
+ ```
404
+
405
+ Run TypeScript, lint, and unit checks:
207
406
 
208
407
  ```bash
209
- pnpm install
408
+ pnpm run check
210
409
  ```
211
410
 
212
- Run a TypeScript check:
411
+ Run browser compatibility checks:
213
412
 
214
413
  ```bash
215
- pnpm exec tsc --noEmit
414
+ pnpm run test:browser
415
+ pnpm run test:browser:firefox
416
+ pnpm run test:browser:webkit
216
417
  ```
217
418
 
218
- Build the package:
419
+ Build and test the real packed artifact against React 18 and 19 consumers:
219
420
 
220
421
  ```bash
221
- pnpm run build
422
+ pnpm run test:package
222
423
  ```
223
424
 
224
- Preview the published package contents:
425
+ Run the full dependency audit or complete local release gate:
225
426
 
226
427
  ```bash
227
- npm pack --dry-run
428
+ pnpm run audit
429
+ pnpm run release:check
228
430
  ```
229
431
 
230
432
  ## License
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("@gsap/react"),r=require("gsap"),a=require("gsap/all.js"),n=require("react"),s=require("clsx"),o=require("tailwind-merge");!function(e,t){void 0===t&&(t={});var r=t.insertAt;if("undefined"!=typeof document){var a=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css","top"===r&&a.firstChild?a.insertBefore(n,a.firstChild):a.appendChild(n),n.styleSheet?n.styleSheet.cssText=e:n.appendChild(document.createTextNode(e))}}('.gsap-react-marquee-container{display:flex;overflow:hidden;position:relative;white-space:nowrap;width:100%}.gsap-react-marquee-container:after{background:linear-gradient(270deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);left:0}.gsap-react-marquee-container:after,.gsap-react-marquee-container:before{content:"";height:100%;pointer-events:none;position:absolute;top:0;width:15%;z-index:10}.gsap-react-marquee-container:before{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;top:60%;width:100%}.gsap-react-marquee-vertical:before{background:linear-gradient(1turn,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;right:auto;top:0;width:100%}.gsap-react-marquee{flex:1;height:max-content;width:auto}.gsap-react-marquee,.gsap-react-marquee-content{display:flex;line-height:100%;white-space:nowrap}.gsap-react-marquee-content{overflow:hidden;width:max-content}');const i=new Set(["","auto","fit-content","min-content","max-content","-moz-fit-content","-webkit-fit-content"]),l=(...e)=>o.twMerge(s.clsx(e)),c=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},u=(e,t,a,n,s)=>{const{spacing:o=16}=s;r.gsap.set(e,{gap:`${o}px`,flexDirection:n?"column":"row"}),r.gsap.set(t,{gap:`${o}px`}),r.gsap.set(a,{overflow:n?"visible":"hidden"})},p=(e,t)=>{const r=e.style[t];if(r)return!i.has(r);const a=window.getComputedStyle(e)[t];if(i.has(a))return!1;const n=e.parentElement;if(!n)return!0;const s=window.getComputedStyle(n)[t];return!("100%"===a&&i.has(s))},d=(e,t)=>p(e,t?"height":"width")?t?e.offsetHeight:e.offsetWidth:t?window.innerHeight:window.innerWidth,g=d,f=(e,t,r)=>{const{fill:a=!1}=r;if(!a||e<=0||t<=0)return 1;const n=e<t?Math.ceil(t/e):1;return Math.min(n,15)},m=f,h=(e,t,r)=>{const{fill:a=!1}=r;return a?"auto":e<=t?"100%":`${e}px`},v=h,w=(e,t,n,s,o,i,l)=>{const{spacing:c=16,speed:u=100,delay:p=0,paused:d=!1,draggable:g=!1}=l,f=e.length-1;if(f<0)return;const m=[],h=[],v=i?"yPercent":"xPercent",w=i?"y":"x",x=i?"height":"width";r.gsap.set(e,{[v]:(e,t)=>{const a=parseFloat(String(r.gsap.getProperty(t,x,"px"))),n=parseFloat(String(r.gsap.getProperty(t,w,"px"))),s=Number(r.gsap.getProperty(t,v));return m[e]=a,h[e]=n/a*100+s,h[e]}}),r.gsap.set(e,{[w]:0});const y=e[f],q=i?y.offsetTop:y.offsetLeft,b=i?y.offsetHeight:y.offsetWidth,S=q+h[f]/100*m[f]-t+b+c;let C,E,T,k;e.forEach((e,r)=>{const a=m[r],s=h[r]/100*a,o=(i?e.offsetTop:e.offsetLeft)+s-t+a;n.to(e,{[v]:(s-o)/a*100,duration:o/u},0).fromTo(e,{[v]:(s-o+S)/a*100},{[v]:h[r],duration:(S-o)/u,immediateRender:!1},o/u)}),n.delay(p);const A=()=>r.gsap.delayedCall(p,()=>{n.reverse(),n.eventCallback("onReverseComplete",()=>{n.totalTime(n.rawTime()+100*n.duration())})});if(s){if(d)return void n.pause();n.progress(1).pause(),C=A()}if("function"==typeof a.Draggable&&g){const e=document.createElement("div");k=e;const t=r.gsap.utils.wrap(0,1);let l,c;const u=()=>{if(!T)return;const e=i?T.startY-T.y:T.startX-T.x;n.progress(t(c+e*l))};a.InertiaPlugin,T=a.Draggable.create(e,{trigger:o,type:i?"y":"x",onPress(){r.gsap.killTweensOf(n),n.pause(),c=n.progress(),l=1/S,r.gsap.set(e,{[w]:c/-l})},onDrag:u,onThrowUpdate:u,overshootTolerance:0,inertia:!0,onThrowComplete(){s?d?n.pause():(n.progress(n.progress()).pause(),null==E||E.kill(),E=A()):n.play()}})[0]}return()=>{null==C||C.kill(),null==E||E.kill(),null==T||T.kill(),null==k||k.remove()}},x=w;r.gsap.registerPlugin(t.useGSAP,a.Observer,a.InertiaPlugin,a.Draggable);const y=n.forwardRef((s,o)=>{var i;const{children:p,className:g,dir:m="left",loop:v=-1,paused:x=!1,delay:y=0,fill:q=!1,scrollFollow:b=!1,scrollSpeed:S=2.5,gradient:C=!1,gradientColor:E=null,pauseOnHover:T=!1,spacing:k=16,speed:A=100,draggable:L=!1}=s,P=n.useRef(null),M=n.useRef(null),[N,W]=n.useState(1),[O,R]=n.useState(null),[j,D]=n.useState(0),H=n.useCallback(e=>{P.current=e,"function"!=typeof o?o&&(o.current=e):o(e)},[o]);n.useLayoutEffect(()=>{if(!C||!P.current)return;const e=c(P.current);R(e)},[C]);const z="up"===m||"down"===m,F="down"===m||"right"===m;n.useLayoutEffect(()=>{const e=P.current;if(!e)return;const t=e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let r=null;const a=()=>{null==r&&(r=requestAnimationFrame(()=>{D(e=>e+1),r=null}))},n="undefined"!=typeof ResizeObserver?new ResizeObserver(a):null;n&&(n.observe(e),t&&n.observe(t));const s=Array.from(e.querySelectorAll("img")),o=()=>a();return s.forEach(e=>{e.complete||(e.addEventListener("load",o),e.addEventListener("error",o))}),()=>{null==n||n.disconnect(),s.forEach(e=>{e.removeEventListener("load",o),e.removeEventListener("error",o)}),null!=r&&cancelAnimationFrame(r)}},[p,g]),t.useGSAP((e,t)=>{if(!M.current||!P.current||!t)return;const n=P.current,s={fill:q,spacing:k,speed:A,delay:y,paused:x,draggable:L},o=r.gsap.utils.toArray(n.querySelectorAll(".gsap-react-marquee")),i=r.gsap.utils.toArray(n.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!i.length)return;u(n,o,i,z,s);const l=z?n.offsetHeight:n.offsetWidth,c=z?i[0].offsetHeight:i[0].offsetWidth,p=d(n,z),g=z?i[0].offsetTop:i[0].offsetLeft;let m=null;const C=Math.min(4,Math.max(1.1,S)),E=f(c,p,s);if(N!==E)return void W(E);const O=r.gsap.timeline({paused:x,repeat:v,defaults:{ease:"none"},onReverseComplete(){O.totalTime(O.rawTime()+100*O.duration())}}),R=o.map(e=>z?e.offsetHeight:e.offsetWidth).reduce((e,t)=>e+t,0),j=h(q?0:R/2,l,s);r.gsap.set(o,{[z?"minHeight":"minWidth"]:j,flex:q?"0 0 auto":"1"});const D=w(q?i:o,g,O,F,o,z,s);b&&(m=a.Observer.create({onChangeY(e){let t=C*(F?-1:1);e.deltaY<0&&(t*=-1),r.gsap.timeline({defaults:{ease:"none"}}).to(O,{timeScale:t*C,duration:.2,overwrite:!0}).to(O,{timeScale:t/C,duration:1},"+=0.3")}}));const H=t(()=>{O.pause()}),B=t(()=>{F?O.reverse():O.play()});return T&&(n.addEventListener("mouseenter",H),n.addEventListener("mouseleave",B)),()=>{n.removeEventListener("mouseenter",H),n.removeEventListener("mouseleave",B),r.gsap.killTweensOf(O),O.kill(),null==m||m.kill(),null==D||D()}},{dependencies:[N,m,v,x,y,q,b,S,T,k,A,L,g,p,j],revertOnUpdate:!0});const B=null!==(i=null!=E?E:C?O:null)&&void 0!==i?i:"transparent",G=n.useMemo(()=>!Number.isFinite(N)||N<=0?null:Array.from({length:N},(t,r)=>e.jsx("div",{className:l("gsap-react-marquee"),children:e.jsx("div",{className:l("gsap-react-marquee-content",g),children:p})},r)),[N,g,p]);return e.jsxs("div",{ref:H,style:{"--gradient-color":B},className:l("gsap-react-marquee-container",{"gsap-react-marquee-vertical":z}),children:[e.jsx("div",{ref:M,className:l("gsap-react-marquee"),children:e.jsx("div",{className:l("gsap-react-marquee-content",g),children:p})}),G]})});y.displayName="GSAPReactMarquee",exports.calculateDuplicateCount=f,exports.calculateDuplicates=m,exports.cn=l,exports.coreAnimation=x,exports.createMarqueeAnimation=w,exports.default=y,exports.getEffectiveBackgroundColor=c,exports.getMinSize=h,exports.getMinWidth=v,exports.getTargetSize=d,exports.getTargetWidth=g,exports.hasDefinedWidth=e=>p(e,"width"),exports.setupContainerStyles=u;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react/jsx-runtime"),t=require("@gsap/react"),r=require("gsap"),n=require("react"),a=require("clsx"),i=require("tailwind-merge");if("undefined"!=typeof document&&!document.getElementById("gsap-react-marquee-styles")){const e=document.createElement("style");e.id="gsap-react-marquee-styles",e.textContent='.gsap-react-marquee-container{white-space:nowrap;width:100%;display:flex;position:relative;overflow:hidden}:where(.gsap-react-marquee-container.gsap-react-marquee-vertical){align-self:stretch;height:100%;min-height:0}.gsap-react-marquee-container:after{content:"";background:linear-gradient(270deg, #fff0 0%, var(--gradient-color) 75%);z-index:10;pointer-events:none;width:15%;height:100%;position:absolute;top:0;left:0}.gsap-react-marquee-container:before{content:"";background:linear-gradient(90deg, #fff0 0%, var(--gradient-color) 75%);z-index:10;pointer-events:none;width:15%;height:100%;position:absolute;top:0;right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg, #fff0 0%, var(--gradient-color) 75%);width:100%;height:50%;top:60%;left:0}.gsap-react-marquee-vertical:before{background:linear-gradient(360deg, #fff0 0%, var(--gradient-color) 75%);width:100%;height:50%;top:0;left:0;right:auto}.gsap-react-marquee{white-space:nowrap;flex:1;width:auto;height:max-content;line-height:100%;display:flex}.gsap-react-marquee-clone,.gsap-react-marquee-clone *{pointer-events:none}.gsap-react-marquee-content{white-space:nowrap;width:max-content;line-height:100%;display:flex;overflow:hidden}',document.head.appendChild(e)}const l="[data-gsap-react-marquee-clone]",o=["a[href]","area[href]","audio[controls]","button","embed","iframe","input:not([type='hidden'])","object","select","summary","textarea","video[controls]","[contenteditable]:not([contenteditable='false'])","[tabindex]"].join(","),s=["[id]","a[href]","area[href]","button","embed","iframe","input","object","select","textarea","[contenteditable]:not([contenteditable='false'])"].join(","),u=["aria-activedescendant","aria-controls","aria-describedby","aria-details","aria-errormessage","aria-flowto","aria-labelledby","aria-owns","for","form","headers","itemref","list"],c=["clip-path","fill","filter","marker-end","marker-mid","marker-start","mask","stroke"],d=/^\s*#/,p=/url\(\s*["']?#/i,f=()=>"undefined"!=typeof HTMLElement&&"inert"in HTMLElement.prototype,g=e=>{const t=e.matches(o);e.removeAttribute("id"),e.removeAttribute("name"),u.forEach(t=>{e.removeAttribute(t)});const r=e.getAttribute("href");r&&d.test(r)&&e.removeAttribute("href");const n=e.getAttribute("xlink:href");n&&d.test(n)&&e.removeAttribute("xlink:href"),c.forEach(t=>{const r=e.getAttribute(t);r&&p.test(r)&&e.removeAttribute(t)}),e instanceof HTMLElement&&Array.from(e.style).forEach(t=>{p.test(e.style.getPropertyValue(t))&&e.style.removeProperty(t)}),t&&e.setAttribute("tabindex","-1")},m=e=>"number"==typeof e&&Number.isFinite(e),v=e=>{const t=m(e.speed)&&e.speed>0?e.speed:100,r=m(e.spacing)&&e.spacing>=0?e.spacing:16,n=m(e.delay)&&e.delay>=0?e.delay:0,a=m(e.scrollSpeed)?e.scrollSpeed:2.5,i=Math.min(4,Math.max(1.1,a)),l=-1===e.loop||m(e.loop)&&Number.isInteger(e.loop)&&e.loop>=0?e.loop:-1,o=m(e.maxDuplicates)&&Number.isInteger(e.maxDuplicates)&&e.maxDuplicates>0?e.maxDuplicates:100;return{delay:n,loop:l,maxDuplicates:Math.min(o,250),scrollSpeed:i,spacing:r,speed:t}},h=(...e)=>e.length>0&&e.every(e=>Number.isFinite(e)&&e>0),b=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},y=(e,t,n,a,i)=>{var l;const{spacing:o}=v(i),s=null!==(l=i.fill)&&void 0!==l&&l;r.gsap.set(e,{gap:`${o}px`,flexDirection:a?"column":"row"}),r.gsap.set(t,{flexDirection:a?"column":"row",gap:`${o}px`,[a?"minHeight":"minWidth"]:s?"auto":"100%",flex:s?"0 0 auto":"1"}),r.gsap.set(n,{flexDirection:a?"column":"row",overflow:a?"visible":"hidden"})},x=(e,t)=>t?e.offsetHeight:e.offsetWidth,R=x,S=(e,t,r)=>{const{fill:n=!1}=r,{maxDuplicates:a,spacing:i}=v(r);if(!n||!h(e,t))return{duplicateCount:1,limitReached:!1,requiredDuplicateCount:1};const l=e+i;if(!h(l))return{duplicateCount:1,limitReached:!1,requiredDuplicateCount:1};const o=t+l,s=Math.ceil((o+i)/l),u=Math.max(1,s-1);return{duplicateCount:Math.min(u,a),limitReached:u>a,requiredDuplicateCount:u}},w=(e,t,r)=>S(e,t,r).duplicateCount,q=w,A=(e,t,r)=>{const{fill:n=!1}=r;return n?"auto":e<=t?"100%":`${e}px`},k=A,C=()=>"undefined"!=typeof process&&"production"===process.env.NODE_ENV,E="undefined"==typeof window?n.useEffect:n.useLayoutEffect;let D=!1;const P=({cancelReverseDelay:e,delay:t,dragTrigger:n,enabled:a,isReverse:i,isVertical:l,plugins:o,positionProperty:s,readPaused:u,restoreControlledState:c,scheduleReverseResume:d,timeline:p,trackLength:f})=>{const g=null==o?void 0:o.Draggable;if("function"!=typeof g||!a)return;const m=document.createElement("div"),v=r.gsap.utils.wrap(0,1);let h,b,y;const x={},R=()=>{if(null==y||y.kill(),i&&t>0&&!u())return p.pause(),void(y=d("gsap-react-marquee-drag-delay"));c()},S=()=>{const e=x.instance;if(!e)return;const t=l?e.startY-e.y:e.startX-e.x;p.progress(v(b+t*h))},w=Boolean(r.gsap.plugins.inertia);return w||D||(D=!0,console.warn("InertiaPlugin required for momentum-based scrolling and snapping. https://greensock.com/club")),x.instance=g.create(m,{trigger:n,type:l?"y":"x",onPress(){var t,n;e(),null==y||y.kill(),null===(n=null===(t=x.instance)||void 0===t?void 0:t.tween)||void 0===n||n.kill(),r.gsap.killTweensOf(m),r.gsap.killTweensOf(p),p.pause(),b=p.progress(),h=1/f,r.gsap.set(m,{[s]:b/-h})},onDrag:S,onThrowUpdate:S,overshootTolerance:0,inertia:w,onRelease(){this.isThrowing||R()},onThrowComplete(){R()}})[0],()=>{var e,t,n;null==y||y.kill(),null===(t=null===(e=x.instance)||void 0===e?void 0:e.tween)||void 0===t||t.kill(),null===(n=x.instance)||void 0===n||n.kill(),r.gsap.killTweensOf(m),m.remove()}},z=({timeline:e,isReverse:t,paused:r})=>{e.timeScale(1),r?e.pause():t?e.reverse():e.play()},L=(e,t,n,a,i,l,o,s,u,c)=>{const{spacing:d,speed:p,delay:f,loop:g}=v(o),{paused:m=!1,draggable:b=!1}=o,y=null!=u?u:()=>m;if(0===e.length)return;const x=l?"yPercent":"xPercent",R=l?"y":"x",S=((e,t,n,a,i,l,o,s)=>{const u=[],c=[],d=e.map(e=>s?((e,t,r)=>{let n=0,a=e;for(;a&&a!==t;)n+=r?a.offsetTop:a.offsetLeft,a=a.offsetParent;if(a===t)return n;const i=e.getBoundingClientRect(),l=t.getBoundingClientRect();return r?i.top-l.top:i.left-l.left})(e,s,a):a?e.offsetTop:e.offsetLeft);if(!d.every(Number.isFinite))return null;if(e.forEach((e,t)=>{const n=Number.parseFloat(String(r.gsap.getProperty(e,o,"px"))),a=Number.parseFloat(String(r.gsap.getProperty(e,l,"px"))),s=Number(r.gsap.getProperty(e,i));u[t]=n,c[t]=h(n)&&Number.isFinite(a)&&Number.isFinite(s)?a/n*100+s:Number.NaN}),!u.every(e=>h(e))||!c.every(Number.isFinite))return null;const p=e.length-1,f=e[p],g=d[p],m=a?f.offsetHeight:f.offsetWidth,v=s?d[0]:t,b=g+c[p]/100*u[p]-v+m+n;return Number.isFinite(v)&&h(m,b)?{effectiveStartPosition:v,initialPercents:c,itemOffsets:d,itemSizes:u,trackLength:b}:null})(e,t,d,l,x,R,l?"height":"width",s);if(!S)return;const w=((e,t,r)=>{const{effectiveStartPosition:n,initialPercents:a,itemOffsets:i,itemSizes:l,trackLength:o}=t,s=e.map((e,t)=>{const s=l[t],u=a[t]/100*s,c=i[t]+u-n+s,d=o-c,p=(u-c)/s*100,f=(u-c+o)/s*100,g=c/r,m=d/r;return![u,c,d,p,f,g,m].every(Number.isFinite)||c<0||d<0||g<0||m<0?null:{entryPercent:f,exitDuration:g,exitPercent:p,item:e,returnDuration:m}});return s.every(e=>null!==e)?s:null})(e,S,p);if(!w)return;let q;((e,t,n,a,i)=>{const l=t.map(({item:e})=>e);r.gsap.set(l,{[a]:e=>n[e]}),r.gsap.set(l,{[i]:0}),t.forEach((t,r)=>{e.to(t.item,{[a]:t.exitPercent,duration:t.exitDuration},0).fromTo(t.item,{[a]:t.entryPercent},{[a]:n[r],duration:t.returnDuration,immediateRender:!1},t.exitDuration)})})(n,w,S.initialPercents,x,R);const A=()=>{z({timeline:n,isReverse:a,paused:y()})},k=e=>{const t=r.gsap.delayedCall(f,()=>{A()});return t.data=e,t};n.eventCallback("onReverseComplete",null),-1===g&&n.eventCallback("onReverseComplete",()=>{n.totalTime(n.rawTime()+100*n.duration())}),a?(-1===g?n.progress(1).pause():n.totalProgress(1).pause(),y()||0===f?A():q=k("gsap-react-marquee-reverse-delay")):(n.delay(f),A());const C=P({cancelReverseDelay:()=>null==q?void 0:q.kill(),delay:f,dragTrigger:i,enabled:b,isReverse:a,isVertical:l,plugins:c,positionProperty:R,readPaused:y,restoreControlledState:A,scheduleReverseResume:k,timeline:n,trackLength:S.trackLength});return()=>{null==q||q.kill(),null==C||C(),n.eventCallback("onReverseComplete",null)}},N=L,M=(e,t)=>null!==e&&null!==t&&Number.isFinite(e)&&Number.isFinite(t)&&Math.abs(e-t)<=.5,T=(e,t)=>t?e.offsetHeight:e.offsetWidth,F=e=>e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let O=null,j=null;const H=e=>{let t=null;return()=>(null!=t||(t=e().catch(e=>{throw t=null,e})),t)},V=H(()=>import("gsap/Observer.js").then(({Observer:e})=>e)),W=H(()=>import("gsap/Draggable.js").then(({Draggable:e})=>({Draggable:e}))),B=new Set,G=e=>{C()||B.has(e)||(B.add(e),Reflect.apply(console.warn,console,[`GSAPReactMarquee: optional GSAP ${e} plugin failed to load; that interactive feature was not initialized.`]))},I="(prefers-reduced-motion: reduce)",$=()=>"undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia(I).matches;r.gsap.registerPlugin(t.useGSAP);const U=(...e)=>e.filter(Boolean).join(" "),Y=n.forwardRef((a,i)=>{var o;const{children:u,className:c,containerClassName:d,containerStyle:p,containerProps:m,dir:R="left",loop:q,paused:k=!1,respectReducedMotion:D=!0,delay:P,fill:N=!1,maxDuplicates:H,scrollFollow:B=!1,scrollSpeed:Y,gradient:_=!1,gradientColor:X=null,pauseOnHover:J=!1,spacing:K,speed:Q,draggable:Z=!1}=a,{delay:ee,loop:te,maxDuplicates:re,scrollSpeed:ne,spacing:ae,speed:ie}=v({delay:P,loop:q,maxDuplicates:H,scrollSpeed:Y,spacing:K,speed:Q}),le=n.useRef(null),oe=n.useRef(null),se=(()=>{const[e,t]=n.useState($);return E(()=>{if("function"!=typeof window.matchMedia)return;const e=window.matchMedia(I),r=()=>t(e.matches);return r(),"function"==typeof e.addEventListener?(e.addEventListener("change",r),()=>e.removeEventListener("change",r)):(e.addListener(r),()=>e.removeListener(r))},[]),e})(),ue=D&&se,{pluginsReady:ce,pluginsRef:de}=(({draggable:e,scrollFollow:t})=>{const a=n.useRef({draggable:null,observer:null}),[i,l]=n.useState({draggable:"idle",observer:"idle"}),o=n.useRef(i);return n.useEffect(()=>{let n=!0;const i=(e,t)=>{if(!n)return;const r={...o.current,[e]:t};o.current=r,l(r)},s=e=>{const t=o.current[e];"loading"!==t&&"failed"!==t||i(e,"idle")};return t&&!a.current.observer?(i("observer","loading"),V().then(e=>{n&&((e=>{O!==e&&(r.gsap.registerPlugin(e),O=e)})(e),a.current.observer=e,i("observer","ready"))}).catch(()=>{n&&(i("observer","failed"),G("observer"))})):t||s("observer"),e&&!a.current.draggable?(i("draggable","loading"),W().then(e=>{n&&((e=>{j!==e&&(r.gsap.registerPlugin(e.Draggable),j=e)})(e),a.current.draggable=e,i("draggable","ready"))}).catch(()=>{n&&(i("draggable","failed"),G("draggable"))})):e||s("draggable"),()=>{n=!1}},[e,t]),{pluginStatuses:i,pluginsReady:!(e&&null===a.current.draggable&&"failed"!==i.draggable||t&&null===a.current.observer&&"failed"!==i.observer),pluginsRef:a}})({draggable:Z&&!ue,scrollFollow:B&&!ue}),pe=n.useRef(k);pe.current=k;const[fe,ge]=n.useState(0),[me,ve]=n.useState(null),he=n.useRef(!1),be=n.useRef(!1),ye=n.useCallback(e=>{le.current=e,"function"!=typeof i?i&&(i.current=e):i(e)},[i]);E(()=>{if(!_||!le.current)return;const e=b(le.current);ve(e)},[d,p,_]),E(()=>{var e;!be.current&&oe.current&&((e=oe.current).querySelector(s)||Array.from(e.querySelectorAll("[tabindex]")).some(e=>e.tabIndex>=0))&&(be.current=!0,C()||Reflect.apply(console.warn,console,["GSAPReactMarquee: 0.4.0 supports presentational children only. Interactive children and stable IDs are unsupported; visual clones are sanitized for safety."]))},[u]),E(()=>{const e=le.current;if(!e)return;const t=f();return((e,t=f())=>{Array.from(e.querySelectorAll(l)).forEach(e=>{e.setAttribute("aria-hidden","true"),e.setAttribute("inert",""),t?(e.inert=!0,e.removeAttribute("data-gsap-react-marquee-inert-fallback")):e.setAttribute("data-gsap-react-marquee-inert-fallback",""),g(e),e.querySelectorAll("*").forEach(g)})})(e,t),((e,t=f())=>{if(t)return()=>{};const r=t=>{var r,n;const a=t.target;if(!(a instanceof Element))return;const i=a.closest(l);if(!i||!e.contains(i))return;const o=a;if(null===(r=o.blur)||void 0===r||r.call(o),document.activeElement===a){const e=document.activeElement;null===(n=e.blur)||void 0===n||n.call(e)}};return e.addEventListener("focusin",r),()=>e.removeEventListener("focusin",r)})(e,t)},[u,fe,ue]);const xe="up"===R||"down"===R;E(()=>{const e=le.current;if(!e)return;const t=Array.from(e.querySelectorAll(".gsap-react-marquee")),r=Array.from(e.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));y(e,t,r,xe,{fill:N,spacing:ae})},[fe,N,xe,ae]);const{beginCloneApplication:Re,cloneApplicationSnapshotRef:Se,measurementSnapshotRef:we,measurementVersion:qe}=(({duplicateCount:e,isVertical:t,rootRef:r})=>{const a=n.useRef(!1),i=n.useRef(null),l=n.useRef(null),o=n.useRef(null),[s,u]=n.useState(0),c=n.useCallback(e=>{return!((t=i.current)===(r=e)||t&&r&&t.axis===r.axis&&M(t.contentSize,r.contentSize)&&M(t.rootSize,r.rootSize)&&M(t.viewportSize,r.viewportSize)||(i.current=e,u(e=>e+1),0));var t,r},[]),d=n.useCallback(()=>{const e=r.current;if(!e)return null;const n=F(e);if(!n)return null;const a=T(e,t),i=T(n,t),l=x(e,t);return h(a,i,l)?{axis:t?"vertical":"horizontal",contentSize:i,rootSize:a,viewportSize:l}:null},[t,r]),p=n.useCallback(()=>c(d()),[c,d]),f=n.useCallback(()=>{const e=r.current;if(!e)return c(null);const n=F(e);if(!n)return c(null);const s=t?"vertical":"horizontal",u=T(n,t),d=i.current;if((null==d?void 0:d.axis)===s&&M(u,d.contentSize))return!1;if(a.current=!1,l.current=null,o.current=null,!h(u))return c(null);if((null==d?void 0:d.axis)===s){const r=c({...d,contentSize:u}),n=T(e,t);return o.current=h(n)?n:null,r}return p()},[p,t,c,r]),g=n.useCallback(()=>{const e=r.current;if(!e||a.current)return!1;const n=T(e,t);if(M(n,o.current))return!1;const l=i.current,s=t?"vertical":"horizontal";return((null==l?void 0:l.axis)!==s||!M(n,l.rootSize))&&(o.current=null,p())},[p,t,r]),m=n.useCallback(e=>{const n=r.current,i=n?T(n,t):null;return null===i||M(i,e.rootSize)||M(i,o.current)?(a.current=!0,l.current=e,!0):(p(),!1)},[p,t,r]);return E(()=>{if(!a.current)return;const e=r.current;if(!e)return;const n=T(e,t);o.current=h(n)?n:null;const i=requestAnimationFrame(()=>{a.current=!1,l.current=null});return()=>cancelAnimationFrame(i)},[e,t,r]),E(()=>{const e=r.current;if(!e)return;const n=F(e),s=t?"vertical":"horizontal",u=i.current;u&&u.axis===s?f():(a.current=!1,l.current=null,o.current=null,p());let c=null;const d=()=>{null===c&&(c=requestAnimationFrame(()=>{c=null,f()}))},m="undefined"==typeof ResizeObserver?null:new ResizeObserver(t=>{const r=!!t.some(({target:e})=>e===n)&&f();t.some(({target:t})=>t===e)&&!r&&g()});null==m||m.observe(e),n&&(null==m||m.observe(n));const v=Array.from(e.querySelectorAll("img"));return v.forEach(e=>{e.complete||(e.addEventListener("load",d),e.addEventListener("error",d))}),()=>{null==m||m.disconnect(),v.forEach(e=>{e.removeEventListener("load",d),e.removeEventListener("error",d)}),null!==c&&cancelAnimationFrame(c)}},[f,p,g,t,r]),{beginCloneApplication:m,cloneApplicationSnapshotRef:l,cloneAppliedRootSizeRef:o,isApplyingMeasuredClonesRef:a,measurementSnapshotRef:i,measurementVersion:s}})({duplicateCount:fe,isVertical:xe,rootRef:le});E(()=>{if(ue)return void ge(e=>0===e?e:0);const e=we.current,t=xe?"vertical":"horizontal";if(!e||e.axis!==t)return;const r=S(e.contentSize,e.viewportSize,{fill:N,maxDuplicates:re,spacing:ae}),{duplicateCount:n}=r;r.limitReached&&!he.current&&(he.current=!0,((e,t)=>{C()||Reflect.apply(console.warn,console,[`GSAPReactMarquee: maxDuplicates=${e} prevents full fill coverage; ${t} duplicates are required.`])})(re,r.requiredDuplicateCount)),fe!==n&&Re(e)&&ge(n)},[Re,fe,N,xe,re,we,qe,ue,ae]),(({cloneApplicationSnapshotRef:e,delay:n,dir:a,draggable:i,duplicateCount:l,fill:o,loop:s,marqueeRef:u,maxDuplicates:c,measurementSnapshotRef:d,measurementVersion:p,paused:f,pausedRef:g,pauseOnHover:m,pluginsReady:v,pluginsRef:b,rootRef:y,scrollFollow:x,scrollSpeed:R,shouldReduceMotion:S,spacing:q,speed:k})=>{const C="up"===a||"down"===a,E="down"===a||"right"===a;t.useGSAP((t,a)=>{var p,D;if(S||!u.current||!y.current||!v||!a)return;const P=y.current,N=null!==(p=e.current)&&void 0!==p?p:d.current,M=C?"vertical":"horizontal";if(!N||N.axis!==M)return;const T={fill:o,maxDuplicates:c,spacing:q,speed:k,delay:n,loop:s,paused:f,draggable:i},F=r.gsap.utils.toArray(P.querySelectorAll(".gsap-react-marquee")),O=r.gsap.utils.toArray(P.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!O.length)return;const j=N.rootSize,H=N.contentSize,V=N.viewportSize,W=C?O[0].offsetTop:O[0].offsetLeft;let B=!1,G=!1;const I=()=>g.current||m&&(B||G);if(!h(j,H,V)||!Number.isFinite(W))return;const $=w(H,V,T);if(l!==$)return;const U=o?"auto":A(H,j,T);r.gsap.set(F,{[C?"minHeight":"minWidth"]:U,flex:o?"0 0 auto":"1"});const Y=o?O:F,_=Y.map(e=>C?e.offsetHeight:e.offsetWidth);if(!h(..._))return;const X=r.gsap.timeline({paused:!0,repeat:s,defaults:{ease:"none"},data:"gsap-react-marquee-base"}),J=L(Y,W,X,E,F,C,T,P,I,null!==(D=b.current.draggable)&&void 0!==D?D:void 0);if(!h(X.duration()))return null==J||J(),void X.kill();const K=()=>{z({timeline:X,isReverse:E,paused:I()})},Q=(({enabled:e,isReverse:t,observer:n,restoreBaseTimeline:a,scrollSpeed:i,shouldRemainPaused:l,timeline:o})=>{var s;let u=null;const c=()=>{null==u||u.kill(),u=null,r.gsap.killTweensOf(o)},d=e&&null!==(s=null==n?void 0:n.create({onChangeY(e){if(c(),l())return void a();const n=t?-1:1,s=n*(e.deltaY<0?-1:1)*i*i;u=r.gsap.timeline({data:"gsap-react-marquee-scroll-response",defaults:{ease:"none"},onComplete(){u=null,a()}}).to(o,{timeScale:s,duration:.2,overwrite:!0}).to(o,{timeScale:n,duration:1},"+=0.3")}}))&&void 0!==s?s:null;return{stop:c,cleanup:()=>{null==d||d.kill(),c()}}})({enabled:x,isReverse:E,observer:b.current.observer,restoreBaseTimeline:K,scrollSpeed:R,shouldRemainPaused:I,timeline:X}),Z=a(()=>{B=!0,Q.stop(),X.pause()}),ee=a(()=>{B=!1,K()}),te=a(()=>{G=!0,Q.stop(),X.pause()}),re=a(e=>{const t=e.relatedTarget;t instanceof Node&&P.contains(t)||(G=!1,K())});return m&&(P.addEventListener("pointerenter",Z),P.addEventListener("pointerleave",ee),P.addEventListener("focusin",te),P.addEventListener("focusout",re)),()=>{P.removeEventListener("pointerenter",Z),P.removeEventListener("pointerleave",ee),P.removeEventListener("focusin",te),P.removeEventListener("focusout",re),Q.cleanup(),null==J||J(),r.gsap.killTweensOf(X),X.kill()}},{dependencies:[l,a,s,f,n,o,c,x,R,m,q,k,i,v,p,S],revertOnUpdate:!0})})({cloneApplicationSnapshotRef:Se,delay:ee,dir:R,draggable:Z,duplicateCount:fe,fill:N,loop:te,marqueeRef:oe,maxDuplicates:re,measurementSnapshotRef:we,measurementVersion:qe,paused:k,pausedRef:pe,pauseOnHover:J,pluginsReady:ce,pluginsRef:de,rootRef:le,scrollFollow:B,scrollSpeed:ne,shouldReduceMotion:ue,spacing:ae,speed:ie});const Ae=null!==(o=null!=X?X:_?me:null)&&void 0!==o?o:"transparent",ke=n.useMemo(()=>ue||!Number.isFinite(fe)||fe<=0?null:Array.from({length:fe},(t,r)=>e.jsx("div",{"aria-hidden":"true",className:U("gsap-react-marquee","gsap-react-marquee-clone"),"data-gsap-react-marquee-clone":"",children:e.jsx("div",{className:U("gsap-react-marquee-content",c),children:u})},r)),[fe,c,u,ue]);return e.jsxs("div",{...m,ref:ye,style:{...p,display:"flex",overflow:"hidden",position:"relative",whiteSpace:"nowrap","--gradient-color":Ae},className:U("gsap-react-marquee-container",xe&&"gsap-react-marquee-vertical",d),children:[e.jsx("div",{ref:oe,className:"gsap-react-marquee","data-gsap-react-marquee-original":"",children:e.jsx("div",{className:U("gsap-react-marquee-content",c),children:u})}),ke]})});Y.displayName="GSAPReactMarquee",exports.calculateDuplicateCount=w,exports.calculateDuplicateCountResult=S,exports.calculateDuplicates=q,exports.cn=(...e)=>i.twMerge(a.clsx(e)),exports.coreAnimation=N,exports.createMarqueeAnimation=L,exports.default=Y,exports.getEffectiveBackgroundColor=b,exports.getMinSize=A,exports.getMinWidth=k,exports.getTargetSize=x,exports.getTargetWidth=R,exports.hasDefinedWidth=e=>h(e.offsetWidth),exports.hasUsableMeasurement=h,exports.normalizeMarqueeOptions=v,exports.resumeTimeline=z,exports.setupContainerStyles=y;
package/dist/index.d.ts CHANGED
@@ -1,17 +1,26 @@
1
1
  import * as react from 'react';
2
- import { ReactNode } from 'react';
3
- import { ClassValue } from 'clsx';
2
+ import { ReactNode, CSSProperties, ComponentPropsWithoutRef } from 'react';
4
3
  import { gsap } from 'gsap';
4
+ import { Draggable } from 'gsap/Draggable';
5
+ import { ClassValue } from 'clsx';
5
6
 
7
+ type DataAttributes = {
8
+ [attribute: `data-${string}`]: boolean | number | string | undefined;
9
+ };
6
10
  type GSAPReactMarqueeProps = {
7
11
  children: ReactNode;
8
12
  className?: string;
13
+ containerClassName?: string;
14
+ containerStyle?: CSSProperties;
15
+ containerProps?: Omit<ComponentPropsWithoutRef<"div">, "children" | "className" | "style" | "ref" | "dangerouslySetInnerHTML"> & DataAttributes;
9
16
  dir?: "right" | "left" | "up" | "down";
10
17
  loop?: number;
11
18
  paused?: boolean;
19
+ respectReducedMotion?: boolean;
12
20
  delay?: number;
13
21
  speed?: number;
14
22
  fill?: boolean;
23
+ maxDuplicates?: number;
15
24
  pauseOnHover?: boolean;
16
25
  gradient?: boolean;
17
26
  gradientColor?: string;
@@ -23,19 +32,48 @@ type GSAPReactMarqueeProps = {
23
32
 
24
33
  declare const GSAPReactMarquee: react.ForwardRefExoticComponent<GSAPReactMarqueeProps & react.RefAttributes<HTMLDivElement>>;
25
34
 
35
+ type MarqueeDraggablePlugins = {
36
+ Draggable: typeof Draggable;
37
+ };
38
+
26
39
  type GSAPTimeline = ReturnType<typeof gsap.timeline>;
40
+ type ResumeTimelineOptions = {
41
+ timeline: GSAPTimeline;
42
+ isReverse: boolean;
43
+ paused: boolean;
44
+ };
45
+ declare const resumeTimeline: ({ timeline, isReverse, paused, }: ResumeTimelineOptions) => void;
46
+ declare const createMarqueeAnimation: (items: HTMLElement[], startPosition: number, timeline: GSAPTimeline, isReverse: boolean, dragTrigger: HTMLElement | HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps, offsetContainer?: HTMLElement, getPaused?: () => boolean, draggablePlugins?: MarqueeDraggablePlugins) => (() => void) | undefined;
47
+ declare const coreAnimation: (items: HTMLElement[], startPosition: number, timeline: GSAPTimeline, isReverse: boolean, dragTrigger: HTMLElement | HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps, offsetContainer?: HTMLElement, getPaused?: () => boolean, draggablePlugins?: MarqueeDraggablePlugins) => (() => void) | undefined;
48
+
49
+ declare const hasUsableMeasurement: (...measurements: number[]) => boolean;
27
50
  declare const cn: (...inputs: ClassValue[]) => string;
28
- declare const getEffectiveBackgroundColor: (el: HTMLElement) => string;
51
+ declare const getEffectiveBackgroundColor: (element: HTMLElement) => string;
29
52
  declare const setupContainerStyles: (containerElement: HTMLElement, marqueeElements: HTMLElement[], contentElements: HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => void;
30
53
  declare const hasDefinedWidth: (element: HTMLElement) => boolean;
31
54
  declare const getTargetSize: (containerElement: HTMLElement, isVertical: boolean) => number;
32
55
  declare const getTargetWidth: (containerElement: HTMLElement, isVertical: boolean) => number;
56
+ type DuplicateCountResult = {
57
+ duplicateCount: number;
58
+ limitReached: boolean;
59
+ requiredDuplicateCount: number;
60
+ };
61
+ declare const calculateDuplicateCountResult: (contentSize: number, targetSize: number, props: GSAPReactMarqueeProps) => DuplicateCountResult;
33
62
  declare const calculateDuplicateCount: (contentSize: number, targetSize: number, props: GSAPReactMarqueeProps) => number;
34
63
  declare const calculateDuplicates: (contentSize: number, targetSize: number, props: GSAPReactMarqueeProps) => number;
35
64
  declare const getMinSize: (itemSize: number, containerSize: number, props: GSAPReactMarqueeProps) => string | number;
36
65
  declare const getMinWidth: (itemSize: number, containerSize: number, props: GSAPReactMarqueeProps) => string | number;
37
- declare const createMarqueeAnimation: (items: HTMLElement[], startPosition: number, timeline: GSAPTimeline, isReverse: boolean, dragTrigger: HTMLElement | HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => (() => void) | undefined;
38
- declare const coreAnimation: (items: HTMLElement[], startPosition: number, timeline: GSAPTimeline, isReverse: boolean, dragTrigger: HTMLElement | HTMLElement[], isVertical: boolean, props: GSAPReactMarqueeProps) => (() => void) | undefined;
39
66
 
40
- export { calculateDuplicateCount, calculateDuplicates, cn, coreAnimation, createMarqueeAnimation, GSAPReactMarquee as default, getEffectiveBackgroundColor, getMinSize, getMinWidth, getTargetSize, getTargetWidth, hasDefinedWidth, setupContainerStyles };
41
- export type { GSAPReactMarqueeProps };
67
+ type NormalizedMarqueeOptions = {
68
+ delay: number;
69
+ loop: -1 | number;
70
+ maxDuplicates: number;
71
+ scrollSpeed: number;
72
+ spacing: number;
73
+ speed: number;
74
+ };
75
+ type NumericMarqueeOptions = Pick<GSAPReactMarqueeProps, "delay" | "loop" | "maxDuplicates" | "scrollSpeed" | "spacing" | "speed">;
76
+ declare const normalizeMarqueeOptions: (options: NumericMarqueeOptions) => NormalizedMarqueeOptions;
77
+
78
+ export { calculateDuplicateCount, calculateDuplicateCountResult, calculateDuplicates, cn, coreAnimation, createMarqueeAnimation, GSAPReactMarquee as default, getEffectiveBackgroundColor, getMinSize, getMinWidth, getTargetSize, getTargetWidth, hasDefinedWidth, hasUsableMeasurement, normalizeMarqueeOptions, resumeTimeline, setupContainerStyles };
79
+ export type { DuplicateCountResult, GSAPReactMarqueeProps, NormalizedMarqueeOptions, ResumeTimelineOptions };
package/dist/index.esm.js CHANGED
@@ -1 +1 @@
1
- import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useGSAP as r}from"@gsap/react";import{gsap as n}from"gsap";import{Draggable as a,InertiaPlugin as o,Observer as i}from"gsap/all.js";import{forwardRef as l,useRef as s,useState as c,useCallback as u,useLayoutEffect as d,useMemo as p}from"react";import{clsx as f}from"clsx";import{twMerge as m}from"tailwind-merge";!function(e,t){void 0===t&&(t={});var r=t.insertAt;if("undefined"!=typeof document){var n=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css","top"===r&&n.firstChild?n.insertBefore(a,n.firstChild):n.appendChild(a),a.styleSheet?a.styleSheet.cssText=e:a.appendChild(document.createTextNode(e))}}('.gsap-react-marquee-container{display:flex;overflow:hidden;position:relative;white-space:nowrap;width:100%}.gsap-react-marquee-container:after{background:linear-gradient(270deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);left:0}.gsap-react-marquee-container:after,.gsap-react-marquee-container:before{content:"";height:100%;pointer-events:none;position:absolute;top:0;width:15%;z-index:10}.gsap-react-marquee-container:before{background:linear-gradient(90deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;top:60%;width:100%}.gsap-react-marquee-vertical:before{background:linear-gradient(1turn,hsla(0,0%,100%,0) 0,var(--gradient-color) 75%);height:50%;left:0;right:auto;top:0;width:100%}.gsap-react-marquee{flex:1;height:max-content;width:auto}.gsap-react-marquee,.gsap-react-marquee-content{display:flex;line-height:100%;white-space:nowrap}.gsap-react-marquee-content{overflow:hidden;width:max-content}');const g=new Set(["","auto","fit-content","min-content","max-content","-moz-fit-content","-webkit-fit-content"]),h=(...e)=>m(f(e)),v=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},w=(e,t,r,a,o)=>{const{spacing:i=16}=o;n.set(e,{gap:`${i}px`,flexDirection:a?"column":"row"}),n.set(t,{gap:`${i}px`}),n.set(r,{overflow:a?"visible":"hidden"})},y=(e,t)=>{const r=e.style[t];if(r)return!g.has(r);const n=window.getComputedStyle(e)[t];if(g.has(n))return!1;const a=e.parentElement;if(!a)return!0;const o=window.getComputedStyle(a)[t];return!("100%"===n&&g.has(o))},q=e=>y(e,"width"),x=(e,t)=>y(e,t?"height":"width")?t?e.offsetHeight:e.offsetWidth:t?window.innerHeight:window.innerWidth,b=x,E=(e,t,r)=>{const{fill:n=!1}=r;if(!n||e<=0||t<=0)return 1;const a=e<t?Math.ceil(t/e):1;return Math.min(a,15)},S=E,T=(e,t,r)=>{const{fill:n=!1}=r;return n?"auto":e<=t?"100%":`${e}px`},k=T,C=(e,t,r,o,i,l,s)=>{const{spacing:c=16,speed:u=100,delay:d=0,paused:p=!1,draggable:f=!1}=s,m=e.length-1;if(m<0)return;const g=[],h=[],v=l?"yPercent":"xPercent",w=l?"y":"x",y=l?"height":"width";n.set(e,{[v]:(e,t)=>{const r=parseFloat(String(n.getProperty(t,y,"px"))),a=parseFloat(String(n.getProperty(t,w,"px"))),o=Number(n.getProperty(t,v));return g[e]=r,h[e]=a/r*100+o,h[e]}}),n.set(e,{[w]:0});const q=e[m],x=l?q.offsetTop:q.offsetLeft,b=l?q.offsetHeight:q.offsetWidth,E=x+h[m]/100*g[m]-t+b+c;let S,T,k,C;e.forEach((e,n)=>{const a=g[n],o=h[n]/100*a,i=(l?e.offsetTop:e.offsetLeft)+o-t+a;r.to(e,{[v]:(o-i)/a*100,duration:i/u},0).fromTo(e,{[v]:(o-i+E)/a*100},{[v]:h[n],duration:(E-i)/u,immediateRender:!1},i/u)}),r.delay(d);const A=()=>n.delayedCall(d,()=>{r.reverse(),r.eventCallback("onReverseComplete",()=>{r.totalTime(r.rawTime()+100*r.duration())})});if(o){if(p)return void r.pause();r.progress(1).pause(),S=A()}if("function"==typeof a&&f){const e=document.createElement("div");C=e;const t=n.utils.wrap(0,1);let s,c;const u=()=>{if(!k)return;const e=l?k.startY-k.y:k.startX-k.x;r.progress(t(c+e*s))};k=a.create(e,{trigger:i,type:l?"y":"x",onPress(){n.killTweensOf(r),r.pause(),c=r.progress(),s=1/E,n.set(e,{[w]:c/-s})},onDrag:u,onThrowUpdate:u,overshootTolerance:0,inertia:!0,onThrowComplete(){o?p?r.pause():(r.progress(r.progress()).pause(),null==T||T.kill(),T=A()):r.play()}})[0]}return()=>{null==S||S.kill(),null==T||T.kill(),null==k||k.kill(),null==C||C.remove()}},A=C;n.registerPlugin(r,i,o,a);const L=l((a,o)=>{var l;const{children:f,className:m,dir:g="left",loop:y=-1,paused:q=!1,delay:b=0,fill:S=!1,scrollFollow:k=!1,scrollSpeed:A=2.5,gradient:L=!1,gradientColor:N=null,pauseOnHover:H=!1,spacing:P=16,speed:W=100,draggable:F=!1}=a,O=s(null),R=s(null),[M,z]=c(1),[Y,$]=c(null),[j,B]=c(0),D=u(e=>{O.current=e,"function"!=typeof o?o&&(o.current=e):o(e)},[o]);d(()=>{if(!L||!O.current)return;const e=v(O.current);$(e)},[L]);const U="up"===g||"down"===g,G="down"===g||"right"===g;d(()=>{const e=O.current;if(!e)return;const t=e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let r=null;const n=()=>{null==r&&(r=requestAnimationFrame(()=>{B(e=>e+1),r=null}))},a="undefined"!=typeof ResizeObserver?new ResizeObserver(n):null;a&&(a.observe(e),t&&a.observe(t));const o=Array.from(e.querySelectorAll("img")),i=()=>n();return o.forEach(e=>{e.complete||(e.addEventListener("load",i),e.addEventListener("error",i))}),()=>{null==a||a.disconnect(),o.forEach(e=>{e.removeEventListener("load",i),e.removeEventListener("error",i)}),null!=r&&cancelAnimationFrame(r)}},[f,m]),r((e,t)=>{if(!R.current||!O.current||!t)return;const r=O.current,a={fill:S,spacing:P,speed:W,delay:b,paused:q,draggable:F},o=n.utils.toArray(r.querySelectorAll(".gsap-react-marquee")),l=n.utils.toArray(r.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!l.length)return;w(r,o,l,U,a);const s=U?r.offsetHeight:r.offsetWidth,c=U?l[0].offsetHeight:l[0].offsetWidth,u=x(r,U),d=U?l[0].offsetTop:l[0].offsetLeft;let p=null;const f=Math.min(4,Math.max(1.1,A)),m=E(c,u,a);if(M!==m)return void z(m);const g=n.timeline({paused:q,repeat:y,defaults:{ease:"none"},onReverseComplete(){g.totalTime(g.rawTime()+100*g.duration())}}),h=o.map(e=>U?e.offsetHeight:e.offsetWidth).reduce((e,t)=>e+t,0),v=T(S?0:h/2,s,a);n.set(o,{[U?"minHeight":"minWidth"]:v,flex:S?"0 0 auto":"1"});const L=C(S?l:o,d,g,G,o,U,a);k&&(p=i.create({onChangeY(e){let t=f*(G?-1:1);e.deltaY<0&&(t*=-1),n.timeline({defaults:{ease:"none"}}).to(g,{timeScale:t*f,duration:.2,overwrite:!0}).to(g,{timeScale:t/f,duration:1},"+=0.3")}}));const N=t(()=>{g.pause()}),Y=t(()=>{G?g.reverse():g.play()});return H&&(r.addEventListener("mouseenter",N),r.addEventListener("mouseleave",Y)),()=>{r.removeEventListener("mouseenter",N),r.removeEventListener("mouseleave",Y),n.killTweensOf(g),g.kill(),null==p||p.kill(),null==L||L()}},{dependencies:[M,g,y,q,b,S,k,A,H,P,W,F,m,f,j],revertOnUpdate:!0});const X=null!==(l=null!=N?N:L?Y:null)&&void 0!==l?l:"transparent",I=p(()=>!Number.isFinite(M)||M<=0?null:Array.from({length:M},(t,r)=>e("div",{className:h("gsap-react-marquee"),children:e("div",{className:h("gsap-react-marquee-content",m),children:f})},r)),[M,m,f]);return t("div",{ref:D,style:{"--gradient-color":X},className:h("gsap-react-marquee-container",{"gsap-react-marquee-vertical":U}),children:[e("div",{ref:R,className:h("gsap-react-marquee"),children:e("div",{className:h("gsap-react-marquee-content",m),children:f})}),I]})});L.displayName="GSAPReactMarquee";export{E as calculateDuplicateCount,S as calculateDuplicates,h as cn,A as coreAnimation,C as createMarqueeAnimation,L as default,v as getEffectiveBackgroundColor,T as getMinSize,k as getMinWidth,x as getTargetSize,b as getTargetWidth,q as hasDefinedWidth,w as setupContainerStyles};
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useGSAP as r}from"@gsap/react";import{gsap as n}from"gsap";import{useEffect as a,useLayoutEffect as i,useRef as l,useState as o,useCallback as s,forwardRef as u,useMemo as c}from"react";import{clsx as d}from"clsx";import{twMerge as p}from"tailwind-merge";if("undefined"!=typeof document&&!document.getElementById("gsap-react-marquee-styles")){const e=document.createElement("style");e.id="gsap-react-marquee-styles",e.textContent='.gsap-react-marquee-container{white-space:nowrap;width:100%;display:flex;position:relative;overflow:hidden}:where(.gsap-react-marquee-container.gsap-react-marquee-vertical){align-self:stretch;height:100%;min-height:0}.gsap-react-marquee-container:after{content:"";background:linear-gradient(270deg, #fff0 0%, var(--gradient-color) 75%);z-index:10;pointer-events:none;width:15%;height:100%;position:absolute;top:0;left:0}.gsap-react-marquee-container:before{content:"";background:linear-gradient(90deg, #fff0 0%, var(--gradient-color) 75%);z-index:10;pointer-events:none;width:15%;height:100%;position:absolute;top:0;right:0}.gsap-react-marquee-vertical:after{background:linear-gradient(180deg, #fff0 0%, var(--gradient-color) 75%);width:100%;height:50%;top:60%;left:0}.gsap-react-marquee-vertical:before{background:linear-gradient(360deg, #fff0 0%, var(--gradient-color) 75%);width:100%;height:50%;top:0;left:0;right:auto}.gsap-react-marquee{white-space:nowrap;flex:1;width:auto;height:max-content;line-height:100%;display:flex}.gsap-react-marquee-clone,.gsap-react-marquee-clone *{pointer-events:none}.gsap-react-marquee-content{white-space:nowrap;width:max-content;line-height:100%;display:flex;overflow:hidden}',document.head.appendChild(e)}const f="[data-gsap-react-marquee-clone]",m=["a[href]","area[href]","audio[controls]","button","embed","iframe","input:not([type='hidden'])","object","select","summary","textarea","video[controls]","[contenteditable]:not([contenteditable='false'])","[tabindex]"].join(","),g=["[id]","a[href]","area[href]","button","embed","iframe","input","object","select","textarea","[contenteditable]:not([contenteditable='false'])"].join(","),v=["aria-activedescendant","aria-controls","aria-describedby","aria-details","aria-errormessage","aria-flowto","aria-labelledby","aria-owns","for","form","headers","itemref","list"],h=["clip-path","fill","filter","marker-end","marker-mid","marker-start","mask","stroke"],b=/^\s*#/,y=/url\(\s*["']?#/i,w=()=>"undefined"!=typeof HTMLElement&&"inert"in HTMLElement.prototype,S=e=>{const t=e.matches(m);e.removeAttribute("id"),e.removeAttribute("name"),v.forEach(t=>{e.removeAttribute(t)});const r=e.getAttribute("href");r&&b.test(r)&&e.removeAttribute("href");const n=e.getAttribute("xlink:href");n&&b.test(n)&&e.removeAttribute("xlink:href"),h.forEach(t=>{const r=e.getAttribute(t);r&&y.test(r)&&e.removeAttribute(t)}),e instanceof HTMLElement&&Array.from(e.style).forEach(t=>{y.test(e.style.getPropertyValue(t))&&e.style.removeProperty(t)}),t&&e.setAttribute("tabindex","-1")},x=e=>"number"==typeof e&&Number.isFinite(e),R=e=>{const t=x(e.speed)&&e.speed>0?e.speed:100,r=x(e.spacing)&&e.spacing>=0?e.spacing:16,n=x(e.delay)&&e.delay>=0?e.delay:0,a=x(e.scrollSpeed)?e.scrollSpeed:2.5,i=Math.min(4,Math.max(1.1,a)),l=-1===e.loop||x(e.loop)&&Number.isInteger(e.loop)&&e.loop>=0?e.loop:-1,o=x(e.maxDuplicates)&&Number.isInteger(e.maxDuplicates)&&e.maxDuplicates>0?e.maxDuplicates:100;return{delay:n,loop:l,maxDuplicates:Math.min(o,250),scrollSpeed:i,spacing:r,speed:t}},q=(...e)=>e.length>0&&e.every(e=>Number.isFinite(e)&&e>0),A=(...e)=>p(d(e)),k=e=>{let t=e;for(;t;){const e=window.getComputedStyle(t).backgroundColor;if(e&&"rgba(0, 0, 0, 0)"!==e&&"transparent"!==e)return e;t=t.parentElement}return"transparent"},E=(e,t,r,a,i)=>{var l;const{spacing:o}=R(i),s=null!==(l=i.fill)&&void 0!==l&&l;n.set(e,{gap:`${o}px`,flexDirection:a?"column":"row"}),n.set(t,{flexDirection:a?"column":"row",gap:`${o}px`,[a?"minHeight":"minWidth"]:s?"auto":"100%",flex:s?"0 0 auto":"1"}),n.set(r,{flexDirection:a?"column":"row",overflow:a?"visible":"hidden"})},C=e=>q(e.offsetWidth),D=(e,t)=>t?e.offsetHeight:e.offsetWidth,P=D,z=(e,t,r)=>{const{fill:n=!1}=r,{maxDuplicates:a,spacing:i}=R(r);if(!n||!q(e,t))return{duplicateCount:1,limitReached:!1,requiredDuplicateCount:1};const l=e+i;if(!q(l))return{duplicateCount:1,limitReached:!1,requiredDuplicateCount:1};const o=t+l,s=Math.ceil((o+i)/l),u=Math.max(1,s-1);return{duplicateCount:Math.min(u,a),limitReached:u>a,requiredDuplicateCount:u}},N=(e,t,r)=>z(e,t,r).duplicateCount,L=N,F=(e,t,r)=>{const{fill:n=!1}=r;return n?"auto":e<=t?"100%":`${e}px`},T=F,M=()=>"undefined"!=typeof process&&"production"===process.env.NODE_ENV,O="undefined"==typeof window?a:i;let H=!1;const V=({cancelReverseDelay:e,delay:t,dragTrigger:r,enabled:a,isReverse:i,isVertical:l,plugins:o,positionProperty:s,readPaused:u,restoreControlledState:c,scheduleReverseResume:d,timeline:p,trackLength:f})=>{const m=null==o?void 0:o.Draggable;if("function"!=typeof m||!a)return;const g=document.createElement("div"),v=n.utils.wrap(0,1);let h,b,y;const w={},S=()=>{if(null==y||y.kill(),i&&t>0&&!u())return p.pause(),void(y=d("gsap-react-marquee-drag-delay"));c()},x=()=>{const e=w.instance;if(!e)return;const t=l?e.startY-e.y:e.startX-e.x;p.progress(v(b+t*h))},R=Boolean(n.plugins.inertia);return R||H||(H=!0,console.warn("InertiaPlugin required for momentum-based scrolling and snapping. https://greensock.com/club")),w.instance=m.create(g,{trigger:r,type:l?"y":"x",onPress(){var t,r;e(),null==y||y.kill(),null===(r=null===(t=w.instance)||void 0===t?void 0:t.tween)||void 0===r||r.kill(),n.killTweensOf(g),n.killTweensOf(p),p.pause(),b=p.progress(),h=1/f,n.set(g,{[s]:b/-h})},onDrag:x,onThrowUpdate:x,overshootTolerance:0,inertia:R,onRelease(){this.isThrowing||S()},onThrowComplete(){S()}})[0],()=>{var e,t,r;null==y||y.kill(),null===(t=null===(e=w.instance)||void 0===e?void 0:e.tween)||void 0===t||t.kill(),null===(r=w.instance)||void 0===r||r.kill(),n.killTweensOf(g),g.remove()}},j=({timeline:e,isReverse:t,paused:r})=>{e.timeScale(1),r?e.pause():t?e.reverse():e.play()},B=(e,t,r,a,i,l,o,s,u,c)=>{const{spacing:d,speed:p,delay:f,loop:m}=R(o),{paused:g=!1,draggable:v=!1}=o,h=null!=u?u:()=>g;if(0===e.length)return;const b=l?"yPercent":"xPercent",y=l?"y":"x",w=((e,t,r,a,i,l,o,s)=>{const u=[],c=[],d=e.map(e=>s?((e,t,r)=>{let n=0,a=e;for(;a&&a!==t;)n+=r?a.offsetTop:a.offsetLeft,a=a.offsetParent;if(a===t)return n;const i=e.getBoundingClientRect(),l=t.getBoundingClientRect();return r?i.top-l.top:i.left-l.left})(e,s,a):a?e.offsetTop:e.offsetLeft);if(!d.every(Number.isFinite))return null;if(e.forEach((e,t)=>{const r=Number.parseFloat(String(n.getProperty(e,o,"px"))),a=Number.parseFloat(String(n.getProperty(e,l,"px"))),s=Number(n.getProperty(e,i));u[t]=r,c[t]=q(r)&&Number.isFinite(a)&&Number.isFinite(s)?a/r*100+s:Number.NaN}),!u.every(e=>q(e))||!c.every(Number.isFinite))return null;const p=e.length-1,f=e[p],m=d[p],g=a?f.offsetHeight:f.offsetWidth,v=s?d[0]:t,h=m+c[p]/100*u[p]-v+g+r;return Number.isFinite(v)&&q(g,h)?{effectiveStartPosition:v,initialPercents:c,itemOffsets:d,itemSizes:u,trackLength:h}:null})(e,t,d,l,b,y,l?"height":"width",s);if(!w)return;const S=((e,t,r)=>{const{effectiveStartPosition:n,initialPercents:a,itemOffsets:i,itemSizes:l,trackLength:o}=t,s=e.map((e,t)=>{const s=l[t],u=a[t]/100*s,c=i[t]+u-n+s,d=o-c,p=(u-c)/s*100,f=(u-c+o)/s*100,m=c/r,g=d/r;return![u,c,d,p,f,m,g].every(Number.isFinite)||c<0||d<0||m<0||g<0?null:{entryPercent:f,exitDuration:m,exitPercent:p,item:e,returnDuration:g}});return s.every(e=>null!==e)?s:null})(e,w,p);if(!S)return;let x;((e,t,r,a,i)=>{const l=t.map(({item:e})=>e);n.set(l,{[a]:e=>r[e]}),n.set(l,{[i]:0}),t.forEach((t,n)=>{e.to(t.item,{[a]:t.exitPercent,duration:t.exitDuration},0).fromTo(t.item,{[a]:t.entryPercent},{[a]:r[n],duration:t.returnDuration,immediateRender:!1},t.exitDuration)})})(r,S,w.initialPercents,b,y);const A=()=>{j({timeline:r,isReverse:a,paused:h()})},k=e=>{const t=n.delayedCall(f,()=>{A()});return t.data=e,t};r.eventCallback("onReverseComplete",null),-1===m&&r.eventCallback("onReverseComplete",()=>{r.totalTime(r.rawTime()+100*r.duration())}),a?(-1===m?r.progress(1).pause():r.totalProgress(1).pause(),h()||0===f?A():x=k("gsap-react-marquee-reverse-delay")):(r.delay(f),A());const E=V({cancelReverseDelay:()=>null==x?void 0:x.kill(),delay:f,dragTrigger:i,enabled:v,isReverse:a,isVertical:l,plugins:c,positionProperty:y,readPaused:h,restoreControlledState:A,scheduleReverseResume:k,timeline:r,trackLength:w.trackLength});return()=>{null==x||x.kill(),null==E||E(),r.eventCallback("onReverseComplete",null)}},I=B,W=(e,t)=>null!==e&&null!==t&&Number.isFinite(e)&&Number.isFinite(t)&&Math.abs(e-t)<=.5,$=(e,t)=>t?e.offsetHeight:e.offsetWidth,G=e=>e.querySelector(".gsap-react-marquee .gsap-react-marquee-content");let Y=null,U=null;const X=e=>{let t=null;return()=>(null!=t||(t=e().catch(e=>{throw t=null,e})),t)},_=X(()=>import("gsap/Observer.js").then(({Observer:e})=>e)),J=X(()=>import("gsap/Draggable.js").then(({Draggable:e})=>({Draggable:e}))),K=new Set,Q=e=>{M()||K.has(e)||(K.add(e),Reflect.apply(console.warn,console,[`GSAPReactMarquee: optional GSAP ${e} plugin failed to load; that interactive feature was not initialized.`]))},Z="(prefers-reduced-motion: reduce)",ee=()=>"undefined"!=typeof window&&"function"==typeof window.matchMedia&&window.matchMedia(Z).matches;n.registerPlugin(r);const te=(...e)=>e.filter(Boolean).join(" "),re=u((i,u)=>{var d;const{children:p,className:m,containerClassName:v,containerStyle:h,containerProps:b,dir:y="left",loop:x,paused:A=!1,respectReducedMotion:C=!0,delay:P,fill:L=!1,maxDuplicates:T,scrollFollow:H=!1,scrollSpeed:V,gradient:I=!1,gradientColor:X=null,pauseOnHover:K=!1,spacing:re,speed:ne,draggable:ae=!1}=i,{delay:ie,loop:le,maxDuplicates:oe,scrollSpeed:se,spacing:ue,speed:ce}=R({delay:P,loop:x,maxDuplicates:T,scrollSpeed:V,spacing:re,speed:ne}),de=l(null),pe=l(null),fe=(()=>{const[e,t]=o(ee);return O(()=>{if("function"!=typeof window.matchMedia)return;const e=window.matchMedia(Z),r=()=>t(e.matches);return r(),"function"==typeof e.addEventListener?(e.addEventListener("change",r),()=>e.removeEventListener("change",r)):(e.addListener(r),()=>e.removeListener(r))},[]),e})(),me=C&&fe,{pluginsReady:ge,pluginsRef:ve}=(({draggable:e,scrollFollow:t})=>{const r=l({draggable:null,observer:null}),[i,s]=o({draggable:"idle",observer:"idle"}),u=l(i);return a(()=>{let a=!0;const i=(e,t)=>{if(!a)return;const r={...u.current,[e]:t};u.current=r,s(r)},l=e=>{const t=u.current[e];"loading"!==t&&"failed"!==t||i(e,"idle")};return t&&!r.current.observer?(i("observer","loading"),_().then(e=>{a&&((e=>{Y!==e&&(n.registerPlugin(e),Y=e)})(e),r.current.observer=e,i("observer","ready"))}).catch(()=>{a&&(i("observer","failed"),Q("observer"))})):t||l("observer"),e&&!r.current.draggable?(i("draggable","loading"),J().then(e=>{a&&((e=>{U!==e&&(n.registerPlugin(e.Draggable),U=e)})(e),r.current.draggable=e,i("draggable","ready"))}).catch(()=>{a&&(i("draggable","failed"),Q("draggable"))})):e||l("draggable"),()=>{a=!1}},[e,t]),{pluginStatuses:i,pluginsReady:!(e&&null===r.current.draggable&&"failed"!==i.draggable||t&&null===r.current.observer&&"failed"!==i.observer),pluginsRef:r}})({draggable:ae&&!me,scrollFollow:H&&!me}),he=l(A);he.current=A;const[be,ye]=o(0),[we,Se]=o(null),xe=l(!1),Re=l(!1),qe=s(e=>{de.current=e,"function"!=typeof u?u&&(u.current=e):u(e)},[u]);O(()=>{if(!I||!de.current)return;const e=k(de.current);Se(e)},[v,h,I]),O(()=>{var e;!Re.current&&pe.current&&((e=pe.current).querySelector(g)||Array.from(e.querySelectorAll("[tabindex]")).some(e=>e.tabIndex>=0))&&(Re.current=!0,M()||Reflect.apply(console.warn,console,["GSAPReactMarquee: 0.4.0 supports presentational children only. Interactive children and stable IDs are unsupported; visual clones are sanitized for safety."]))},[p]),O(()=>{const e=de.current;if(!e)return;const t=w();return((e,t=w())=>{Array.from(e.querySelectorAll(f)).forEach(e=>{e.setAttribute("aria-hidden","true"),e.setAttribute("inert",""),t?(e.inert=!0,e.removeAttribute("data-gsap-react-marquee-inert-fallback")):e.setAttribute("data-gsap-react-marquee-inert-fallback",""),S(e),e.querySelectorAll("*").forEach(S)})})(e,t),((e,t=w())=>{if(t)return()=>{};const r=t=>{var r,n;const a=t.target;if(!(a instanceof Element))return;const i=a.closest(f);if(!i||!e.contains(i))return;const l=a;if(null===(r=l.blur)||void 0===r||r.call(l),document.activeElement===a){const e=document.activeElement;null===(n=e.blur)||void 0===n||n.call(e)}};return e.addEventListener("focusin",r),()=>e.removeEventListener("focusin",r)})(e,t)},[p,be,me]);const Ae="up"===y||"down"===y;O(()=>{const e=de.current;if(!e)return;const t=Array.from(e.querySelectorAll(".gsap-react-marquee")),r=Array.from(e.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));E(e,t,r,Ae,{fill:L,spacing:ue})},[be,L,Ae,ue]);const{beginCloneApplication:ke,cloneApplicationSnapshotRef:Ee,measurementSnapshotRef:Ce,measurementVersion:De}=(({duplicateCount:e,isVertical:t,rootRef:r})=>{const n=l(!1),a=l(null),i=l(null),u=l(null),[c,d]=o(0),p=s(e=>{return!((t=a.current)===(r=e)||t&&r&&t.axis===r.axis&&W(t.contentSize,r.contentSize)&&W(t.rootSize,r.rootSize)&&W(t.viewportSize,r.viewportSize)||(a.current=e,d(e=>e+1),0));var t,r},[]),f=s(()=>{const e=r.current;if(!e)return null;const n=G(e);if(!n)return null;const a=$(e,t),i=$(n,t),l=D(e,t);return q(a,i,l)?{axis:t?"vertical":"horizontal",contentSize:i,rootSize:a,viewportSize:l}:null},[t,r]),m=s(()=>p(f()),[p,f]),g=s(()=>{const e=r.current;if(!e)return p(null);const l=G(e);if(!l)return p(null);const o=t?"vertical":"horizontal",s=$(l,t),c=a.current;if((null==c?void 0:c.axis)===o&&W(s,c.contentSize))return!1;if(n.current=!1,i.current=null,u.current=null,!q(s))return p(null);if((null==c?void 0:c.axis)===o){const r=p({...c,contentSize:s}),n=$(e,t);return u.current=q(n)?n:null,r}return m()},[m,t,p,r]),v=s(()=>{const e=r.current;if(!e||n.current)return!1;const i=$(e,t);if(W(i,u.current))return!1;const l=a.current,o=t?"vertical":"horizontal";return((null==l?void 0:l.axis)!==o||!W(i,l.rootSize))&&(u.current=null,m())},[m,t,r]),h=s(e=>{const a=r.current,l=a?$(a,t):null;return null===l||W(l,e.rootSize)||W(l,u.current)?(n.current=!0,i.current=e,!0):(m(),!1)},[m,t,r]);return O(()=>{if(!n.current)return;const e=r.current;if(!e)return;const a=$(e,t);u.current=q(a)?a:null;const l=requestAnimationFrame(()=>{n.current=!1,i.current=null});return()=>cancelAnimationFrame(l)},[e,t,r]),O(()=>{const e=r.current;if(!e)return;const l=G(e),o=t?"vertical":"horizontal",s=a.current;s&&s.axis===o?g():(n.current=!1,i.current=null,u.current=null,m());let c=null;const d=()=>{null===c&&(c=requestAnimationFrame(()=>{c=null,g()}))},p="undefined"==typeof ResizeObserver?null:new ResizeObserver(t=>{const r=!!t.some(({target:e})=>e===l)&&g();t.some(({target:t})=>t===e)&&!r&&v()});null==p||p.observe(e),l&&(null==p||p.observe(l));const f=Array.from(e.querySelectorAll("img"));return f.forEach(e=>{e.complete||(e.addEventListener("load",d),e.addEventListener("error",d))}),()=>{null==p||p.disconnect(),f.forEach(e=>{e.removeEventListener("load",d),e.removeEventListener("error",d)}),null!==c&&cancelAnimationFrame(c)}},[g,m,v,t,r]),{beginCloneApplication:h,cloneApplicationSnapshotRef:i,cloneAppliedRootSizeRef:u,isApplyingMeasuredClonesRef:n,measurementSnapshotRef:a,measurementVersion:c}})({duplicateCount:be,isVertical:Ae,rootRef:de});O(()=>{if(me)return void ye(e=>0===e?e:0);const e=Ce.current,t=Ae?"vertical":"horizontal";if(!e||e.axis!==t)return;const r=z(e.contentSize,e.viewportSize,{fill:L,maxDuplicates:oe,spacing:ue}),{duplicateCount:n}=r;r.limitReached&&!xe.current&&(xe.current=!0,((e,t)=>{M()||Reflect.apply(console.warn,console,[`GSAPReactMarquee: maxDuplicates=${e} prevents full fill coverage; ${t} duplicates are required.`])})(oe,r.requiredDuplicateCount)),be!==n&&ke(e)&&ye(n)},[ke,be,L,Ae,oe,Ce,De,me,ue]),(({cloneApplicationSnapshotRef:e,delay:t,dir:a,draggable:i,duplicateCount:l,fill:o,loop:s,marqueeRef:u,maxDuplicates:c,measurementSnapshotRef:d,measurementVersion:p,paused:f,pausedRef:m,pauseOnHover:g,pluginsReady:v,pluginsRef:h,rootRef:b,scrollFollow:y,scrollSpeed:w,shouldReduceMotion:S,spacing:x,speed:R})=>{const A="up"===a||"down"===a,k="down"===a||"right"===a;r((r,a)=>{var p,E;if(S||!u.current||!b.current||!v||!a)return;const C=b.current,D=null!==(p=e.current)&&void 0!==p?p:d.current,P=A?"vertical":"horizontal";if(!D||D.axis!==P)return;const z={fill:o,maxDuplicates:c,spacing:x,speed:R,delay:t,loop:s,paused:f,draggable:i},L=n.utils.toArray(C.querySelectorAll(".gsap-react-marquee")),T=n.utils.toArray(C.querySelectorAll(".gsap-react-marquee .gsap-react-marquee-content"));if(!T.length)return;const M=D.rootSize,O=D.contentSize,H=D.viewportSize,V=A?T[0].offsetTop:T[0].offsetLeft;let I=!1,W=!1;const $=()=>m.current||g&&(I||W);if(!q(M,O,H)||!Number.isFinite(V))return;const G=N(O,H,z);if(l!==G)return;const Y=o?"auto":F(O,M,z);n.set(L,{[A?"minHeight":"minWidth"]:Y,flex:o?"0 0 auto":"1"});const U=o?T:L,X=U.map(e=>A?e.offsetHeight:e.offsetWidth);if(!q(...X))return;const _=n.timeline({paused:!0,repeat:s,defaults:{ease:"none"},data:"gsap-react-marquee-base"}),J=B(U,V,_,k,L,A,z,C,$,null!==(E=h.current.draggable)&&void 0!==E?E:void 0);if(!q(_.duration()))return null==J||J(),void _.kill();const K=()=>{j({timeline:_,isReverse:k,paused:$()})},Q=(({enabled:e,isReverse:t,observer:r,restoreBaseTimeline:a,scrollSpeed:i,shouldRemainPaused:l,timeline:o})=>{var s;let u=null;const c=()=>{null==u||u.kill(),u=null,n.killTweensOf(o)},d=e&&null!==(s=null==r?void 0:r.create({onChangeY(e){if(c(),l())return void a();const r=t?-1:1,s=r*(e.deltaY<0?-1:1)*i*i;u=n.timeline({data:"gsap-react-marquee-scroll-response",defaults:{ease:"none"},onComplete(){u=null,a()}}).to(o,{timeScale:s,duration:.2,overwrite:!0}).to(o,{timeScale:r,duration:1},"+=0.3")}}))&&void 0!==s?s:null;return{stop:c,cleanup:()=>{null==d||d.kill(),c()}}})({enabled:y,isReverse:k,observer:h.current.observer,restoreBaseTimeline:K,scrollSpeed:w,shouldRemainPaused:$,timeline:_}),Z=a(()=>{I=!0,Q.stop(),_.pause()}),ee=a(()=>{I=!1,K()}),te=a(()=>{W=!0,Q.stop(),_.pause()}),re=a(e=>{const t=e.relatedTarget;t instanceof Node&&C.contains(t)||(W=!1,K())});return g&&(C.addEventListener("pointerenter",Z),C.addEventListener("pointerleave",ee),C.addEventListener("focusin",te),C.addEventListener("focusout",re)),()=>{C.removeEventListener("pointerenter",Z),C.removeEventListener("pointerleave",ee),C.removeEventListener("focusin",te),C.removeEventListener("focusout",re),Q.cleanup(),null==J||J(),n.killTweensOf(_),_.kill()}},{dependencies:[l,a,s,f,t,o,c,y,w,g,x,R,i,v,p,S],revertOnUpdate:!0})})({cloneApplicationSnapshotRef:Ee,delay:ie,dir:y,draggable:ae,duplicateCount:be,fill:L,loop:le,marqueeRef:pe,maxDuplicates:oe,measurementSnapshotRef:Ce,measurementVersion:De,paused:A,pausedRef:he,pauseOnHover:K,pluginsReady:ge,pluginsRef:ve,rootRef:de,scrollFollow:H,scrollSpeed:se,shouldReduceMotion:me,spacing:ue,speed:ce});const Pe=null!==(d=null!=X?X:I?we:null)&&void 0!==d?d:"transparent",ze=c(()=>me||!Number.isFinite(be)||be<=0?null:Array.from({length:be},(t,r)=>e("div",{"aria-hidden":"true",className:te("gsap-react-marquee","gsap-react-marquee-clone"),"data-gsap-react-marquee-clone":"",children:e("div",{className:te("gsap-react-marquee-content",m),children:p})},r)),[be,m,p,me]);return t("div",{...b,ref:qe,style:{...h,display:"flex",overflow:"hidden",position:"relative",whiteSpace:"nowrap","--gradient-color":Pe},className:te("gsap-react-marquee-container",Ae&&"gsap-react-marquee-vertical",v),children:[e("div",{ref:pe,className:"gsap-react-marquee","data-gsap-react-marquee-original":"",children:e("div",{className:te("gsap-react-marquee-content",m),children:p})}),ze]})});re.displayName="GSAPReactMarquee";export{N as calculateDuplicateCount,z as calculateDuplicateCountResult,L as calculateDuplicates,A as cn,I as coreAnimation,B as createMarqueeAnimation,re as default,k as getEffectiveBackgroundColor,F as getMinSize,T as getMinWidth,D as getTargetSize,P as getTargetWidth,C as hasDefinedWidth,q as hasUsableMeasurement,R as normalizeMarqueeOptions,j as resumeTimeline,E as setupContainerStyles};
package/package.json CHANGED
@@ -3,16 +3,20 @@
3
3
  "author": "David Domenico Piscopo",
4
4
  "description": "A high-performance React marquee component powered by GSAP",
5
5
  "license": "MIT",
6
- "version": "0.3.2",
6
+ "version": "0.4.0",
7
+ "packageManager": "pnpm@10.34.5",
8
+ "engines": {
9
+ "node": ">=20"
10
+ },
7
11
  "type": "module",
8
12
  "main": "dist/index.cjs",
9
13
  "module": "dist/index.esm.js",
10
14
  "types": "dist/index.d.ts",
11
15
  "exports": {
12
16
  ".": {
17
+ "types": "./dist/index.d.ts",
13
18
  "import": "./dist/index.esm.js",
14
- "require": "./dist/index.cjs",
15
- "types": "./dist/index.d.ts"
19
+ "require": "./dist/index.cjs"
16
20
  }
17
21
  },
18
22
  "files": [
@@ -23,8 +27,21 @@
23
27
  "LICENSE"
24
28
  ],
25
29
  "scripts": {
26
- "build": "rollup -c",
27
- "build:watch": "rollup -c -w",
30
+ "clean": "node scripts/clean-dist.mjs",
31
+ "build": "pnpm run clean && rollup -c && node scripts/clean-dist.mjs --declarations-only",
32
+ "build:watch": "pnpm run clean && rollup -c -w",
33
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json --noEmit",
34
+ "lint": "eslint .",
35
+ "test": "vitest run",
36
+ "test:watch": "vitest",
37
+ "test:coverage": "vitest run --coverage",
38
+ "test:browser": "playwright test --project=chromium",
39
+ "test:browser:firefox": "playwright test --project=firefox",
40
+ "test:browser:webkit": "playwright test --project=webkit",
41
+ "test:package": "pnpm run build && node tests/package/run-smoke.mjs && node tests/package/packed-smoke.mjs && node tests/package/bundle-smoke.mjs",
42
+ "check": "pnpm run typecheck && pnpm run lint && pnpm run test",
43
+ "audit": "pnpm audit --audit-level=moderate",
44
+ "release:check": "pnpm run check && pnpm run test:browser && pnpm run test:browser:firefox && pnpm run test:browser:webkit && pnpm run test:package && pnpm run audit && git diff --check",
28
45
  "prepare": "npm run build"
29
46
  },
30
47
  "keywords": [
@@ -48,22 +65,36 @@
48
65
  "peerDependencies": {
49
66
  "@gsap/react": "^2.1.1",
50
67
  "gsap": "^3.12.0",
51
- "react": ">=18",
52
- "react-dom": ">=18"
68
+ "react": ">=18"
53
69
  },
54
70
  "devDependencies": {
71
+ "@eslint/js": "^9.39.5",
55
72
  "@gsap/react": "^2.1.1",
56
- "@rollup/plugin-commonjs": "^25.0.7",
57
- "@rollup/plugin-node-resolve": "^15.2.3",
58
- "@rollup/plugin-terser": "^0.4.4",
59
- "@rollup/plugin-typescript": "^11.1.6",
73
+ "@playwright/test": "^1.61.1",
74
+ "@rollup/plugin-commonjs": "^29.0.3",
75
+ "@rollup/plugin-node-resolve": "^16.0.3",
76
+ "@rollup/plugin-terser": "^1.0.0",
77
+ "@rollup/plugin-typescript": "^12.3.0",
78
+ "@testing-library/jest-dom": "^6.9.1",
79
+ "@testing-library/react": "^16.3.2",
80
+ "@types/node": "^22.20.1",
60
81
  "@types/react": "^18.3.3",
61
82
  "@types/react-dom": "^18.3.0",
62
- "rollup": "^4.18.0",
63
- "rollup-plugin-dts": "^6.1.1",
64
- "rollup-plugin-postcss": "^4.0.2",
83
+ "@vitest/coverage-v8": "^4.1.10",
84
+ "eslint": "^9.39.5",
85
+ "eslint-plugin-react-hooks": "^5.2.0",
86
+ "globals": "^16.5.0",
87
+ "jsdom": "^29.1.1",
88
+ "lightningcss": "^1.32.0",
89
+ "react": "^19.2.7",
90
+ "react-dom": "^19.2.7",
91
+ "rollup": "^4.62.2",
92
+ "rollup-plugin-dts": "^6.4.1",
65
93
  "tslib": "^2.8.1",
66
- "typescript": "^5.5.2"
94
+ "typescript": "^5.5.2",
95
+ "typescript-eslint": "^8.64.0",
96
+ "vite": "^8.1.5",
97
+ "vitest": "^4.1.10"
67
98
  },
68
99
  "dependencies": {
69
100
  "clsx": "^2.1.1",