@scarlett-player/gestures 1.8.0 → 1.9.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 +80 -0
- package/dist/index.cjs +12 -9
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +12 -9
- package/package.json +3 -3
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# @scarlett-player/gestures
|
|
2
|
+
|
|
3
|
+
Touch gestures for [Scarlett Player](https://scarlettplayer.com). Double-tap the right of the picture to jump forward, the left to jump back, keep tapping to go further, and single-tap to toggle the controls. Gestures are gated on `pointerType === 'touch'`, so a mouse or pen never triggers any of them and desktop behaviour is unchanged.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @scarlett-player/core @scarlett-player/gestures
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`@scarlett-player/core` is a peer dependency. `@scarlett-player/ui` is optional: tap-to-toggle needs it, double-tap seeking does not.
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createPlayer } from '@scarlett-player/core';
|
|
17
|
+
import { uiPlugin } from '@scarlett-player/ui';
|
|
18
|
+
import { createGesturesPlugin } from '@scarlett-player/gestures';
|
|
19
|
+
|
|
20
|
+
const player = await createPlayer({
|
|
21
|
+
container: '#player',
|
|
22
|
+
src: 'https://example.com/video.m3u8',
|
|
23
|
+
plugins: [uiPlugin(), createGesturesPlugin({ seekSeconds: 10 })],
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
| Option | Type | Default | Description |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `enabled` | `boolean \| 'auto'` | `'auto'` | `'auto'` installs the gesture surface when `matchMedia('(any-pointer: coarse)')` matches, so a touchscreen laptop counts. `true` forces it, `false` disables the plugin. No user agent sniffing |
|
|
32
|
+
| `seekSeconds` | `number` | `10` | Seconds moved per seek step |
|
|
33
|
+
| `doubleTapWindowMs` | `number` | `275` | Second tap within this window makes a double tap |
|
|
34
|
+
| `accumulationWindowMs` | `number` | `650` | A further tap in the same zone within this window extends the seek |
|
|
35
|
+
| `zones` | `{ left?, right? }` | `{ left: 0.33, right: 0.33 }` | Fractions of the width given to each seek zone. The middle is inert on purpose |
|
|
36
|
+
| `slopPx` | `number` | `10` | Movement in CSS pixels that turns a tap into a drag and cancels it |
|
|
37
|
+
| `feedback` | `boolean` | `true` | Show the zone highlight and cumulative seconds label |
|
|
38
|
+
| `haptics` | `boolean` | `true` | `navigator.vibrate(10)` on each seek step where supported. iOS Safari ignores it |
|
|
39
|
+
| `tapToToggleControls` | `boolean` | `true` | Let a single tap show or hide the controls |
|
|
40
|
+
|
|
41
|
+
## Behaviour
|
|
42
|
+
|
|
43
|
+
- Not installed for audio: when `mediaType` is `'audio'` at init the plugin stays inactive.
|
|
44
|
+
- Seeking is refused while Chromecast or AirPlay is active, on live streams with no seekable range, and on VOD until a finite duration is known.
|
|
45
|
+
- On live DVR a seek is clamped to the seekable range. A forward tap at the live edge announces "Already at the live edge" instead of showing a ripple.
|
|
46
|
+
- Each tap in a run adds `seekSeconds`; the label shows the cumulative total. A tap in the opposite zone ends the run rather than reversing it, and a drag or a second finger cancels it.
|
|
47
|
+
- A single tap shows hidden controls immediately. Hiding waits out `doubleTapWindowMs` so a seek is never preceded by the controls blinking away, and nothing hides while paused.
|
|
48
|
+
- The surface never calls `preventDefault` or `stopPropagation`, so document-level click handlers (menus closing, for example) still fire.
|
|
49
|
+
|
|
50
|
+
The plugin exposes `ownsTapInteraction()`, which the UI package checks before running its own show-controls logic on touch. It returns `true` while the plugin is active and `tapToToggleControls` is on.
|
|
51
|
+
|
|
52
|
+
## Events
|
|
53
|
+
|
|
54
|
+
| Event | Payload | When |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `gesture:tap` | `{ zone }` | A single tap landed. `zone` is `'left'`, `'middle'` or `'right'` |
|
|
57
|
+
| `gesture:seek` | `{ direction, seconds, cumulative }` | A seek step moved the playhead. `direction` is `'forward'` or `'backward'` |
|
|
58
|
+
|
|
59
|
+
Each seek step also emits the core `playback:seeking` event with the target time.
|
|
60
|
+
|
|
61
|
+
## CSS
|
|
62
|
+
|
|
63
|
+
Styles are injected once per document in a `<style id="sp-gestures-styles">`. There are no CSS custom properties.
|
|
64
|
+
|
|
65
|
+
| Class | Element |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `.sp-gestures` | The surface. Absolutely positioned at `z-index: 6`, leaving the bottom 64px to the progress and control bars |
|
|
68
|
+
| `.sp-gestures__zone` | One seek zone, `--left` or `--right`, sized from `zones`. `--active` while feedback is showing |
|
|
69
|
+
| `.sp-gestures__label` | The cumulative seconds label inside a zone |
|
|
70
|
+
| `.sp-gestures__live` | Visually hidden polite live region carrying the announcement |
|
|
71
|
+
|
|
72
|
+
The zone transition is disabled under `prefers-reduced-motion: reduce`.
|
|
73
|
+
|
|
74
|
+
## Lower level exports
|
|
75
|
+
|
|
76
|
+
`createRecognizer`, `zoneFor` and `DEFAULT_RECOGNIZER_OPTIONS` expose the pure tap state machine, and `GestureOverlay` the DOM surface, for hosts that want to build their own handling on top.
|
|
77
|
+
|
|
78
|
+
## License
|
|
79
|
+
|
|
80
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -178,6 +178,7 @@ function createRecognizer(options = {}) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
// src/overlay.ts
|
|
181
|
+
var import_core = require("@scarlett-player/core");
|
|
181
182
|
var STYLE_ID = "sp-gestures-styles";
|
|
182
183
|
var styles = `
|
|
183
184
|
.sp-gestures {
|
|
@@ -250,7 +251,7 @@ var GestureOverlay = class {
|
|
|
250
251
|
constructor(container, options) {
|
|
251
252
|
this.container = container;
|
|
252
253
|
this.options = options;
|
|
253
|
-
this.
|
|
254
|
+
this.releaseStyles = null;
|
|
254
255
|
this.hideTimer = null;
|
|
255
256
|
this.pointerHandler = (event) => {
|
|
256
257
|
if (event.pointerType !== "touch") return;
|
|
@@ -325,8 +326,8 @@ var GestureOverlay = class {
|
|
|
325
326
|
this.el.removeEventListener("pointerup", this.pointerHandler);
|
|
326
327
|
this.el.removeEventListener("pointercancel", this.pointerHandler);
|
|
327
328
|
this.el.remove();
|
|
328
|
-
this.
|
|
329
|
-
this.
|
|
329
|
+
this.releaseStyles?.();
|
|
330
|
+
this.releaseStyles = null;
|
|
330
331
|
}
|
|
331
332
|
/** Exposed for tests and for hosts that want to inspect the surface. */
|
|
332
333
|
getElement() {
|
|
@@ -341,17 +342,19 @@ var GestureOverlay = class {
|
|
|
341
342
|
zone.appendChild(label);
|
|
342
343
|
return { zone, label };
|
|
343
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Claim the shared gesture stylesheet.
|
|
347
|
+
*
|
|
348
|
+
* Reference-counted: two players on one page share the sheet, and destroying
|
|
349
|
+
* either used to take it away from the other.
|
|
350
|
+
*/
|
|
344
351
|
injectStyles() {
|
|
345
|
-
|
|
346
|
-
this.styleEl = document.createElement("style");
|
|
347
|
-
this.styleEl.id = STYLE_ID;
|
|
348
|
-
this.styleEl.textContent = styles;
|
|
349
|
-
document.head.appendChild(this.styleEl);
|
|
352
|
+
this.releaseStyles = (0, import_core.injectSharedStyles)(STYLE_ID, styles);
|
|
350
353
|
}
|
|
351
354
|
};
|
|
352
355
|
|
|
353
356
|
// src/version.ts
|
|
354
|
-
var PKG_VERSION = true ? "1.
|
|
357
|
+
var PKG_VERSION = true ? "1.9.0" : "0.0.0-dev";
|
|
355
358
|
|
|
356
359
|
// src/index.ts
|
|
357
360
|
function hasCoarsePointer() {
|
package/dist/index.d.cts
CHANGED
|
@@ -201,7 +201,7 @@ declare class GestureOverlay {
|
|
|
201
201
|
private zones;
|
|
202
202
|
private labels;
|
|
203
203
|
private live;
|
|
204
|
-
private
|
|
204
|
+
private releaseStyles;
|
|
205
205
|
private hideTimer;
|
|
206
206
|
private readonly pointerHandler;
|
|
207
207
|
constructor(container: HTMLElement, options: OverlayOptions);
|
|
@@ -220,6 +220,12 @@ declare class GestureOverlay {
|
|
|
220
220
|
/** Exposed for tests and for hosts that want to inspect the surface. */
|
|
221
221
|
getElement(): HTMLElement;
|
|
222
222
|
private createZone;
|
|
223
|
+
/**
|
|
224
|
+
* Claim the shared gesture stylesheet.
|
|
225
|
+
*
|
|
226
|
+
* Reference-counted: two players on one page share the sheet, and destroying
|
|
227
|
+
* either used to take it away from the other.
|
|
228
|
+
*/
|
|
223
229
|
private injectStyles;
|
|
224
230
|
}
|
|
225
231
|
|
package/dist/index.d.ts
CHANGED
|
@@ -201,7 +201,7 @@ declare class GestureOverlay {
|
|
|
201
201
|
private zones;
|
|
202
202
|
private labels;
|
|
203
203
|
private live;
|
|
204
|
-
private
|
|
204
|
+
private releaseStyles;
|
|
205
205
|
private hideTimer;
|
|
206
206
|
private readonly pointerHandler;
|
|
207
207
|
constructor(container: HTMLElement, options: OverlayOptions);
|
|
@@ -220,6 +220,12 @@ declare class GestureOverlay {
|
|
|
220
220
|
/** Exposed for tests and for hosts that want to inspect the surface. */
|
|
221
221
|
getElement(): HTMLElement;
|
|
222
222
|
private createZone;
|
|
223
|
+
/**
|
|
224
|
+
* Claim the shared gesture stylesheet.
|
|
225
|
+
*
|
|
226
|
+
* Reference-counted: two players on one page share the sheet, and destroying
|
|
227
|
+
* either used to take it away from the other.
|
|
228
|
+
*/
|
|
223
229
|
private injectStyles;
|
|
224
230
|
}
|
|
225
231
|
|
package/dist/index.js
CHANGED
|
@@ -147,6 +147,7 @@ function createRecognizer(options = {}) {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
// src/overlay.ts
|
|
150
|
+
import { injectSharedStyles } from "@scarlett-player/core";
|
|
150
151
|
var STYLE_ID = "sp-gestures-styles";
|
|
151
152
|
var styles = `
|
|
152
153
|
.sp-gestures {
|
|
@@ -219,7 +220,7 @@ var GestureOverlay = class {
|
|
|
219
220
|
constructor(container, options) {
|
|
220
221
|
this.container = container;
|
|
221
222
|
this.options = options;
|
|
222
|
-
this.
|
|
223
|
+
this.releaseStyles = null;
|
|
223
224
|
this.hideTimer = null;
|
|
224
225
|
this.pointerHandler = (event) => {
|
|
225
226
|
if (event.pointerType !== "touch") return;
|
|
@@ -294,8 +295,8 @@ var GestureOverlay = class {
|
|
|
294
295
|
this.el.removeEventListener("pointerup", this.pointerHandler);
|
|
295
296
|
this.el.removeEventListener("pointercancel", this.pointerHandler);
|
|
296
297
|
this.el.remove();
|
|
297
|
-
this.
|
|
298
|
-
this.
|
|
298
|
+
this.releaseStyles?.();
|
|
299
|
+
this.releaseStyles = null;
|
|
299
300
|
}
|
|
300
301
|
/** Exposed for tests and for hosts that want to inspect the surface. */
|
|
301
302
|
getElement() {
|
|
@@ -310,17 +311,19 @@ var GestureOverlay = class {
|
|
|
310
311
|
zone.appendChild(label);
|
|
311
312
|
return { zone, label };
|
|
312
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Claim the shared gesture stylesheet.
|
|
316
|
+
*
|
|
317
|
+
* Reference-counted: two players on one page share the sheet, and destroying
|
|
318
|
+
* either used to take it away from the other.
|
|
319
|
+
*/
|
|
313
320
|
injectStyles() {
|
|
314
|
-
|
|
315
|
-
this.styleEl = document.createElement("style");
|
|
316
|
-
this.styleEl.id = STYLE_ID;
|
|
317
|
-
this.styleEl.textContent = styles;
|
|
318
|
-
document.head.appendChild(this.styleEl);
|
|
321
|
+
this.releaseStyles = injectSharedStyles(STYLE_ID, styles);
|
|
319
322
|
}
|
|
320
323
|
};
|
|
321
324
|
|
|
322
325
|
// src/version.ts
|
|
323
|
-
var PKG_VERSION = true ? "1.
|
|
326
|
+
var PKG_VERSION = true ? "1.9.0" : "0.0.0-dev";
|
|
324
327
|
|
|
325
328
|
// src/index.ts
|
|
326
329
|
function hasCoarsePointer() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scarlett-player/gestures",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Gestures Plugin for Scarlett Player - double-tap seek zones and tap to toggle controls",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"dist"
|
|
23
23
|
],
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@scarlett-player/core": "^1.
|
|
25
|
+
"@scarlett-player/core": "^1.8.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@vitest/coverage-v8": "^1.6.0",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"tsup": "^8.5.1",
|
|
31
31
|
"typescript": "^5.3.0",
|
|
32
32
|
"vitest": "^1.6.0",
|
|
33
|
-
"@scarlett-player/core": "1.
|
|
33
|
+
"@scarlett-player/core": "1.9.0"
|
|
34
34
|
},
|
|
35
35
|
"keywords": [
|
|
36
36
|
"video",
|