diff --git a/src/components/table/Table.ts b/src/components/table/Table.ts index 85ac1df..6e68463 100644 --- a/src/components/table/Table.ts +++ b/src/components/table/Table.ts @@ -12,7 +12,7 @@ export class Table extends ExcelComponent { constructor($root: any) { super($root, { name: 'Table', - listeners: ['mousedown'], + listeners: ['mousedown', 'keydown'], }); } @@ -35,4 +35,8 @@ export class Table extends ExcelComponent { selectHandler(event, this.selection); resizeHandler(this.$root, event); } + + onKeydown(event: KeyboardEvent) { + selectHandler(event, this.selection); + } } diff --git a/src/components/table/TableSelection.ts b/src/components/table/TableSelection.ts index 661e8e1..edefab8 100644 --- a/src/components/table/TableSelection.ts +++ b/src/components/table/TableSelection.ts @@ -1,9 +1,10 @@ import { $, Dom } from '../../core/dom'; +import { getParamsFromCellId } from './table.functions'; export class TableSelection { static selectedClassName = 'selected'; private group: Dom[]; - private current: Dom; + public current: Dom; constructor() { this.group = []; @@ -15,16 +16,30 @@ export class TableSelection { this.group = [$el]; this.current = $el; $el.addClass(TableSelection.selectedClassName); + $el.focus(); + } + + selectByCellId(cellID: { col: number, row: number }) { + let { col, row } = cellID; + + if (col <= 0) col = 0; + if (row <= 0) row = 0; + + this.clearSelection(); + this.current = $(`[data-id="${row}:${col}"]`); + this.current.focus(); + this.current.addClass(TableSelection.selectedClassName); } clearSelection() { - this.group.forEach(el => el.removeClass(TableSelection.selectedClassName)); + this.group.forEach(el => el?.removeClass(TableSelection.selectedClassName)); + this.current?.removeClass(TableSelection.selectedClassName); this.group = []; } selectGroup($el: Dom) { - const startCellParams = this.getParamsFromCellId(this.current.data.id); - const selectedCellParams = this.getParamsFromCellId($el.data.id); + const startCellParams = getParamsFromCellId(this.current.data.id); + const selectedCellParams = getParamsFromCellId($el.data.id); const startCol = Math.min(startCellParams.col, selectedCellParams.col); const endCol = Math.max(startCellParams.col, selectedCellParams.col); @@ -42,11 +57,4 @@ export class TableSelection { } } } - - getParamsFromCellId(cellId: string) { - const row = +cellId.split(':')[0]; - const col = +cellId.split(':')[1]; - - return { col, row }; - } } diff --git a/src/components/table/handlers/table.select.handler.ts b/src/components/table/handlers/table.select.handler.ts index 834dba1..6bdeb71 100644 --- a/src/components/table/handlers/table.select.handler.ts +++ b/src/components/table/handlers/table.select.handler.ts @@ -1,10 +1,75 @@ import { $ } from '../../../core/dom'; -import { isCell } from '../table.functions'; +import { getParamsFromCellId, isCell } from '../table.functions'; import { TableSelection } from '../TableSelection'; -export function selectHandler(event: MouseEvent, selection: TableSelection) { - if (isCell(event)) { - if (event.shiftKey) selection.selectGroup($(event.target as HTMLElement)); - else selection.select($(event.target as HTMLElement)); +export function selectHandler(event: MouseEvent | KeyboardEvent, selection: TableSelection) { + switch (event.type) { + case 'mousedown': { + onMouseDownHandler(); + break; + } + case 'keydown': { + onKeyDownHandler(); + break; + } + default: break; + } + + function onMouseDownHandler() { + if (isCell(event)) { + if (event.shiftKey) selection.selectGroup($(event.target as HTMLElement)); + else selection.select($(event.target as HTMLElement)); + } + } + + function onKeyDownHandler() { + const { key } = event as KeyboardEvent; + const handleKeys = [ + 'ArrowDown', + 'ArrowUp', + 'ArrowRight', + 'ArrowLeft', + 'Enter', + 'Tab', + ]; + + if (!selection?.current || !handleKeys.includes(key)) return; + + const currentCellId = selection.current?.data?.id; + let { row, col } = getParamsFromCellId(currentCellId); + + switch (key) { + case 'ArrowDown': { + row++; + break; + } + case 'ArrowUp': { + row--; + break; + } + case 'ArrowRight': { + col++; + break; + } + case 'ArrowLeft': { + col--; + break; + } + case 'Enter': { + if (event.shiftKey) return; + + event.preventDefault(); + row++; + break; + } + case 'Tab': { + event.preventDefault(); + col++; + break; + } + default: break; + } + + selection.selectByCellId({ row, col }); } } diff --git a/src/components/table/table.functions.ts b/src/components/table/table.functions.ts index ef9571f..e4e7afb 100644 --- a/src/components/table/table.functions.ts +++ b/src/components/table/table.functions.ts @@ -3,3 +3,10 @@ import { $ } from '../../core/dom'; export function isCell(event: Event): boolean { return $(event.target as HTMLElement).data.type === 'cell'; } + +export function getParamsFromCellId(cellId: string) { + const row = +cellId.split(':')[0]; + const col = +cellId.split(':')[1]; + + return { col, row }; +} diff --git a/src/core/dom.ts b/src/core/dom.ts index c98804e..845e202 100644 --- a/src/core/dom.ts +++ b/src/core/dom.ts @@ -53,6 +53,10 @@ export class Dom implements DomClass { this.$el.removeEventListener(eventType, callback); } + focus() { + this.$el.focus(); + } + get data() { return this.$el.dataset; } @@ -80,11 +84,11 @@ export class Dom implements DomClass { } addClass(className: string) { - this.$el.classList.add(className); + this.$el?.classList.add(className); } removeClass(className: string) { - this.$el.classList.remove(className); + this.$el?.classList.remove(className); } }