react-sway 0.4.2 → 0.5.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/CHANGELOG.md +17 -0
- package/README.md +48 -10
- package/dist/ReactSway.d.ts +14 -6
- package/dist/depth-position.d.ts +9 -0
- package/dist/index.cjs +215 -76
- package/dist/index.d.ts +1 -0
- package/dist/index.js +193 -78
- package/package.json +15 -13
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
// \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
2
23
|
let react = require("react");
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
24
|
+
react = __toESM(react, 1);
|
|
25
|
+
// src/depth-position.ts
|
|
26
|
+
var DEFAULT_DEPTH_PERSPECTIVE = 800;
|
|
27
|
+
var DEFAULT_DEPTH_SPACING = 420;
|
|
28
|
+
/** Bounds pixel lengths so periods and fade denominators remain finite and nonzero. */
|
|
29
|
+
function normalizeDepthLength(value, fallback) {
|
|
30
|
+
return Number.isFinite(value) && value > 0 ? Math.min(Number.MAX_SAFE_INTEGER, Math.max(1, value)) : fallback;
|
|
31
|
+
}
|
|
32
|
+
function getDepthItemState(position, index, count, perspective = 800, spacing = 420) {
|
|
33
|
+
if (count <= 0) return {
|
|
34
|
+
opacity: 0,
|
|
35
|
+
z: 0
|
|
36
|
+
};
|
|
37
|
+
const period = count * spacing;
|
|
38
|
+
const phase = ((index * spacing + position) % period + period) % period;
|
|
39
|
+
const fade = Math.min(1, Math.min(phase, period - phase) / (spacing * .45));
|
|
40
|
+
return {
|
|
41
|
+
opacity: fade * fade * (3 - 2 * fade),
|
|
42
|
+
z: perspective * .7 - phase
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// src/edge-hover-velocity.ts
|
|
8
46
|
/** Default edge-hover activation thickness in pixels. */
|
|
9
47
|
var DEFAULT_EDGE_HOVER_SIZE = 96;
|
|
10
48
|
/** Default maximum edge-hover speed relative to the configured base speed. */
|
|
@@ -50,10 +88,10 @@ function getEdgeHoverVelocityScale(intensity, speedMultiplier = 6) {
|
|
|
50
88
|
if (depth < EDGE_HOVER_ENTRY_FEATHER_INTENSITY) return direction * smoothUnitStep(depth / EDGE_HOVER_ENTRY_FEATHER_INTENSITY);
|
|
51
89
|
const physicalEdgeScale = Math.min(EDGE_HOVER_PHYSICAL_EDGE_SPEED_MULTIPLIER, maximumScale);
|
|
52
90
|
if (depth <= EDGE_HOVER_PHYSICAL_EDGE_INTENSITY) {
|
|
53
|
-
const physicalEdgeProgress = (depth - EDGE_HOVER_ENTRY_FEATHER_INTENSITY) /
|
|
91
|
+
const physicalEdgeProgress = (depth - EDGE_HOVER_ENTRY_FEATHER_INTENSITY) / .42;
|
|
54
92
|
return direction * (1 + (physicalEdgeScale - 1) * smoothUnitStep(physicalEdgeProgress));
|
|
55
93
|
}
|
|
56
|
-
const beyondEdgeProgress = (depth - EDGE_HOVER_PHYSICAL_EDGE_INTENSITY) /
|
|
94
|
+
const beyondEdgeProgress = (depth - EDGE_HOVER_PHYSICAL_EDGE_INTENSITY) / .5;
|
|
57
95
|
return direction * (physicalEdgeScale + (maximumScale - physicalEdgeScale) * smoothUnitStep(beyondEdgeProgress));
|
|
58
96
|
}
|
|
59
97
|
/**
|
|
@@ -79,9 +117,7 @@ function getEdgeHoverIntensity(pointerPosition, visibleInterval, edgeHoverSize)
|
|
|
79
117
|
}
|
|
80
118
|
return 0;
|
|
81
119
|
}
|
|
82
|
-
|
|
83
|
-
//#region src/wheel-input.ts
|
|
84
|
-
/** Fallback pixel height for wheel events reported in line units. */
|
|
120
|
+
// src/wheel-input.ts
|
|
85
121
|
var WHEEL_LINE_HEIGHT_FALLBACK_PX = 16;
|
|
86
122
|
/** WheelEvent deltaMode value for line-based deltas. */
|
|
87
123
|
var WHEEL_MODE_LINE = 1;
|
|
@@ -115,8 +151,10 @@ function normalizeWheelDelta(event, container, axis) {
|
|
|
115
151
|
* the gesture. This partial function is the wheel-ownership boundary.
|
|
116
152
|
*/
|
|
117
153
|
function getOwnedWheelDelta(event, container, isHorizontal, wheelMode) {
|
|
154
|
+
if (event.ctrlKey) return null;
|
|
118
155
|
const deltaX = normalizeWheelDelta(event, container, "x");
|
|
119
156
|
const deltaY = normalizeWheelDelta(event, container, "y");
|
|
157
|
+
if (!Number.isFinite(deltaX) || !Number.isFinite(deltaY) || deltaX === 0 && deltaY === 0) return null;
|
|
120
158
|
const absDeltaX = Math.abs(deltaX);
|
|
121
159
|
const absDeltaY = Math.abs(deltaY);
|
|
122
160
|
if (isHorizontal) {
|
|
@@ -126,11 +164,7 @@ function getOwnedWheelDelta(event, container, isHorizontal, wheelMode) {
|
|
|
126
164
|
if (wheelMode === "axis" && absDeltaX > absDeltaY) return null;
|
|
127
165
|
return deltaY;
|
|
128
166
|
}
|
|
129
|
-
|
|
130
|
-
//#region src/ReactSway.tsx
|
|
131
|
-
/**
|
|
132
|
-
* Core ReactSway component implementing axis-aware infinite scrolling interactions.
|
|
133
|
-
*/
|
|
167
|
+
// src/ReactSway.tsx
|
|
134
168
|
/** Velocity applied per arrow key press (pixels). */
|
|
135
169
|
var ARROW_KEY_VELOCITY = 15;
|
|
136
170
|
/** Default friction coefficient applied to velocity each frame. */
|
|
@@ -161,6 +195,10 @@ var WHEEL_VELOCITY_MULTIPLIER = .14;
|
|
|
161
195
|
var WHEEL_IMMEDIATE_DELTA_FACTOR = .14;
|
|
162
196
|
/** Minimum touch movement before a gesture is classified by axis. */
|
|
163
197
|
var TOUCH_AXIS_LOCK_THRESHOLD_PX = 8;
|
|
198
|
+
var INERT_ATTRIBUTE = { inert: Number.parseInt(react.version, 10) >= 19 ? true : "" };
|
|
199
|
+
function isInteractiveTarget(target) {
|
|
200
|
+
return target instanceof Element && Boolean(target.closest("a, button, input, select, textarea, [contenteditable]:not([contenteditable=\"false\"])"));
|
|
201
|
+
}
|
|
164
202
|
function getVisibleEdgeInterval(rect, isHorizontal) {
|
|
165
203
|
const rectEnd = isHorizontal ? rect.right : rect.bottom;
|
|
166
204
|
const rectStart = isHorizontal ? rect.left : rect.top;
|
|
@@ -180,11 +218,11 @@ function getVisibleEdgeInterval(rect, isHorizontal) {
|
|
|
180
218
|
* Renders children in a continuously looping scroll area with support for
|
|
181
219
|
* auto-scrolling, mouse drag, touch swipe, wheel, keyboard interactions,
|
|
182
220
|
* and optional edge-hover scrolling.
|
|
183
|
-
*
|
|
221
|
+
* Planar content is duplicated to create a seamless loop effect. Duplicate content
|
|
184
222
|
* is wrapped in `<aside>` elements with `aria-hidden="true"` for accessibility.
|
|
185
223
|
*
|
|
186
|
-
*
|
|
187
|
-
* and
|
|
224
|
+
* Reduced motion stops depth auto-scroll, lowers planar auto-scroll speed,
|
|
225
|
+
* and disables momentum effects.
|
|
188
226
|
*
|
|
189
227
|
* @example
|
|
190
228
|
* ```tsx
|
|
@@ -194,8 +232,14 @@ function getVisibleEdgeInterval(rect, isHorizontal) {
|
|
|
194
232
|
* </ReactSway>
|
|
195
233
|
* ```
|
|
196
234
|
*/
|
|
197
|
-
var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollable content", axis, autoScroll = true, children, className, direction = "up", draggable = true, edgeHoverInputMode = "hybrid", edgeHoverScroll = false, edgeHoverSize = 96, edgeHoverSpeedMultiplier = 6, friction = DEFAULT_FRICTION, keyboard = true, lazy = true, lazyRootMargin = DEFAULT_LAZY_ROOT_MARGIN, lazyThreshold = DEFAULT_LAZY_THRESHOLD, onPause, onResume, onScroll, pauseOnInteraction = true, resumeDelay = DEFAULT_RESUME_DELAY, speed = DEFAULT_SPEED, wheelEnabled = true, wheelMode = "axis" }, ref) {
|
|
198
|
-
const
|
|
235
|
+
var ReactSway = (0, react.forwardRef)(function ReactSway({ active = true, ariaLabel = "Scrollable content", axis, autoScroll = true, children, className, depthPerspective = 800, depthSpacing = 420, direction = "up", draggable = true, edgeHoverInputMode = "hybrid", edgeHoverScroll = false, edgeHoverSize = 96, edgeHoverSpeedMultiplier = 6, friction = DEFAULT_FRICTION, keyboard = true, lazy = true, lazyRootMargin = DEFAULT_LAZY_ROOT_MARGIN, lazyThreshold = DEFAULT_LAZY_THRESHOLD, onPause, onResume, onScroll, pauseOnInteraction = true, reducedMotion = false, resumeDelay = DEFAULT_RESUME_DELAY, speed = DEFAULT_SPEED, wheelEnabled = true, wheelMode = "axis" }, ref) {
|
|
236
|
+
const scrollAxis = (0, react.useMemo)(() => axis ?? (direction === "toward" || direction === "away" ? "depth" : direction === "left" || direction === "right" ? "horizontal" : "vertical"), [axis, direction]);
|
|
237
|
+
const isHorizontal = scrollAxis === "horizontal";
|
|
238
|
+
const isDepth = scrollAxis === "depth";
|
|
239
|
+
const depthChildren = (0, react.useMemo)(() => react.Children.toArray(children), [children]);
|
|
240
|
+
const enabled = active && (!isDepth || depthChildren.length > 0);
|
|
241
|
+
const normalizedDepthPerspective = normalizeDepthLength(depthPerspective, 800);
|
|
242
|
+
const normalizedDepthSpacing = normalizeDepthLength(depthSpacing, 420);
|
|
199
243
|
const normalizedEdgeHoverSize = (0, react.useMemo)(() => Number.isFinite(edgeHoverSize) ? Math.max(1, edgeHoverSize) : 96, [edgeHoverSize]);
|
|
200
244
|
const normalizedEdgeHoverSpeedMultiplier = (0, react.useMemo)(() => normalizeEdgeHoverSpeedMultiplier(edgeHoverSpeedMultiplier), [edgeHoverSpeedMultiplier]);
|
|
201
245
|
const normalizedFriction = (0, react.useMemo)(() => Number.isFinite(friction) ? Math.min(Math.max(friction, 0), 1) : DEFAULT_FRICTION, [friction]);
|
|
@@ -203,17 +247,22 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
203
247
|
const normalizedResumeDelay = (0, react.useMemo)(() => Number.isFinite(resumeDelay) ? Math.max(0, resumeDelay) : DEFAULT_RESUME_DELAY, [resumeDelay]);
|
|
204
248
|
const normalizedSpeed = (0, react.useMemo)(() => Number.isFinite(speed) ? Math.max(0, speed) : DEFAULT_SPEED, [speed]);
|
|
205
249
|
const touchAction = (0, react.useMemo)(() => {
|
|
206
|
-
if (!draggable) return "auto";
|
|
250
|
+
if (!enabled || !draggable) return "auto";
|
|
207
251
|
return isHorizontal ? "pan-y" : "pan-x";
|
|
208
|
-
}, [
|
|
209
|
-
|
|
252
|
+
}, [
|
|
253
|
+
draggable,
|
|
254
|
+
enabled,
|
|
255
|
+
isHorizontal
|
|
256
|
+
]);
|
|
257
|
+
const overscrollBehavior = enabled && wheelEnabled && wheelMode === "capture" ? "contain" : "auto";
|
|
210
258
|
const [isPaused, setIsPaused] = (0, react.useState)(false);
|
|
211
259
|
const [isTabActive, setIsTabActive] = (0, react.useState)(() => typeof document === "undefined" || !document.hidden);
|
|
212
260
|
const [loopSegmentCount, setLoopSegmentCount] = (0, react.useState)(MIN_LOOP_SEGMENTS);
|
|
213
|
-
const [
|
|
261
|
+
const [systemReducedMotion, setSystemReducedMotion] = (0, react.useState)(() => {
|
|
214
262
|
if (typeof window === "undefined") return false;
|
|
215
263
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
216
264
|
});
|
|
265
|
+
const prefersReducedMotion = systemReducedMotion || reducedMotion;
|
|
217
266
|
const animationFrameRef = (0, react.useRef)(null);
|
|
218
267
|
const animationLoopRef = (0, react.useRef)(null);
|
|
219
268
|
const autoScrollRef = (0, react.useRef)({
|
|
@@ -260,7 +309,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
260
309
|
if (typeof window === "undefined") return;
|
|
261
310
|
const mediaQuery = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
262
311
|
const handleChange = (e) => {
|
|
263
|
-
|
|
312
|
+
setSystemReducedMotion(e.matches);
|
|
264
313
|
};
|
|
265
314
|
mediaQuery.addEventListener("change", handleChange);
|
|
266
315
|
return () => {
|
|
@@ -300,11 +349,28 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
300
349
|
}, [isHorizontal]);
|
|
301
350
|
const renderPosition = (0, react.useCallback)((rawPosition) => {
|
|
302
351
|
if (!containerRef.current) return;
|
|
352
|
+
if (isDepth) {
|
|
353
|
+
containerRef.current.style.transform = "translate3d(0, 0, 0)";
|
|
354
|
+
const items = originalGroupRef.current?.children;
|
|
355
|
+
if (!items) return;
|
|
356
|
+
Array.from(items).forEach((item, index) => {
|
|
357
|
+
const { opacity, z } = getDepthItemState(rawPosition, index, items.length, normalizedDepthPerspective, normalizedDepthSpacing);
|
|
358
|
+
const element = item;
|
|
359
|
+
element.style.transform = `translate3d(0, 0, ${z}px)`;
|
|
360
|
+
element.style.opacity = String(opacity);
|
|
361
|
+
});
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
303
364
|
const currentLoopPoint = loopPointRef.current;
|
|
304
365
|
let visualPosition = rawPosition % (currentLoopPoint || 1);
|
|
305
366
|
if (visualPosition > 0 && currentLoopPoint > 0) visualPosition -= currentLoopPoint;
|
|
306
367
|
containerRef.current.style.transform = isHorizontal ? `translate3d(${visualPosition}px, 0, 0)` : `translate3d(0, ${visualPosition}px, 0)`;
|
|
307
|
-
}, [
|
|
368
|
+
}, [
|
|
369
|
+
isDepth,
|
|
370
|
+
isHorizontal,
|
|
371
|
+
normalizedDepthPerspective,
|
|
372
|
+
normalizedDepthSpacing
|
|
373
|
+
]);
|
|
308
374
|
const commitPosition = (0, react.useCallback)((nextPosition) => {
|
|
309
375
|
if (positionRef.current === nextPosition) return;
|
|
310
376
|
positionRef.current = nextPosition;
|
|
@@ -325,11 +391,14 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
325
391
|
}, [draggable]);
|
|
326
392
|
const recalculateLoopPoint = (0, react.useCallback)(() => {
|
|
327
393
|
if (!containerRef.current || !originalGroupRef.current) return;
|
|
328
|
-
const originalContentSize = isHorizontal ? originalGroupRef.current.scrollWidth : originalGroupRef.current.scrollHeight;
|
|
329
|
-
if (originalContentSize <= 0)
|
|
394
|
+
const originalContentSize = isDepth ? depthChildren.length * normalizedDepthSpacing : isHorizontal ? originalGroupRef.current.scrollWidth : originalGroupRef.current.scrollHeight;
|
|
395
|
+
if (originalContentSize <= 0) {
|
|
396
|
+
if (isDepth) loopPointRef.current = 0;
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
330
399
|
const viewport = containerRef.current.parentElement ?? containerRef.current;
|
|
331
400
|
const viewportSize = isHorizontal ? Math.max(viewport.clientWidth, 1) : Math.max(viewport.clientHeight, 1);
|
|
332
|
-
const nextLoopSegmentCount = Math.max(MIN_LOOP_SEGMENTS, Math.ceil(viewportSize / originalContentSize) + 1);
|
|
401
|
+
const nextLoopSegmentCount = isDepth ? 1 : Math.max(MIN_LOOP_SEGMENTS, Math.ceil(viewportSize / originalContentSize) + 1);
|
|
333
402
|
const nextLoopPoint = originalContentSize;
|
|
334
403
|
const previousMeasurement = lastLoopMeasurementRef.current;
|
|
335
404
|
const nextMeasurement = {
|
|
@@ -342,7 +411,13 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
342
411
|
setLoopSegmentCount((previousLoopSegmentCount) => previousLoopSegmentCount === nextLoopSegmentCount ? previousLoopSegmentCount : nextLoopSegmentCount);
|
|
343
412
|
loopPointRef.current = nextLoopPoint;
|
|
344
413
|
renderPosition(positionRef.current);
|
|
345
|
-
}, [
|
|
414
|
+
}, [
|
|
415
|
+
depthChildren.length,
|
|
416
|
+
isDepth,
|
|
417
|
+
isHorizontal,
|
|
418
|
+
normalizedDepthSpacing,
|
|
419
|
+
renderPosition
|
|
420
|
+
]);
|
|
346
421
|
(0, react.useEffect)(() => {
|
|
347
422
|
autoScrollRef.current.desired = autoScroll;
|
|
348
423
|
if (!autoScroll) {
|
|
@@ -367,6 +442,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
367
442
|
scheduleLoopPointRecalculation();
|
|
368
443
|
}, [
|
|
369
444
|
children,
|
|
445
|
+
isDepth,
|
|
370
446
|
isHorizontal,
|
|
371
447
|
scheduleLoopPointRecalculation
|
|
372
448
|
]);
|
|
@@ -421,7 +497,14 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
421
497
|
touchDragListenerCleanupRef.current = null;
|
|
422
498
|
}, []);
|
|
423
499
|
const handleKeyDown = (0, react.useCallback)((e) => {
|
|
424
|
-
if (!keyboard) return;
|
|
500
|
+
if (!enabled || !keyboard || isInteractiveTarget(e.target)) return;
|
|
501
|
+
const arrowDelta = isHorizontal ? e.key === "ArrowLeft" ? ARROW_KEY_VELOCITY : e.key === "ArrowRight" ? -15 : 0 : e.key === "ArrowUp" ? ARROW_KEY_VELOCITY : e.key === "ArrowDown" ? -15 : 0;
|
|
502
|
+
if (prefersReducedMotion && arrowDelta !== 0) {
|
|
503
|
+
e.preventDefault();
|
|
504
|
+
commitPosition(wrapPosition(positionRef.current + arrowDelta));
|
|
505
|
+
velocityRef.current = 0;
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
425
508
|
switch (e.key) {
|
|
426
509
|
case " ":
|
|
427
510
|
e.preventDefault();
|
|
@@ -468,18 +551,19 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
468
551
|
velocityRef.current = 0;
|
|
469
552
|
pauseAutoScroll();
|
|
470
553
|
scheduleAutoScrollResume();
|
|
471
|
-
break;
|
|
472
|
-
default: break;
|
|
473
554
|
}
|
|
474
555
|
startAnimationLoop();
|
|
475
556
|
}, [
|
|
476
557
|
commitPosition,
|
|
558
|
+
enabled,
|
|
477
559
|
isHorizontal,
|
|
478
560
|
keyboard,
|
|
479
561
|
pauseAutoScroll,
|
|
562
|
+
prefersReducedMotion,
|
|
480
563
|
scheduleAutoScrollResume,
|
|
481
564
|
startAnimationLoop,
|
|
482
|
-
togglePause
|
|
565
|
+
togglePause,
|
|
566
|
+
wrapPosition
|
|
483
567
|
]);
|
|
484
568
|
const handleMouseMove = (0, react.useCallback)((e) => {
|
|
485
569
|
if (!isDraggingRef.current) return;
|
|
@@ -537,7 +621,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
537
621
|
handleMouseUp
|
|
538
622
|
]);
|
|
539
623
|
const handleMouseDown = (0, react.useCallback)((e) => {
|
|
540
|
-
if (!draggable) return;
|
|
624
|
+
if (!enabled || !draggable || e.button !== 0 || isInteractiveTarget(e.target)) return;
|
|
541
625
|
e.preventDefault();
|
|
542
626
|
containerRef.current?.focus();
|
|
543
627
|
setDraggingState(true);
|
|
@@ -550,6 +634,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
550
634
|
}, [
|
|
551
635
|
attachMouseDragListeners,
|
|
552
636
|
draggable,
|
|
637
|
+
enabled,
|
|
553
638
|
pauseAutoScroll,
|
|
554
639
|
setDraggingState,
|
|
555
640
|
startAnimationLoop
|
|
@@ -573,6 +658,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
573
658
|
if (touchInteractionStateRef.current === "idle" || touchInteractionStateRef.current === "ignored") return;
|
|
574
659
|
if (e.touches.length !== 1) return;
|
|
575
660
|
const touch = e.touches[0];
|
|
661
|
+
if (!touch) return;
|
|
576
662
|
if (touchInteractionStateRef.current === "pending") {
|
|
577
663
|
const totalDeltaX = touch.clientX - touchStartXRef.current;
|
|
578
664
|
const totalDeltaY = touch.clientY - touchStartYRef.current;
|
|
@@ -617,18 +703,24 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
617
703
|
handleTouchMove
|
|
618
704
|
]);
|
|
619
705
|
const handleTouchStart = (0, react.useCallback)((e) => {
|
|
620
|
-
if (!draggable || e.touches.length !== 1) return;
|
|
706
|
+
if (!enabled || !draggable || e.touches.length !== 1 || isInteractiveTarget(e.target)) return;
|
|
707
|
+
const touch = e.touches[0];
|
|
708
|
+
if (!touch) return;
|
|
621
709
|
containerRef.current?.focus();
|
|
622
710
|
touchInteractionStateRef.current = "pending";
|
|
623
|
-
lastTouchXRef.current =
|
|
624
|
-
lastTouchYRef.current =
|
|
625
|
-
touchStartXRef.current =
|
|
626
|
-
touchStartYRef.current =
|
|
711
|
+
lastTouchXRef.current = touch.clientX;
|
|
712
|
+
lastTouchYRef.current = touch.clientY;
|
|
713
|
+
touchStartXRef.current = touch.clientX;
|
|
714
|
+
touchStartYRef.current = touch.clientY;
|
|
627
715
|
velocityRef.current = 0;
|
|
628
716
|
attachTouchDragListeners();
|
|
629
|
-
}, [
|
|
717
|
+
}, [
|
|
718
|
+
attachTouchDragListeners,
|
|
719
|
+
draggable,
|
|
720
|
+
enabled
|
|
721
|
+
]);
|
|
630
722
|
const handleWheel = (0, react.useCallback)((e) => {
|
|
631
|
-
if (!wheelEnabled) return false;
|
|
723
|
+
if (!enabled || !wheelEnabled || e.defaultPrevented) return false;
|
|
632
724
|
const currentContainer = e.currentTarget instanceof HTMLElement ? e.currentTarget : containerRef.current;
|
|
633
725
|
if (!currentContainer) return false;
|
|
634
726
|
const ownedDelta = getOwnedWheelDelta(e, currentContainer, isHorizontal, wheelMode);
|
|
@@ -645,6 +737,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
645
737
|
return true;
|
|
646
738
|
}, [
|
|
647
739
|
commitPosition,
|
|
740
|
+
enabled,
|
|
648
741
|
isHorizontal,
|
|
649
742
|
pauseAutoScroll,
|
|
650
743
|
scheduleAutoScrollResume,
|
|
@@ -654,7 +747,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
654
747
|
wrapPosition
|
|
655
748
|
]);
|
|
656
749
|
const applyEdgeHoverPoint = (0, react.useCallback)((point) => {
|
|
657
|
-
if (!edgeHoverScroll) return false;
|
|
750
|
+
if (!enabled || !edgeHoverScroll) return false;
|
|
658
751
|
const viewport = containerRef.current?.parentElement ?? containerRef.current;
|
|
659
752
|
if (!viewport) return false;
|
|
660
753
|
const visibleInterval = readVisibleEdgeInterval(viewport);
|
|
@@ -667,6 +760,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
667
760
|
return true;
|
|
668
761
|
}, [
|
|
669
762
|
edgeHoverScroll,
|
|
763
|
+
enabled,
|
|
670
764
|
isHorizontal,
|
|
671
765
|
normalizedEdgeHoverSize,
|
|
672
766
|
readVisibleEdgeInterval,
|
|
@@ -681,6 +775,9 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
681
775
|
externalEdgeHoverLeaseRef.current = false;
|
|
682
776
|
setEdgeHoverIntensity(0);
|
|
683
777
|
}, [setEdgeHoverIntensity]);
|
|
778
|
+
(0, react.useEffect)(() => {
|
|
779
|
+
if (!edgeHoverScroll) clearEdgeHover();
|
|
780
|
+
}, [clearEdgeHover, edgeHoverScroll]);
|
|
684
781
|
(0, react.useImperativeHandle)(ref, () => ({
|
|
685
782
|
clearEdgeHover,
|
|
686
783
|
handleEdgeHover,
|
|
@@ -700,7 +797,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
700
797
|
}, [setEdgeHoverIntensity]);
|
|
701
798
|
(0, react.useEffect)(() => {
|
|
702
799
|
const currentContainer = containerRef.current;
|
|
703
|
-
if (!currentContainer) return;
|
|
800
|
+
if (!currentContainer || !enabled) return;
|
|
704
801
|
if (draggable) {
|
|
705
802
|
currentContainer.addEventListener("mousedown", handleMouseDown);
|
|
706
803
|
currentContainer.addEventListener("touchstart", handleTouchStart, { passive: true });
|
|
@@ -738,6 +835,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
738
835
|
detachMouseDragListeners,
|
|
739
836
|
detachTouchDragListeners,
|
|
740
837
|
draggable,
|
|
838
|
+
enabled,
|
|
741
839
|
edgeHoverScroll,
|
|
742
840
|
edgeHoverInputMode,
|
|
743
841
|
clearEdgeHover,
|
|
@@ -748,6 +846,24 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
748
846
|
handleWheel,
|
|
749
847
|
wheelEnabled
|
|
750
848
|
]);
|
|
849
|
+
(0, react.useEffect)(() => {
|
|
850
|
+
if (enabled) return;
|
|
851
|
+
clearInactivityTimer();
|
|
852
|
+
clearEdgeHover();
|
|
853
|
+
detachMouseDragListeners();
|
|
854
|
+
detachTouchDragListeners();
|
|
855
|
+
touchInteractionStateRef.current = "idle";
|
|
856
|
+
setDraggingState(false);
|
|
857
|
+
velocityRef.current = 0;
|
|
858
|
+
autoScrollRef.current.active = autoScrollRef.current.desired;
|
|
859
|
+
}, [
|
|
860
|
+
clearEdgeHover,
|
|
861
|
+
clearInactivityTimer,
|
|
862
|
+
detachMouseDragListeners,
|
|
863
|
+
detachTouchDragListeners,
|
|
864
|
+
enabled,
|
|
865
|
+
setDraggingState
|
|
866
|
+
]);
|
|
751
867
|
(0, react.useEffect)(() => {
|
|
752
868
|
window.addEventListener("resize", scheduleLoopPointRecalculation);
|
|
753
869
|
return () => {
|
|
@@ -796,7 +912,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
796
912
|
};
|
|
797
913
|
}, [clearInactivityTimer]);
|
|
798
914
|
(0, react.useEffect)(() => {
|
|
799
|
-
if (!isTabActive || isPaused) {
|
|
915
|
+
if (!enabled || !isTabActive || isPaused) {
|
|
800
916
|
if (animationFrameRef.current !== null) {
|
|
801
917
|
cancelAnimationFrame(animationFrameRef.current);
|
|
802
918
|
animationFrameRef.current = null;
|
|
@@ -805,7 +921,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
805
921
|
return;
|
|
806
922
|
}
|
|
807
923
|
lastFrameTimeRef.current = 0;
|
|
808
|
-
const directionMultiplier = direction === "down" || direction === "right" ? 1 : -1;
|
|
924
|
+
const directionMultiplier = direction === "away" || direction === "down" || direction === "right" ? 1 : -1;
|
|
809
925
|
const animate = (currentTime) => {
|
|
810
926
|
let deltaTime = lastFrameTimeRef.current ? (currentTime - lastFrameTimeRef.current) / MS_PER_FRAME_60FPS : 1;
|
|
811
927
|
deltaTime = Math.min(deltaTime, MAX_DELTA_TIME);
|
|
@@ -816,7 +932,7 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
816
932
|
let nextPosition = positionRef.current;
|
|
817
933
|
const effectiveSpeed = prefersReducedMotion ? normalizedSpeed * REDUCED_MOTION_SPEED_FACTOR : normalizedSpeed;
|
|
818
934
|
const edgeHoverMultiplier = edgeHoverScroll ? getEdgeHoverVelocityScale(edgeHoverIntensityRef.current, normalizedEdgeHoverSpeedMultiplier) : directionMultiplier;
|
|
819
|
-
const shouldAutoScroll = (autoScrollRef.current.desired && autoScrollRef.current.active || externalEdgeHoverLeaseRef.current) && !isDraggingRef.current && (!edgeHoverScroll || edgeHoverMultiplier !== 0);
|
|
935
|
+
const shouldAutoScroll = (autoScrollRef.current.desired && autoScrollRef.current.active || externalEdgeHoverLeaseRef.current) && !isDraggingRef.current && !(isDepth && prefersReducedMotion) && (!edgeHoverScroll || edgeHoverMultiplier !== 0);
|
|
820
936
|
if (shouldAutoScroll) {
|
|
821
937
|
const autoScrollDeltaTime = edgeHoverScroll ? Math.min(deltaTime, MAX_EDGE_HOVER_DELTA_TIME) : deltaTime;
|
|
822
938
|
nextPosition += edgeHoverMultiplier * effectiveSpeed * autoScrollDeltaTime;
|
|
@@ -845,6 +961,8 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
845
961
|
commitPosition,
|
|
846
962
|
direction,
|
|
847
963
|
edgeHoverScroll,
|
|
964
|
+
enabled,
|
|
965
|
+
isDepth,
|
|
848
966
|
isPaused,
|
|
849
967
|
isTabActive,
|
|
850
968
|
normalizedFriction,
|
|
@@ -877,56 +995,77 @@ var ReactSway = (0, react.forwardRef)(function ReactSway({ ariaLabel = "Scrollab
|
|
|
877
995
|
lazyRootMargin,
|
|
878
996
|
normalizedLazyThreshold
|
|
879
997
|
]);
|
|
880
|
-
return /* @__PURE__ */
|
|
998
|
+
return /* @__PURE__ */ react.default.createElement("section", {
|
|
881
999
|
"aria-label": ariaLabel,
|
|
882
1000
|
className: `react-sway-container scroller-content${className ? ` ${className}` : ""}`,
|
|
883
1001
|
ref: containerRef,
|
|
884
1002
|
style: {
|
|
885
|
-
cursor: draggable ? isDraggingRef.current ? "grabbing" : "grab" : "default",
|
|
1003
|
+
cursor: enabled && draggable ? isDraggingRef.current ? "grabbing" : "grab" : "default",
|
|
886
1004
|
display: isHorizontal ? "flex" : void 0,
|
|
887
1005
|
flexDirection: isHorizontal ? "row" : void 0,
|
|
888
|
-
height: isHorizontal ? "100%" : "max-content",
|
|
1006
|
+
height: isHorizontal || isDepth ? "100%" : "max-content",
|
|
889
1007
|
MozUserSelect: "none",
|
|
890
1008
|
msUserSelect: "none",
|
|
891
1009
|
minHeight: isHorizontal ? void 0 : "100%",
|
|
892
|
-
overflow: "hidden",
|
|
1010
|
+
overflow: isDepth ? "visible" : "hidden",
|
|
1011
|
+
perspective: isDepth ? normalizedDepthPerspective : void 0,
|
|
893
1012
|
overscrollBehavior,
|
|
894
1013
|
pointerEvents: "auto",
|
|
895
1014
|
position: "absolute",
|
|
896
1015
|
touchAction,
|
|
897
1016
|
transform: "translate3d(0, 0, 0)",
|
|
1017
|
+
transformStyle: isDepth ? "preserve-3d" : void 0,
|
|
898
1018
|
userSelect: "none",
|
|
899
1019
|
WebkitUserSelect: "none",
|
|
900
1020
|
width: isHorizontal ? "max-content" : "100%",
|
|
901
|
-
willChange: "transform",
|
|
1021
|
+
willChange: isDepth ? void 0 : "transform",
|
|
902
1022
|
zIndex: 1
|
|
903
1023
|
},
|
|
904
1024
|
onKeyDown: keyboard ? handleKeyDown : void 0,
|
|
905
|
-
tabIndex: keyboard ? 0 : void 0
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1025
|
+
tabIndex: enabled && keyboard ? 0 : void 0
|
|
1026
|
+
}, /* @__PURE__ */ react.default.createElement("div", {
|
|
1027
|
+
className: "content-group original",
|
|
1028
|
+
ref: originalGroupRef,
|
|
1029
|
+
style: isDepth ? {
|
|
1030
|
+
height: "100%",
|
|
1031
|
+
position: "relative",
|
|
1032
|
+
transformStyle: "preserve-3d"
|
|
1033
|
+
} : isHorizontal ? {
|
|
1034
|
+
display: "flex",
|
|
1035
|
+
flex: "0 0 auto"
|
|
1036
|
+
} : void 0
|
|
1037
|
+
}, isDepth ? depthChildren.map((child, index) => {
|
|
1038
|
+
const { opacity, z } = getDepthItemState(0, index, depthChildren.length, normalizedDepthPerspective, normalizedDepthSpacing);
|
|
1039
|
+
return /* @__PURE__ */ react.default.createElement("div", {
|
|
1040
|
+
className: "react-sway-depth-item",
|
|
1041
|
+
key: typeof child === "object" && "key" in child ? child.key : index,
|
|
1042
|
+
style: {
|
|
1043
|
+
height: 0,
|
|
1044
|
+
left: "50%",
|
|
1045
|
+
opacity,
|
|
1046
|
+
position: "absolute",
|
|
1047
|
+
top: "50%",
|
|
1048
|
+
transform: `translate3d(0, 0, ${z}px)`,
|
|
1049
|
+
width: 0,
|
|
1050
|
+
willChange: "transform, opacity"
|
|
1051
|
+
}
|
|
1052
|
+
}, child);
|
|
1053
|
+
}) : children), Array.from({ length: isDepth ? 0 : loopSegmentCount - 1 }, (_, duplicateIndex) => /* @__PURE__ */ react.default.createElement("aside", {
|
|
1054
|
+
"aria-hidden": "true",
|
|
1055
|
+
className: "content-group duplicate",
|
|
1056
|
+
"data-duplicate": "true",
|
|
1057
|
+
...INERT_ATTRIBUTE,
|
|
1058
|
+
key: `duplicate-${duplicateIndex}`,
|
|
1059
|
+
role: "presentation",
|
|
1060
|
+
style: isHorizontal ? {
|
|
1061
|
+
display: "flex",
|
|
1062
|
+
flex: "0 0 auto",
|
|
1063
|
+
pointerEvents: "none"
|
|
1064
|
+
} : { pointerEvents: "none" }
|
|
1065
|
+
}, children)));
|
|
928
1066
|
});
|
|
929
|
-
|
|
1067
|
+
exports.DEFAULT_DEPTH_PERSPECTIVE = DEFAULT_DEPTH_PERSPECTIVE;
|
|
1068
|
+
exports.DEFAULT_DEPTH_SPACING = DEFAULT_DEPTH_SPACING;
|
|
930
1069
|
exports.DEFAULT_EDGE_HOVER_SIZE = DEFAULT_EDGE_HOVER_SIZE;
|
|
931
1070
|
exports.DEFAULT_EDGE_HOVER_SPEED_MULTIPLIER = DEFAULT_EDGE_HOVER_SPEED_MULTIPLIER;
|
|
932
1071
|
exports.ReactSway = ReactSway;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,5 +2,6 @@
|
|
|
2
2
|
* Public package entrypoint for ReactSway exports.
|
|
3
3
|
*/
|
|
4
4
|
export { default as ReactSway } from './ReactSway';
|
|
5
|
+
export { DEFAULT_DEPTH_PERSPECTIVE, DEFAULT_DEPTH_SPACING, } from './depth-position';
|
|
5
6
|
export { DEFAULT_EDGE_HOVER_SIZE, DEFAULT_EDGE_HOVER_SPEED_MULTIPLIER, } from './edge-hover-velocity';
|
|
6
7
|
export type { ReactSwayHandle, ReactSwayProps, SwayAxis, SwayDirection, SwayEdgeHoverInputMode, SwayWheelMode, } from './ReactSway';
|