simple-csv-editor
Advanced tools
Comparing version
{ | ||
"name": "simple-csv-editor", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "A table editor for easily editing and retrieving CSV data.", | ||
@@ -5,0 +5,0 @@ "main": "src/simple-csv-editor.js", |
class SimpleCsvEditor { | ||
static controlsClassName = 'controls'; | ||
controlDefinitions = new Map([ | ||
['addRowBtn', () => { this.addRow(); }], | ||
['addColumnBtn', () => { this.addColumn(); }], | ||
['deleteRowBtn', () => { this.deleteRow(); }], | ||
['deleteColumnBtn', () => { this.deleteColumn(); }], | ||
['clearBtn', () => { this.setCsv(''); }], | ||
]); | ||
constructor({ | ||
@@ -18,4 +8,2 @@ id, | ||
quoteChar = '"', | ||
controls = null, | ||
enableDefaultControls = false, | ||
}) { | ||
@@ -33,3 +21,2 @@ if (Papa == null) { | ||
this.#registerControls(controls, enableDefaultControls); | ||
this.table = this.editor.appendChild(document.createElement('table')); | ||
@@ -52,35 +39,95 @@ | ||
#registerControls(controlsParam, enableDefaultControls) { | ||
const controlsElement = this.editor.appendChild(document.createElement('div')); | ||
controlsElement.className = SimpleCsvEditor.controlsClassName; | ||
#triggerOnChange() { | ||
if (this.onChange == null) { | ||
return; | ||
} | ||
this.onChange(this.getCsv()); | ||
} | ||
const controls = controlsParam ?? (enableDefaultControls | ||
? Array.from(this.controlDefinitions.keys()).map((className) => ({ | ||
className, | ||
label: className, | ||
})) | ||
: []); | ||
static #checkCursorPosition(cell) { | ||
const selection = window.getSelection(); | ||
if (selection.rangeCount === 0) { | ||
return null; | ||
} | ||
for (const control of controls) { | ||
const newButton = controlsElement.appendChild(document.createElement('button')); | ||
newButton.className = control.className; | ||
newButton.textContent = control.label; | ||
const range = selection.getRangeAt(0); | ||
const startNode = range.startContainer; | ||
const { startOffset } = range; | ||
const endNode = range.endContainer; | ||
const { endOffset } = range; | ||
newButton.addEventListener('click', () => { | ||
this.controlDefinitions.get(newButton.className)(); | ||
if (this.onChange != null) { | ||
this.onChange(this.getCsv()); | ||
} | ||
}); | ||
if (startNode === cell.firstChild && startOffset === 0) { | ||
return 'start'; | ||
} | ||
if (endNode === cell.lastChild && endOffset === cell.lastChild.textContent.length) { | ||
return 'end'; | ||
} | ||
return 'middle'; | ||
} | ||
static #jumpToPositionInCellGeneric(cell, idx) { | ||
if (cell == null) { | ||
return; | ||
} | ||
if (cell.firstChild == null) { | ||
cell.appendChild(document.createTextNode('')); | ||
} | ||
const textNode = cell.firstChild; | ||
const range = document.createRange(); | ||
range.setStart(textNode, idx); | ||
range.collapse(true); | ||
const selection = window.getSelection(); | ||
selection.removeAllRanges(); | ||
selection.addRange(range); | ||
} | ||
static #jumpToStartOfCell(cell) { | ||
this.#jumpToPositionInCellGeneric(cell, 0); | ||
} | ||
static #jumpToEndOfCell(cell) { | ||
this.#jumpToPositionInCellGeneric(cell, cell?.firstChild?.textContent.length); | ||
} | ||
#addCellToRow(row, cellIdx = -1) { | ||
const newCell = row.insertCell(cellIdx); | ||
newCell.contentEditable = true; | ||
if (this.onChange != null) { | ||
newCell.addEventListener('input', () => { | ||
this.onChange(this.getCsv()); | ||
}); | ||
} | ||
newCell.addEventListener('input', () => { | ||
this.#triggerOnChange(); | ||
}); | ||
newCell.addEventListener('keydown', (event) => { | ||
const rowIdx = event.target.parentElement.rowIndex; | ||
const { rows } = row.parentElement; | ||
switch (event.key) { | ||
case 'Enter': { | ||
event.preventDefault(); | ||
const newRowIdx = (event.shiftKey) ? rowIdx : rowIdx + 1; | ||
this.addRow(newRowIdx); | ||
rows[newRowIdx].cells[newCell.cellIndex].focus(); | ||
this.#triggerOnChange(); | ||
break; | ||
} | ||
case 'ArrowUp': | ||
event.preventDefault(); | ||
SimpleCsvEditor.#jumpToEndOfCell(rows[rowIdx - 1]?.cells[newCell.cellIndex]); | ||
break; | ||
case 'ArrowDown': | ||
event.preventDefault(); | ||
SimpleCsvEditor.#jumpToEndOfCell(rows[rowIdx + 1]?.cells[newCell.cellIndex]); | ||
break; | ||
case 'ArrowLeft': | ||
if (SimpleCsvEditor.#checkCursorPosition(newCell) === 'start') { | ||
event.preventDefault(); | ||
SimpleCsvEditor.#jumpToEndOfCell(row.cells[newCell.cellIndex - 1]); | ||
} | ||
break; | ||
case 'ArrowRight': | ||
if (SimpleCsvEditor.#checkCursorPosition(newCell) === 'end') { | ||
event.preventDefault(); | ||
SimpleCsvEditor.#jumpToStartOfCell(row.cells[newCell.cellIndex + 1]); | ||
} | ||
break; | ||
default: // Do nothing | ||
} | ||
}); | ||
} | ||
@@ -124,4 +171,5 @@ | ||
addRow(rowIdx = -1) { | ||
const firstRow = (this.table.rows.length > 0) ? this.table.rows[0] : null; | ||
const newRow = this.table.insertRow(rowIdx); | ||
for (let cellIdx = 0; cellIdx < this.table.rows[0].cells.length; cellIdx += 1) { | ||
for (let cellIdx = 0; cellIdx < (firstRow ?? newRow).cells.length; cellIdx += 1) { | ||
this.#addCellToRow(newRow); | ||
@@ -128,0 +176,0 @@ } |
Sorry, the diff of this file is not supported yet
30433
4.89%232
27.47%