Added keyboard navigation
This commit is contained in:
parent
8aad89631b
commit
29b95c6b73
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Vite + TS</title>
|
<title>Spreadsheet example</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="spreadsheet"></div>
|
<div id="spreadsheet"></div>
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ export class Editor {
|
||||||
this.element.classList.add('hide')
|
this.element.classList.add('hide')
|
||||||
this.element.blur()
|
this.element.blur()
|
||||||
window.removeEventListener('click', this.handleClickOutside)
|
window.removeEventListener('click', this.handleClickOutside)
|
||||||
|
this.element.removeEventListener('keydown', this.handleKeydown)
|
||||||
|
|
||||||
|
this.root.focusTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
show(position: Position) {
|
show(position: Position) {
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export class Scroller {
|
||||||
private verticalScroller: HTMLDivElement
|
private verticalScroller: HTMLDivElement
|
||||||
private horizontalScroller: HTMLDivElement
|
private horizontalScroller: HTMLDivElement
|
||||||
private root: Spreadsheet
|
private root: Spreadsheet
|
||||||
|
|
||||||
private isSelecting = false
|
private isSelecting = false
|
||||||
|
|
||||||
constructor(root: Spreadsheet) {
|
constructor(root: Spreadsheet) {
|
||||||
|
|
@ -24,6 +24,7 @@ export class Scroller {
|
||||||
|
|
||||||
this.element.style.height = this.root.config.view.height + 'px'
|
this.element.style.height = this.root.config.view.height + 'px'
|
||||||
this.element.style.width = this.root.config.view.width + 'px'
|
this.element.style.width = this.root.config.view.width + 'px'
|
||||||
|
this.element.tabIndex = -1
|
||||||
|
|
||||||
this.updateScrollerSize() //* Init size set
|
this.updateScrollerSize() //* Init size set
|
||||||
|
|
||||||
|
|
@ -31,10 +32,10 @@ export class Scroller {
|
||||||
|
|
||||||
this.element.addEventListener('mousedown', this.handleClick)
|
this.element.addEventListener('mousedown', this.handleClick)
|
||||||
this.element.addEventListener('mousemove', event => {
|
this.element.addEventListener('mousemove', event => {
|
||||||
if(!this.isSelecting) return;
|
if (!this.isSelecting) return;
|
||||||
const {offsetX, offsetY} = event
|
const { offsetX, offsetY } = event
|
||||||
const lastSelectedCell = this.root.getCellByCoords(offsetX, offsetY)
|
const lastSelectedCell = this.root.getCellByCoords(offsetX, offsetY)
|
||||||
if(this.root.selection.selectedRange) {
|
if (this.root.selection.selectedRange) {
|
||||||
this.root.selection.selectedRange.to = lastSelectedCell
|
this.root.selection.selectedRange.to = lastSelectedCell
|
||||||
}
|
}
|
||||||
this.root.renderSheet()
|
this.root.renderSheet()
|
||||||
|
|
@ -49,18 +50,64 @@ export class Scroller {
|
||||||
this.root.showEditor(position)
|
this.root.showEditor(position)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.element.addEventListener('keydown', this.handleKeydown)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleKeydown = (event: KeyboardEvent) => {
|
||||||
|
event.preventDefault()
|
||||||
|
console.log(event.key)
|
||||||
|
//* Navigation
|
||||||
|
if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'].includes(event.key)) {
|
||||||
|
this.root.selection.selectedRange = null
|
||||||
|
switch (event.key) {
|
||||||
|
case 'ArrowLeft': {
|
||||||
|
if (this.root.selection.selectedCell && this.root.selection.selectedCell.column > 0) {
|
||||||
|
console.log('tick')
|
||||||
|
this.root.selection.selectedCell.column -= 1
|
||||||
|
this.root.renderSheet()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ArrowRight': {
|
||||||
|
if (this.root.selection.selectedCell && this.root.selection.selectedCell.column < this.root.config.columns.length - 1) {
|
||||||
|
this.root.selection.selectedCell.column += 1
|
||||||
|
this.root.renderSheet()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ArrowUp': {
|
||||||
|
if (this.root.selection.selectedCell && this.root.selection.selectedCell.row > 0) {
|
||||||
|
this.root.selection.selectedCell.row -= 1
|
||||||
|
this.root.renderSheet()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ArrowDown': {
|
||||||
|
if (this.root.selection.selectedCell && this.root.selection.selectedCell.row < this.root.config.rows.length - 1) {
|
||||||
|
this.root.selection.selectedCell.row += 1
|
||||||
|
this.root.renderSheet()
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(event.key === 'F2') {
|
||||||
|
if(!this.root.selection.selectedCell) return;
|
||||||
|
this.root.showEditor(this.root.selection.selectedCell)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleClick = (event: MouseEvent) => {
|
private handleClick = (event: MouseEvent) => {
|
||||||
if(event.button !== 0) return; // Left mouse button
|
if (event.button !== 0) return; // Left mouse button
|
||||||
const {offsetX, offsetY} = event
|
const { offsetX, offsetY } = event
|
||||||
const clickedCell = this.root.getCellByCoords(offsetX, offsetY)
|
const clickedCell = this.root.getCellByCoords(offsetX, offsetY)
|
||||||
this.isSelecting = true
|
this.isSelecting = true
|
||||||
this.root.selection.selectedRange = {
|
this.root.selection.selectedRange = {
|
||||||
from: clickedCell,
|
from: clickedCell,
|
||||||
to: clickedCell
|
to: clickedCell
|
||||||
}
|
}
|
||||||
this.root.selection.selectedCell = clickedCell
|
this.root.selection.selectedCell = clickedCell
|
||||||
|
|
||||||
this.root.renderSheet()
|
this.root.renderSheet()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,13 +59,11 @@ export class Sheet {
|
||||||
const firstColIdx = this.root.viewport.firstCol
|
const firstColIdx = this.root.viewport.firstCol
|
||||||
|
|
||||||
for(let row = firstRowIdx; row <= lastRowIdx; row++) {
|
for(let row = firstRowIdx; row <= lastRowIdx; row++) {
|
||||||
|
|
||||||
for(let col = firstColIdx; col <= lastColIdx; col++ ) {
|
for(let col = firstColIdx; col <= lastColIdx; col++ ) {
|
||||||
if(!this.root.config.columns[col] || !this.root.config.rows[row]) break; //* Prevent read undefined
|
if(!this.root.config.columns[col] || !this.root.config.rows[row]) break; //* Prevent read undefined
|
||||||
|
|
||||||
this.renderCell({column: col, row})
|
this.renderCell({column: col, row})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,13 @@ export class Spreadsheet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildComponent(): void {
|
private buildComponent(): void {
|
||||||
|
|
||||||
const content = document.createElement('div') //* Abstract
|
const content = document.createElement('div') //* Abstract
|
||||||
content.classList.add('content')
|
|
||||||
content.appendChild(this.header.element)
|
content.appendChild(this.header.element)
|
||||||
content.appendChild(this.sheet.element)
|
content.appendChild(this.sheet.element)
|
||||||
|
|
||||||
|
content.classList.add('content')
|
||||||
|
|
||||||
this.table.element.appendChild(this.toolbar.element)
|
this.table.element.appendChild(this.toolbar.element)
|
||||||
this.table.element.appendChild(content)
|
this.table.element.appendChild(content)
|
||||||
this.table.element.appendChild(this.scroller.element)
|
this.table.element.appendChild(this.scroller.element)
|
||||||
|
|
@ -97,6 +99,10 @@ export class Spreadsheet {
|
||||||
return this.config.view
|
return this.config.view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
focusTable() {
|
||||||
|
this.scroller.element.focus()
|
||||||
|
}
|
||||||
|
|
||||||
getCellByCoords(x: number, y: number) {
|
getCellByCoords(x: number, y: number) {
|
||||||
return this.sheet.getCellByCoords(x, y)
|
return this.sheet.getCellByCoords(x, y)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
isolation: isolate;
|
isolation: isolate;
|
||||||
border: 2px solid black;
|
border: 2px solid black;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sheet{
|
.sheet{
|
||||||
|
|
@ -21,6 +23,9 @@
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue