UNPKG

@aidenlx/player

Version:

Headless web components that make integrating media on the a web a breeze.

204 lines (202 loc) 5.99 kB
import { __decorateClass } from "../../chunks/chunk.LNH2V2XS.js"; import { deferredPromise, DisposalBin, isMouseEvent, isPointerEvent, isTouchEvent, listen, vdsEvent } from "@vidstack/foundation"; import { css, LitElement } from "lit"; import { property } from "lit/decorators.js"; import { mediaContainerContext, mediaStoreContext } from "../../media"; const pendingActions = /* @__PURE__ */ new Map(); class GestureElement extends LitElement { constructor() { super(...arguments); this._disposal = new DisposalBin(); this._mediaContainerConsumer = mediaContainerContext.consume(this); this.repeat = 0; this.priority = 10; this._mediaCurrentTime = 0; this._currentToggleState = false; this._mediaStoreConsumer = mediaStoreContext.consume(this); } static get styles() { return [ css` :host { display: block; contain: content; z-index: 0; opacity: 0; visibility: hidden; pointer-events: none !important; } :host([hidden]) { display: none; } ` ]; } get _mediaContainer() { return this._mediaContainerConsumer.value.value; } get _pendingActions() { return this._mediaContainer ? pendingActions.get(this._mediaContainer) : void 0; } get _pendingAction() { return this._pendingActions?.get(this); } connectedCallback() { super.connectedCallback(); window.requestAnimationFrame(() => { if (this._mediaContainer) { pendingActions.set(this._mediaContainer, /* @__PURE__ */ new Map()); } }); } willUpdate(changedProperties) { this._attachListener(); this._subscribeToToggleStore(); this._subscribeToSeekStore(); super.willUpdate(changedProperties); } disconnectedCallback() { this._disposal.empty(); this._pendingAction?.[1].resolve(); this._pendingActions?.delete(this); super.disconnectedCallback(); } performAction(event) { if (!this.action) return; let detail; let eventType = this.action; if (this.action.startsWith("toggle:")) { eventType = this._getToggleEventType(); } if (this.action.startsWith("seek:")) { eventType = "seek"; detail = this._mediaCurrentTime + Number(this.action.split(":")[1]); } this.dispatchEvent(vdsEvent(`vds-${eventType}-request`, { bubbles: true, composed: true, detail, triggerEvent: event })); } _attachListener() { this._disposal.empty(); if (!this._mediaContainer || !this.type || !this.action) return; let count = 0; let timeoutId; const resolve = (skip = false) => { count += 1; window.clearTimeout(timeoutId); timeoutId = window.setTimeout(() => { const promise = this._pendingAction?.[1]; if (skip) { this._pendingActions?.delete(this); } processPendingActions(this._mediaContainer); count = 0; promise?.resolve(); }, 250); }; const off = listen(this._mediaContainer, this.type, (event) => { if (!this._validateEvent(event)) return; event.preventDefault(); if (count == 0) { this._pendingActions?.set(this, [event, deferredPromise()]); } resolve(count < this.repeat); }); this._disposal.add(off); } _validateEvent(event) { if (isPointerEvent(event) || isMouseEvent(event) || isTouchEvent(event)) { const touch = isTouchEvent(event) ? event.touches[0] : void 0; const clientX = touch?.clientX ?? event.clientX; const clientY = touch?.clientY ?? event.clientY; const rect = this.getBoundingClientRect(); const isValidRegion = clientY >= rect.top && clientY <= rect.bottom && clientX >= rect.left && clientX <= rect.right; return event.type.includes("leave") ? !isValidRegion : isValidRegion; } return true; } _subscribeToSeekStore() { if (!this.action?.startsWith("seek")) return; const unsub = this._mediaStore.currentTime.subscribe(($currentTime) => { this._mediaCurrentTime = $currentTime; }); this._disposal.add(unsub); } get _mediaStore() { return this._mediaStoreConsumer.value; } _getToggleEventType() { const toggleType = this.action?.split(":")[1]; switch (toggleType) { case "paused": return this._currentToggleState ? "play" : "pause"; case "muted": return this._currentToggleState ? "unmute" : "mute"; case "fullscreen": return `${this._currentToggleState ? "exit" : "enter"}-fullscreen`; default: return ""; } } _subscribeToToggleStore() { if (!this.action?.startsWith("toggle:")) return; const action = this.action.split(":")[1]; const unsub = this._mediaStore[action]?.subscribe(($value) => { this._currentToggleState = $value; }); if (unsub) this._disposal.add(unsub); } } __decorateClass([ property() ], GestureElement.prototype, "type", 2); __decorateClass([ property({ type: Number }) ], GestureElement.prototype, "repeat", 2); __decorateClass([ property({ type: Number }) ], GestureElement.prototype, "priority", 2); __decorateClass([ property() ], GestureElement.prototype, "action", 2); const inProgress = /* @__PURE__ */ new WeakSet(); async function processPendingActions(container) { if (inProgress.has(container)) return; const actions = pendingActions.get(container); if (!actions) return; inProgress.add(container); const wait = Array.from(actions.values()).map((action) => action[1].promise); await Promise.all(wait); const gestures = Array.from(actions.keys()); const highestPriority = Math.min(...gestures.map((g) => g.priority)); gestures.filter((g) => g.priority <= highestPriority).map((g) => { const event = actions.get(g)[0]; g.performAction(event); }); actions.clear(); inProgress.delete(container); } export { GestureElement };