mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-16 21:24:59 +08:00
move quote formatting out of react component
This commit is contained in:
parent
b72d1a78ec
commit
b35a3531bb
@ -22,9 +22,11 @@ import EditorModel from '../../../editor/model';
|
|||||||
import HistoryManager from '../../../editor/history';
|
import HistoryManager from '../../../editor/history';
|
||||||
import {setCaretPosition} from '../../../editor/caret';
|
import {setCaretPosition} from '../../../editor/caret';
|
||||||
import {
|
import {
|
||||||
|
replaceRangeAndExpandSelection,
|
||||||
|
formatRangeAsQuote,
|
||||||
formatInline,
|
formatInline,
|
||||||
} from '../../../editor/operations';
|
} from '../../../editor/operations';
|
||||||
import {getCaretOffsetAndText, getRangeForSelection, getSelectionOffsetAndText} from '../../../editor/dom';
|
import {getCaretOffsetAndText, getRangeForSelection} from '../../../editor/dom';
|
||||||
import Autocomplete from '../rooms/Autocomplete';
|
import Autocomplete from '../rooms/Autocomplete';
|
||||||
import {autoCompleteCreator} from '../../../editor/parts';
|
import {autoCompleteCreator} from '../../../editor/parts';
|
||||||
import {renderModel} from '../../../editor/render';
|
import {renderModel} from '../../../editor/render';
|
||||||
@ -427,37 +429,6 @@ export default class BasicMessageEditor extends React.Component {
|
|||||||
return caretPosition;
|
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) {
|
_wrapSelectionAsInline(prefix, suffix = prefix) {
|
||||||
const range = getRangeForSelection(
|
const range = getRangeForSelection(
|
||||||
this._editorRef,
|
this._editorRef,
|
||||||
@ -479,30 +450,11 @@ export default class BasicMessageEditor extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_formatQuote = () => {
|
_formatQuote = () => {
|
||||||
const {model} = this.props;
|
const range = getRangeForSelection(
|
||||||
const {partCreator} = this.props.model;
|
this._editorRef,
|
||||||
this._replaceSelection(range => {
|
this.props.model,
|
||||||
const parts = range.parts;
|
document.getSelection());
|
||||||
parts.splice(0, 0, partCreator.plain("> "));
|
formatRangeAsQuote(range);
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_formatCodeBlock = () => {
|
_formatCodeBlock = () => {
|
||||||
|
@ -45,7 +45,7 @@ export function getCaretOffsetAndText(editor, sel) {
|
|||||||
return {caret: offset, text};
|
return {caret: offset, text};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSelectionOffsetAndText(editor, selectionNode, selectionOffset) {
|
function getSelectionOffsetAndText(editor, selectionNode, selectionOffset) {
|
||||||
// sometimes selectionNode is an element, and then selectionOffset means
|
// sometimes selectionNode is an element, and then selectionOffset means
|
||||||
// the index of a child element ... - 1 🤷
|
// the index of a child element ... - 1 🤷
|
||||||
if (selectionNode.nodeType === Node.ELEMENT_NODE && selectionOffset !== 0) {
|
if (selectionNode.nodeType === Node.ELEMENT_NODE && selectionOffset !== 0) {
|
||||||
|
@ -23,4 +23,8 @@ export default class DocumentOffset {
|
|||||||
asPosition(model) {
|
asPosition(model) {
|
||||||
return model.positionForOffset(this.offset, this.atNodeEnd);
|
return model.positionForOffset(this.offset, this.atNodeEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add(delta, atNodeEnd = false) {
|
||||||
|
return new DocumentOffset(this.offset + delta, atNodeEnd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
export function formatInline(range, prefix, suffix = prefix) {
|
||||||
const {model, parts} = range;
|
const {model, parts} = range;
|
||||||
const {partCreator} = model;
|
const {partCreator} = model;
|
||||||
|
@ -33,6 +33,10 @@ export default class Range {
|
|||||||
this._start = this._start.backwardsWhile(this._model, predicate);
|
this._start = this._start.backwardsWhile(this._model, predicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get model() {
|
||||||
|
return this._model;
|
||||||
|
}
|
||||||
|
|
||||||
get text() {
|
get text() {
|
||||||
let text = "";
|
let text = "";
|
||||||
this._start.iteratePartsBetween(this._end, this._model, (part, startIdx, endIdx) => {
|
this._start.iteratePartsBetween(this._end, this._model, (part, startIdx, endIdx) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user