@enigmax/primitives 0.4.0 → 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/dist/{chunk-W2DRHYC2.js → chunk-2DVVWPHM.js} +9 -2
- package/dist/index.d.ts +18 -3
- package/dist/index.js +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +2 -2
- package/package.json +1 -1
- package/recipes/marquee.css.tsx +9 -5
- package/recipes/marquee.tailwind.tsx +9 -5
- package/src/core/marquee.ts +28 -4
- package/src/index.ts +1 -1
- package/src/react/index.ts +1 -1
- package/src/react/use-marquee.ts +2 -2
|
@@ -11,7 +11,7 @@ var DEFAULTS = {
|
|
|
11
11
|
reverse: false,
|
|
12
12
|
vertical: false,
|
|
13
13
|
draggable: true,
|
|
14
|
-
|
|
14
|
+
hover: "off",
|
|
15
15
|
decay: DEFAULT_DECAY,
|
|
16
16
|
manageStyles: true,
|
|
17
17
|
copies: "clone"
|
|
@@ -37,10 +37,17 @@ function createMarquee(lane, track, options = {}) {
|
|
|
37
37
|
let lastTouchAt = 0;
|
|
38
38
|
const motionQuery = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
39
39
|
let reducedMotion = motionQuery?.matches ?? false;
|
|
40
|
+
function hoverTarget(cruise) {
|
|
41
|
+
const hover = opts.hover ?? "off";
|
|
42
|
+
if (hover === "off") return cruise;
|
|
43
|
+
if (hover === "pause") return 0;
|
|
44
|
+
if (typeof hover === "number") return cruise * hover;
|
|
45
|
+
return (cruise < 0 ? -1 : 1) * Math.abs(hover.speed);
|
|
46
|
+
}
|
|
40
47
|
function target() {
|
|
41
48
|
if (reducedMotion || paused) return 0;
|
|
42
49
|
const cruise = opts.reverse ? -opts.speed : opts.speed;
|
|
43
|
-
return hovering ? cruise
|
|
50
|
+
return hovering ? hoverTarget(cruise) : cruise;
|
|
44
51
|
}
|
|
45
52
|
function axisSize(element) {
|
|
46
53
|
return opts.vertical ? element.offsetHeight : element.offsetWidth;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,21 @@ export { F as FuseConstructor, a as FuseLike, S as SearchInstance, b as SearchMa
|
|
|
11
11
|
* comments marked "non-negotiable" name the bug that the obvious implementation
|
|
12
12
|
* shipped with.
|
|
13
13
|
*/
|
|
14
|
+
/**
|
|
15
|
+
* What a MOUSE resting on the row does to its speed. One option covers every case, so
|
|
16
|
+
* there is no pile of booleans to reconcile:
|
|
17
|
+
*
|
|
18
|
+
* - `"off"` ignore hover entirely (the default)
|
|
19
|
+
* - `"pause"` stop while the pointer is on it
|
|
20
|
+
* - a number multiply the cruise speed: `0.15` crawls, `1` changes nothing, `2` doubles
|
|
21
|
+
* - `{ speed }` an ABSOLUTE speed in px/s, whatever the cruise is; direction still
|
|
22
|
+
* follows `reverse`, so you give a magnitude and not a sign
|
|
23
|
+
*
|
|
24
|
+
* Touch never triggers any of it - see the pointerType note on the hover handler.
|
|
25
|
+
*/
|
|
26
|
+
type MarqueeHover = number | "off" | "pause" | {
|
|
27
|
+
speed: number;
|
|
28
|
+
};
|
|
14
29
|
interface MarqueeOptions {
|
|
15
30
|
/**
|
|
16
31
|
* Pixels per second. NOT a duration: a duration ties the speed to the item
|
|
@@ -23,8 +38,8 @@ interface MarqueeOptions {
|
|
|
23
38
|
vertical?: boolean;
|
|
24
39
|
/** Allow grabbing and throwing the row. */
|
|
25
40
|
draggable?: boolean;
|
|
26
|
-
/**
|
|
27
|
-
|
|
41
|
+
/** What a mouse resting on the row does to its speed. See {@link MarqueeHover}. */
|
|
42
|
+
hover?: MarqueeHover;
|
|
28
43
|
/** Fraction of the remaining velocity gap left after one second. */
|
|
29
44
|
decay?: number;
|
|
30
45
|
/**
|
|
@@ -147,4 +162,4 @@ interface InputInstance {
|
|
|
147
162
|
*/
|
|
148
163
|
declare function createInput(input: HTMLInputElement, options?: InputOptions): InputInstance;
|
|
149
164
|
|
|
150
|
-
export { type InputAction, type InputActionState, type InputIcon, type InputInstance, type InputOptions, type MarqueeInstance, type MarqueeOptions, createInput, createMarquee };
|
|
165
|
+
export { type InputAction, type InputActionState, type InputIcon, type InputInstance, type InputOptions, type MarqueeHover, type MarqueeInstance, type MarqueeOptions, createInput, createMarquee };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createInput, createMarquee } from './chunk-
|
|
1
|
+
export { createInput, createMarquee } from './chunk-2DVVWPHM.js';
|
|
2
2
|
export { createSearch } from './chunk-UZFEEFMF.js';
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RefObject } from 'react';
|
|
2
2
|
import { MarqueeOptions, InputOptions } from '../index.js';
|
|
3
|
-
export { InputAction, InputIcon, MarqueeInstance } from '../index.js';
|
|
3
|
+
export { InputAction, InputIcon, MarqueeHover, MarqueeInstance } from '../index.js';
|
|
4
4
|
import { b as SearchMatch, c as SearchOptions } from '../search-CsO3L1Lw.js';
|
|
5
5
|
export { F as FuseConstructor } from '../search-CsO3L1Lw.js';
|
|
6
6
|
|
|
@@ -28,7 +28,7 @@ interface UseMarqueeResult {
|
|
|
28
28
|
* to the item count, so the row accelerates every time content is added.
|
|
29
29
|
*
|
|
30
30
|
* ```tsx
|
|
31
|
-
* const { laneRef, trackRef, copies } = useMarquee({ speed: 80,
|
|
31
|
+
* const { laneRef, trackRef, copies } = useMarquee({ speed: 80, hover: 0.15 });
|
|
32
32
|
* return (
|
|
33
33
|
* <div ref={laneRef}>
|
|
34
34
|
* <div ref={trackRef} style={{ display: "flex" }}>
|
package/dist/react/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createMarquee, createInput } from '../chunk-
|
|
1
|
+
import { createMarquee, createInput } from '../chunk-2DVVWPHM.js';
|
|
2
2
|
import { createSearch } from '../chunk-UZFEEFMF.js';
|
|
3
3
|
import { useRef, useState, useMemo, useEffect, useLayoutEffect } from 'react';
|
|
4
4
|
|
|
@@ -39,7 +39,7 @@ function useMarquee(options = {}) {
|
|
|
39
39
|
}, []);
|
|
40
40
|
useIsomorphicLayoutEffect(() => {
|
|
41
41
|
instanceRef.current?.update(options);
|
|
42
|
-
}, [options.speed, options.reverse, options.vertical, options.draggable, options.
|
|
42
|
+
}, [options.speed, options.reverse, options.vertical, options.draggable, options.hover, options.decay]);
|
|
43
43
|
useIsomorphicLayoutEffect(() => {
|
|
44
44
|
instanceRef.current?.measure();
|
|
45
45
|
}, [copies]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enigmax/primitives",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Headless interaction primitives: the behaviour, the timing and the accessibility of components like a draggable infinite marquee, with no styles of their own. Framework-agnostic core plus thin adapters.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
package/recipes/marquee.css.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "./marquee.css";
|
|
2
2
|
import type { ReactNode, Ref } from "react";
|
|
3
|
-
import { useMarquee } from "@enigmax/primitives/react";
|
|
3
|
+
import { useMarquee, type MarqueeHover } from "@enigmax/primitives/react";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* A styled marquee, yours to edit.
|
|
@@ -12,8 +12,12 @@ export interface MarqueeProps<T> {
|
|
|
12
12
|
items: T[];
|
|
13
13
|
/** Pixels per second. Never a duration - the row would speed up as items are added. */
|
|
14
14
|
speed?: number;
|
|
15
|
-
/**
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* What a mouse resting on the row does to its speed:
|
|
17
|
+
* "off" ignores hover, "pause" stops, a number multiplies the cruise speed
|
|
18
|
+
* (0.15 crawls, 2 doubles), { speed } sets an absolute px/s.
|
|
19
|
+
*/
|
|
20
|
+
hover?: MarqueeHover;
|
|
17
21
|
reverse?: boolean;
|
|
18
22
|
/** Fade both ends so items enter and leave instead of being cut. */
|
|
19
23
|
fade?: boolean;
|
|
@@ -24,13 +28,13 @@ export interface MarqueeProps<T> {
|
|
|
24
28
|
export function Marquee<T>({
|
|
25
29
|
items,
|
|
26
30
|
speed = 70,
|
|
27
|
-
|
|
31
|
+
hover = "off",
|
|
28
32
|
reverse = false,
|
|
29
33
|
fade = true,
|
|
30
34
|
className = "",
|
|
31
35
|
children
|
|
32
36
|
}: MarqueeProps<T>) {
|
|
33
|
-
const { laneRef, trackRef, copies, dragging } = useMarquee({ speed,
|
|
37
|
+
const { laneRef, trackRef, copies, dragging } = useMarquee({ speed, hover, reverse });
|
|
34
38
|
|
|
35
39
|
return (
|
|
36
40
|
<div
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode, Ref } from "react";
|
|
2
|
-
import { useMarquee } from "@enigmax/primitives/react";
|
|
2
|
+
import { useMarquee, type MarqueeHover } from "@enigmax/primitives/react";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* A styled marquee, yours to edit.
|
|
@@ -16,8 +16,12 @@ export interface MarqueeProps<T> {
|
|
|
16
16
|
items: T[];
|
|
17
17
|
/** Pixels per second. Never a duration - the row would speed up as items are added. */
|
|
18
18
|
speed?: number;
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* What a mouse resting on the row does to its speed:
|
|
21
|
+
* "off" ignores hover, "pause" stops, a number multiplies the cruise speed
|
|
22
|
+
* (0.15 crawls, 2 doubles), { speed } sets an absolute px/s.
|
|
23
|
+
*/
|
|
24
|
+
hover?: MarqueeHover;
|
|
21
25
|
reverse?: boolean;
|
|
22
26
|
/** Fade both ends so items enter and leave instead of being cut. */
|
|
23
27
|
fade?: boolean;
|
|
@@ -28,13 +32,13 @@ export interface MarqueeProps<T> {
|
|
|
28
32
|
export function Marquee<T>({
|
|
29
33
|
items,
|
|
30
34
|
speed = 70,
|
|
31
|
-
|
|
35
|
+
hover = "off",
|
|
32
36
|
reverse = false,
|
|
33
37
|
fade = true,
|
|
34
38
|
className = "",
|
|
35
39
|
children
|
|
36
40
|
}: MarqueeProps<T>) {
|
|
37
|
-
const { laneRef, trackRef, copies, dragging } = useMarquee({ speed,
|
|
41
|
+
const { laneRef, trackRef, copies, dragging } = useMarquee({ speed, hover, reverse });
|
|
38
42
|
|
|
39
43
|
return (
|
|
40
44
|
<div
|
package/src/core/marquee.ts
CHANGED
|
@@ -29,6 +29,20 @@ const TOUCH_HOVER_SUPPRESS_MS = 1000;
|
|
|
29
29
|
/** Below this the row is idle and the frame loop can stop. */
|
|
30
30
|
const IDLE_EPSILON = 0.01;
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* What a MOUSE resting on the row does to its speed. One option covers every case, so
|
|
34
|
+
* there is no pile of booleans to reconcile:
|
|
35
|
+
*
|
|
36
|
+
* - `"off"` ignore hover entirely (the default)
|
|
37
|
+
* - `"pause"` stop while the pointer is on it
|
|
38
|
+
* - a number multiply the cruise speed: `0.15` crawls, `1` changes nothing, `2` doubles
|
|
39
|
+
* - `{ speed }` an ABSOLUTE speed in px/s, whatever the cruise is; direction still
|
|
40
|
+
* follows `reverse`, so you give a magnitude and not a sign
|
|
41
|
+
*
|
|
42
|
+
* Touch never triggers any of it - see the pointerType note on the hover handler.
|
|
43
|
+
*/
|
|
44
|
+
export type MarqueeHover = number | "off" | "pause" | { speed: number; };
|
|
45
|
+
|
|
32
46
|
export interface MarqueeOptions {
|
|
33
47
|
/**
|
|
34
48
|
* Pixels per second. NOT a duration: a duration ties the speed to the item
|
|
@@ -41,8 +55,8 @@ export interface MarqueeOptions {
|
|
|
41
55
|
vertical?: boolean;
|
|
42
56
|
/** Allow grabbing and throwing the row. */
|
|
43
57
|
draggable?: boolean;
|
|
44
|
-
/**
|
|
45
|
-
|
|
58
|
+
/** What a mouse resting on the row does to its speed. See {@link MarqueeHover}. */
|
|
59
|
+
hover?: MarqueeHover;
|
|
46
60
|
/** Fraction of the remaining velocity gap left after one second. */
|
|
47
61
|
decay?: number;
|
|
48
62
|
/**
|
|
@@ -94,7 +108,7 @@ const DEFAULTS: ResolvedOptions = {
|
|
|
94
108
|
reverse: false,
|
|
95
109
|
vertical: false,
|
|
96
110
|
draggable: true,
|
|
97
|
-
|
|
111
|
+
hover: "off",
|
|
98
112
|
decay: DEFAULT_DECAY,
|
|
99
113
|
manageStyles: true,
|
|
100
114
|
copies: "clone"
|
|
@@ -134,11 +148,21 @@ export function createMarquee(lane: HTMLElement, track: HTMLElement, options: Ma
|
|
|
134
148
|
: null;
|
|
135
149
|
let reducedMotion = motionQuery?.matches ?? false;
|
|
136
150
|
|
|
151
|
+
/** The speed a mouse on the row asks for. One option, four shapes, no flags to reconcile. */
|
|
152
|
+
function hoverTarget(cruise: number): number {
|
|
153
|
+
const hover = opts.hover ?? "off";
|
|
154
|
+
if (hover === "off") return cruise;
|
|
155
|
+
if (hover === "pause") return 0;
|
|
156
|
+
if (typeof hover === "number") return cruise * hover;
|
|
157
|
+
// An absolute speed is a magnitude: the row keeps travelling the way it was.
|
|
158
|
+
return (cruise < 0 ? -1 : 1) * Math.abs(hover.speed);
|
|
159
|
+
}
|
|
160
|
+
|
|
137
161
|
/** Read live every frame, never captured: cruise, hover and reduced motion all ease. */
|
|
138
162
|
function target(): number {
|
|
139
163
|
if (reducedMotion || paused) return 0;
|
|
140
164
|
const cruise = opts.reverse ? -opts.speed : opts.speed;
|
|
141
|
-
return hovering ? cruise
|
|
165
|
+
return hovering ? hoverTarget(cruise) : cruise;
|
|
142
166
|
}
|
|
143
167
|
|
|
144
168
|
function axisSize(element: HTMLElement): number {
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { createMarquee, type MarqueeOptions, type MarqueeInstance } from "@/core/marquee";
|
|
1
|
+
export { createMarquee, type MarqueeOptions, type MarqueeInstance, type MarqueeHover } from "@/core/marquee";
|
|
2
2
|
export { createInput, type InputOptions, type InputInstance, type InputAction, type InputIcon, type InputActionState } from "@/core/input";
|
|
3
3
|
export { createSearch, type SearchOptions, type SearchInstance, type SearchMatch, type FuseConstructor, type FuseLike } from "@/core/search";
|
package/src/react/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { useMarquee, type UseMarqueeResult } from "@/react/use-marquee";
|
|
2
|
-
export { type MarqueeOptions, type MarqueeInstance } from "@/core/marquee";
|
|
2
|
+
export { type MarqueeOptions, type MarqueeInstance, type MarqueeHover } from "@/core/marquee";
|
|
3
3
|
export { useInput, type UseInputResult } from "@/react/use-input";
|
|
4
4
|
export { useSearch, type UseSearchResult } from "@/react/use-search";
|
|
5
5
|
export { type InputOptions, type InputAction, type InputIcon } from "@/core/input";
|
package/src/react/use-marquee.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface UseMarqueeResult {
|
|
|
29
29
|
* to the item count, so the row accelerates every time content is added.
|
|
30
30
|
*
|
|
31
31
|
* ```tsx
|
|
32
|
-
* const { laneRef, trackRef, copies } = useMarquee({ speed: 80,
|
|
32
|
+
* const { laneRef, trackRef, copies } = useMarquee({ speed: 80, hover: 0.15 });
|
|
33
33
|
* return (
|
|
34
34
|
* <div ref={laneRef}>
|
|
35
35
|
* <div ref={trackRef} style={{ display: "flex" }}>
|
|
@@ -87,7 +87,7 @@ export function useMarquee(options: MarqueeOptions = {}): UseMarqueeResult {
|
|
|
87
87
|
|
|
88
88
|
useIsomorphicLayoutEffect(() => {
|
|
89
89
|
instanceRef.current?.update(options);
|
|
90
|
-
}, [options.speed, options.reverse, options.vertical, options.draggable, options.
|
|
90
|
+
}, [options.speed, options.reverse, options.vertical, options.draggable, options.hover, options.decay]);
|
|
91
91
|
|
|
92
92
|
// A copy count change means new children; the lap has to be read again.
|
|
93
93
|
useIsomorphicLayoutEffect(() => {
|