@tinymce/tinymce-react
Advanced tools
Comparing version 3.11.2-rc.20210324015840574.29b229e to 3.11.2-rc.20210326020226308.92b7f46
@@ -7,2 +7,10 @@ # Change log | ||
## Unreleased | ||
### Added | ||
- Storybook demo for a controlled component with a fixed value. INT-2352 | ||
- Storybook demo for a controlled component with a maximum length. INT-2462 | ||
### Changed | ||
- When used as a controlled component the editor will rollback a change if it is not set via the `value` prop within a millisecond. INT-2352 | ||
## 3.11.1 - 2021-03-24 | ||
@@ -9,0 +17,0 @@ ### Fixed |
@@ -49,2 +49,4 @@ /** | ||
private boundHandlers; | ||
private rollbackTimer; | ||
private valueCursor; | ||
constructor(props: Partial<IAllProps>); | ||
@@ -63,4 +65,8 @@ componentDidUpdate(prevProps: Partial<IAllProps>): void; | ||
private bindHandlers; | ||
private rollbackChange; | ||
private handleBeforeInput; | ||
private handleBeforeInputSpecial; | ||
private handleEditorChange; | ||
private handleEditorChangeSpecial; | ||
private initialise; | ||
} |
@@ -42,2 +42,4 @@ "use strict"; | ||
var EditorPropTypes_1 = require("./EditorPropTypes"); | ||
var changeEvents = function () { var _a, _b, _c; return ((_c = (_b = (_a = TinyMCE_1.getTinymce()) === null || _a === void 0 ? void 0 : _a.Env) === null || _b === void 0 ? void 0 : _b.browser) === null || _c === void 0 ? void 0 : _c.isIE()) ? 'change keyup compositionend setcontent' : 'input compositionend setcontent'; }; | ||
var beforeInputEvent = Utils_1.isBeforeInputEventAvailable() ? 'beforeinput' : 'SelectionChange'; | ||
var Editor = /** @class */ (function (_super) { | ||
@@ -48,2 +50,33 @@ __extends(Editor, _super); | ||
var _this = _super.call(this, props) || this; | ||
_this.rollbackTimer = undefined; | ||
_this.valueCursor = undefined; | ||
_this.rollbackChange = function () { | ||
var editor = _this.editor; | ||
var value = _this.props.value; | ||
if (editor && value && value !== _this.currentContent) { | ||
editor.undoManager.ignore(function () { | ||
editor.setContent(value); | ||
// only restore cursor on inline editors when they are focused | ||
// as otherwise it will cause a focus grab | ||
if (_this.valueCursor && (!_this.inline || editor.hasFocus())) { | ||
try { | ||
editor.selection.moveToBookmark(_this.valueCursor); | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
}); | ||
} | ||
_this.rollbackTimer = undefined; | ||
}; | ||
_this.handleBeforeInput = function (_evt) { | ||
var _a; | ||
if (_this.props.value !== undefined && _this.props.value === _this.currentContent && _this.editor) { | ||
_this.valueCursor = (_a = _this.editor) === null || _a === void 0 ? void 0 : _a.selection.getBookmark(3); | ||
} | ||
}; | ||
_this.handleBeforeInputSpecial = function (evt) { | ||
if (evt.key === 'Enter' || evt.key === 'Backspace' || evt.key === 'Delete') { | ||
_this.handleBeforeInput(evt); | ||
} | ||
}; | ||
_this.handleEditorChange = function (_evt) { | ||
@@ -53,2 +86,7 @@ var editor = _this.editor; | ||
var newContent = editor.getContent(); | ||
if (_this.props.value !== undefined && _this.props.value !== newContent) { | ||
// start a timer and revert to the value if not applied in time | ||
clearTimeout(_this.rollbackTimer); | ||
_this.rollbackTimer = window.setTimeout(_this.rollbackChange, 1); | ||
} | ||
if (newContent !== _this.currentContent) { | ||
@@ -64,2 +102,7 @@ _this.currentContent = newContent; | ||
}; | ||
_this.handleEditorChangeSpecial = function (evt) { | ||
if (evt.key === 'Backspace' || evt.key === 'Delete') { | ||
_this.handleEditorChange(evt); | ||
} | ||
}; | ||
_this.initialise = function () { | ||
@@ -128,2 +171,6 @@ var target = _this.elementRef.current; | ||
var _a, _b; | ||
if (this.rollbackTimer) { | ||
clearTimeout(this.rollbackTimer); | ||
this.rollbackTimer = undefined; | ||
} | ||
if (this.editor) { | ||
@@ -145,3 +192,3 @@ this.bindHandlers(prevProps); | ||
// so we don't try to keep their selection unless they are currently focused | ||
var bookmark; | ||
var cursor; | ||
if (!_this.inline || localEditor_1.hasFocus()) { | ||
@@ -151,12 +198,20 @@ try { | ||
// possibly only in inline mode but I'm not taking chances | ||
bookmark = localEditor_1.selection.getBookmark(3); | ||
cursor = localEditor_1.selection.getBookmark(3); | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
var valueCursor = _this.valueCursor; | ||
localEditor_1.setContent(_this.props.value); | ||
if (bookmark) { | ||
try { | ||
localEditor_1.selection.moveToBookmark(bookmark); | ||
if (!_this.inline || localEditor_1.hasFocus()) { | ||
for (var _i = 0, _a = [cursor, valueCursor]; _i < _a.length; _i++) { | ||
var bookmark = _a[_i]; | ||
if (bookmark) { | ||
try { | ||
localEditor_1.selection.moveToBookmark(bookmark); | ||
_this.valueCursor = bookmark; | ||
break; | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
@@ -185,3 +240,7 @@ }); | ||
if (editor) { | ||
editor.off('change keyup compositionend setcontent', this.handleEditorChange); | ||
editor.off(changeEvents(), this.handleEditorChange); | ||
editor.off(beforeInputEvent, this.handleBeforeInput); | ||
editor.off('keypress', this.handleEditorChangeSpecial); | ||
editor.off('keydown', this.handleBeforeInputSpecial); | ||
editor.off('NewBlock', this.handleEditorChange); | ||
Object.keys(this.boundHandlers).forEach(function (eventName) { | ||
@@ -244,6 +303,14 @@ editor.off(eventName, _this.boundHandlers[eventName]); | ||
if (!wasControlled && nowControlled) { | ||
this.editor.on('change keyup compositionend setcontent', this.handleEditorChange); | ||
this.editor.on(changeEvents(), this.handleEditorChange); | ||
this.editor.on(beforeInputEvent, this.handleBeforeInput); | ||
this.editor.on('keydown', this.handleBeforeInputSpecial); | ||
this.editor.on('keyup', this.handleEditorChangeSpecial); | ||
this.editor.on('NewBlock', this.handleEditorChange); | ||
} | ||
else if (wasControlled && !nowControlled) { | ||
this.editor.off('change keyup compositionend setcontent', this.handleEditorChange); | ||
this.editor.off(changeEvents(), this.handleEditorChange); | ||
this.editor.off(beforeInputEvent, this.handleBeforeInput); | ||
this.editor.off('keydown', this.handleBeforeInputSpecial); | ||
this.editor.off('keyup', this.handleEditorChangeSpecial); | ||
this.editor.off('NewBlock', this.handleEditorChange); | ||
} | ||
@@ -250,0 +317,0 @@ } |
@@ -18,2 +18,3 @@ /** | ||
export declare const mergePlugins: (initPlugins: string | string[] | undefined, inputPlugins: string | string[] | undefined) => string[]; | ||
export declare const isBeforeInputEventAvailable: () => boolean; | ||
export {}; |
@@ -10,3 +10,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.mergePlugins = exports.isTextareaOrInput = exports.uuid = exports.configHandlers = exports.configHandlers2 = exports.isFunction = void 0; | ||
exports.isBeforeInputEventAvailable = exports.mergePlugins = exports.isTextareaOrInput = exports.uuid = exports.configHandlers = exports.configHandlers2 = exports.isFunction = void 0; | ||
var EditorPropTypes_1 = require("./components/EditorPropTypes"); | ||
@@ -62,1 +62,3 @@ var isFunction = function (x) { return typeof x === 'function'; }; | ||
exports.mergePlugins = mergePlugins; | ||
var isBeforeInputEventAvailable = function () { return window.InputEvent && typeof InputEvent.prototype.getTargetRanges === 'function'; }; | ||
exports.isBeforeInputEventAvailable = isBeforeInputEventAvailable; |
@@ -49,2 +49,4 @@ /** | ||
private boundHandlers; | ||
private rollbackTimer; | ||
private valueCursor; | ||
constructor(props: Partial<IAllProps>); | ||
@@ -63,4 +65,8 @@ componentDidUpdate(prevProps: Partial<IAllProps>): void; | ||
private bindHandlers; | ||
private rollbackChange; | ||
private handleBeforeInput; | ||
private handleBeforeInputSpecial; | ||
private handleEditorChange; | ||
private handleEditorChangeSpecial; | ||
private initialise; | ||
} |
@@ -37,4 +37,6 @@ /** | ||
import { getTinymce } from '../TinyMCE'; | ||
import { isFunction, isTextareaOrInput, mergePlugins, uuid, configHandlers } from '../Utils'; | ||
import { isFunction, isTextareaOrInput, mergePlugins, uuid, configHandlers, isBeforeInputEventAvailable } from '../Utils'; | ||
import { EditorPropTypes } from './EditorPropTypes'; | ||
var changeEvents = function () { var _a, _b, _c; return ((_c = (_b = (_a = getTinymce()) === null || _a === void 0 ? void 0 : _a.Env) === null || _b === void 0 ? void 0 : _b.browser) === null || _c === void 0 ? void 0 : _c.isIE()) ? 'change keyup compositionend setcontent' : 'input compositionend setcontent'; }; | ||
var beforeInputEvent = isBeforeInputEventAvailable() ? 'beforeinput' : 'SelectionChange'; | ||
var Editor = /** @class */ (function (_super) { | ||
@@ -45,2 +47,33 @@ __extends(Editor, _super); | ||
var _this = _super.call(this, props) || this; | ||
_this.rollbackTimer = undefined; | ||
_this.valueCursor = undefined; | ||
_this.rollbackChange = function () { | ||
var editor = _this.editor; | ||
var value = _this.props.value; | ||
if (editor && value && value !== _this.currentContent) { | ||
editor.undoManager.ignore(function () { | ||
editor.setContent(value); | ||
// only restore cursor on inline editors when they are focused | ||
// as otherwise it will cause a focus grab | ||
if (_this.valueCursor && (!_this.inline || editor.hasFocus())) { | ||
try { | ||
editor.selection.moveToBookmark(_this.valueCursor); | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
}); | ||
} | ||
_this.rollbackTimer = undefined; | ||
}; | ||
_this.handleBeforeInput = function (_evt) { | ||
var _a; | ||
if (_this.props.value !== undefined && _this.props.value === _this.currentContent && _this.editor) { | ||
_this.valueCursor = (_a = _this.editor) === null || _a === void 0 ? void 0 : _a.selection.getBookmark(3); | ||
} | ||
}; | ||
_this.handleBeforeInputSpecial = function (evt) { | ||
if (evt.key === 'Enter' || evt.key === 'Backspace' || evt.key === 'Delete') { | ||
_this.handleBeforeInput(evt); | ||
} | ||
}; | ||
_this.handleEditorChange = function (_evt) { | ||
@@ -50,2 +83,7 @@ var editor = _this.editor; | ||
var newContent = editor.getContent(); | ||
if (_this.props.value !== undefined && _this.props.value !== newContent) { | ||
// start a timer and revert to the value if not applied in time | ||
clearTimeout(_this.rollbackTimer); | ||
_this.rollbackTimer = window.setTimeout(_this.rollbackChange, 1); | ||
} | ||
if (newContent !== _this.currentContent) { | ||
@@ -61,2 +99,7 @@ _this.currentContent = newContent; | ||
}; | ||
_this.handleEditorChangeSpecial = function (evt) { | ||
if (evt.key === 'Backspace' || evt.key === 'Delete') { | ||
_this.handleEditorChange(evt); | ||
} | ||
}; | ||
_this.initialise = function () { | ||
@@ -125,2 +168,6 @@ var target = _this.elementRef.current; | ||
var _a, _b; | ||
if (this.rollbackTimer) { | ||
clearTimeout(this.rollbackTimer); | ||
this.rollbackTimer = undefined; | ||
} | ||
if (this.editor) { | ||
@@ -142,3 +189,3 @@ this.bindHandlers(prevProps); | ||
// so we don't try to keep their selection unless they are currently focused | ||
var bookmark; | ||
var cursor; | ||
if (!_this.inline || localEditor_1.hasFocus()) { | ||
@@ -148,12 +195,20 @@ try { | ||
// possibly only in inline mode but I'm not taking chances | ||
bookmark = localEditor_1.selection.getBookmark(3); | ||
cursor = localEditor_1.selection.getBookmark(3); | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
var valueCursor = _this.valueCursor; | ||
localEditor_1.setContent(_this.props.value); | ||
if (bookmark) { | ||
try { | ||
localEditor_1.selection.moveToBookmark(bookmark); | ||
if (!_this.inline || localEditor_1.hasFocus()) { | ||
for (var _i = 0, _a = [cursor, valueCursor]; _i < _a.length; _i++) { | ||
var bookmark = _a[_i]; | ||
if (bookmark) { | ||
try { | ||
localEditor_1.selection.moveToBookmark(bookmark); | ||
_this.valueCursor = bookmark; | ||
break; | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
} | ||
catch (e) { /* ignore */ } | ||
} | ||
@@ -182,3 +237,7 @@ }); | ||
if (editor) { | ||
editor.off('change keyup compositionend setcontent', this.handleEditorChange); | ||
editor.off(changeEvents(), this.handleEditorChange); | ||
editor.off(beforeInputEvent, this.handleBeforeInput); | ||
editor.off('keypress', this.handleEditorChangeSpecial); | ||
editor.off('keydown', this.handleBeforeInputSpecial); | ||
editor.off('NewBlock', this.handleEditorChange); | ||
Object.keys(this.boundHandlers).forEach(function (eventName) { | ||
@@ -241,6 +300,14 @@ editor.off(eventName, _this.boundHandlers[eventName]); | ||
if (!wasControlled && nowControlled) { | ||
this.editor.on('change keyup compositionend setcontent', this.handleEditorChange); | ||
this.editor.on(changeEvents(), this.handleEditorChange); | ||
this.editor.on(beforeInputEvent, this.handleBeforeInput); | ||
this.editor.on('keydown', this.handleBeforeInputSpecial); | ||
this.editor.on('keyup', this.handleEditorChangeSpecial); | ||
this.editor.on('NewBlock', this.handleEditorChange); | ||
} | ||
else if (wasControlled && !nowControlled) { | ||
this.editor.off('change keyup compositionend setcontent', this.handleEditorChange); | ||
this.editor.off(changeEvents(), this.handleEditorChange); | ||
this.editor.off(beforeInputEvent, this.handleBeforeInput); | ||
this.editor.off('keydown', this.handleBeforeInputSpecial); | ||
this.editor.off('keyup', this.handleEditorChangeSpecial); | ||
this.editor.off('NewBlock', this.handleEditorChange); | ||
} | ||
@@ -247,0 +314,0 @@ } |
@@ -18,2 +18,3 @@ /** | ||
export declare const mergePlugins: (initPlugins: string | string[] | undefined, inputPlugins: string | string[] | undefined) => string[]; | ||
export declare const isBeforeInputEventAvailable: () => boolean; | ||
export {}; |
@@ -52,1 +52,2 @@ /** | ||
export var mergePlugins = function (initPlugins, inputPlugins) { return normalizePluginArray(initPlugins).concat(normalizePluginArray(inputPlugins)); }; | ||
export var isBeforeInputEventAvailable = function () { return window.InputEvent && typeof InputEvent.prototype.getTargetRanges === 'function'; }; |
@@ -69,4 +69,4 @@ { | ||
}, | ||
"version": "3.11.2-rc.20210324015840574.29b229e", | ||
"version": "3.11.2-rc.20210326020226308.92b7f46", | ||
"name": "@tinymce/tinymce-react" | ||
} |
80610
1583