import { MediaChromeButton } from './media-chrome-button.js'; import { globalThis, document } from './utils/server-safe-globals.js'; import { MediaUIEvents, MediaUIAttributes } from './constants.js'; import { verbs } from './labels/labels.js'; import { getBooleanAttr, setBooleanAttr } from './utils/element-utils.js'; const { MEDIA_TIME_IS_LIVE, MEDIA_PAUSED } = MediaUIAttributes; const { MEDIA_SEEK_TO_LIVE_REQUEST, MEDIA_PLAY_REQUEST } = MediaUIEvents; const indicatorSVG = ''; const slotTemplate = document.createElement('template'); slotTemplate.innerHTML = /*html*/ ` ${indicatorSVG} ${ /* A new line between spacer and text creates inconsistent spacing between slotted items and default slots. */ '' }  LIVE `; const updateAriaAttributes = (el: MediaLiveButton): void => { const isPausedOrNotLive = el.mediaPaused || !el.mediaTimeIsLive; const label = isPausedOrNotLive ? verbs.SEEK_LIVE() : verbs.PLAYING_LIVE(); el.setAttribute('aria-label', label); isPausedOrNotLive ? el.removeAttribute('aria-disabled') : el.setAttribute('aria-disabled', 'true'); }; /** * @slot indicator - The default is an SVG of a circle that changes to red when the video or audio is live. Can be replaced with your own SVG or font icon. * @slot spacer - A simple text space ( ) between the indicator and the text. * @slot text - The text content of the button, with a default of “LIVE”. * * @attr {boolean} mediapaused - (read-only) Present if the media is paused. * @attr {boolean} mediatimeislive - (read-only) Present if the media playback is live. * * @cssproperty [--media-live-button-display = inline-flex] - `display` property of button. * @cssproperty --media-live-button-icon-color - `fill` and `color` of not live button icon. * @cssproperty --media-live-button-indicator-color - `fill` and `color` of live button icon. */ class MediaLiveButton extends MediaChromeButton { static get observedAttributes(): string[] { return [...super.observedAttributes, MEDIA_PAUSED, MEDIA_TIME_IS_LIVE]; } constructor(options: object = {}) { super({ slotTemplate, ...options }); } connectedCallback(): void { updateAriaAttributes(this); super.connectedCallback(); } attributeChangedCallback( attrName: string, oldValue: string | null, newValue: string | null ): void { super.attributeChangedCallback(attrName, oldValue, newValue); updateAriaAttributes(this); } /** * @type {boolean} Is the media paused */ get mediaPaused(): boolean { return getBooleanAttr(this, MediaUIAttributes.MEDIA_PAUSED); } set mediaPaused(value: boolean) { setBooleanAttr(this, MediaUIAttributes.MEDIA_PAUSED, value); } /** * @type {boolean} Is the media playback currently live */ get mediaTimeIsLive(): boolean { return getBooleanAttr(this, MediaUIAttributes.MEDIA_TIME_IS_LIVE); } set mediaTimeIsLive(value: boolean) { setBooleanAttr(this, MediaUIAttributes.MEDIA_TIME_IS_LIVE, value); } handleClick(): void { // If we're live and not paused, don't allow seek to live if (!this.mediaPaused && this.mediaTimeIsLive) return; this.dispatchEvent( new globalThis.CustomEvent(MEDIA_SEEK_TO_LIVE_REQUEST, { composed: true, bubbles: true, }) ); // If we're paused, also automatically play if (this.hasAttribute(MEDIA_PAUSED)) { this.dispatchEvent( new globalThis.CustomEvent(MEDIA_PLAY_REQUEST, { composed: true, bubbles: true, }) ); } } } if (!globalThis.customElements.get('media-live-button')) { globalThis.customElements.define('media-live-button', MediaLiveButton); } export default MediaLiveButton;