import { css, html, LitElement, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators.js"; @customElement("play-soundbite") export class PlaySoundbite extends LitElement { @property({ attribute: "audio-src" }) audioSrc!: string; @property({ type: Number, attribute: "start-time" }) startTime!: number; @property({ type: Number }) duration!: number; @property({ attribute: "play-label" }) playLabel!: string; @property({ attribute: "playing-label" }) playingLabel!: string; @state() _audio: HTMLAudioElement | null = null; @state() _isPlaying = false; @state() _isLoading = false; _audioEvents = [ { name: "play", onEvent: () => { this._isPlaying = true; }, }, { name: "pause", onEvent: () => { this._isPlaying = false; }, }, { name: "timeupdate", onEvent: () => { if (this._audio) { if (this._audio.currentTime < this.startTime) { this._isLoading = true; this._audio.currentTime = this.startTime; } else if (this._audio.currentTime > this.startTime + this.duration) { this.stopSoundbite(); } else { this._isLoading = false; } } }, }, ]; playSoundbite() { if (this._audio === null) { this._audio = new Audio(this.audioSrc); for (const event of this._audioEvents) { this._audio.addEventListener(event.name, event.onEvent); } } this._audio.currentTime = this.startTime; this._audio.play(); } stopSoundbite() { if (this._audio !== null) { this._audio.pause(); this._audio.currentTime = this.startTime; } } disconnectedCallback(): void { if (this._audio) { for (const event of this._audioEvents) { this._audio.removeEventListener(event.name, event.onEvent); } } } static styles = css` button { background-color: hsl(var(--color-accent-base)); cursor: pointer; display: inline-flex; align-items: center; padding: 0.5rem; font-size: 0.875rem; border: 2px solid transparent; border-radius: 9999px; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); } button:hover { background-color: hsl(var(--color-accent-hover)); } button:focus { outline: none; box-shadow: 0 0 0 2px hsl(var(--color-background-base)), 0 0 0 4px hsl(var(--color-accent-base)); } button.playing { background-color: hsl(var(--color-background-base)); border: 2px solid hsl(var(--color-accent-base)); } button.playing:hover { background-color: hsl(var(--color-background-elevated)); } button.playing svg { color: hsl(var(--color-accent-base)); } svg { color: hsl(var(--color-accent-contrast)); } @keyframes spin { to { transform: rotate(360deg); } } .animate-spin { animation: spin 3s linear infinite; } `; render(): TemplateResult<1> { return html``; } }