mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-28 10:31:20 -08:00
refactor(core): [CORE-23] complete keyboard controls and focus lifecycle
This commit is contained in:
1 parent
faaa3e8c2d
commit
8ed0a5228c
190 files changed
+53981
-489
No files matched your search
@@ -0,0 +1,66 @@
|
||||
import type ResourceScope from '../lifecycle/scope'
|
||||
import type { InfoHost } from './poll'
|
||||
import { keyboardButton } from '../accessibility/button'
|
||||
import { claimKey, plainKey } from '../accessibility/keyboard'
|
||||
import { resolveFocusOrigin } from '../accessibility/overlay-focus'
|
||||
import { focusPlayer } from '../accessibility/player-focus'
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { listen } from '../lifecycle/resources'
|
||||
|
||||
const origins = new WeakMap<InfoHost, HTMLElement>()
|
||||
|
||||
export function infoKeyboard(art: InfoHost, scope: ResourceScope, close: () => void): void {
|
||||
const { $player, $info, $infoClose } = art.template
|
||||
if (!$info)
|
||||
return
|
||||
const active = () => !scope.closed && !isClosing(art)
|
||||
const visible = () => $player.classList.contains('art-info-show')
|
||||
const closeLabel = art.i18n.get('Close')
|
||||
if (!active())
|
||||
return
|
||||
const groupLabel = art.i18n.get('Video Info')
|
||||
if (!active())
|
||||
return
|
||||
$infoClose.setAttribute('aria-label', closeLabel)
|
||||
$info.setAttribute('role', 'group')
|
||||
$info.setAttribute('aria-label', groupLabel)
|
||||
keyboardButton(scope, $infoClose, () => $infoClose.click(), visible)
|
||||
listen(scope, $info, 'keydown', (input) => {
|
||||
const event = input as KeyboardEvent
|
||||
if (active() && visible() && plainKey(event) && !event.defaultPrevented && event.key === 'Escape') {
|
||||
claimKey(event)
|
||||
close()
|
||||
}
|
||||
})
|
||||
const changed = (show: boolean) => {
|
||||
if (!active())
|
||||
return
|
||||
const doc = $player.ownerDocument
|
||||
const focused = doc.activeElement as HTMLElement | null
|
||||
if (show) {
|
||||
if (!$info.contains(focused)) {
|
||||
origins.delete(art)
|
||||
if (focused && $player.contains(focused)) {
|
||||
origins.set(art, resolveFocusOrigin(focused))
|
||||
if ($player.classList.contains('art-keyboard-focus'))
|
||||
$infoClose.focus({ preventScroll: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const origin = origins.get(art)
|
||||
origins.delete(art)
|
||||
if (!$info.contains(focused))
|
||||
return
|
||||
const visibility = origin && doc.defaultView?.getComputedStyle(origin).visibility
|
||||
if (origin?.isConnected && $player.contains(origin) && origin.getClientRects().length && visibility !== 'hidden' && visibility !== 'collapse' && !origin.matches(':disabled') && !origin.closest('[inert]') && origin.getAttribute('aria-disabled') !== 'true')
|
||||
origin.focus({ preventScroll: true })
|
||||
else
|
||||
focusPlayer(art)
|
||||
}
|
||||
}
|
||||
art.on('info', changed)
|
||||
scope.add(() => {
|
||||
art.off('info', changed)
|
||||
})
|
||||
}
|
||||
@@ -3,9 +3,11 @@ import type { ComponentHost } from '../component/types'
|
||||
import type ResourceScope from '../lifecycle/scope'
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { timeout } from '../lifecycle/resources'
|
||||
import { infoKeyboard } from './keyboard'
|
||||
|
||||
export interface InfoHost extends ComponentHost, SubscriptionHost<{ destroy: [] }> {
|
||||
template: { $player: HTMLElement, $infoPanel: HTMLElement, $infoClose: HTMLElement, $video: object }
|
||||
export interface InfoHost extends ComponentHost, SubscriptionHost<{ destroy: [], info: [boolean] }> {
|
||||
template: { $player: HTMLElement, $info: HTMLElement | null, $infoPanel: HTMLElement, $infoClose: HTMLElement, $video: object }
|
||||
i18n: { get: (key: string) => string }
|
||||
constructor: { INFO_LOOP_TIME: number }
|
||||
proxy: ComponentHost['events']['proxy']
|
||||
}
|
||||
@@ -36,6 +38,7 @@ export function pollInfo(art: InfoHost, scope: ResourceScope, close: () => void)
|
||||
scope.add(() => {
|
||||
art.off('destroy', destroy)
|
||||
})
|
||||
infoKeyboard(art, scope, close)
|
||||
const loop = () => {
|
||||
try {
|
||||
for (const item of items) {
|
||||
|
||||
Reference in new issue
Block a user