react-sway 0.3.0 → 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/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0
4
+
5
+ - Added horizontal and vertical axis semantics with page-safe touch behavior.
6
+ - Added bounded edge-hover acceleration, configurable zone geometry, and public defaults.
7
+ - Added external pointer and wheel routing through `ReactSwayHandle`.
8
+ - Added axis-aware and full-capture wheel ownership modes.
9
+ - Added a consumer `className` hook and stricter numeric input normalization.
10
+ - Corrected the peer contract to React 17 or newer, matching the published JSX runtime imports.
11
+ - Improved loop measurement, listener cleanup, reduced-motion behavior, accessibility, and regression coverage.
12
+
13
+ ## 0.3.1
14
+
15
+ - Updated development dependencies without changing the package runtime contract.
16
+
17
+ ## 0.3.0
18
+
19
+ - Established the previous public ReactSway package contract.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lafitte Mehdy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -4,26 +4,30 @@ A React component for smooth, infinite, and interactive content scrolling on the
4
4
 
5
5
  ## What is `react-sway`?
6
6
 
7
- React Sway takes your list of items and makes them scroll endlessly. It's designed to be easy to use and performant, with auto-scrolling that pauses when users interact. Edge-hover layouts can also be configured to scroll only while the pointer hovers the active axis boundary: top/bottom for vertical loops or left/right for horizontal loops.
7
+ React Sway takes your list of items and makes them scroll endlessly. It's designed to be easy to use and performant, with auto-scrolling that pauses when users interact. Edge-hover layouts can also be configured to scroll only while the pointer hovers the active axis boundary: top/bottom for vertical loops or left/right for horizontal loops. Edge-hover speed ramps from idle to a bounded multiple of the configured speed as the pointer moves deeper into the boundary zone.
8
8
 
9
- It works by duplicating your content to create a seamless loop and uses CSS transforms for smooth animation. The duplicated content is wrapped in `<aside>` elements with `aria-hidden="true"` and `role="presentation"` to ensure good accessibility and helps search engines understand the content structure.
9
+ It works by duplicating your content to create a seamless loop and uses CSS transforms for smooth animation. The duplicated content is wrapped in inert `<aside>` elements with `aria-hidden="true"` and `role="presentation"` so assistive technology, pointer input, and search crawlers keep treating the original group as canonical.
10
10
 
11
11
  ### Core Features
12
12
 
13
13
  * **Smooth Infinite Scroll:** Content loops continuously on the vertical or horizontal axis.
14
14
  * **Auto-Scroll:** Scrolls automatically, with configurable speed and direction.
15
- * **Edge Hover:** Use `edgeHoverScroll` to keep auto-scroll idle until the pointer enters the active axis boundary zone.
15
+ * **Edge Hover:** Use `edgeHoverScroll` to keep auto-scroll idle until the pointer enters the active axis boundary zone, with speed scaled smoothly by boundary depth and capped by `edgeHoverSpeedMultiplier`.
16
16
  * **User Friendly Interactions:**
17
17
  * Click and drag to scroll.
18
18
  * Swipe on touch devices.
19
19
  * Mouse wheel support with axis-aware intent detection, delta-mode normalization, and velocity capping.
20
20
  * Keyboard controls: Spacebar to pause/resume, Arrow keys to scroll, Home/End to jump.
21
+ * **External Input Bridge:** Use `ref` with `ReactSwayHandle` to route wheel and edge-hover intent from a parent drag/drop surface into Sway.
22
+ * **Idle-Safe Listeners:** Global drag listeners are attached only during active drag or touch gestures and cleaned up on release, cancel, blur, and unmount.
21
23
  * **Responsive:** Adjusts to window resizing with debounced recalculation.
22
24
  * **Lazy Visibility Detection:** Add a `content-item` class to your child elements, and `react-sway` automatically uses an IntersectionObserver to add a `.visible` class when they enter the viewport. Useful for triggering CSS animations or deferred rendering. Configurable via `lazy`, `lazyRootMargin`, and `lazyThreshold` props.
23
25
  * **Reduced Motion Support:** Respects `prefers-reduced-motion: reduce` by lowering auto-scroll speed and disabling momentum effects.
24
26
 
25
27
  ## Installation
26
28
 
