import { MediaChromeButton } from './media-chrome-button.js'; import { globalThis } from './utils/server-safe-globals.js'; import { MediaUIEvents, MediaUIAttributes } from './constants.js'; import { getBooleanAttr, setBooleanAttr } from './utils/element-utils.js'; import { t } from './utils/i18n.js'; const { MEDIA_TIME_IS_LIVE, MEDIA_PAUSED } = MediaUIAttributes; const { MEDIA_SEEK_TO_LIVE_REQUEST, MEDIA_PLAY_REQUEST } = MediaUIEvents; const indicatorSVG = ''; function getSlotTemplateHTML(_attrs: Record) { return /*html*/ ` ${indicatorSVG} ${ /* A new line between spacer and text creates inconsistent spacing between slotted items and default slots. */ '' }  ${t('live')} `; } const updateAriaAttributes = (el: MediaLiveButton): void => { const isPausedOrNotLive = el.mediaPaused || !el.mediaTimeIsLive; const label = isPausedOrNotLive ? t('seek to live') : t('playing live'); el.setAttribute('aria-label', label); const textSlot = el.shadowRoot?.querySelector('slot[name="text"]'); if (textSlot) textSlot.textContent = t('live'); 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 getSlotTemplateHTML = getSlotTemplateHTML; static get observedAttributes() { return [ ...super.observedAttributes, MEDIA_TIME_IS_LIVE, MEDIA_PAUSED, ]; } connectedCallback(): void { super.connectedCallback(); updateAriaAttributes(this); } 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;