Skip to content

Commit 68a60ae

Browse files
eguitarzbantic
authored andcommitted
feat(textInput): able to unregister single or all text input handlers (#484)
1 parent 2fe7f0f commit 68a60ae

File tree

6 files changed

+95
-0
lines changed

6 files changed

+95
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ editor.onTextInput({
241241
The editor has several default text input handlers that are defined in
242242
`src/js/editor/text-input-handlers.js`.
243243

244+
To remove default text input handlers, simply call the unregister function.
245+
```javascript
246+
editor.unregisterAllTextInputHandlers();
247+
```
248+
244249
### DOM Parsing hooks
245250

246251
A developer can override the default parsing behavior for leaf DOM nodes in

src/js/editor/editor.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,7 @@ class Editor {
710710
* Register a handler that will be invoked by the editor after the user enters
711711
* matching text.
712712
* @param {Object} inputHandler
713+
* @param {String} inputHandler.name Required. Used by identifying handlers.
713714
* @param {String} [inputHandler.text] Required if `match` is not provided
714715
* @param {RegExp} [inputHandler.match] Required if `text` is not provided
715716
* @param {Function} inputHandler.run This callback is invoked with the {@link Editor}
@@ -724,6 +725,25 @@ class Editor {
724725
this._eventManager.registerInputHandler(inputHandler);
725726
}
726727

728+
/**
729+
* Unregister all text input handlers
730+
*
731+
* @public
732+
*/
733+
unregisterAllTextInputHandlers() {
734+
this._eventManager.unregisterAllTextInputHandlers();
735+
}
736+
737+
/**
738+
* Unregister text input handler by name
739+
* @param {String} name The name of handler to be removed
740+
*
741+
* @public
742+
*/
743+
unregisterTextInputHandler(name) {
744+
this._eventManager.unregisterInputHandler(name);
745+
}
746+
727747
/**
728748
* @param {Function} callback Called when the editor's state (active markups or
729749
* active sections) has changed, either via user input or programmatically

src/js/editor/event-manager.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ export default class EventManager {
4545
this._textInputHandler.register(inputHandler);
4646
}
4747

48+
unregisterInputHandler(name) {
49+
this._textInputHandler.unregister(name);
50+
}
51+
52+
unregisterAllTextInputHandlers() {
53+
this._textInputHandler.destroy();
54+
this._textInputHandler = new TextInputHandler(this.editor);
55+
}
56+
4857
_addListener(context, type) {
4958
assert(`Missing listener for ${type}`, !!this[type]);
5059

src/js/editor/text-input-handler.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { endsWith } from 'mobiledoc-kit/utils/string-utils';
22
import assert from 'mobiledoc-kit/utils/assert';
3+
import deprecate from 'mobiledoc-kit/utils/deprecate';
34

45
class TextInputHandler {
56
constructor(editor) {
@@ -12,6 +13,15 @@ class TextInputHandler {
1213
this._handlers.push(handler);
1314
}
1415

16+
unregister(name) {
17+
let handlers = this._handlers;
18+
for (let i=0; i<handlers.length; i++) {
19+
if (handlers[i].name === name) {
20+
handlers.splice(i, 1);
21+
}
22+
}
23+
}
24+
1525
handle(string) {
1626
let { editor } = this;
1727
editor.insertText(string);
@@ -40,6 +50,7 @@ class TextInputHandler {
4050
}
4151

4252
_validateHandler(handler) {
53+
deprecate('Registered input handlers require a "name" property so that they can be unregistered', !!handler.name);
4354
return !!handler.run && // has `run`
4455
(!!handler.text || !!handler.match) && // and `text` or `match`
4556
!(!!handler.text && !!handler.match); // not both `text` and `match`

src/js/editor/text-input-handlers.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,20 +53,23 @@ export function replaceWithHeaderSection(editor, headingTagName) {
5353

5454
export const DEFAULT_TEXT_INPUT_HANDLERS = [
5555
{
56+
name: 'ul',
5657
// "* " -> ul
5758
match: /^\* $/,
5859
run(editor) {
5960
replaceWithListSection(editor, 'ul');
6061
}
6162
},
6263
{
64+
name: 'ol',
6365
// "1" -> ol, "1." -> ol
6466
match: /^1\.? $/,
6567
run(editor) {
6668
replaceWithListSection(editor, 'ol');
6769
}
6870
},
6971
{
72+
name: 'heading',
7073
// "# " -> h1, "## " -> h2, "### " -> h3
7174
match: /^(#{1,3}) $/,
7275
run(editor, matches) {

tests/acceptance/editor-input-handlers-test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ test('an input handler will trigger anywhere in the text', (assert) => {
177177
let expandCount = 0;
178178
let lastMatches;
179179
editor.onTextInput({
180+
name: 'at',
180181
text: '@',
181182
run: (editor, matches) => {
182183
expandCount++;
@@ -210,6 +211,7 @@ test('an input handler can provide a `match` instead of `text`', (assert) => {
210211
let lastMatches;
211212
let regex = /.(.)X$/;
212213
editor.onTextInput({
214+
name: 'test',
213215
match: regex,
214216
run: (editor, matches) => {
215217
expandCount++;
@@ -243,6 +245,7 @@ test('an input handler can provide a `match` that matches at start and end', (as
243245
let lastMatches;
244246
let regex = /^\d\d\d$/;
245247
editor.onTextInput({
248+
name: 'test',
246249
match: regex,
247250
run: (editor, matches) => {
248251
expandCount++;
@@ -273,6 +276,7 @@ test('input handler can be triggered by TAB', (assert) => {
273276

274277
let didMatch;
275278
editor.onTextInput({
279+
name: 'test',
276280
match: /abc\t/,
277281
run() {
278282
didMatch = true;
@@ -283,3 +287,46 @@ test('input handler can be triggered by TAB', (assert) => {
283287

284288
assert.ok(didMatch);
285289
});
290+
291+
test('can unregister all handlers', (assert) => {
292+
editor = Helpers.editor.buildFromText('');
293+
// there are 3 default helpers
294+
assert.equal(editor._eventManager._textInputHandler._handlers.length, 3);
295+
editor.onTextInput({
296+
name: 'first',
297+
match: /abc\t/,
298+
run() {}
299+
});
300+
editor.onTextInput({
301+
name: 'second',
302+
match: /abc\t/,
303+
run() {}
304+
});
305+
assert.equal(editor._eventManager._textInputHandler._handlers.length, 5);
306+
editor.unregisterAllTextInputHandlers();
307+
assert.equal(editor._eventManager._textInputHandler._handlers.length, 0);
308+
});
309+
310+
test('can unregister handler by name', (assert) => {
311+
editor = Helpers.editor.buildFromText('');
312+
const handlerName = 'ul';
313+
let handlers = editor._eventManager._textInputHandler._handlers;
314+
assert.ok(handlers.filter(handler => handler.name === handlerName).length);
315+
editor.unregisterTextInputHandler(handlerName);
316+
assert.notOk(handlers.filter(handler => handler.name === handlerName).length);
317+
});
318+
319+
test('can unregister handlers by duplicate name', (assert) => {
320+
editor = Helpers.editor.buildFromText('');
321+
const handlerName = 'ul';
322+
editor.onTextInput({
323+
name: handlerName,
324+
match: /abc/,
325+
run() {}
326+
});
327+
let handlers = editor._eventManager._textInputHandler._handlers;
328+
assert.equal(handlers.length, 4); // 3 default + 1 custom handlers
329+
editor.unregisterTextInputHandler(handlerName);
330+
assert.equal(handlers.length, 2);
331+
assert.notOk(handlers.filter(handler => handler.name === handlerName).length);
332+
});

0 commit comments

Comments
 (0)