move quote formatting out of react component

pull/21833/head
Bruno Windels 2019-09-04 12:40:03 +02:00
parent b72d1a78ec
commit b35a3531bb
5 changed files with 55 additions and 57 deletions

View File

@ -22,9 +22,11 @@ import EditorModel from '../../../editor/model';
import HistoryManager from '../../../editor/history';
import {setCaretPosition} from '../../../editor/caret';
import {
replaceRangeAndExpandSelection,
formatRangeAsQuote,
formatInline,
} from '../../../editor/operations';
import {getCaretOffsetAndText, getRangeForSelection, getSelectionOffsetAndText} from '../../../editor/dom';
import {getCaretOffsetAndText, getRangeForSelection} from '../../../editor/dom';
import Autocomplete from '../rooms/Autocomplete';
import {autoCompleteCreator} from '../../../editor/parts';
import {renderModel} from '../../../editor/render';
@ -427,37 +429,6 @@ export default class BasicMessageEditor extends React.Component {
return caretPosition;
}
_replaceSelection(callback) {
const selection = document.getSelection();
if (selection.isCollapsed) {
return;
}
const focusOffset = getSelectionOffsetAndText(
this._editorRef,
selection.focusNode,
selection.focusOffset,
).offset;
const anchorOffset = getSelectionOffsetAndText(
this._editorRef,
selection.anchorNode,
selection.anchorOffset,
).offset;
const {model} = this.props;
const focusPosition = focusOffset.asPosition(model);
const anchorPosition = anchorOffset.asPosition(model);
const range = model.startRange(focusPosition, anchorPosition);
const firstPosition = focusPosition.compare(anchorPosition) < 0 ? focusPosition : anchorPosition;
model.transform(() => {
const oldLen = range.length;
const newParts = callback(range);
const addedLen = range.replace(newParts);
const lastOffset = firstPosition.asOffset(model);
lastOffset.offset += oldLen + addedLen;
return lastOffset.asPosition(model);
});
}
_wrapSelectionAsInline(prefix, suffix = prefix) {
const range = getRangeForSelection(
this._editorRef,
@ -479,30 +450,11 @@ export default class BasicMessageEditor extends React.Component {
}
_formatQuote = () => {
const {model} = this.props;
const {partCreator} = this.props.model;
this._replaceSelection(range => {
const parts = range.parts;
parts.splice(0, 0, partCreator.plain("> "));
const startsWithPartial = range.start.offset !== 0;
const isFirstPart = range.start.index === 0;
const previousIsNewline = !isFirstPart && model.parts[range.start.index - 1].type === "newline";
// prepend a newline if there is more text before the range on this line
if (startsWithPartial || (!isFirstPart && !previousIsNewline)) {
parts.splice(0, 0, partCreator.newline());
}
// start at position 1 to make sure we skip the potentially inserted newline above,
// as we already inserted a quote sign for it above
for (let i = 1; i < parts.length; ++i) {
const part = parts[i];
if (part.type === "newline") {
parts.splice(i + 1, 0, partCreator.plain("> "));
}
}
parts.push(partCreator.newline());
parts.push(partCreator.newline());
return parts;
});
const range = getRangeForSelection(
this._editorRef,
this.props.model,
document.getSelection());
formatRangeAsQuote(range);
}
_formatCodeBlock = () => {

View File

@ -45,7 +45,7 @@ export function getCaretOffsetAndText(editor, sel) {
return {caret: offset, text};
}
export function getSelectionOffsetAndText(editor, selectionNode, selectionOffset) {
function getSelectionOffsetAndText(editor, selectionNode, selectionOffset) {
// sometimes selectionNode is an element, and then selectionOffset means
// the index of a child element ... - 1 🤷
if (selectionNode.nodeType === Node.ELEMENT_NODE && selectionOffset !== 0) {

View File

@ -23,4 +23,8 @@ export default class DocumentOffset {
asPosition(model) {
return model.positionForOffset(this.offset, this.atNodeEnd);
}
add(delta, atNodeEnd = false) {
return new DocumentOffset(this.offset + delta, atNodeEnd);
}
}

View File

@ -29,6 +29,44 @@ export function replaceRangeAndExpandSelection(model, range, newParts) {
});
}
export function rangeStartsAtBeginningOfLine(range) {
const {model} = range;
const startsWithPartial = range.start.offset !== 0;
const isFirstPart = range.start.index === 0;
const previousIsNewline = !isFirstPart && model.parts[range.start.index - 1].type === "newline";
return !startsWithPartial && (isFirstPart || previousIsNewline);
}
export function rangeEndsAtEndOfLine(range) {
const {model} = range;
const lastPart = model.parts[range.end.index];
const endsWithPartial = range.end.offset !== lastPart.length;
const isLastPart = range.end.index === model.parts.length - 1;
const nextIsNewline = !isLastPart && model.parts[range.end.index + 1].type === "newline";
return !endsWithPartial && (isLastPart || nextIsNewline);
}
export function formatRangeAsQuote(range) {
const {model, parts} = range;
const {partCreator} = model;
for (let i = 0; i < parts.length; ++i) {
const part = parts[i];
if (part.type === "newline") {
parts.splice(i + 1, 0, partCreator.plain("> "));
}
}
parts.unshift(partCreator.plain("> "));
if (!rangeStartsAtBeginningOfLine(range)) {
parts.unshift(partCreator.newline());
}
if (rangeEndsAtEndOfLine(range)) {
parts.push(partCreator.newline());
}
parts.push(partCreator.newline());
replaceRangeAndExpandSelection(model, range, parts);
}
export function formatInline(range, prefix, suffix = prefix) {
const {model, parts} = range;
const {partCreator} = model;

View File

@ -33,6 +33,10 @@ export default class Range {
this._start = this._start.backwardsWhile(this._model, predicate);
}
get model() {
return this._model;
}
get text() {
let text = "";
this._start.iteratePartsBetween(this._end, this._model, (part, startIdx, endIdx) => {