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 +224 -22
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +45 -7
- package/dist/index.esm.js +1 -1
- package/package.json +46 -15
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`, `
|
|
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
|
|
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.
|
|
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` |
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
408
|
+
pnpm run check
|
|
210
409
|
```
|
|
211
410
|
|
|
212
|
-
Run
|
|
411
|
+
Run browser compatibility checks:
|
|
213
412
|
|
|
214
413
|
```bash
|
|
215
|
-
pnpm
|
|
414
|
+
pnpm run test:browser
|
|
415
|
+
pnpm run test:browser:firefox
|
|
416
|
+
pnpm run test:browser:webkit
|
|
216
417
|
```
|
|
217
418
|
|
|
218
|
-
Build the
|
|
419
|
+
Build and test the real packed artifact against React 18 and 19 consumers:
|
|
219
420
|
|
|
220
421
|
```bash
|
|
221
|
-
pnpm run
|
|
422
|
+
pnpm run test:package
|
|
222
423
|
```
|
|
223
424
|
|
|
224
|
-
|
|
425
|
+
Run the full dependency audit or complete local release gate:
|
|
225
426
|
|
|
226
427
|
```bash
|
|
227
|
-
|
|
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: (
|
|
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
|
-
|
|
41
|
-
|
|
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.
|
|
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
|
-
"
|
|
27
|
-
"build
|
|
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
|
-
"@
|
|
57
|
-
"@rollup/plugin-
|
|
58
|
-
"@rollup/plugin-
|
|
59
|
-
"@rollup/plugin-
|
|
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
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
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",
|