Files
ArtPlayer/packages/artplayer/src/display/mini-view.ts
T

106 lines
3.4 KiB
TypeScript

import type ResourceScope from '../lifecycle/scope'
import type { MiniHost } from './types'
import { keyboardButton } from '../accessibility/button'
import { claimKey, plainKey } from '../accessibility/keyboard'
import { isClosing } from '../lifecycle/instance'
import { listen } from '../lifecycle/resources'
import { silencePromise } from '../utils/error'
import { miniDrag } from './mini-drag'
export interface MiniView {
element: HTMLElement
cancelDrag: () => void
fresh: boolean
focus?: () => void
}
export function createMiniView(art: MiniHost, parent: ResourceScope, active: () => boolean, hide: () => void): MiniView | undefined {
const scope = parent.child()
if (scope.closed)
return
const existing = art.template.$mini
if (existing) {
const display = existing.style.display
scope.add(() => {
existing.style.display = display
})
return { element: existing, cancelDrag: () => {}, fresh: false }
}
const document = art.template.$player.ownerDocument
const element = document.createElement('div')
element.className = 'art-mini-popup'
scope.add(() => {
element.remove()
})
try {
const close = document.createElement('div')
close.className = 'art-mini-close'
close.append(art.icons.close)
if (scope.closed)
return
const state = document.createElement('div')
state.className = 'art-mini-state'
const play = art.icons.play
const pause = art.icons.pause
if (scope.closed)
return
state.append(play, pause)
element.append(close, state)
keyboardButton(scope, close, () => close.click(), active)
keyboardButton(scope, state, () => (art.playing ? pause : play).click(), active)
const closeLabel = art.i18n.get('Close')
if (scope.closed || isClosing(art))
return
close.setAttribute('aria-label', closeLabel)
const groupLabel = art.i18n.get('Mini Player')
if (scope.closed || isClosing(art))
return
element.setAttribute('role', 'group')
element.setAttribute('aria-label', groupLabel)
listen(scope, element, 'keydown', (input) => {
const event = input as KeyboardEvent
if (active() && plainKey(event) && !event.defaultPrevented && event.key === 'Escape') {
claimKey(event)
hide()
}
})
listen(scope, close, 'click', hide)
listen(scope, play, 'click', () => {
silencePromise(art.play())
})
listen(scope, pause, 'click', () => {
art.pause()
})
const update = () => {
if (scope.closed)
return
const label = art.i18n.get(art.playing ? 'Pause' : 'Play')
if (scope.closed || isClosing(art))
return
state.setAttribute('aria-label', label)
play.style.display = art.playing ? 'none' : 'flex'
pause.style.display = art.playing ? 'flex' : 'none'
}
update()
for (const name of ['video:playing', 'video:pause', 'video:timeupdate'] as const) {
art.on(name, update)
scope.add(() => {
art.off(name, update)
})
}
const cancelDrag = miniDrag(art, element, scope, active)
if (scope.closed)
return
art.template.$mini = element
document.body.append(element)
if (!scope.closed)
return { element, cancelDrag, fresh: true, focus: () => close.focus({ preventScroll: true }) }
}
catch (error) {
scope.dispose()
if (art.template.$mini === element)
delete art.template.$mini
throw error
}
}