add transform step during editor model update

pull/21833/head
Bruno Windels 2019-08-26 16:09:46 +02:00
parent 0e65f71a37
commit f8f0e77bde
1 changed files with 38 additions and 2 deletions

View File

@ -19,6 +19,15 @@ import {diffAtCaret, diffDeletion} from "./diff";
import DocumentPosition from "./position"; import DocumentPosition from "./position";
import Range from "./range"; import Range from "./range";
/**
* @callback TransformCallback
* @param {DocumentPosition?} caretPosition the position where the caret should be position
* @param {string?} inputType the inputType of the DOM input event
* @param {object?} diff an object with `removed` and `added` strings
* @return {Number?} addedLen how many characters were added/removed (-) before the caret during the transformation step.
This is used to adjust the caret position.
*/
export default class EditorModel { export default class EditorModel {
constructor(parts, partCreator, updateCallback = null) { constructor(parts, partCreator, updateCallback = null) {
this._parts = parts; this._parts = parts;
@ -26,7 +35,19 @@ export default class EditorModel {
this._activePartIdx = null; this._activePartIdx = null;
this._autoComplete = null; this._autoComplete = null;
this._autoCompletePartIdx = null; this._autoCompletePartIdx = null;
this._transformCallback = null;
this.setUpdateCallback(updateCallback); this.setUpdateCallback(updateCallback);
this._updateInProgress = false;
}
/** Set a callback for the transformation step.
* While processing an update, right before calling the update callback,
* a transform callback can be called, which serves to do modifications
* on the model that can span multiple parts. Also see `startRange()`.
* @param {TransformCallback} transformCallback
*/
setTransformCallback(transformCallback) {
this._transformCallback = transformCallback;
} }
setUpdateCallback(updateCallback) { setUpdateCallback(updateCallback) {
@ -133,6 +154,7 @@ export default class EditorModel {
} }
update(newValue, inputType, caret) { update(newValue, inputType, caret) {
this._updateInProgress = true;
const diff = this._diff(newValue, inputType, caret); const diff = this._diff(newValue, inputType, caret);
const position = this.positionForOffset(diff.at, caret.atNodeEnd); const position = this.positionForOffset(diff.at, caret.atNodeEnd);
let removedOffsetDecrease = 0; let removedOffsetDecrease = 0;
@ -147,11 +169,23 @@ export default class EditorModel {
} }
this._mergeAdjacentParts(); this._mergeAdjacentParts();
const caretOffset = diff.at - removedOffsetDecrease + addedLen; const caretOffset = diff.at - removedOffsetDecrease + addedLen;
const newPosition = this.positionForOffset(caretOffset, true); let newPosition = this.positionForOffset(caretOffset, true);
this._setActivePart(newPosition, canOpenAutoComplete); this._setActivePart(newPosition, canOpenAutoComplete);
if (this._transformCallback) {
const transformAddedLen = this._transform(newPosition, inputType, diff);
if (transformAddedLen !== 0) {
newPosition = this.positionForOffset(caretOffset + transformAddedLen, true);
}
}
this._updateInProgress = false;
this._updateCallback(newPosition, inputType, diff); this._updateCallback(newPosition, inputType, diff);
} }
_transform(newPosition, inputType, diff) {
const result = this._transformCallback(newPosition, inputType, diff);
return Number.isFinite(result) ? result : 0;
}
_setActivePart(pos, canOpenAutoComplete) { _setActivePart(pos, canOpenAutoComplete) {
const {index} = pos; const {index} = pos;
const part = this._parts[index]; const part = this._parts[index];
@ -367,6 +401,8 @@ export default class EditorModel {
insertIdx += 1; insertIdx += 1;
} }
this._mergeAdjacentParts(); this._mergeAdjacentParts();
this._updateCallback(); if (!this._updateInProgress) {
this._updateCallback();
}
} }
} }