You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
713 lines
23 KiB
713 lines
23 KiB
import { cloneDeep, isEqual } from 'lodash-es'; |
|
import Delta, { AttributeMap } from 'quill-delta'; |
|
import { EmbedBlot, Scope, TextBlot } from 'parchment'; |
|
import Quill from '../core/quill.js'; |
|
import logger from '../core/logger.js'; |
|
import Module from '../core/module.js'; |
|
const debug = logger('quill:keyboard'); |
|
const SHORTKEY = /Mac/i.test(navigator.platform) ? 'metaKey' : 'ctrlKey'; |
|
class Keyboard extends Module { |
|
static match(evt, binding) { |
|
if (['altKey', 'ctrlKey', 'metaKey', 'shiftKey'].some(key => { |
|
return !!binding[key] !== evt[key] && binding[key] !== null; |
|
})) { |
|
return false; |
|
} |
|
return binding.key === evt.key || binding.key === evt.which; |
|
} |
|
constructor(quill, options) { |
|
super(quill, options); |
|
this.bindings = {}; |
|
// @ts-expect-error Fix me later |
|
Object.keys(this.options.bindings).forEach(name => { |
|
// @ts-expect-error Fix me later |
|
if (this.options.bindings[name]) { |
|
// @ts-expect-error Fix me later |
|
this.addBinding(this.options.bindings[name]); |
|
} |
|
}); |
|
this.addBinding({ |
|
key: 'Enter', |
|
shiftKey: null |
|
}, this.handleEnter); |
|
this.addBinding({ |
|
key: 'Enter', |
|
metaKey: null, |
|
ctrlKey: null, |
|
altKey: null |
|
}, () => {}); |
|
if (/Firefox/i.test(navigator.userAgent)) { |
|
// Need to handle delete and backspace for Firefox in the general case #1171 |
|
this.addBinding({ |
|
key: 'Backspace' |
|
}, { |
|
collapsed: true |
|
}, this.handleBackspace); |
|
this.addBinding({ |
|
key: 'Delete' |
|
}, { |
|
collapsed: true |
|
}, this.handleDelete); |
|
} else { |
|
this.addBinding({ |
|
key: 'Backspace' |
|
}, { |
|
collapsed: true, |
|
prefix: /^.?$/ |
|
}, this.handleBackspace); |
|
this.addBinding({ |
|
key: 'Delete' |
|
}, { |
|
collapsed: true, |
|
suffix: /^.?$/ |
|
}, this.handleDelete); |
|
} |
|
this.addBinding({ |
|
key: 'Backspace' |
|
}, { |
|
collapsed: false |
|
}, this.handleDeleteRange); |
|
this.addBinding({ |
|
key: 'Delete' |
|
}, { |
|
collapsed: false |
|
}, this.handleDeleteRange); |
|
this.addBinding({ |
|
key: 'Backspace', |
|
altKey: null, |
|
ctrlKey: null, |
|
metaKey: null, |
|
shiftKey: null |
|
}, { |
|
collapsed: true, |
|
offset: 0 |
|
}, this.handleBackspace); |
|
this.listen(); |
|
} |
|
addBinding(keyBinding) { |
|
let context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; |
|
let handler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; |
|
const binding = normalize(keyBinding); |
|
if (binding == null) { |
|
debug.warn('Attempted to add invalid keyboard binding', binding); |
|
return; |
|
} |
|
if (typeof context === 'function') { |
|
context = { |
|
handler: context |
|
}; |
|
} |
|
if (typeof handler === 'function') { |
|
handler = { |
|
handler |
|
}; |
|
} |
|
const keys = Array.isArray(binding.key) ? binding.key : [binding.key]; |
|
keys.forEach(key => { |
|
const singleBinding = { |
|
...binding, |
|
key, |
|
...context, |
|
...handler |
|
}; |
|
this.bindings[singleBinding.key] = this.bindings[singleBinding.key] || []; |
|
this.bindings[singleBinding.key].push(singleBinding); |
|
}); |
|
} |
|
listen() { |
|
this.quill.root.addEventListener('keydown', evt => { |
|
if (evt.defaultPrevented || evt.isComposing) return; |
|
|
|
// evt.isComposing is false when pressing Enter/Backspace when composing in Safari |
|
// https://bugs.webkit.org/show_bug.cgi?id=165004 |
|
const isComposing = evt.keyCode === 229 && (evt.key === 'Enter' || evt.key === 'Backspace'); |
|
if (isComposing) return; |
|
const bindings = (this.bindings[evt.key] || []).concat(this.bindings[evt.which] || []); |
|
const matches = bindings.filter(binding => Keyboard.match(evt, binding)); |
|
if (matches.length === 0) return; |
|
// @ts-expect-error |
|
const blot = Quill.find(evt.target, true); |
|
if (blot && blot.scroll !== this.quill.scroll) return; |
|
const range = this.quill.getSelection(); |
|
if (range == null || !this.quill.hasFocus()) return; |
|
const [line, offset] = this.quill.getLine(range.index); |
|
const [leafStart, offsetStart] = this.quill.getLeaf(range.index); |
|
const [leafEnd, offsetEnd] = range.length === 0 ? [leafStart, offsetStart] : this.quill.getLeaf(range.index + range.length); |
|
const prefixText = leafStart instanceof TextBlot ? leafStart.value().slice(0, offsetStart) : ''; |
|
const suffixText = leafEnd instanceof TextBlot ? leafEnd.value().slice(offsetEnd) : ''; |
|
const curContext = { |
|
collapsed: range.length === 0, |
|
// @ts-expect-error Fix me later |
|
empty: range.length === 0 && line.length() <= 1, |
|
format: this.quill.getFormat(range), |
|
line, |
|
offset, |
|
prefix: prefixText, |
|
suffix: suffixText, |
|
event: evt |
|
}; |
|
const prevented = matches.some(binding => { |
|
if (binding.collapsed != null && binding.collapsed !== curContext.collapsed) { |
|
return false; |
|
} |
|
if (binding.empty != null && binding.empty !== curContext.empty) { |
|
return false; |
|
} |
|
if (binding.offset != null && binding.offset !== curContext.offset) { |
|
return false; |
|
} |
|
if (Array.isArray(binding.format)) { |
|
// any format is present |
|
if (binding.format.every(name => curContext.format[name] == null)) { |
|
return false; |
|
} |
|
} else if (typeof binding.format === 'object') { |
|
// all formats must match |
|
if (!Object.keys(binding.format).every(name => { |
|
// @ts-expect-error Fix me later |
|
if (binding.format[name] === true) return curContext.format[name] != null; |
|
// @ts-expect-error Fix me later |
|
if (binding.format[name] === false) return curContext.format[name] == null; |
|
// @ts-expect-error Fix me later |
|
return isEqual(binding.format[name], curContext.format[name]); |
|
})) { |
|
return false; |
|
} |
|
} |
|
if (binding.prefix != null && !binding.prefix.test(curContext.prefix)) { |
|
return false; |
|
} |
|
if (binding.suffix != null && !binding.suffix.test(curContext.suffix)) { |
|
return false; |
|
} |
|
// @ts-expect-error Fix me later |
|
return binding.handler.call(this, range, curContext, binding) !== true; |
|
}); |
|
if (prevented) { |
|
evt.preventDefault(); |
|
} |
|
}); |
|
} |
|
handleBackspace(range, context) { |
|
// Check for astral symbols |
|
const length = /[\uD800-\uDBFF][\uDC00-\uDFFF]$/.test(context.prefix) ? 2 : 1; |
|
if (range.index === 0 || this.quill.getLength() <= 1) return; |
|
let formats = {}; |
|
const [line] = this.quill.getLine(range.index); |
|
let delta = new Delta().retain(range.index - length).delete(length); |
|
if (context.offset === 0) { |
|
// Always deleting newline here, length always 1 |
|
const [prev] = this.quill.getLine(range.index - 1); |
|
if (prev) { |
|
const isPrevLineEmpty = prev.statics.blotName === 'block' && prev.length() <= 1; |
|
if (!isPrevLineEmpty) { |
|
// @ts-expect-error Fix me later |
|
const curFormats = line.formats(); |
|
const prevFormats = this.quill.getFormat(range.index - 1, 1); |
|
formats = AttributeMap.diff(curFormats, prevFormats) || {}; |
|
if (Object.keys(formats).length > 0) { |
|
// line.length() - 1 targets \n in line, another -1 for newline being deleted |
|
const formatDelta = new Delta() |
|
// @ts-expect-error Fix me later |
|
.retain(range.index + line.length() - 2).retain(1, formats); |
|
delta = delta.compose(formatDelta); |
|
} |
|
} |
|
} |
|
} |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.focus(); |
|
} |
|
handleDelete(range, context) { |
|
// Check for astral symbols |
|
const length = /^[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(context.suffix) ? 2 : 1; |
|
if (range.index >= this.quill.getLength() - length) return; |
|
let formats = {}; |
|
const [line] = this.quill.getLine(range.index); |
|
let delta = new Delta().retain(range.index).delete(length); |
|
// @ts-expect-error Fix me later |
|
if (context.offset >= line.length() - 1) { |
|
const [next] = this.quill.getLine(range.index + 1); |
|
if (next) { |
|
// @ts-expect-error Fix me later |
|
const curFormats = line.formats(); |
|
const nextFormats = this.quill.getFormat(range.index, 1); |
|
formats = AttributeMap.diff(curFormats, nextFormats) || {}; |
|
if (Object.keys(formats).length > 0) { |
|
delta = delta.retain(next.length() - 1).retain(1, formats); |
|
} |
|
} |
|
} |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.focus(); |
|
} |
|
handleDeleteRange(range) { |
|
deleteRange({ |
|
range, |
|
quill: this.quill |
|
}); |
|
this.quill.focus(); |
|
} |
|
handleEnter(range, context) { |
|
const lineFormats = Object.keys(context.format).reduce((formats, format) => { |
|
if (this.quill.scroll.query(format, Scope.BLOCK) && !Array.isArray(context.format[format])) { |
|
formats[format] = context.format[format]; |
|
} |
|
return formats; |
|
}, {}); |
|
const delta = new Delta().retain(range.index).delete(range.length).insert('\n', lineFormats); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.setSelection(range.index + 1, Quill.sources.SILENT); |
|
this.quill.focus(); |
|
} |
|
} |
|
const defaultOptions = { |
|
bindings: { |
|
bold: makeFormatHandler('bold'), |
|
italic: makeFormatHandler('italic'), |
|
underline: makeFormatHandler('underline'), |
|
indent: { |
|
// highlight tab or tab at beginning of list, indent or blockquote |
|
key: 'Tab', |
|
format: ['blockquote', 'indent', 'list'], |
|
handler(range, context) { |
|
if (context.collapsed && context.offset !== 0) return true; |
|
this.quill.format('indent', '+1', Quill.sources.USER); |
|
return false; |
|
} |
|
}, |
|
outdent: { |
|
key: 'Tab', |
|
shiftKey: true, |
|
format: ['blockquote', 'indent', 'list'], |
|
// highlight tab or tab at beginning of list, indent or blockquote |
|
handler(range, context) { |
|
if (context.collapsed && context.offset !== 0) return true; |
|
this.quill.format('indent', '-1', Quill.sources.USER); |
|
return false; |
|
} |
|
}, |
|
'outdent backspace': { |
|
key: 'Backspace', |
|
collapsed: true, |
|
shiftKey: null, |
|
metaKey: null, |
|
ctrlKey: null, |
|
altKey: null, |
|
format: ['indent', 'list'], |
|
offset: 0, |
|
handler(range, context) { |
|
if (context.format.indent != null) { |
|
this.quill.format('indent', '-1', Quill.sources.USER); |
|
} else if (context.format.list != null) { |
|
this.quill.format('list', false, Quill.sources.USER); |
|
} |
|
} |
|
}, |
|
'indent code-block': makeCodeBlockHandler(true), |
|
'outdent code-block': makeCodeBlockHandler(false), |
|
'remove tab': { |
|
key: 'Tab', |
|
shiftKey: true, |
|
collapsed: true, |
|
prefix: /\t$/, |
|
handler(range) { |
|
this.quill.deleteText(range.index - 1, 1, Quill.sources.USER); |
|
} |
|
}, |
|
tab: { |
|
key: 'Tab', |
|
handler(range, context) { |
|
if (context.format.table) return true; |
|
this.quill.history.cutoff(); |
|
const delta = new Delta().retain(range.index).delete(range.length).insert('\t'); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.history.cutoff(); |
|
this.quill.setSelection(range.index + 1, Quill.sources.SILENT); |
|
return false; |
|
} |
|
}, |
|
'blockquote empty enter': { |
|
key: 'Enter', |
|
collapsed: true, |
|
format: ['blockquote'], |
|
empty: true, |
|
handler() { |
|
this.quill.format('blockquote', false, Quill.sources.USER); |
|
} |
|
}, |
|
'list empty enter': { |
|
key: 'Enter', |
|
collapsed: true, |
|
format: ['list'], |
|
empty: true, |
|
handler(range, context) { |
|
const formats = { |
|
list: false |
|
}; |
|
if (context.format.indent) { |
|
formats.indent = false; |
|
} |
|
this.quill.formatLine(range.index, range.length, formats, Quill.sources.USER); |
|
} |
|
}, |
|
'checklist enter': { |
|
key: 'Enter', |
|
collapsed: true, |
|
format: { |
|
list: 'checked' |
|
}, |
|
handler(range) { |
|
const [line, offset] = this.quill.getLine(range.index); |
|
const formats = { |
|
// @ts-expect-error Fix me later |
|
...line.formats(), |
|
list: 'checked' |
|
}; |
|
const delta = new Delta().retain(range.index).insert('\n', formats) |
|
// @ts-expect-error Fix me later |
|
.retain(line.length() - offset - 1).retain(1, { |
|
list: 'unchecked' |
|
}); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.setSelection(range.index + 1, Quill.sources.SILENT); |
|
this.quill.scrollSelectionIntoView(); |
|
} |
|
}, |
|
'header enter': { |
|
key: 'Enter', |
|
collapsed: true, |
|
format: ['header'], |
|
suffix: /^$/, |
|
handler(range, context) { |
|
const [line, offset] = this.quill.getLine(range.index); |
|
const delta = new Delta().retain(range.index).insert('\n', context.format) |
|
// @ts-expect-error Fix me later |
|
.retain(line.length() - offset - 1).retain(1, { |
|
header: null |
|
}); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.setSelection(range.index + 1, Quill.sources.SILENT); |
|
this.quill.scrollSelectionIntoView(); |
|
} |
|
}, |
|
'table backspace': { |
|
key: 'Backspace', |
|
format: ['table'], |
|
collapsed: true, |
|
offset: 0, |
|
handler() {} |
|
}, |
|
'table delete': { |
|
key: 'Delete', |
|
format: ['table'], |
|
collapsed: true, |
|
suffix: /^$/, |
|
handler() {} |
|
}, |
|
'table enter': { |
|
key: 'Enter', |
|
shiftKey: null, |
|
format: ['table'], |
|
handler(range) { |
|
const module = this.quill.getModule('table'); |
|
if (module) { |
|
// @ts-expect-error |
|
const [table, row, cell, offset] = module.getTable(range); |
|
const shift = tableSide(table, row, cell, offset); |
|
if (shift == null) return; |
|
let index = table.offset(); |
|
if (shift < 0) { |
|
const delta = new Delta().retain(index).insert('\n'); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.setSelection(range.index + 1, range.length, Quill.sources.SILENT); |
|
} else if (shift > 0) { |
|
index += table.length(); |
|
const delta = new Delta().retain(index).insert('\n'); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.setSelection(index, Quill.sources.USER); |
|
} |
|
} |
|
} |
|
}, |
|
'table tab': { |
|
key: 'Tab', |
|
shiftKey: null, |
|
format: ['table'], |
|
handler(range, context) { |
|
const { |
|
event, |
|
line: cell |
|
} = context; |
|
const offset = cell.offset(this.quill.scroll); |
|
if (event.shiftKey) { |
|
this.quill.setSelection(offset - 1, Quill.sources.USER); |
|
} else { |
|
this.quill.setSelection(offset + cell.length(), Quill.sources.USER); |
|
} |
|
} |
|
}, |
|
'list autofill': { |
|
key: ' ', |
|
shiftKey: null, |
|
collapsed: true, |
|
format: { |
|
'code-block': false, |
|
blockquote: false, |
|
table: false |
|
}, |
|
prefix: /^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/, |
|
handler(range, context) { |
|
if (this.quill.scroll.query('list') == null) return true; |
|
const { |
|
length |
|
} = context.prefix; |
|
const [line, offset] = this.quill.getLine(range.index); |
|
if (offset > length) return true; |
|
let value; |
|
switch (context.prefix.trim()) { |
|
case '[]': |
|
case '[ ]': |
|
value = 'unchecked'; |
|
break; |
|
case '[x]': |
|
value = 'checked'; |
|
break; |
|
case '-': |
|
case '*': |
|
value = 'bullet'; |
|
break; |
|
default: |
|
value = 'ordered'; |
|
} |
|
this.quill.insertText(range.index, ' ', Quill.sources.USER); |
|
this.quill.history.cutoff(); |
|
const delta = new Delta().retain(range.index - offset).delete(length + 1) |
|
// @ts-expect-error Fix me later |
|
.retain(line.length() - 2 - offset).retain(1, { |
|
list: value |
|
}); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.history.cutoff(); |
|
this.quill.setSelection(range.index - length, Quill.sources.SILENT); |
|
return false; |
|
} |
|
}, |
|
'code exit': { |
|
key: 'Enter', |
|
collapsed: true, |
|
format: ['code-block'], |
|
prefix: /^$/, |
|
suffix: /^\s*$/, |
|
handler(range) { |
|
const [line, offset] = this.quill.getLine(range.index); |
|
let numLines = 2; |
|
let cur = line; |
|
while (cur != null && cur.length() <= 1 && cur.formats()['code-block']) { |
|
// @ts-expect-error |
|
cur = cur.prev; |
|
numLines -= 1; |
|
// Requisite prev lines are empty |
|
if (numLines <= 0) { |
|
const delta = new Delta() |
|
// @ts-expect-error Fix me later |
|
.retain(range.index + line.length() - offset - 2).retain(1, { |
|
'code-block': null |
|
}).delete(1); |
|
this.quill.updateContents(delta, Quill.sources.USER); |
|
this.quill.setSelection(range.index - 1, Quill.sources.SILENT); |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
}, |
|
'embed left': makeEmbedArrowHandler('ArrowLeft', false), |
|
'embed left shift': makeEmbedArrowHandler('ArrowLeft', true), |
|
'embed right': makeEmbedArrowHandler('ArrowRight', false), |
|
'embed right shift': makeEmbedArrowHandler('ArrowRight', true), |
|
'table down': makeTableArrowHandler(false), |
|
'table up': makeTableArrowHandler(true) |
|
} |
|
}; |
|
Keyboard.DEFAULTS = defaultOptions; |
|
function makeCodeBlockHandler(indent) { |
|
return { |
|
key: 'Tab', |
|
shiftKey: !indent, |
|
format: { |
|
'code-block': true |
|
}, |
|
handler(range, _ref) { |
|
let { |
|
event |
|
} = _ref; |
|
const CodeBlock = this.quill.scroll.query('code-block'); |
|
// @ts-expect-error |
|
const { |
|
TAB |
|
} = CodeBlock; |
|
if (range.length === 0 && !event.shiftKey) { |
|
this.quill.insertText(range.index, TAB, Quill.sources.USER); |
|
this.quill.setSelection(range.index + TAB.length, Quill.sources.SILENT); |
|
return; |
|
} |
|
const lines = range.length === 0 ? this.quill.getLines(range.index, 1) : this.quill.getLines(range); |
|
let { |
|
index, |
|
length |
|
} = range; |
|
lines.forEach((line, i) => { |
|
if (indent) { |
|
line.insertAt(0, TAB); |
|
if (i === 0) { |
|
index += TAB.length; |
|
} else { |
|
length += TAB.length; |
|
} |
|
// @ts-expect-error Fix me later |
|
} else if (line.domNode.textContent.startsWith(TAB)) { |
|
line.deleteAt(0, TAB.length); |
|
if (i === 0) { |
|
index -= TAB.length; |
|
} else { |
|
length -= TAB.length; |
|
} |
|
} |
|
}); |
|
this.quill.update(Quill.sources.USER); |
|
this.quill.setSelection(index, length, Quill.sources.SILENT); |
|
} |
|
}; |
|
} |
|
function makeEmbedArrowHandler(key, shiftKey) { |
|
const where = key === 'ArrowLeft' ? 'prefix' : 'suffix'; |
|
return { |
|
key, |
|
shiftKey, |
|
altKey: null, |
|
[where]: /^$/, |
|
handler(range) { |
|
let { |
|
index |
|
} = range; |
|
if (key === 'ArrowRight') { |
|
index += range.length + 1; |
|
} |
|
const [leaf] = this.quill.getLeaf(index); |
|
if (!(leaf instanceof EmbedBlot)) return true; |
|
if (key === 'ArrowLeft') { |
|
if (shiftKey) { |
|
this.quill.setSelection(range.index - 1, range.length + 1, Quill.sources.USER); |
|
} else { |
|
this.quill.setSelection(range.index - 1, Quill.sources.USER); |
|
} |
|
} else if (shiftKey) { |
|
this.quill.setSelection(range.index, range.length + 1, Quill.sources.USER); |
|
} else { |
|
this.quill.setSelection(range.index + range.length + 1, Quill.sources.USER); |
|
} |
|
return false; |
|
} |
|
}; |
|
} |
|
function makeFormatHandler(format) { |
|
return { |
|
key: format[0], |
|
shortKey: true, |
|
handler(range, context) { |
|
this.quill.format(format, !context.format[format], Quill.sources.USER); |
|
} |
|
}; |
|
} |
|
function makeTableArrowHandler(up) { |
|
return { |
|
key: up ? 'ArrowUp' : 'ArrowDown', |
|
collapsed: true, |
|
format: ['table'], |
|
handler(range, context) { |
|
// TODO move to table module |
|
const key = up ? 'prev' : 'next'; |
|
const cell = context.line; |
|
const targetRow = cell.parent[key]; |
|
if (targetRow != null) { |
|
if (targetRow.statics.blotName === 'table-row') { |
|
// @ts-expect-error |
|
let targetCell = targetRow.children.head; |
|
let cur = cell; |
|
while (cur.prev != null) { |
|
// @ts-expect-error |
|
cur = cur.prev; |
|
targetCell = targetCell.next; |
|
} |
|
const index = targetCell.offset(this.quill.scroll) + Math.min(context.offset, targetCell.length() - 1); |
|
this.quill.setSelection(index, 0, Quill.sources.USER); |
|
} |
|
} else { |
|
// @ts-expect-error |
|
const targetLine = cell.table()[key]; |
|
if (targetLine != null) { |
|
if (up) { |
|
this.quill.setSelection(targetLine.offset(this.quill.scroll) + targetLine.length() - 1, 0, Quill.sources.USER); |
|
} else { |
|
this.quill.setSelection(targetLine.offset(this.quill.scroll), 0, Quill.sources.USER); |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
}; |
|
} |
|
function normalize(binding) { |
|
if (typeof binding === 'string' || typeof binding === 'number') { |
|
binding = { |
|
key: binding |
|
}; |
|
} else if (typeof binding === 'object') { |
|
binding = cloneDeep(binding); |
|
} else { |
|
return null; |
|
} |
|
if (binding.shortKey) { |
|
binding[SHORTKEY] = binding.shortKey; |
|
delete binding.shortKey; |
|
} |
|
return binding; |
|
} |
|
|
|
// TODO: Move into quill.ts or editor.ts |
|
function deleteRange(_ref2) { |
|
let { |
|
quill, |
|
range |
|
} = _ref2; |
|
const lines = quill.getLines(range); |
|
let formats = {}; |
|
if (lines.length > 1) { |
|
const firstFormats = lines[0].formats(); |
|
const lastFormats = lines[lines.length - 1].formats(); |
|
formats = AttributeMap.diff(lastFormats, firstFormats) || {}; |
|
} |
|
quill.deleteText(range, Quill.sources.USER); |
|
if (Object.keys(formats).length > 0) { |
|
quill.formatLine(range.index, 1, formats, Quill.sources.USER); |
|
} |
|
quill.setSelection(range.index, Quill.sources.SILENT); |
|
} |
|
function tableSide(_table, row, cell, offset) { |
|
if (row.prev == null && row.next == null) { |
|
if (cell.prev == null && cell.next == null) { |
|
return offset === 0 ? -1 : 1; |
|
} |
|
return cell.prev == null ? -1 : 1; |
|
} |
|
if (row.prev == null) { |
|
return -1; |
|
} |
|
if (row.next == null) { |
|
return 1; |
|
} |
|
return null; |
|
} |
|
export { Keyboard as default, SHORTKEY, normalize, deleteRange }; |
|
//# sourceMappingURL=keyboard.js.map
|