@banou/media-player 0.10.19 → 0.10.20
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/index.js
CHANGED
|
@@ -2351,6 +2351,7 @@ ${gt}
|
|
|
2351
2351
|
e = Date.now() + zt;
|
|
2352
2352
|
};
|
|
2353
2353
|
}, [M]), d(() => () => clearInterval(S.current), []), /* @__PURE__ */ E("div", {
|
|
2354
|
+
className: "control-bar",
|
|
2354
2355
|
css: Bt,
|
|
2355
2356
|
style: { ...c ? {
|
|
2356
2357
|
opacity: "0",
|
|
@@ -2681,47 +2682,51 @@ ${gt}
|
|
|
2681
2682
|
let s = W(), c = W((e) => e.hideUI), l = W((e) => e.setHideUI), u = W((e) => e.hasMedia), f = W((e) => e.passThroughPictureInPicture), m = p(void 0), h = p("mouse"), g = p({
|
|
2682
2683
|
x: -1,
|
|
2683
2684
|
y: -1
|
|
2684
|
-
}), _ = p(null);
|
|
2685
|
+
}), _ = p(!1), v = p(null);
|
|
2685
2686
|
d(() => () => clearTimeout(m.current), []), d(() => {
|
|
2686
2687
|
u || (clearTimeout(m.current), l(!1));
|
|
2687
2688
|
}, [u, l]);
|
|
2688
|
-
let
|
|
2689
|
+
let y = () => {
|
|
2689
2690
|
u && l(!0);
|
|
2690
|
-
},
|
|
2691
|
-
|
|
2692
|
-
},
|
|
2691
|
+
}, b = (t) => {
|
|
2692
|
+
v.current = t, typeof e == "function" ? e(t) : e && (e.current = t);
|
|
2693
|
+
}, x = () => {
|
|
2693
2694
|
if (s.passThroughPictureInPicture?.armed) return !0;
|
|
2694
2695
|
if (h.current !== "mouse") return !1;
|
|
2695
2696
|
let { x: e, y: t } = g.current;
|
|
2696
|
-
if (!
|
|
2697
|
+
if (!v.current || e < 0) return !1;
|
|
2697
2698
|
let n = document.elementFromPoint(e, t);
|
|
2698
|
-
return
|
|
2699
|
-
},
|
|
2700
|
-
if (
|
|
2701
|
-
m.current = setTimeout(
|
|
2699
|
+
return !n || !v.current.contains(n) || n.closest(".video") ? !1 : !n.closest(".control-bar") || _.current || !!v.current.querySelector(".control-bar [aria-expanded=\"true\"]");
|
|
2700
|
+
}, S = () => {
|
|
2701
|
+
if (x()) {
|
|
2702
|
+
m.current = setTimeout(S, Zt);
|
|
2702
2703
|
return;
|
|
2703
2704
|
}
|
|
2704
|
-
|
|
2705
|
-
},
|
|
2706
|
-
l(!1), clearTimeout(m.current), m.current = setTimeout(
|
|
2707
|
-
}
|
|
2705
|
+
y();
|
|
2706
|
+
}, C = () => {
|
|
2707
|
+
l(!1), clearTimeout(m.current), m.current = setTimeout(S, Zt);
|
|
2708
|
+
}, w = (e) => {
|
|
2709
|
+
h.current = e.pointerType, e.pointerType === "mouse" && (g.current = {
|
|
2710
|
+
x: e.clientX,
|
|
2711
|
+
y: e.clientY
|
|
2712
|
+
}, C());
|
|
2713
|
+
}, D = (e) => {
|
|
2714
|
+
h.current = e.pointerType, _.current = !0;
|
|
2715
|
+
}, O = () => {
|
|
2716
|
+
_.current = !1;
|
|
2717
|
+
}, k = p(!0);
|
|
2708
2718
|
return /* @__PURE__ */ E("div", {
|
|
2709
2719
|
css: en,
|
|
2710
|
-
ref:
|
|
2711
|
-
onPointerMove:
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
}, S());
|
|
2716
|
-
},
|
|
2717
|
-
onPointerDown: (e) => {
|
|
2718
|
-
h.current = e.pointerType;
|
|
2719
|
-
},
|
|
2720
|
+
ref: b,
|
|
2721
|
+
onPointerMove: w,
|
|
2722
|
+
onPointerDown: D,
|
|
2723
|
+
onPointerUp: O,
|
|
2724
|
+
onPointerCancel: O,
|
|
2720
2725
|
onMouseOut: (e) => {
|
|
2721
2726
|
let t = e.relatedTarget;
|
|
2722
2727
|
if (t && e.currentTarget.contains(t)) return;
|
|
2723
2728
|
let { left: n, right: r, top: i, bottom: a } = e.currentTarget.getBoundingClientRect();
|
|
2724
|
-
e.clientX >= n && e.clientX < r && e.clientY >= i && e.clientY < a || (clearTimeout(m.current),
|
|
2729
|
+
e.clientX >= n && e.clientX < r && e.clientY >= i && e.clientY < a || (clearTimeout(m.current), y());
|
|
2725
2730
|
},
|
|
2726
2731
|
className: c ? "hide" : "",
|
|
2727
2732
|
tabIndex: a === "player" ? -1 : void 0,
|
|
@@ -2738,23 +2743,27 @@ ${gt}
|
|
|
2738
2743
|
}, e)),
|
|
2739
2744
|
i === !1 ? null : /* @__PURE__ */ T(Vt, {
|
|
2740
2745
|
shortcuts: a,
|
|
2741
|
-
root:
|
|
2746
|
+
root: v
|
|
2742
2747
|
}),
|
|
2743
2748
|
/* @__PURE__ */ T(Wt, {}),
|
|
2744
2749
|
/* @__PURE__ */ T(Xt, {}),
|
|
2745
|
-
f?.armed &&
|
|
2750
|
+
f?.armed && v.current ? /* @__PURE__ */ T("div", {
|
|
2746
2751
|
className: "pass-through",
|
|
2747
|
-
style: { clipPath: $t(f.armed,
|
|
2752
|
+
style: { clipPath: $t(f.armed, v.current.getBoundingClientRect()) },
|
|
2748
2753
|
onPointerOver: f.disarm
|
|
2749
2754
|
}) : null,
|
|
2750
2755
|
/* @__PURE__ */ E("div", {
|
|
2751
2756
|
className: "video",
|
|
2752
|
-
onClick: () => {
|
|
2757
|
+
onClick: (e) => {
|
|
2753
2758
|
if (h.current === "mouse") {
|
|
2754
|
-
|
|
2759
|
+
if (e.detail > 1) return;
|
|
2760
|
+
k.current = s.paused, s.togglePaused();
|
|
2755
2761
|
return;
|
|
2756
2762
|
}
|
|
2757
|
-
c ?
|
|
2763
|
+
c ? C() : (clearTimeout(m.current), y());
|
|
2764
|
+
},
|
|
2765
|
+
onDoubleClick: () => {
|
|
2766
|
+
h.current === "mouse" && (k.current ? s.pause() : s.play().catch(() => {}), s.toggleFullscreen());
|
|
2758
2767
|
},
|
|
2759
2768
|
children: [t ? /* @__PURE__ */ T("video", {
|
|
2760
2769
|
ref: t,
|
package/package.json
CHANGED
|
@@ -147,6 +147,8 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
147
147
|
const lastPointerType = useRef<string>('mouse')
|
|
148
148
|
// where the mouse last was, which is where it still is when nothing has moved
|
|
149
149
|
const pointer = useRef({ x: -1, y: -1 })
|
|
150
|
+
// a mouse button held down with the mouse still is a drag on the seekbar or the volume, not yet over
|
|
151
|
+
const pressed = useRef(false)
|
|
150
152
|
const root = useRef<HTMLDivElement | null>(null)
|
|
151
153
|
|
|
152
154
|
useEffect(() => () => clearTimeout(autoHide.current), [])
|
|
@@ -189,9 +191,10 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
189
191
|
* and a right click offers the browser's video menu rather than the control's. That is how a link
|
|
190
192
|
* drawn in an overlay stops behaving like a link, which is what this exists to stop.
|
|
191
193
|
*
|
|
192
|
-
* Only what actually takes pointer events can be hit
|
|
193
|
-
*
|
|
194
|
-
*
|
|
194
|
+
* Only what actually takes pointer events can be hit: the chrome's own layers opt out, and an
|
|
195
|
+
* overlay item opts out until an app opts a child back in. The picture and the control bar are
|
|
196
|
+
* excluded here because resting on them is exactly when hiding is right, unless a button is held
|
|
197
|
+
* on the bar or one of its menus is open, which are still in use.
|
|
195
198
|
*
|
|
196
199
|
* The mouse only. A finger has no resting position, and `elementFromPoint` on the last tap would
|
|
197
200
|
* keep the controls up forever after one press.
|
|
@@ -205,7 +208,9 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
205
208
|
const { x, y } = pointer.current
|
|
206
209
|
if (!root.current || x < 0) return false
|
|
207
210
|
const hit = document.elementFromPoint(x, y)
|
|
208
|
-
|
|
211
|
+
if (!hit || !root.current.contains(hit) || hit.closest('.video')) return false
|
|
212
|
+
if (hit.closest('.control-bar')) return pressed.current || !!root.current.querySelector('.control-bar [aria-expanded="true"]')
|
|
213
|
+
return true
|
|
209
214
|
}
|
|
210
215
|
|
|
211
216
|
const hideUnlessTheMouseIsOnAControl = () => {
|
|
@@ -233,11 +238,20 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
233
238
|
|
|
234
239
|
const onPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {
|
|
235
240
|
lastPointerType.current = event.pointerType
|
|
241
|
+
pressed.current = true
|
|
236
242
|
}
|
|
237
243
|
|
|
244
|
+
const onPointerUp = () => {
|
|
245
|
+
pressed.current = false
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const pausedBeforeClick = useRef(true)
|
|
249
|
+
|
|
238
250
|
// play/pause with a mouse, show or hide the controls with a finger, which has no other way back
|
|
239
|
-
const onVideoClick = () => {
|
|
251
|
+
const onVideoClick = (event: React.MouseEvent) => {
|
|
240
252
|
if (lastPointerType.current === 'mouse') {
|
|
253
|
+
if (event.detail > 1) return
|
|
254
|
+
pausedBeforeClick.current = player.paused
|
|
241
255
|
player.togglePaused()
|
|
242
256
|
return
|
|
243
257
|
}
|
|
@@ -248,6 +262,15 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
248
262
|
}
|
|
249
263
|
}
|
|
250
264
|
|
|
265
|
+
// The first click toggled playback, so it is put back rather than toggled again: a remote media
|
|
266
|
+
// reports its pause late, and a second toggle would read it as still playing and pause it twice.
|
|
267
|
+
const onVideoDoubleClick = () => {
|
|
268
|
+
if (lastPointerType.current !== 'mouse') return
|
|
269
|
+
if (pausedBeforeClick.current) player.pause()
|
|
270
|
+
else void player.play().catch(() => {})
|
|
271
|
+
void player.toggleFullscreen()
|
|
272
|
+
}
|
|
273
|
+
|
|
251
274
|
// relatedTarget alone is not enough: the browser fires mouseout with a null one while the pointer
|
|
252
275
|
// still sits over the video, so a null is confirmed against the pointer's own coordinates.
|
|
253
276
|
const onMouseOut: React.DOMAttributes<HTMLDivElement>['onMouseOut'] = (ev) => {
|
|
@@ -266,6 +289,8 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
266
289
|
ref={setRoot}
|
|
267
290
|
onPointerMove={onPointerMove}
|
|
268
291
|
onPointerDown={onPointerDown}
|
|
292
|
+
onPointerUp={onPointerUp}
|
|
293
|
+
onPointerCancel={onPointerUp}
|
|
269
294
|
onMouseOut={onMouseOut}
|
|
270
295
|
className={hideUI ? 'hide' : ''}
|
|
271
296
|
// focusable, so a click on the picture puts the focus, and with it the shortcuts, inside the player
|
|
@@ -294,7 +319,7 @@ export const Chrome = ({ ref, onVideoRef, onSubtitleRef, overlay, controls, shor
|
|
|
294
319
|
/>
|
|
295
320
|
)
|
|
296
321
|
: null}
|
|
297
|
-
<div className="video" onClick={onVideoClick}>
|
|
322
|
+
<div className="video" onClick={onVideoClick} onDoubleClick={onVideoDoubleClick}>
|
|
298
323
|
{onVideoRef ? <video ref={onVideoRef} playsInline /> : null}
|
|
299
324
|
{children}
|
|
300
325
|
</div>
|
|
@@ -308,6 +308,7 @@ export const ControlBar = (
|
|
|
308
308
|
// Armed, the whole bar takes no pointer events, or the rows around the control would still catch
|
|
309
309
|
// the click at its box. The chrome's layer covers everything but the control meanwhile.
|
|
310
310
|
<div
|
|
311
|
+
className='control-bar'
|
|
311
312
|
css={style}
|
|
312
313
|
style={{ ...hideUI ? { opacity: '0', pointerEvents: 'none' } : armed ? { pointerEvents: 'none' } : {} }}
|
|
313
314
|
>
|