media-chrome
Version:
Custom elements (web components) for making audio and video player controls that look great in your website or app.
124 lines (106 loc) • 3.32 kB
text/typescript
import { globalThis } from './utils/server-safe-globals.js';
import { MediaChromeRange } from './media-chrome-range.js';
import { MediaUIAttributes, MediaUIEvents } from './constants.js';
import { t } from './utils/i18n.js';
import {
getBooleanAttr,
getNumericAttr,
getStringAttr,
setBooleanAttr,
setNumericAttr,
setStringAttr,
} from './utils/element-utils.js';
const DEFAULT_VOLUME = 1;
const toVolume = (el: any): number => {
if (el.mediaMuted) return 0;
return el.mediaVolume;
};
const formatAsPercentString = (value: number): string =>
`${Math.round(value * 100)}%`;
/**
* @attr {string} mediavolume - (read-only) Set to the media volume.
* @attr {boolean} mediamuted - (read-only) Set to the media muted state.
* @attr {string} mediavolumeunavailable - (read-only) Set if changing volume is unavailable.
*
* @cssproperty [--media-volume-range-display = inline-block] - `display` property of range.
*/
class MediaVolumeRange extends MediaChromeRange {
static get observedAttributes(): string[] {
return [
...super.observedAttributes,
MediaUIAttributes.MEDIA_VOLUME,
MediaUIAttributes.MEDIA_MUTED,
MediaUIAttributes.MEDIA_VOLUME_UNAVAILABLE,
];
}
#handleRangeInput: () => void = () => {
const detail = this.range.value;
const evt = new globalThis.CustomEvent(
MediaUIEvents.MEDIA_VOLUME_REQUEST,
{
composed: true,
bubbles: true,
detail,
}
);
this.dispatchEvent(evt);
}
connectedCallback(): void {
super.connectedCallback();
this.range.setAttribute('aria-label', t('volume'));
this.range.addEventListener('input', this.#handleRangeInput);
}
disconnectedCallback(): void {
this.range.removeEventListener('input', this.#handleRangeInput);
super.disconnectedCallback();
}
attributeChangedCallback(
attrName: string,
oldValue: string | null,
newValue: string | null
): void {
super.attributeChangedCallback(attrName, oldValue, newValue);
if (
attrName === MediaUIAttributes.MEDIA_VOLUME ||
attrName === MediaUIAttributes.MEDIA_MUTED
) {
this.range.valueAsNumber = toVolume(this);
this.range.setAttribute(
'aria-valuetext',
formatAsPercentString(this.range.valueAsNumber)
);
this.updateBar();
}
}
/**
*
*/
get mediaVolume(): number {
return getNumericAttr(this, MediaUIAttributes.MEDIA_VOLUME, DEFAULT_VOLUME);
}
set mediaVolume(value: number) {
setNumericAttr(this, MediaUIAttributes.MEDIA_VOLUME, value);
}
/**
* Is the media currently muted
*/
get mediaMuted(): boolean {
return getBooleanAttr(this, MediaUIAttributes.MEDIA_MUTED);
}
set mediaMuted(value: boolean) {
setBooleanAttr(this, MediaUIAttributes.MEDIA_MUTED, value);
}
/**
* The volume unavailability state
*/
get mediaVolumeUnavailable(): string | undefined {
return getStringAttr(this, MediaUIAttributes.MEDIA_VOLUME_UNAVAILABLE);
}
set mediaVolumeUnavailable(value: string | undefined) {
setStringAttr(this, MediaUIAttributes.MEDIA_VOLUME_UNAVAILABLE, value);
}
}
if (!globalThis.customElements.get('media-volume-range')) {
globalThis.customElements.define('media-volume-range', MediaVolumeRange);
}
export default MediaVolumeRange;