mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-23 22:01: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,44 @@
|
||||
import type ResourceScope from '../lifecycle/scope'
|
||||
import type { LockHost } from './lock'
|
||||
import { keyboardButton } from '../accessibility/button'
|
||||
import { claimKey, plainKey } from '../accessibility/keyboard'
|
||||
import { suspendFocus } from '../accessibility/suspend-focus'
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { listen } from '../lifecycle/resources'
|
||||
|
||||
export function lockKeyboard(art: LockHost, element: HTMLElement, scope: ResourceScope, locked: () => boolean): (state: boolean) => void {
|
||||
let suspension: ResourceScope | undefined
|
||||
const active = () => !scope.closed && !isClosing(art)
|
||||
const label = art.i18n.get('Lock')
|
||||
if (!active())
|
||||
return () => {}
|
||||
element.setAttribute('aria-label', label)
|
||||
keyboardButton(scope, element)
|
||||
listen(scope, element, 'keydown', (input) => {
|
||||
const event = input as KeyboardEvent
|
||||
if (active() && locked() && plainKey(event) && !event.defaultPrevented && event.key === 'Escape') {
|
||||
claimKey(event)
|
||||
element.click()
|
||||
}
|
||||
})
|
||||
const update = (state: boolean) => {
|
||||
if (!active())
|
||||
return
|
||||
element.setAttribute('aria-pressed', String(state))
|
||||
if (state && !suspension) {
|
||||
const { $player, $bottom } = art.template
|
||||
const hiddenFocus = $bottom.contains($bottom.ownerDocument.activeElement) && $player.classList.contains('art-keyboard-focus')
|
||||
suspension = scope.child()
|
||||
suspendFocus(suspension, $bottom)
|
||||
if (active() && hiddenFocus)
|
||||
element.focus({ preventScroll: true })
|
||||
}
|
||||
else if (!state && suspension) {
|
||||
const previous = suspension
|
||||
suspension = undefined
|
||||
previous.dispose()
|
||||
}
|
||||
}
|
||||
update(locked())
|
||||
return update
|
||||
}
|
||||
@@ -3,9 +3,11 @@ import { appendElement } from '../component/dom'
|
||||
import { entryScope, subscribeEntry } from '../component/resources'
|
||||
import { isClosing } from '../lifecycle/instance'
|
||||
import { addClass, hasClass, removeClass, setStyle } from '../utils'
|
||||
import { lockKeyboard } from './lock-keyboard'
|
||||
|
||||
export interface LockHost extends SubscriptionHost<{ lock: [boolean] }> {
|
||||
template: { $player: HTMLElement }
|
||||
template: { $player: HTMLElement, $bottom: HTMLElement }
|
||||
i18n: { get: (key: string) => string }
|
||||
icons: { lock: string | Element, unlock: string | Element }
|
||||
isLock: boolean
|
||||
emit: (name: 'lock', state: boolean) => unknown
|
||||
@@ -57,7 +59,17 @@ export default function lock(art: LockHost): { name: string, state: boolean } {
|
||||
const $unlock = appendElement($el, icons.unlock)
|
||||
if (scope.closed || isClosing(art))
|
||||
return
|
||||
setStyle($lock, 'display', 'none')
|
||||
const updateKeyboard = lockKeyboard(art, $el, scope, getState)
|
||||
if (scope.closed || isClosing(art))
|
||||
return
|
||||
if (getState()) {
|
||||
setStyle($lock, 'display', 'inline-flex')
|
||||
if (!scope.closed && !isClosing(art))
|
||||
setStyle($unlock, 'display', 'none')
|
||||
}
|
||||
else {
|
||||
setStyle($lock, 'display', 'none')
|
||||
}
|
||||
|
||||
subscribeEntry<{ lock: [boolean] }, 'lock'>(art, $el, 'lock', (state) => {
|
||||
if (isClosing(art))
|
||||
@@ -65,6 +77,8 @@ export default function lock(art: LockHost): { name: string, state: boolean } {
|
||||
setStyle($lock, 'display', state ? 'inline-flex' : 'none')
|
||||
if (!scope.closed && !isClosing(art))
|
||||
setStyle($unlock, 'display', state ? 'none' : 'inline-flex')
|
||||
if (!scope.closed && !isClosing(art))
|
||||
updateKeyboard(state)
|
||||
})
|
||||
},
|
||||
click() {
|
||||
|
||||
Reference in new issue
Block a user