kopia lustrzana https://github.com/nolanlawson/pinafore
120 wiersze
3.6 KiB
HTML
120 wiersze
3.6 KiB
HTML
<script>
|
|
import {
|
|
isVisible,
|
|
firstVisibleElementIndex,
|
|
scrollIntoViewIfNeeded
|
|
} from '../../_utils/scrollIntoView'
|
|
import {
|
|
addShortcutFallback,
|
|
removeShortcutFallback,
|
|
onKeyDownInShortcutScope
|
|
} from '../../_utils/shortcuts'
|
|
import { smoothScroll } from '../../_utils/smoothScroll'
|
|
import { getScrollContainer } from '../../_utils/scrollContainer'
|
|
|
|
const VISIBILITY_CHECK_DELAY_MS = 600
|
|
|
|
const keyToElement = key => document.getElementById(key)
|
|
const elementToKey = element => element.getAttribute('id')
|
|
const scope = 'global'
|
|
|
|
export default {
|
|
data: () => ({
|
|
activeItemChangeTime: 0,
|
|
elements: document.getElementsByClassName('shortcut-list-item')
|
|
}),
|
|
oncreate () {
|
|
addShortcutFallback(scope, this)
|
|
},
|
|
ondestroy () {
|
|
removeShortcutFallback(scope, this)
|
|
},
|
|
methods: {
|
|
onKeyDown (event) {
|
|
if (event.key === 'j' || event.key === 'ArrowDown') {
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
this.changeActiveItem(1, event.timeStamp)
|
|
return
|
|
}
|
|
if (event.key === 'k' || event.key === 'ArrowUp') {
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
this.changeActiveItem(-1, event.timeStamp)
|
|
return
|
|
}
|
|
let activeItemKey = this.checkActiveItem(event.timeStamp)
|
|
if (!activeItemKey) {
|
|
const { elements } = this.get()
|
|
const index = firstVisibleElementIndex(elements).first
|
|
if (index >= 0) {
|
|
activeItemKey = elementToKey(elements[index])
|
|
}
|
|
}
|
|
if (activeItemKey) {
|
|
onKeyDownInShortcutScope(activeItemKey, event)
|
|
}
|
|
},
|
|
changeActiveItem (movement, timeStamp) {
|
|
const { elements } = this.get()
|
|
let index = -1
|
|
let activeItemKey = this.checkActiveItem(timeStamp)
|
|
if (activeItemKey) {
|
|
const len = elements.length
|
|
let i = -1
|
|
while (++i < len) {
|
|
if (elementToKey(elements[i]) === activeItemKey) {
|
|
index = i
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if (index === 0 && movement === -1) {
|
|
activeItemKey = null
|
|
this.set({ activeItemKey })
|
|
smoothScroll(getScrollContainer(), 0, /* horizontal */ false, /* preferFast */ false)
|
|
return
|
|
}
|
|
if (index === -1) {
|
|
const { first, firstComplete } = firstVisibleElementIndex(elements)
|
|
index = (movement > 0) ? firstComplete : first
|
|
} else {
|
|
index += movement
|
|
}
|
|
if (index >= 0 && index < elements.length) {
|
|
activeItemKey = elementToKey(elements[index])
|
|
this.setActiveItem(activeItemKey, timeStamp)
|
|
scrollIntoViewIfNeeded(keyToElement(activeItemKey))
|
|
}
|
|
},
|
|
checkActiveItem (timeStamp) {
|
|
const activeElement = document.activeElement
|
|
if (!activeElement) {
|
|
return null
|
|
}
|
|
const activeItem = activeElement.getAttribute('id')
|
|
if (!activeItem) {
|
|
return null
|
|
}
|
|
const { activeItemChangeTime } = this.get()
|
|
if ((timeStamp - activeItemChangeTime) > VISIBILITY_CHECK_DELAY_MS &&
|
|
!isVisible(keyToElement(activeItem))) {
|
|
this.setActiveItem(null, 0)
|
|
return null
|
|
}
|
|
return activeItem
|
|
},
|
|
setActiveItem (key, timeStamp) {
|
|
this.set({ activeItemChangeTime: timeStamp })
|
|
try {
|
|
keyToElement(key).focus({
|
|
preventScroll: true
|
|
})
|
|
} catch (err) {
|
|
console.error('Ignored focus error', err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|