@aidenlx/player
Version:
Headless web components that make integrating media on the a web a breeze.
352 lines (351 loc) • 11.8 kB
JavaScript
import {
__decorateClass
} from "../../chunks/chunk.LNH2V2XS.js";
import {
clampNumber,
DisposalBin,
eventListener,
focusVisiblePolyfill,
get,
getNumberOfDecimalPlaces,
rafThrottle,
round,
setAttribute,
setAttributeIfEmpty,
setCSSProperty,
vdsEvent
} from "@vidstack/foundation";
import {
html,
LitElement
} from "lit";
import { property } from "lit/decorators.js";
import { MediaRemoteControl } from "../../media";
import { sliderStoreContext } from "./store";
import { sliderElementStyles } from "./styles";
var SliderKeyDirection = /* @__PURE__ */ ((SliderKeyDirection2) => {
SliderKeyDirection2[SliderKeyDirection2["Left"] = -1] = "Left";
SliderKeyDirection2[SliderKeyDirection2["ArrowLeft"] = -1] = "ArrowLeft";
SliderKeyDirection2[SliderKeyDirection2["Up"] = -1] = "Up";
SliderKeyDirection2[SliderKeyDirection2["ArrowUp"] = -1] = "ArrowUp";
SliderKeyDirection2[SliderKeyDirection2["Right"] = 1] = "Right";
SliderKeyDirection2[SliderKeyDirection2["ArrowRight"] = 1] = "ArrowRight";
SliderKeyDirection2[SliderKeyDirection2["Down"] = 1] = "Down";
SliderKeyDirection2[SliderKeyDirection2["ArrowDown"] = 1] = "ArrowDown";
return SliderKeyDirection2;
})(SliderKeyDirection || {});
class SliderElement extends LitElement {
constructor() {
super();
this._sliderStoreProvider = sliderStoreContext.provide(this);
this.disabled = false;
this.value = 50;
this._step = 1;
this._keyboardStep = 1;
this.shiftKeyMultiplier = 5;
this.customValueText = false;
this._mediaRemote = new MediaRemoteControl(this);
this._disconnectDisposal = new DisposalBin();
this._handlePointerEnter = eventListener(this, "pointerenter", () => {
if (this.disabled)
return;
this.setAttribute("pointing", "");
this.store.pointing.set(true);
});
this._handlePointerMove = eventListener(this, "pointermove", (event) => {
if (this.disabled || this.isDragging)
return;
const value = this._getValueBasedOnThumbPosition(event);
this.store.pointerValue.set(value);
this._dispatchPointerValueChange(event);
});
this._handlePointerLeave = eventListener(this, "pointerleave", () => {
if (this.disabled)
return;
this.removeAttribute("pointing");
this.store.pointing.set(false);
});
this._handlePointerDown = eventListener(this, "pointerdown", (event) => {
if (this.disabled)
return;
this._startDragging(event);
this._onDrag(event);
});
this._handleKeydown = eventListener(this, "keydown", (event) => {
if (this.disabled)
return;
const { key, shiftKey } = event;
const isValidKey = Object.keys(SliderKeyDirection).includes(key);
if (!isValidKey)
return;
const modifiedStep = !shiftKey ? this.keyboardStep : this.keyboardStep * this.shiftKeyMultiplier;
const direction = Number(SliderKeyDirection[key]);
const diff = modifiedStep * direction;
const steps = (this.value + diff) / this.step;
const value = this.step * steps;
this.value = this._getClampedValue(value);
this._dispatchValueChange(event);
});
this._handleFillValueChange = eventListener(this, "vds-slider-value-change", this._updateFillCSSProps.bind(this));
this._handlePointerValueChange = eventListener(this, "vds-slider-pointer-value-change", this._updatePointerCSSProps.bind(this));
this._onDrag = rafThrottle((event) => {
if (this.disabled || !this.isDragging)
return;
const value = this._getValueBasedOnThumbPosition(event);
this.store.pointerValue.set(value);
this._dispatchPointerValueChange(event);
});
this._handleDocumentPointerUp = eventListener(this, "pointerup", (event) => {
if (this.disabled || !this.isDragging)
return;
this._stopDragging(event);
}, { target: document });
this._handleDocumentTouchMove = eventListener(this, "touchmove", (event) => {
if (this.disabled || !this.isDragging)
return;
event.preventDefault();
}, { target: document, passive: false });
this._handleDocumentPointerMove = eventListener(this, "pointermove", (event) => {
if (this.disabled || !this.isDragging)
return;
this._onDrag(event);
}, { target: document });
this._lastDispatchedValue = this.value;
this._dispatchValueChange = rafThrottle((event) => {
if (this.value === this._lastDispatchedValue)
return;
this.dispatchEvent(vdsEvent("vds-slider-value-change", {
detail: this.value,
triggerEvent: event
}));
this._lastDispatchedValue = this.value;
});
this._lastDispatchedPointerValue = this.pointerValue;
this._dispatchPointerValueChange = rafThrottle((event) => {
if (this.pointerValue === this._lastDispatchedPointerValue)
return;
const events = [
"vds-slider-pointer-value-change",
this.isDragging && "vds-slider-drag-value-change"
];
events.forEach((eventType) => {
if (eventType) {
this.dispatchEvent(vdsEvent(eventType, {
detail: this.pointerValue,
triggerEvent: event
}));
}
});
this._lastDispatchedPointerValue = this.pointerValue;
});
if (false)
logElementLifecycle(this);
focusVisiblePolyfill(this);
}
static get styles() {
return [sliderElementStyles];
}
static get parts() {
return [];
}
get store() {
return this._sliderStoreProvider.value;
}
get min() {
return get(this.store.min);
}
set min(newMin) {
this.store.min.set(newMin);
}
get max() {
return get(this.store.max);
}
set max(newMax) {
this.store.max.set(newMax);
}
get step() {
return this._step;
}
set step(newStep) {
this._step = newStep;
}
get keyboardStep() {
return this._keyboardStep;
}
set keyboardStep(newStep) {
this._keyboardStep = newStep;
}
get isDragging() {
return get(this.store.dragging);
}
get fillRate() {
const range = this.max - this.min, offset = this.value - this.min;
return range > 0 ? offset / range : 0;
}
get fillPercent() {
return this.fillRate * 100;
}
get pointerValue() {
return get(this.store.pointerValue);
}
get pointerRate() {
const range = this.max - this.min, offset = this.pointerValue - this.min;
return range > 0 ? offset / range : 0;
}
get pointerPercent() {
return this.pointerRate * 100;
}
connectedCallback() {
super.connectedCallback();
this._setupAriaAttrs();
this._updateFillCSSProps();
this._updatePointerCSSProps();
this._disconnectDisposal.add(this.store.interactive.subscribe(($interactive) => {
setAttribute(this, "interactive", $interactive);
}));
}
willUpdate(changedProperties) {
if (changedProperties.has("value") || changedProperties.has("min") || changedProperties.has("max")) {
this.value = this._getClampedValue(this.value);
this.store.value.set(this.value);
this._updateFillCSSProps();
this._dispatchValueChange();
}
if (changedProperties.has("disabled") && this.disabled) {
this.store.dragging.set(false);
this.store.pointing.set(false);
this.removeAttribute("dragging");
this.removeAttribute("pointing");
this.removeAttribute("interactive");
setAttribute(this, "aria-disabled", this.disabled);
}
if (!this.customValueText) {
this._updateAriaValueAttrs();
}
super.willUpdate(changedProperties);
}
disconnectedCallback() {
this._onDrag.cancel();
this._disconnectDisposal.empty();
super.disconnectedCallback();
}
_updateFillCSSProps() {
setCSSProperty(this, "slider-fill-value", `${this.value}`);
setCSSProperty(this, "slider-fill-rate", `${this.fillRate}`);
setCSSProperty(this, "slider-fill-percent", `${this.fillPercent}%`);
}
_updatePointerCSSProps() {
setCSSProperty(this, "slider-pointer-value", `${this.pointerValue}`);
setCSSProperty(this, "slider-pointer-rate", `${this.pointerRate}`);
setCSSProperty(this, "slider-pointer-percent", `${this.pointerPercent}%`);
}
render() {
return this._renderSlider();
}
_renderSlider() {
return html`${this._renderDefaultSlot()}`;
}
_renderDefaultSlot() {
return html`<slot></slot>`;
}
_setupAriaAttrs() {
setAttributeIfEmpty(this, "role", "slider");
setAttributeIfEmpty(this, "tabindex", "0");
setAttributeIfEmpty(this, "aria-orientation", "horizontal");
setAttributeIfEmpty(this, "autocomplete", "off");
}
_updateAriaValueAttrs() {
this.setAttribute("aria-valuemin", this._getValueMin());
this.setAttribute("aria-valuenow", this._getValueNow());
this.setAttribute("aria-valuemax", this._getValueMax());
this.setAttribute("aria-valuetext", this._getValueText());
}
_getValueMin() {
return String(this.min);
}
_getValueNow() {
return String(this.value);
}
_getValueMax() {
return String(this.max);
}
_getValueText() {
return `${round(this.value / this.max * 100, 2)}%`;
}
_startDragging(event) {
if (this.isDragging)
return;
this.store.dragging.set(true);
this.setAttribute("dragging", "");
const value = this._getValueBasedOnThumbPosition(event);
this.store.pointerValue.set(value);
this._dispatchPointerValueChange(event);
this.dispatchEvent(vdsEvent("vds-slider-drag-start", {
triggerEvent: event,
detail: this.value
}));
this._mediaRemote.pauseIdling(event);
}
_stopDragging(event) {
if (!this.isDragging)
return;
this.store.dragging.set(false);
this._dispatchValueChange.cancel();
this.removeAttribute("dragging");
const value = this._getValueBasedOnThumbPosition(event);
this.value = value;
this.store.pointerValue.set(value);
this._dispatchValueChange(event);
this._dispatchPointerValueChange(event);
this.dispatchEvent(vdsEvent("vds-slider-drag-end", {
triggerEvent: event,
detail: this.value
}));
this._mediaRemote.resumeIdling(event);
}
_getClampedValue(value) {
return clampNumber(this.min, round(value, getNumberOfDecimalPlaces(this.step)), this.max);
}
_getValueFromRate(rate) {
const boundRate = clampNumber(0, rate, 1);
const range = this.max - this.min;
const fill = range * boundRate;
const stepRatio = Math.round(fill / this.step);
const steps = this.step * stepRatio;
return this.min + steps;
}
_getValueBasedOnThumbPosition(event) {
const thumbClientX = event.clientX;
const { left: trackLeft, width: trackWidth } = this.getBoundingClientRect();
const thumbPositionRate = (thumbClientX - trackLeft) / trackWidth;
return this._getValueFromRate(thumbPositionRate);
}
}
__decorateClass([
property({ reflect: true, type: Number })
], SliderElement.prototype, "min", 1);
__decorateClass([
property({ reflect: true, type: Number })
], SliderElement.prototype, "max", 1);
__decorateClass([
property({ reflect: true, type: Boolean })
], SliderElement.prototype, "disabled", 2);
__decorateClass([
property({ reflect: true, type: Number })
], SliderElement.prototype, "value", 2);
__decorateClass([
property({ type: Number, reflect: true })
], SliderElement.prototype, "step", 1);
__decorateClass([
property({ attribute: "keyboard-step", type: Number })
], SliderElement.prototype, "keyboardStep", 1);
__decorateClass([
property({ attribute: "shift-key-multiplier", type: Number })
], SliderElement.prototype, "shiftKeyMultiplier", 2);
__decorateClass([
property({ type: Boolean, attribute: "custom-value-text" })
], SliderElement.prototype, "customValueText", 2);
export {
SliderElement,
SliderKeyDirection
};