Skip to content

Commit

Permalink
perf: 键盘移动表格单元格聚焦光标
Browse files Browse the repository at this point in the history
  • Loading branch information
hy16657 committed Jun 30, 2024
1 parent 4d3ae2a commit b05eee2
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/configs/hotkey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export const HOTKEY_DOC = [
type: '表格编辑',
children: [
{ label: '聚焦到下一个单元格', value: 'Tab' },
{ label: '移动焦点单元格', value: '↑ / ← / ↓ / →' },
{ label: '在上方插入一行', value: 'Ctrl + ↑' },
{ label: '在下方插入一行', value: 'Ctrl + ↓' },
{ label: '在左侧插入一列', value: 'Ctrl + ←' },
Expand Down
123 changes: 115 additions & 8 deletions src/views/components/element/TableElement/EditableTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,13 @@ const clearSelectedCellText = () => {
tableCells.value = _tableCells
}
const focusActiveCell = () => {
nextTick(() => {
const textRef = document.querySelector('.cell-text.active') as HTMLInputElement
if (textRef) textRef.focus()
})
}
// 将焦点移动到下一个单元格
// 当前行右边有单元格时,焦点右移
// 当前行右边无单元格(已处在行末),且存在下一行时,焦点移动至下一行行首
Expand All @@ -454,10 +461,86 @@ const tabActiveCell = () => {
else startCell.value = nextCell
// 移动焦点后自动聚焦文本
nextTick(() => {
const textRef = document.querySelector('.cell-text.active') as HTMLInputElement
if (textRef) textRef.focus()
})
focusActiveCell()
}
// 移动焦点(上下左右)
const moveActiveCell = (dir: 'UP' | 'DOWN' | 'LEFT' | 'RIGHT') => {
const rowIndex = +selectedCells.value[0].split('_')[0]
const colIndex = +selectedCells.value[0].split('_')[1]
const rowLen = tableCells.value.length
const colLen = tableCells.value[0].length
const getEffectivePos = (pos: [number, number]): [number, number] => {
if (pos[0] < 0 || pos[1] < 0 || pos[0] > rowLen - 1 || pos[1] > colLen - 1) return [0, 0]
const p = `${pos[0]}_${pos[1]}`
if (!hideCells.value.includes(p)) return pos
if (dir === 'UP') {
return getEffectivePos([pos[0], pos[1] - 1])
}
if (dir === 'DOWN') {
return getEffectivePos([pos[0], pos[1] - 1])
}
if (dir === 'LEFT') {
return getEffectivePos([pos[0] - 1, pos[1]])
}
if (dir === 'RIGHT') {
return getEffectivePos([pos[0] - 1, pos[1]])
}
return [0, 0]
}
if (dir === 'UP') {
const _rowIndex = rowIndex - 1
if (_rowIndex < 0) return
endCell.value = []
startCell.value = getEffectivePos([_rowIndex, colIndex])
}
else if (dir === 'DOWN') {
const _rowIndex = rowIndex + 1
if (_rowIndex > rowLen - 1) return
endCell.value = []
startCell.value = getEffectivePos([_rowIndex, colIndex])
}
else if (dir === 'LEFT') {
const _colIndex = colIndex - 1
if (_colIndex < 0) return
endCell.value = []
startCell.value = getEffectivePos([rowIndex, _colIndex])
}
else if (dir === 'RIGHT') {
const _colIndex = colIndex + 1
if (_colIndex > colLen - 1) return
endCell.value = []
startCell.value = getEffectivePos([rowIndex, _colIndex])
}
focusActiveCell()
}
// 获取光标位置
const getCaretPosition = (element: HTMLDivElement) => {
const selection = window.getSelection()
if (selection && selection.rangeCount > 0) {
const range = selection.getRangeAt(0)
const preCaretRange = range.cloneRange()
preCaretRange.selectNodeContents(element)
preCaretRange.setEnd(range.startContainer, range.startOffset)
const start = preCaretRange.toString().length
preCaretRange.setEnd(range.endContainer, range.endOffset)
const end = preCaretRange.toString().length
const len = element.textContent?.length || 0
return { start, end, len }
}
return null
}
// 表格快捷键监听
Expand All @@ -470,26 +553,50 @@ const keydownListener = (e: KeyboardEvent) => {
e.preventDefault()
tabActiveCell()
}
if (e.ctrlKey && key === KEYS.UP) {
else if (e.ctrlKey && key === KEYS.UP) {
e.preventDefault()
const rowIndex = +selectedCells.value[0].split('_')[0]
insertRow(rowIndex)
}
if (e.ctrlKey && key === KEYS.DOWN) {
else if (e.ctrlKey && key === KEYS.DOWN) {
e.preventDefault()
const rowIndex = +selectedCells.value[0].split('_')[0]
insertRow(rowIndex + 1)
}
if (e.ctrlKey && key === KEYS.LEFT) {
else if (e.ctrlKey && key === KEYS.LEFT) {
e.preventDefault()
const colIndex = +selectedCells.value[0].split('_')[1]
insertCol(colIndex)
}
if (e.ctrlKey && key === KEYS.RIGHT) {
else if (e.ctrlKey && key === KEYS.RIGHT) {
e.preventDefault()
const colIndex = +selectedCells.value[0].split('_')[1]
insertCol(colIndex + 1)
}
else if (key === KEYS.UP) {
const range = getCaretPosition(e.target as HTMLDivElement)
if (range && range.start === range.end && range.start === 0) {
moveActiveCell('UP')
}
}
else if (key === KEYS.DOWN) {
const range = getCaretPosition(e.target as HTMLDivElement)
if (range && range.start === range.end && range.start === range.len) {
moveActiveCell('DOWN')
}
}
else if (key === KEYS.LEFT) {
const range = getCaretPosition(e.target as HTMLDivElement)
if (range && range.start === range.end && range.start === 0) {
moveActiveCell('LEFT')
}
}
else if (key === KEYS.RIGHT) {
const range = getCaretPosition(e.target as HTMLDivElement)
if (range && range.start === range.end && range.start === range.len) {
moveActiveCell('RIGHT')
}
}
}
else if (key === KEYS.DELETE) {
clearSelectedCellText()
Expand Down

0 comments on commit b05eee2

Please sign in to comment.