UNPKG

media-chrome

Version:

Custom elements (web components) for making audio and video player controls that look great in your website or app.

154 lines (136 loc) 5.31 kB
import { MediaStateReceiverAttributes } from './constants.js'; import MediaController from './media-controller.js'; import { getOrInsertCSSRule, namedNodeMapToObject } from './utils/element-utils.js'; import { globalThis } from './utils/server-safe-globals.js'; // Todo: Use data locals: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toLocaleTimeString function getTemplateHTML(_attrs: Record<string, string>, _props: Record<string, any> = {}) { return /*html*/ ` <style> :host { font: var(--media-font, var(--media-font-weight, normal) var(--media-font-size, 14px) / var(--media-text-content-height, var(--media-control-height, 24px)) var(--media-font-family, helvetica neue, segoe ui, roboto, arial, sans-serif)); color: var(--media-text-color, var(--media-primary-color, rgb(238 238 238))); background: var(--media-text-background, var(--media-control-background, var(--media-secondary-color, rgb(20 20 30 / .7)))); padding: var(--media-control-padding, 10px); display: inline-flex; justify-content: center; align-items: center; vertical-align: middle; box-sizing: border-box; text-align: center; pointer-events: auto; } ${ /* Only show outline when keyboard focusing. https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo */ '' } :host(:focus-visible) { box-shadow: var(--media-focus-box-shadow, inset 0 0 0 2px rgb(27 127 204 / .9)); outline: 0; } ${ /* * hide default focus ring, particularly when using mouse */ '' } :host(:where(:focus)) { box-shadow: none; outline: 0; } </style> ${this.getSlotTemplateHTML(_attrs, _props)} `; } function getSlotTemplateHTML(_attrs: Record<string, string>, _props: Record<string, any>) { return /*html*/ ` <slot></slot> `; } /** * @slot - Default slotted elements. * * @attr {string} mediacontroller - The element `id` of the media controller to connect to (if not nested within). * * @cssproperty --media-primary-color - Default color of text. * @cssproperty --media-secondary-color - Default color of background. * @cssproperty --media-text-color - `color` of text. * * @cssproperty --media-control-display - `display` property of control. * @cssproperty --media-control-background - `background` of control. * @cssproperty --media-control-padding - `padding` of control. * @cssproperty --media-control-height - `line-height` of control. * * @cssproperty --media-font - `font` shorthand property. * @cssproperty --media-font-weight - `font-weight` property. * @cssproperty --media-font-family - `font-family` property. * @cssproperty --media-font-size - `font-size` property. * @cssproperty --media-text-content-height - `line-height` of text. * @cssproperty --media-text-background - `background` of text display. */ class MediaTextDisplay extends globalThis.HTMLElement { static shadowRootOptions = { mode: 'open' as ShadowRootMode }; static getTemplateHTML = getTemplateHTML; static getSlotTemplateHTML = getSlotTemplateHTML; #mediaController: MediaController | null; static get observedAttributes(): string[] { return [MediaStateReceiverAttributes.MEDIA_CONTROLLER]; } constructor() { super(); if (!this.shadowRoot) { // Set up the Shadow DOM if not using Declarative Shadow DOM. this.attachShadow((this.constructor as typeof MediaTextDisplay).shadowRootOptions); const attrs = namedNodeMapToObject(this.attributes); this.shadowRoot.innerHTML = (this.constructor as typeof MediaTextDisplay).getTemplateHTML(attrs); } } attributeChangedCallback( attrName: string, oldValue: string | null, newValue: string | null ): void { if (attrName === MediaStateReceiverAttributes.MEDIA_CONTROLLER) { if (oldValue) { this.#mediaController?.unassociateElement?.(this); this.#mediaController = null; } if (newValue && this.isConnected) { // @ts-ignore this.#mediaController = this.getRootNode()?.getElementById(newValue); this.#mediaController?.associateElement?.(this); } } } connectedCallback(): void { const { style } = getOrInsertCSSRule(this.shadowRoot, ':host'); style.setProperty( 'display', `var(--media-control-display, var(--${this.localName}-display, inline-flex))` ); const mediaControllerId = this.getAttribute( MediaStateReceiverAttributes.MEDIA_CONTROLLER ); if (mediaControllerId) { // @ts-ignore this.#mediaController = (this.getRootNode() as Document)?.getElementById( mediaControllerId ); this.#mediaController?.associateElement?.(this); } } disconnectedCallback(): void { // Use cached mediaController, getRootNode() doesn't work if disconnected. this.#mediaController?.unassociateElement?.(this); this.#mediaController = null; } } if (!globalThis.customElements.get('media-text-display')) { globalThis.customElements.define('media-text-display', MediaTextDisplay); } export { MediaTextDisplay }; export default MediaTextDisplay;