codimd/public/js/lib/editor/table-editor.js

206 lines
6.5 KiB
JavaScript

/* global CodeMirror, $ */
import { TableEditor, Point, options, Alignment, FormatType } from '@susisu/mte-kernel'
// port of the code from: https://github.com/susisu/mte-demo/blob/master/src/main.js
// text editor interface
// see https://doc.esdoc.org/github.com/susisu/mte-kernel/class/lib/text-editor.js~ITextEditor.html
class TextEditorInterface {
constructor (editor) {
this.editor = editor
this.doc = editor.getDoc()
this.transaction = false
this.onDidFinishTransaction = null
}
getCursorPosition () {
const { line, ch } = this.doc.getCursor()
return new Point(line, ch)
}
setCursorPosition (pos) {
this.doc.setCursor({ line: pos.row, ch: pos.column })
}
setSelectionRange (range) {
this.doc.setSelection(
{ line: range.start.row, ch: range.start.column },
{ line: range.end.row, ch: range.end.column }
)
}
getLastRow () {
return this.doc.lineCount() - 1
}
acceptsTableEdit () {
return true
}
getLine (row) {
return this.doc.getLine(row)
}
insertLine (row, line) {
const lastRow = this.getLastRow()
if (row > lastRow) {
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
'\n' + line,
{ line: lastRow, ch: lastLine.length },
{ line: lastRow, ch: lastLine.length }
)
} else {
this.doc.replaceRange(
line + '\n',
{ line: row, ch: 0 },
{ line: row, ch: 0 }
)
}
}
deleteLine (row) {
const lastRow = this.getLastRow()
if (row >= lastRow) {
if (lastRow > 0) {
const preLastLine = this.getLine(lastRow - 1)
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
'',
{ line: lastRow - 1, ch: preLastLine.length },
{ line: lastRow, ch: lastLine.length }
)
} else {
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
'',
{ line: lastRow, ch: 0 },
{ line: lastRow, ch: lastLine.length }
)
}
} else {
this.doc.replaceRange(
'',
{ line: row, ch: 0 },
{ line: row + 1, ch: 0 }
)
}
}
replaceLines (startRow, endRow, lines) {
const lastRow = this.getLastRow()
if (endRow > lastRow) {
const lastLine = this.getLine(lastRow)
this.doc.replaceRange(
lines.join('\n'),
{ line: startRow, ch: 0 },
{ line: lastRow, ch: lastLine.length }
)
} else {
this.doc.replaceRange(
lines.join('\n') + '\n',
{ line: startRow, ch: 0 },
{ line: endRow, ch: 0 }
)
}
}
transact (func) {
this.transaction = true
func()
this.transaction = false
if (this.onDidFinishTransaction) {
this.onDidFinishTransaction.call(undefined)
}
}
}
export function initTableEditor (editor) {
// create an interface to the text editor
const editorIntf = new TextEditorInterface(editor)
// create a table editor object
const tableEditor = new TableEditor(editorIntf)
// options for the table editor
const opts = options({
smartCursor: true,
formatType: FormatType.NORMAL
})
// keymap of the commands
// from https://github.com/susisu/mte-demo/blob/master/src/main.js
const keyMap = CodeMirror.normalizeKeyMap({
Tab: () => { tableEditor.nextCell(opts) },
'Shift-Tab': () => { tableEditor.previousCell(opts) },
Enter: () => { tableEditor.nextRow(opts) },
'Ctrl-Enter': () => { tableEditor.escape(opts) },
'Cmd-Enter': () => { tableEditor.escape(opts) },
'Shift-Ctrl-Left': () => { tableEditor.alignColumn(Alignment.LEFT, opts) },
'Shift-Cmd-Left': () => { tableEditor.alignColumn(Alignment.LEFT, opts) },
'Shift-Ctrl-Right': () => { tableEditor.alignColumn(Alignment.RIGHT, opts) },
'Shift-Cmd-Right': () => { tableEditor.alignColumn(Alignment.RIGHT, opts) },
'Shift-Ctrl-Up': () => { tableEditor.alignColumn(Alignment.CENTER, opts) },
'Shift-Cmd-Up': () => { tableEditor.alignColumn(Alignment.CENTER, opts) },
'Shift-Ctrl-Down': () => { tableEditor.alignColumn(Alignment.NONE, opts) },
'Shift-Cmd-Down': () => { tableEditor.alignColumn(Alignment.NONE, opts) },
'Ctrl-Left': () => { tableEditor.moveFocus(0, -1, opts) },
'Cmd-Left': () => { tableEditor.moveFocus(0, -1, opts) },
'Ctrl-Right': () => { tableEditor.moveFocus(0, 1, opts) },
'Cmd-Right': () => { tableEditor.moveFocus(0, 1, opts) },
'Ctrl-Up': () => { tableEditor.moveFocus(-1, 0, opts) },
'Cmd-Up': () => { tableEditor.moveFocus(-1, 0, opts) },
'Ctrl-Down': () => { tableEditor.moveFocus(1, 0, opts) },
'Cmd-Down': () => { tableEditor.moveFocus(1, 0, opts) },
'Ctrl-K Ctrl-I': () => { tableEditor.insertRow(opts) },
'Cmd-K Cmd-I': () => { tableEditor.insertRow(opts) },
'Ctrl-L Ctrl-I': () => { tableEditor.deleteRow(opts) },
'Cmd-L Cmd-I': () => { tableEditor.deleteRow(opts) },
'Ctrl-K Ctrl-J': () => { tableEditor.insertColumn(opts) },
'Cmd-K Cmd-J': () => { tableEditor.insertColumn(opts) },
'Ctrl-L Ctrl-J': () => { tableEditor.deleteColumn(opts) },
'Cmd-L Cmd-J': () => { tableEditor.deleteColumn(opts) },
'Alt-Shift-Ctrl-Left': () => { tableEditor.moveColumn(-1, opts) },
'Alt-Shift-Cmd-Left': () => { tableEditor.moveColumn(-1, opts) },
'Alt-Shift-Ctrl-Right': () => { tableEditor.moveColumn(1, opts) },
'Alt-Shift-Cmd-Right': () => { tableEditor.moveColumn(1, opts) },
'Alt-Shift-Ctrl-Up': () => { tableEditor.moveRow(-1, opts) },
'Alt-Shift-Cmd-Up': () => { tableEditor.moveRow(-1, opts) },
'Alt-Shift-Ctrl-Down': () => { tableEditor.moveRow(1, opts) },
'Alt-Shift-Cmd-Down': () => { tableEditor.moveRow(1, opts) }
})
let lastActive
// enable keymap if the cursor is in a table
function updateActiveState () {
const tableTools = $('.toolbar .table-tools')
const active = tableEditor.cursorIsInTable(opts)
// avoid to update if state not changed
if (lastActive === active) {
return
}
if (active) {
tableTools.show()
tableTools.parent().scrollLeft(tableTools.parent()[0].scrollWidth)
editor.addKeyMap(keyMap)
} else {
tableTools.hide()
editor.removeKeyMap(keyMap)
tableEditor.resetSmartCursor()
}
lastActive = active
}
// event subscriptions
editor.on('cursorActivity', () => {
if (!editorIntf.transaction) {
updateActiveState()
}
})
editor.on('changes', () => {
if (!editorIntf.transaction) {
updateActiveState()
}
})
editorIntf.onDidFinishTransaction = () => {
updateActiveState()
}
return tableEditor
}