From b72d1a78eca2afedfd223acd88d61296742fcb97 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 4 Sep 2019 12:37:27 +0200 Subject: [PATCH] move inline formatting code out of react component --- .../views/rooms/BasicMessageComposer.js | 25 ++++++------ src/editor/dom.js | 16 ++++++++ src/editor/operations.js | 38 +++++++++++++++++++ 3 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 src/editor/operations.js diff --git a/src/components/views/rooms/BasicMessageComposer.js b/src/components/views/rooms/BasicMessageComposer.js index 81f4dcc8fe..39d24b1bab 100644 --- a/src/components/views/rooms/BasicMessageComposer.js +++ b/src/components/views/rooms/BasicMessageComposer.js @@ -21,7 +21,10 @@ import PropTypes from 'prop-types'; import EditorModel from '../../../editor/model'; import HistoryManager from '../../../editor/history'; import {setCaretPosition} from '../../../editor/caret'; -import {getCaretOffsetAndText, getSelectionOffsetAndText} from '../../../editor/dom'; +import { + formatInline, +} from '../../../editor/operations'; +import {getCaretOffsetAndText, getRangeForSelection, getSelectionOffsetAndText} from '../../../editor/dom'; import Autocomplete from '../rooms/Autocomplete'; import {autoCompleteCreator} from '../../../editor/parts'; import {renderModel} from '../../../editor/render'; @@ -455,26 +458,24 @@ export default class BasicMessageEditor extends React.Component { }); } - _wrapSelection(prefix, suffix = prefix) { - const {partCreator} = this.props.model; - this._replaceSelection(range => { - const parts = range.parts; - parts.splice(0, 0, partCreator.plain(prefix)); - parts.push(partCreator.plain(suffix)); - return parts; - }); + _wrapSelectionAsInline(prefix, suffix = prefix) { + const range = getRangeForSelection( + this._editorRef, + this.props.model, + document.getSelection()); + formatInline(range, prefix, suffix); } _formatBold = () => { - this._wrapSelection("**"); + this._wrapSelectionAsInline("**"); } _formatItalic = () => { - this._wrapSelection("*"); + this._wrapSelectionAsInline("*"); } _formatStrikethrough = () => { - this._wrapSelection("", ""); + this._wrapSelectionAsInline("", ""); } _formatQuote = () => { diff --git a/src/editor/dom.js b/src/editor/dom.js index 45e30d421a..03ee7f2cfc 100644 --- a/src/editor/dom.js +++ b/src/editor/dom.js @@ -142,3 +142,19 @@ function getTextNodeValue(node) { return nodeText; } } + +export function getRangeForSelection(editor, model, selection) { + const focusOffset = getSelectionOffsetAndText( + editor, + selection.focusNode, + selection.focusOffset, + ).offset; + const anchorOffset = getSelectionOffsetAndText( + editor, + selection.anchorNode, + selection.anchorOffset, + ).offset; + const focusPosition = focusOffset.asPosition(model); + const anchorPosition = anchorOffset.asPosition(model); + return model.startRange(focusPosition, anchorPosition); +} diff --git a/src/editor/operations.js b/src/editor/operations.js new file mode 100644 index 0000000000..4f0757948a --- /dev/null +++ b/src/editor/operations.js @@ -0,0 +1,38 @@ +/* +Copyright 2019 New Vector Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * Some common queries and transformations on the editor model + */ + +export function replaceRangeAndExpandSelection(model, range, newParts) { + model.transform(() => { + const oldLen = range.length; + const addedLen = range.replace(newParts); + const firstOffset = range.start.asOffset(model); + const lastOffset = firstOffset.add(oldLen + addedLen); + return model.startRange(firstOffset.asPosition(model), lastOffset.asPosition(model)); + }); +} + +export function formatInline(range, prefix, suffix = prefix) { + const {model, parts} = range; + const {partCreator} = model; + parts.unshift(partCreator.plain(prefix)); + parts.push(partCreator.plain(suffix)); + replaceRangeAndExpandSelection(model, range, parts); +}