Files
ArtPlayer/packages/artplayer/src/control/index.ts
T

137 lines
3.5 KiB
TypeScript

import type { EntryInput, EntryOption, EventCleanup, SelectorItem } from '../component/types'
import type { ControlHost } from './types'
import { focusVisibility } from '../accessibility/focus'
import { ownEntry } from '../component/resources'
import { getScope, isClosing } from '../lifecycle/instance'
import {
addClass,
errorHandle,
includeFromEvent,
isMobile,
removeClass,
} from '../utils'
import Component from '../utils/component'
import { installControls } from './builtins'
import { observeControlLayout } from './layout'
import { controlEvents } from './resources'
import { checkSelector, renderSelector } from './selector'
export default class Control extends Component<ControlHost> {
declare isHover: boolean
declare timer: number
declare setting?: HTMLDivElement
declare thumbnails?: HTMLDivElement
constructor(art: ControlHost) {
super(art)
this.isHover = false
this.name = 'control'
this.timer = Date.now()
const { constructor } = art
const { $player, $bottom } = this.art.template
const scope = ownEntry(art, $bottom)
const keyboardFocused = focusVisibility(scope, $player, () => {
this.show = true
})
const { on, proxy } = controlEvents(art, $bottom)
proxy($player, 'focusin', (event) => {
art.isFocus = true
art.isInput = (event.target as Element | null)?.tagName === 'INPUT'
})
proxy($player, 'focusout', (event) => {
if (!$player.contains(event.relatedTarget as Node | null)) {
art.isFocus = false
art.isInput = false
}
})
on('mousemove', () => {
if (!isMobile) {
this.show = true
}
})
on('click', () => {
if (isMobile) {
this.toggle()
}
else {
this.show = true
}
})
on('document:mousemove', (event) => {
this.isHover = includeFromEvent(event, $bottom)
})
on('video:timeupdate', () => {
if (
!art.setting.show
&& !this.isHover
&& !art.isInput
&& !keyboardFocused()
&& art.playing
&& this.show
&& Date.now() - this.timer >= constructor.CONTROL_HIDE_TIME
) {
this.show = false
}
})
on('control', (state) => {
if (state) {
removeClass($player, 'art-hide-cursor')
addClass($player, 'art-hover')
this.timer = Date.now()
}
else {
addClass($player, 'art-hide-cursor')
removeClass($player, 'art-hover')
}
})
this.init()
if (!getScope(art).closed)
observeControlLayout(art)
}
init(): void {
installControls(this)
}
override add(getOption: EntryInput<ControlHost>): undefined {
if (isClosing(this.art))
return
const option = typeof getOption === 'function' ? getOption(this.art) : getOption
const { $progress, $controlsLeft, $controlsRight } = this.art.template
switch (option.position) {
case 'top':
this.$parent = $progress
break
case 'left':
this.$parent = $controlsLeft
break
case 'right':
this.$parent = $controlsRight
break
default:
errorHandle(false, `Control option.position must one of 'top', 'left', 'right'`)
break
}
super.add(option)
}
check(target?: SelectorItem): void {
checkSelector(target)
}
override selector(option: EntryOption<ControlHost>, $ref: HTMLDivElement, events: EventCleanup[]): void {
renderSelector(this.art, target => this.check(target), option, $ref, events)
}
}