@coreui/vue-pro
Version:
UI Components Library for Vue.js
160 lines (141 loc) • 5.07 kB
text/typescript
import { defineComponent, h, onUpdated, PropType, ref, watch } from 'vue'
import { useIsVisible } from '../../composables'
import { getNextActiveElement, isRTL } from '../../utils'
export interface Element {
value: number | string
label: number | string
}
const CTimePickerRollCol = defineComponent({
name: 'CTimePickerRollCol',
props: {
ariaLabel: String,
columnIndex: Number,
columnRefs: {
type: Array as PropType<(HTMLElement | null)[]>,
default: () => [],
},
elements: {
type: Array as PropType<Element[]>,
required: true,
},
selected: [Number, String, null],
setColumnRef: Function as PropType<(index: number, el: HTMLElement | null) => void>,
},
emits: ['click'],
setup(props, { emit }) {
const init = ref(true)
const colRef = ref<HTMLElement>()
const isVisible = useIsVisible(colRef)
const scrollToSelectedElement = () => {
const nodeEl = colRef.value?.querySelector('.selected')
if (isVisible.value && nodeEl && nodeEl instanceof HTMLElement) {
colRef.value?.scrollTo({
top: nodeEl.offsetTop,
behavior: init.value ? 'instant' : 'smooth',
})
}
}
watch(isVisible, () => {
scrollToSelectedElement()
if (isVisible.value) {
init.value = false
}
})
watch(colRef, () => {
if (props.columnIndex !== undefined && props.setColumnRef) {
props.setColumnRef(props.columnIndex, colRef.value || null)
}
}, { immediate: true })
onUpdated(() => {
scrollToSelectedElement()
})
const moveFocusToNextColumn = () => {
if (!props.columnRefs || props.columnIndex === undefined) return
if (props.columnIndex < props.columnRefs.length - 1) {
const column = props.columnRefs[props.columnIndex + 1]
const focusableCell = column?.querySelector('.time-picker-roll-cell[tabindex="0"]') as HTMLElement
focusableCell?.focus()
}
}
const moveFocusToPreviousColumn = () => {
if (!props.columnRefs || props.columnIndex === undefined) return
if (props.columnIndex > 0) {
const column = props.columnRefs[props.columnIndex - 1]
const focusableCell = column?.querySelector('.time-picker-roll-cell[tabindex="0"]') as HTMLElement
focusableCell?.focus()
}
}
const handleKeyDown = (event: KeyboardEvent, value: number | string) => {
if (event.code === 'Space' || event.key === 'Enter') {
event.preventDefault()
emit('click', value)
moveFocusToNextColumn()
return
}
if (colRef.value && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
event.preventDefault()
const target = event.target as HTMLElement
const items = [...colRef.value.querySelectorAll('.time-picker-roll-cell')] as HTMLElement[]
getNextActiveElement(items, target, event.key === 'ArrowDown', true).focus()
return
}
if (colRef.value && (event.key === 'Home' || event.key === 'End')) {
event.preventDefault()
const items = [...colRef.value.querySelectorAll('.time-picker-roll-cell')] as HTMLElement[]
const index = event.key === 'Home' ? 0 : items.length - 1
items[index]?.focus()
return
}
if (colRef.value && (event.key === 'ArrowLeft' || event.key === 'ArrowRight')) {
event.preventDefault()
const rtl = isRTL(colRef.value)
const shouldGoLeft =
(event.key === 'ArrowLeft' && !rtl) || (event.key === 'ArrowRight' && rtl)
if (shouldGoLeft) {
moveFocusToPreviousColumn()
} else {
moveFocusToNextColumn()
}
}
}
return () =>
h(
'div',
{
class: 'time-picker-roll-col',
onFocusout: (event: FocusEvent) => {
const currentTarget = event.currentTarget as HTMLElement
const relatedTarget = event.relatedTarget as HTMLElement | null
if (currentTarget && (!relatedTarget || !currentTarget.contains(relatedTarget))) {
scrollToSelectedElement()
}
},
ref: colRef,
role: 'listbox',
'aria-label': props.ariaLabel,
},
props.elements.map((element, index) => {
const isSelected = element.value === props.selected
return h(
'div',
{
class: [
'time-picker-roll-cell',
{
selected: isSelected,
},
],
onClick: () => emit('click', element.value),
onKeydown: (event: KeyboardEvent) => handleKeyDown(event, element.value),
role: 'option',
tabindex: isSelected ? 0 : props.selected ? -1 : index === 0 ? 0 : -1,
'aria-label': element.label.toString(),
'aria-selected': isSelected,
},
element.label
)
})
)
},
})
export { CTimePickerRollCol }