29
+ React Sway supports React and React DOM 17 or newer. Its published ESM and CommonJS artifacts use React's modern JSX runtime.
30
+
27
31
  ```bash
28
32
  npm install react-sway
29
33
  # or
@@ -36,54 +40,121 @@ pnpm add react-sway
36
40
 
37
41
  ```tsx
38
42
  import { ReactSway } from 'react-sway';
39
- import './index.css'; // Your global styles
43
+ import './index.css';
40
44
 
41
45
  function SwayUsageExample() {
42
46
  return (
43
- <div className="scroller-container"> {/* Style this container to define ReactSway's area (e.g., full-page, or a specific height) */}
44
- <ReactSway>
45
- <div className="content-item" style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }}>
46
- <h2>Seamless Scrolling</h2>
47
- <p>Experience buttery smooth infinite scrolling with no stutters or pauses.</p>
48
- </div>
49
-
50
- <div className="content-item" style={{ background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' }}>
51
- <h2>Touch & Mouse Support</h2>
52
- <p>Interact naturally with touch gestures, mouse wheel, or click-and-drag.</p>
53
- </div>
54
-
55
- <div className="content-item" style={{ background: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }}>
56
- <h2>Momentum Scrolling</h2>
57
- <p>Flick to scroll with realistic physics-based momentum and friction.</p>
58
- </div>
59
-
60
- <div className="content-item" style={{ background: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)' }}>
61
- <h2>Performance Optimized</h2>
62
- <p>Using requestAnimationFrame for 60+ FPS scrolling on all devices.</p>
63
- </div>
64
-
65
- <div className="content-item" style={{ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }}>
66
- <h2>Responsive Design</h2>
67
- <p>Adapts perfectly to any screen size and device orientation.</p>
68
- </div>
69
-
70
- <div className="content-item" style={{ background: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)' }}>
71
- <h2>No Native Scroll</h2>
72
- <p>Custom implementation avoids native scroll jank and inconsistencies.</p>
73
- </div>
74
-
75
- <div className="content-item" style={{ background: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)' }}>
76
- <h2>Continuous Loop</h2>
77
- <p>Content loops seamlessly without any visible seams or jumps.</p>
78
- </div>
79
- </ReactSway>
80
- </div>
47
+ <section className="composer-grid" aria-label="Selected media order">
48
+ <aside className="composer-cell composer-copy">
49
+ <p>Selected order</p>
50
+ <h2>Horizontal media rail</h2>
51
+ </aside>
52
+
53
+ <div className="composer-cell rail-cell">
54
+ <ReactSway
55
+ axis="horizontal"
56
+ direction="left"
57
+ edgeHoverScroll
58
+ edgeHoverSize={72}
59
+ keyboard={false}
60
+ lazy={false}
61
+ speed={0.8}
62
+ wheelMode="capture"
63
+ >
64
+ <ol className="media-rail" aria-label="Selected post media order">
65
+ {['Cover', 'Wide crop', 'Motion study', 'Detail frame', 'Archive still'].map((title, index) => (
66
+ <li className="media-card" key={title}>
67
+ <span>{String(index + 1).padStart(2, '0')}</span>
68
+ <strong>{title}</strong>
69
+ </li>
70
+ ))}
71
+ </ol>
72
+ </ReactSway>
73
+ </div>
74
+ </section>
81
75
  );
82
76
  }
83
77
 
84
78
  export default SwayUsageExample;
85
79
  ```
86
80
 
81
+ ```css
82
+ .composer-grid {
83
+ --line: rgb(255 255 255 / 0.12);
84
+
85
+ background: var(--line);
86
+ display: grid;
87
+ gap: 1px;
88
+ grid-template-columns: minmax(12rem, 0.7fr) minmax(0, 1.3fr);
89
+ min-height: 18rem;
90
+ }
91
+
92
+ .composer-cell {
93
+ background: #050505;
94
+ color: white;
95
+ min-width: 0;
96
+ overflow: hidden;
97
+ padding: 1rem;
98
+ position: relative;
99
+ }
100
+
101
+ .composer-copy {
102
+ align-content: end;
103
+ display: grid;
104
+ gap: 0.5rem;
105
+ }
106
+
107
+ .composer-copy p {
108
+ color: rgb(255 255 255 / 0.58);
109
+ font: 700 0.72rem/1 system-ui;
110
+ letter-spacing: 0.08em;
111
+ text-transform: uppercase;
112
+ }
113
+
114
+ .composer-copy h2 {
115
+ font: 400 clamp(1.6rem, 4vw, 3rem) / 0.95 Georgia, serif;
116
+ }
117
+
118
+ .rail-cell {
119
+ min-height: 100%;
120
+ }
121
+
122
+ .rail-cell .react-sway-container {
123
+ left: 0;
124
+ top: 0;
125
+ }
126
+
127
+ .media-rail {
128
+ display: flex;
129
+ gap: 0.7rem;
130
+ height: 100%;
131
+ list-style: none;
132
+ margin: 0;
133
+ padding: 1rem;
134
+ }
135
+
136
+ .media-card {
137
+ background: #0b0b0b;
138
+ box-shadow: inset 0 0 0 1px var(--line);
139
+ display: grid;
140
+ flex: 0 0 clamp(9rem, 22vw, 12rem);
141
+ grid-template-rows: auto 1fr;
142
+ min-width: 0;
143
+ padding: 0.75rem;
144
+ }
145
+
146
+ .media-card span {
147
+ color: rgb(255 255 255 / 0.52);
148
+ font: 700 0.68rem/1 system-ui;
149
+ }
150
+
151
+ .media-card strong {
152
+ align-self: end;
153
+ color: white;
154
+ font: 400 1.35rem/0.98 Georgia, serif;
155
+ }
156
+ ```
157
+
87
158
  ### Auto-scroll and edge-hover examples
88
159
 
89
160
  React Sway can either move automatically or stay idle until the pointer reaches an axis boundary. The resulting examples are the product of two axes and two trigger modes.
@@ -209,27 +280,104 @@ function MinimalEdgeHoverExamples() {
209
280
 
210
281
  | Prop | Type | Default | Description |
211
282
  |------|------|---------|-------------|
212
- | `axis` | `'vertical' \| 'horizontal'` | Inferred from `direction`, otherwise `'vertical'` | Scroll axis. Use `'horizontal'` for left/right loops. |
283
+ | `ariaLabel` | `string` | `'Scrollable content'` | Accessible name for the scrolling region. |
284
+ | `axis` | `'horizontal' \| 'vertical'` | Inferred from `direction`, otherwise `'vertical'` | Scroll axis. Use `'horizontal'` for left/right loops. |
213
285
  | `autoScroll` | `boolean` | `true` | Enable/disable auto-scrolling. |
214
286
  | `children` | `ReactNode` | - | Content elements to render in the scroll container. |
287
+ | `className` | `string` | - | Additional class name applied to the semantic scrolling region. |
215
288
  | `direction` | `'down' \| 'left' \| 'right' \| 'up'` | `'up'` | Auto-scroll direction. `left` and `right` imply horizontal scrolling when `axis` is omitted. |
216
289
  | `draggable` | `boolean` | `true` | Enable mouse/touch drag interaction. |
217
- | `edgeHoverScroll` | `boolean` | `false` | Only auto-scroll while hovering the active axis boundary: top/bottom for vertical, left/right for horizontal. |
290
+ | `edgeHoverInputMode` | `'external' \| 'hybrid'` | `'hybrid'` | Use intrinsic pointer sensing plus the imperative bridge, or external imperative ownership only for composed drag surfaces. |
291
+ | `edgeHoverScroll` | `boolean` | `false` | Only auto-scroll while hovering the active axis boundary: top/bottom for vertical, left/right for horizontal. A short ingress feather removes sensor chatter; velocity then accelerates through the physical edge and reaches its bounded cap one activation-zone width beyond it. |
218
292
  | `edgeHoverSize` | `number` | `96` | Thickness in pixels of each edge-hover activation zone. |
293
+ | `edgeHoverSpeedMultiplier` | `number` | `6` | Maximum edge-hover velocity as a multiple of `speed`. The physical edge retains the established 3x response, then accelerates to the bounded `1`–`10` maximum beyond the viewport; set `1` for constant speed. |
219
294
  | `friction` | `number` | `0.95` | Momentum decay coefficient (0-1, lower = more friction). |
220
295
  | `keyboard` | `boolean` | `true` | Enable keyboard controls (Space, Arrow keys, Home/End). |
221
296
  | `lazy` | `boolean` | `true` | Enable lazy visibility detection via IntersectionObserver. |
222
297
  | `lazyRootMargin` | `string` | `'100px'` | IntersectionObserver `rootMargin` for lazy visibility detection. |
223
- | `lazyThreshold` | `number` | `0.01` | IntersectionObserver `threshold` for lazy visibility detection. |
224
- | `onPause` | `() => void` | - | Fired when scrolling pauses. |
225
- | `onResume` | `() => void` | - | Fired when scrolling resumes after pause. |
298
+ | `lazyThreshold` | `number` | `0.01` | IntersectionObserver `threshold`, normalized to the inclusive `0`–`1` interval. |
299
+ | `onPause` | `() => void` | - | Fired once when active autonomous scrolling transitions to interaction-paused. |
300
+ | `onResume` | `() => void` | - | Fired when interaction-paused autonomous scrolling resumes. |
226
301
  | `onScroll` | `(position: number) => void` | - | Fired on every position change with the current scroll position. |
227
302
  | `pauseOnInteraction` | `boolean` | `true` | Pause auto-scroll during user interaction. |
228
303
  | `resumeDelay` | `number` | `2000` | Milliseconds before auto-scroll resumes after interaction. |
229
- | `speed` | `number` | `0.5` | Auto-scroll speed in pixels per frame at 60fps. |
304
+ | `speed` | `number` | `0.5` | Baseline auto-scroll speed in pixels per frame at 60fps. Edge-hover mode preserves this baseline inside the active zone and adds bounded depth acceleration. |
230
305
  | `wheelEnabled` | `boolean` | `true` | Enable mouse wheel scrolling. |
231
306
  | `wheelMode` | `'axis' \| 'capture'` | `'axis'` | Define whether wheel input only consumes gestures aligned with the Sway axis or captures every wheel event. |
232
307
 
308
+ ## Imperative handle
309
+
310
+ `ReactSway` supports `ref` through `ReactSwayHandle`. This is intended for composed interactions where another component owns pointer capture, such as drag-and-drop reorder rails, but Sway should still receive wheel or edge-hover intent.
311
+
312
+ ```tsx
313
+ import { useEffect, useRef } from 'react';
314
+ import {
315
+ DEFAULT_EDGE_HOVER_SIZE,
316
+ DEFAULT_EDGE_HOVER_SPEED_MULTIPLIER,
317
+ ReactSway,
318
+ type ReactSwayHandle,
319
+ } from 'react-sway';
320
+
321
+ function ExternalDragBridge() {
322
+ const swayRef = useRef<ReactSwayHandle | null>(null);
323
+ const clearEdgeHover = () => swayRef.current?.clearEdgeHover();
324
+
325
+ useEffect(() => clearEdgeHover, []);
326
+
327
+ return (
328
+ <div
329
+ onPointerCancel={clearEdgeHover}
330
+ onPointerMove={(event) => {
331
+ swayRef.current?.handleEdgeHover({
332
+ clientX: event.clientX,
333
+ clientY: event.clientY,
334
+ });
335
+ }}
336
+ onPointerUp={clearEdgeHover}
337
+ onWheel={(event) => {
338
+ swayRef.current?.handleWheel(event.nativeEvent);
339
+ }}
340
+ >
341
+ <ReactSway
342
+ axis="horizontal"
343
+ direction="left"
344
+ draggable={false}
345
+ edgeHoverInputMode="external"
346
+ edgeHoverScroll
347
+ edgeHoverSize={DEFAULT_EDGE_HOVER_SIZE}
348
+ edgeHoverSpeedMultiplier={DEFAULT_EDGE_HOVER_SPEED_MULTIPLIER}
349
+ keyboard={false}
350
+ ref={swayRef}
351
+ wheelMode="capture"
352
+ >
353
+ <ol className="media-rail">
354
+ <li>Cover</li>
355
+ <li>Detail</li>
356
+ <li>Motion</li>
357
+ </ol>
358
+ </ReactSway>
359
+ </div>
360
+ );
361
+ }
362
+ ```
363
+
364
+ | Method | Description |
365
+ |--------|-------------|
366
+ | `handleEdgeHover(point)` | Acquires or updates external edge-hover ownership and applies depth-scaled speed. Returns `true` when an enabled, visible instance accepted the sample; the pointer may still be in the inactive center. External intent can drive motion when autonomous `autoScroll` is disabled. |
367
+ | `handleWheel(event)` | Routes an external wheel event through Sway's wheel normalization and ownership policy. Returns `true` when Sway consumed the event. |
368
+ | `clearEdgeHover()` | Releases external ownership, clears active edge-hover intensity, and lets the animation loop sleep when there is no momentum. |
369
+
370
+ External drag owners should retain pointer capture or route global pointer coordinates while held. Use `edgeHoverInputMode="external"` when the parent interaction is the sole input authority; this prevents intrinsic pointer sensing from competing with the drag owner. Always call `clearEdgeHover` on release, cancellation, lost ownership, and unmount.
371
+
372
+ `DEFAULT_EDGE_HOVER_SIZE` and `DEFAULT_EDGE_HOVER_SPEED_MULTIPLIER` are public named constants. They let application-level interaction profiles inherit the package defaults without duplicating magic numbers. `edgeHoverSize` controls activation-zone geometry; `edgeHoverSpeedMultiplier` controls bounded peak velocity. The smooth acceleration curve remains package-owned so intrinsic and externally routed input share stable motion semantics.
373
+
374
+ ## Runtime and accessibility contract
375
+
376
+ - Duplicate loop groups are inert, `aria-hidden`, presentation-only, non-interactive, and pointer-disabled.
377
+ - Mouse and touch global listeners are installed only during active drag gestures and are removed on release, cancel, blur, unmount, or prop changes.
378
+ - Edge-hover mode does not continuously request animation frames while idle; it wakes when the pointer enters an edge, wheel input arrives, momentum exists, or auto-scroll resumes.
379
+ - `wheelMode="axis"` lets cross-axis page scroll pass through. `wheelMode="capture"` intentionally lets Sway own every wheel event it receives.
380
+
233
381
  ## License
234
382
 
235
383
  This package is licensed under the [MIT License](../../LICENSE).
@@ -2,27 +2,69 @@
2
2
  * Core ReactSway component implementing axis-aware infinite scrolling interactions.
3
3
  */
4
4
  import { type ReactNode } from 'react';
5
+ import { type SwayWheelMode } from './wheel-input';
6
+ export type { SwayWheelMode } from './wheel-input';
5
7
  export type SwayAxis = 'horizontal' | 'vertical';
6
8
  export type SwayDirection = 'down' | 'left' | 'right' | 'up';
7
- export type SwayWheelMode = 'axis' | 'capture';
9
+ export type SwayEdgeHoverInputMode = 'external' | 'hybrid';
10
+ export interface ReactSwayHandle {
11
+ /**
12
+ * Routes an external pointer sample through ReactSway's edge-hover policy.
13
+ *
14
+ * This is useful when another interaction owns pointer capture but still
15
+ * wants ReactSway to keep its boundary scrolling semantics.
16
+ * Returns true when an enabled, visible instance accepted the sample. A true
17
+ * result does not imply motion when the pointer is in the inactive center.
18
+ * The first accepted sample acquires an external-input lease that survives
19
+ * native pointer leave; consumers must call `clearEdgeHover` on termination.
20
+ */
21
+ handleEdgeHover: (point: {
22
+ clientX: number;
23
+ clientY: number;
24
+ }) => boolean;
25
+ /**
26
+ * Routes an external wheel event through ReactSway's normalized wheel physics.
27
+ *
28
+ * Returns true when the event was consumed by ReactSway and false when the
29
+ * current wheel policy leaves the event to native scroll chaining.
30
+ */
31
+ handleWheel: (event: globalThis.WheelEvent) => boolean;
32
+ /** Releases external edge-hover ownership and clears active intensity. */
33
+ clearEdgeHover: () => void;
34
+ }
8
35
  /**
9
36
  * Props for the ReactSway infinite scrolling component.
10
37
  */
11
38
  export interface ReactSwayProps {
39
+ /** Accessible name for the scrolling region. */
40
+ ariaLabel?: string;
12
41
  /** Scroll axis. Horizontal directions imply `horizontal` when omitted. @default 'vertical' */
13
42
  axis?: SwayAxis;
14
43
  /** Enable/disable auto-scrolling. @default true */
15
44
  autoScroll?: boolean;
16
45
  /** Content elements to render in the infinite scroll container. */
17
46
  children: ReactNode;
47
+ /** Additional class name applied to the semantic scrolling region. */
48
+ className?: string;
18
49
  /** Auto-scroll direction. @default 'up' */
19
50
  direction?: SwayDirection;
20
51
  /** Enable mouse/touch drag interaction. @default true */
21
52
  draggable?: boolean;
22
- /** Only auto-scroll while the pointer hovers an axis boundary. @default false */
53
+ /**
54
+ * Selects intrinsic pointer sensing plus the imperative bridge (`hybrid`),
55
+ * or imperative external ownership only (`external`). @default 'hybrid'
56
+ */
57
+ edgeHoverInputMode?: SwayEdgeHoverInputMode;
58
+ /**
59
+ * Only auto-scroll while the pointer hovers an axis boundary. Active motion
60
+ * preserves baseline speed, then accelerates toward the visible edge.
61
+ * @default false
62
+ */
23
63
  edgeHoverScroll?: boolean;
24
64
  /** Edge-hover activation thickness in pixels. @default 96 */
25
65
  edgeHoverSize?: number;
66
+ /** Maximum edge-hover velocity relative to the baseline `speed`. @default 6 */
67
+ edgeHoverSpeedMultiplier?: number;
26
68
  /** Momentum decay coefficient (0-1, lower = more friction). @default 0.95 */
27
69
  friction?: number;
28
70
  /** Enable keyboard controls (Space, Arrow keys, Home/End). @default true */
@@ -43,7 +85,7 @@ export interface ReactSwayProps {
43
85
  pauseOnInteraction?: boolean;
44
86
  /** Milliseconds before auto-scroll resumes after interaction. @default 2000 */
45
87
  resumeDelay?: number;
46
- /** Auto-scroll speed in pixels per frame at 60fps. @default 0.5 */
88
+ /** Baseline auto-scroll speed in pixels per frame at 60fps. @default 0.5 */
47
89
  speed?: number;
48
90
  /** Enable mouse wheel scrolling. @default true */
49
91
  wheelEnabled?: boolean;
@@ -78,5 +120,5 @@ export interface ReactSwayProps {
78
120
  * </ReactSway>
79
121
  * ```
80
122
  */
81
- declare function ReactSway({ axis, autoScroll, children, direction, draggable, edgeHoverScroll, edgeHoverSize, friction, keyboard, lazy, lazyRootMargin, lazyThreshold, onPause, onResume, onScroll, pauseOnInteraction, resumeDelay, speed, wheelEnabled, wheelMode, }: ReactSwayProps): import("react").JSX.Element;
123
+ declare const ReactSway: import("react").ForwardRefExoticComponent<ReactSwayProps & import("react").RefAttributes<ReactSwayHandle>>;
82
124
  export default ReactSway;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Defines the bounded spatial mapping from edge-hover depth to Sway velocity.
3
+ */
4
+ /** Default edge-hover activation thickness in pixels. */
5
+ export declare const DEFAULT_EDGE_HOVER_SIZE = 96;
6
+ /** Default maximum edge-hover speed relative to the configured base speed. */
7
+ export declare const DEFAULT_EDGE_HOVER_SPEED_MULTIPLIER = 6;
8
+ export interface VisibleEdgeInterval {
9
+ end: number;
10
+ size: number;
11
+ start: number;
12
+ }
13
+ export declare function normalizeEdgeHoverIntensity(value: number): number;
14
+ export declare function normalizeEdgeHoverSpeedMultiplier(value: number): number;
15
+ /**
16
+ * Maps signed edge depth onto a bounded velocity scale.
17
+ *
18
+ * Intensity carries direction and lies in [-1, 1]. The speed multiplier is
19
+ * the maximum amplification reached one activation-zone width beyond the
20
+ * visible edge.
21
+ */
22
+ export declare function getEdgeHoverVelocityScale(intensity: number, speedMultiplier?: number): number;
23
+ /**
24
+ * Computes signed edge depth using a symmetric linear ramp.
25
+ *
26
+ * The inner edge of either activation zone maps to zero, the physical edge
27
+ * maps to half intensity, and motion keeps accelerating outside the viewport
28
+ * until it reaches the cap one activation-zone width beyond the visible edge.
29
+ */
30
+ export declare function getEdgeHoverIntensity(pointerPosition: number, visibleInterval: VisibleEdgeInterval, edgeHoverSize: number): number;