diff --git a/lib/KTable/index.vue b/lib/KTable/index.vue index 6e2c6ab00..ea9a31a6c 100644 --- a/lib/KTable/index.vue +++ b/lib/KTable/index.vue @@ -331,17 +331,36 @@ * - header highlight */ handleKeydown(event, rowIndex, colIndex) { - const key = event.key; + switch (event.key) { + case 'ArrowUp': + case 'ArrowDown': + case 'ArrowLeft': + case 'ArrowRight': + this.handleArrowKeys(event.key, rowIndex, colIndex); + break; + case 'Enter': + this.handleEnterKey(rowIndex, colIndex); + break; + case 'Tab': + this.handleTabKey(event, rowIndex, colIndex); + break; + default: + break; + } + }, + + handleArrowKeys(key, rowIndex, colIndex) { const totalRows = this.rows.length; const totalCols = this.headers.length; - + const lastRowIndex = totalRows - 1; + const lastColIndex = totalCols - 1; let nextRowIndex = rowIndex; let nextColIndex = colIndex; switch (key) { case 'ArrowUp': if (rowIndex === -1) { - nextRowIndex = totalRows - 1; + nextRowIndex = lastRowIndex; } else { nextRowIndex = rowIndex - 1; } @@ -349,10 +368,10 @@ case 'ArrowDown': if (rowIndex === -1) { nextRowIndex = 0; - } else if (rowIndex === totalRows - 1) { + } else if (rowIndex === lastRowIndex) { nextRowIndex = -1; } else { - nextRowIndex = (rowIndex + 1) % totalRows; + nextRowIndex = rowIndex + 1; } break; case 'ArrowLeft': @@ -360,127 +379,122 @@ if (colIndex > 0) { nextColIndex = colIndex - 1; } else { - nextColIndex = totalCols - 1; - nextRowIndex = totalRows - 1; + nextColIndex = lastColIndex; + } + if (colIndex === 0) { + nextRowIndex = lastRowIndex; + } else { + nextRowIndex = -1; } - } else if (colIndex > 0) { - nextColIndex = colIndex - 1; } else { - nextColIndex = totalCols - 1; - nextRowIndex = rowIndex > 0 ? rowIndex - 1 : -1; + if (colIndex > 0) { + nextColIndex = colIndex - 1; + } else { + nextColIndex = lastColIndex; + } + if (colIndex === 0) { + if (rowIndex > 0) { + nextRowIndex = rowIndex - 1; + } else { + nextRowIndex = -1; + } + } else { + nextRowIndex = rowIndex; + } } break; case 'ArrowRight': - if (colIndex === totalCols - 1) { - if (rowIndex === totalRows - 1) { - nextColIndex = 0; + if (colIndex === lastColIndex) { + nextColIndex = 0; + if (rowIndex === lastRowIndex) { nextRowIndex = -1; } else { - nextColIndex = 0; nextRowIndex = rowIndex + 1; } } else { nextColIndex = colIndex + 1; } break; - case 'Enter': - if (rowIndex === -1 && this.sortable) { - this.handleSort(colIndex); - } - break; - case 'Tab': { - // Identify all focusable elements inside the current cell - const currentCell = this.getCell(rowIndex, colIndex); + } + this.updateFocusState(nextRowIndex, nextColIndex); + event.preventDefault(); + }, - // Collect focusable elements using native DOM methods - const focusableElements = []; + handleEnterKey(rowIndex, colIndex) { + if (rowIndex === -1 && this.sortable) { + this.handleSort(colIndex); + } + }, + + handleTabKey(event, rowIndex, colIndex) { + const totalRows = this.rows.length; + const totalCols = this.headers.length; + let nextRowIndex = rowIndex; + let nextColIndex = colIndex; - if (currentCell) { - const buttons = currentCell.getElementsByTagName('button'); - const links = currentCell.getElementsByTagName('a'); - const inputs = currentCell.getElementsByTagName('input'); - const selects = currentCell.getElementsByTagName('select'); - const textareas = currentCell.getElementsByTagName('textarea'); + const currentCell = this.getCell(rowIndex, colIndex); + const focusableElements = this.getFocusableElements(currentCell); + const focusedElement = document.activeElement; - focusableElements.push(...buttons, ...links, ...inputs, ...selects, ...textareas); - } + // Include the cell itself as the first focusable element + const cellAndFocusableElements = [currentCell, ...focusableElements]; + const focusedIndex = cellAndFocusableElements.indexOf(focusedElement); - const focusedElementIndex = focusableElements.indexOf(document.activeElement); - if (focusableElements.length > 0) { - if (!event.shiftKey) { - // if navigating between more focusable elements within the cell - if (focusedElementIndex < focusableElements.length - 1) { - focusableElements[focusedElementIndex + 1].focus(); - event.preventDefault(); - return; - } else { - if (colIndex < totalCols - 1) { - nextColIndex = colIndex + 1; - } else if (rowIndex < totalRows - 1) { - nextColIndex = 0; - nextRowIndex = rowIndex + 1; - } else { - // Allow default behavior when reaching the last cell - return; - } - } - } else { - if (focusedElementIndex < focusableElements.length - 1) { - // if navigating between more focusable elements within the cell - focusableElements[focusedElementIndex + 1].focus(); - event.preventDefault(); - return; - } else { - if (colIndex > 0) { - nextColIndex = colIndex - 1; - } else if (rowIndex > 0) { - nextColIndex = totalCols - 1; - nextRowIndex = rowIndex - 1; - } else { - // Allow default behavior when reaching the first cell - return; - } - } - } + if (!event.shiftKey) { + // Tab key navigation + if (focusedIndex < cellAndFocusableElements.length - 1) { + // Move to the next focusable element within the cell + cellAndFocusableElements[focusedIndex + 1].focus(); + event.preventDefault(); + return; + } else { + // Move to the next cell + if (colIndex < totalCols - 1) { + nextColIndex = colIndex + 1; + } else if (rowIndex < totalRows - 1) { + nextColIndex = 0; + nextRowIndex = rowIndex + 1; } else { - if (!event.shiftKey) { - if (colIndex < totalCols - 1) { - nextColIndex = colIndex + 1; - } else if (rowIndex < totalRows - 1) { - nextColIndex = 0; - nextRowIndex = rowIndex + 1; - } else { - // Allow default behavior when reaching the last cell - return; - } - } else { - if (colIndex > 0) { - nextColIndex = colIndex - 1; - } else if (rowIndex > 0) { - nextColIndex = totalCols - 1; - nextRowIndex = rowIndex - 1; - } else { - // Allow default behavior when reaching the first cell - return; - } - } + return; // Allow default Tab behavior when reaching the end } - - break; + this.updateFocusState(nextRowIndex, nextColIndex); + event.preventDefault(); } - - default: + } else { + // Shift+Tab key navigation + if (focusedIndex < cellAndFocusableElements.length - 1) { + // Move to the previous focusable element within the cell + cellAndFocusableElements[focusedIndex + 1].focus(); + event.preventDefault(); return; + } else { + // Move to the last focusable element of the previous cell + if (colIndex > 0) { + nextColIndex = colIndex - 1; + } else if (rowIndex > 0) { + nextColIndex = totalCols - 1; + nextRowIndex = rowIndex - 1; + } else { + return; // Allow default Shift+Tab behavior when at the beginning + } + this.updateFocusState(nextRowIndex, nextColIndex); + event.preventDefault(); + } } - - this.focusCell(nextRowIndex, nextColIndex); + }, + updateFocusState(nextRowIndex, nextColIndex) { this.focusedRowIndex = nextRowIndex === -1 ? null : nextRowIndex; this.focusedColIndex = nextColIndex; - this.highlightHeader(nextColIndex); + this.focusCell(nextRowIndex, nextColIndex); + }, - event.preventDefault(); + getFocusableElements(cell) { + if (!cell) return []; + const selectors = 'button, a, input, select, textarea'; + return Array.from(cell.querySelectorAll(selectors)); }, + getCell(rowIndex, colIndex) { if (rowIndex === -1) { return this.$refs[`header-${colIndex}`][0];