91 lines
2.0 KiB
JavaScript
91 lines
2.0 KiB
JavaScript
const KEY_DELTA_MAP = {
|
|
ArrowLeft: -1,
|
|
ArrowUp: -1,
|
|
ArrowRight: 1,
|
|
ArrowDown: 1,
|
|
}
|
|
|
|
const FOCUSABLE_SELECTORS = [
|
|
'input:not([type="hidden"]):not([disabled])',
|
|
'textarea:not([disabled])',
|
|
'select:not([disabled])',
|
|
'.el-input__inner',
|
|
'.el-input-number',
|
|
'.el-select',
|
|
'.el-tree-select',
|
|
'[tabindex]:not([tabindex="-1"])',
|
|
]
|
|
|
|
function focusControl(container) {
|
|
if (!container) return
|
|
|
|
const focus = (el) => {
|
|
if (!el) return
|
|
el.focus?.()
|
|
if (el.select && !el.readOnly) {
|
|
el.select()
|
|
}
|
|
}
|
|
|
|
if (container.matches?.('input, textarea, select')) {
|
|
focus(container)
|
|
return
|
|
}
|
|
|
|
const directTarget = container.querySelector(FOCUSABLE_SELECTORS.join(', '))
|
|
if (directTarget) {
|
|
focusControl(directTarget)
|
|
return
|
|
}
|
|
|
|
focus(container)
|
|
}
|
|
|
|
function getFormItems(root) {
|
|
const propItems = Array.from(root.querySelectorAll('[data-prop]'))
|
|
if (propItems.length) {
|
|
return propItems
|
|
}
|
|
return Array.from(root.querySelectorAll('.el-form-item'))
|
|
}
|
|
|
|
function createHandler(root) {
|
|
return function handleKeyDown(event) {
|
|
const delta = KEY_DELTA_MAP[event.key]
|
|
if (!delta) return
|
|
|
|
const currentItem = event.target.closest('[data-prop], .el-form-item')
|
|
if (!currentItem || !root.contains(currentItem)) return
|
|
|
|
const items = getFormItems(root)
|
|
if (!items.length) return
|
|
|
|
const currentIndex = items.indexOf(currentItem)
|
|
if (currentIndex === -1) return
|
|
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
|
|
const nextIndex = (currentIndex + delta + items.length) % items.length
|
|
const targetItem = items[nextIndex]
|
|
if (targetItem) {
|
|
focusControl(targetItem)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default {
|
|
mounted(el) {
|
|
const handler = createHandler(el)
|
|
el.__arrowNavigateHandler = handler
|
|
el.addEventListener('keydown', handler, true)
|
|
},
|
|
beforeUnmount(el) {
|
|
if (el.__arrowNavigateHandler) {
|
|
el.removeEventListener('keydown', el.__arrowNavigateHandler, true)
|
|
delete el.__arrowNavigateHandler
|
|
}
|
|
},
|
|
}
|
|
|