jodit 3.6.16 → 3.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/.eslintrc.js +1 -1
  2. package/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +0 -0
  3. package/{PULL_REQUEST_TEMPLATE.md → .github/PULL_REQUEST_TEMPLATE.md} +0 -0
  4. package/CHANGELOG.MD +76 -5
  5. package/build/jodit.css +23 -19
  6. package/build/jodit.es2018.css +22 -18
  7. package/build/jodit.es2018.en.css +22 -18
  8. package/build/jodit.es2018.en.js +153 -99
  9. package/build/jodit.es2018.en.min.css +1 -1
  10. package/build/jodit.es2018.en.min.js +2 -2
  11. package/build/jodit.es2018.js +153 -99
  12. package/build/jodit.es2018.min.css +1 -1
  13. package/build/jodit.es2018.min.js +2 -2
  14. package/build/jodit.js +884 -809
  15. package/build/jodit.min.css +2 -2
  16. package/build/jodit.min.js +2 -2
  17. package/package.json +24 -24
  18. package/src/core/async.ts +22 -8
  19. package/src/core/component/component.ts +4 -2
  20. package/src/core/component/view-component.ts +1 -1
  21. package/src/core/decorators/cache.ts +1 -1
  22. package/src/core/decorators/component.ts +1 -1
  23. package/src/core/decorators/debounce.ts +5 -4
  24. package/src/core/decorators/idle.ts +40 -0
  25. package/src/core/decorators/index.ts +1 -0
  26. package/src/core/decorators/nonenumerable.ts +1 -1
  27. package/src/core/decorators/spy.ts +2 -3
  28. package/src/core/decorators/watch.ts +9 -5
  29. package/src/core/events/events-native.ts +1 -1
  30. package/src/core/global.ts +7 -3
  31. package/src/core/helpers/checker/is-plain-object.ts +1 -2
  32. package/src/core/helpers/checker/is-url.ts +4 -1
  33. package/src/core/helpers/scroll-into-view.ts +1 -1
  34. package/src/core/helpers/size/position.ts +1 -22
  35. package/src/core/helpers/string/stringify.ts +2 -2
  36. package/src/core/helpers/type.ts +0 -44
  37. package/src/core/helpers/utils/utils.ts +1 -1
  38. package/src/core/plugin.ts +3 -1
  39. package/src/core/ui/button/button/button.ts +2 -2
  40. package/src/core/ui/button/group/group.ts +6 -4
  41. package/src/core/ui/element.ts +2 -2
  42. package/src/core/ui/form/block/block.ts +1 -1
  43. package/src/core/ui/form/form.ts +5 -5
  44. package/src/core/ui/form/inputs/area/area.ts +3 -3
  45. package/src/core/ui/form/inputs/checkbox/checkbox.ts +2 -2
  46. package/src/core/ui/form/inputs/file/file.ts +6 -4
  47. package/src/core/ui/form/inputs/input/input.ts +7 -5
  48. package/src/core/ui/form/inputs/select/select.ts +9 -7
  49. package/src/core/ui/list/group.ts +4 -4
  50. package/src/core/ui/list/list.ts +2 -2
  51. package/src/core/ui/popup/popup.ts +3 -3
  52. package/src/core/ui/progress-bar/progress-bar.ts +3 -3
  53. package/src/core/view/view-with-toolbar.ts +5 -2
  54. package/src/core/view/view.ts +2 -2
  55. package/src/jodit.ts +37 -15
  56. package/src/modules/context-menu/context-menu.ts +1 -1
  57. package/src/modules/dialog/dialog.ts +4 -4
  58. package/src/modules/file-browser/builders/context-menu.ts +1 -1
  59. package/src/modules/file-browser/data-provider.ts +1 -1
  60. package/src/modules/file-browser/file-browser.ts +3 -3
  61. package/src/modules/image-editor/image-editor.ts +1 -1
  62. package/src/modules/observer/observer.ts +1 -1
  63. package/src/modules/observer/snapshot.ts +1 -1
  64. package/src/modules/status-bar/status-bar.ts +1 -1
  65. package/src/modules/table.ts +3 -1
  66. package/src/modules/toolbar/button/button.ts +13 -10
  67. package/src/modules/toolbar/button/content.ts +3 -3
  68. package/src/modules/toolbar/collection/collection.ts +9 -6
  69. package/src/modules/toolbar/collection/editor-collection.ts +26 -4
  70. package/src/modules/uploader/uploader.ts +1 -1
  71. package/src/modules/widget/file-selector/file-selector.ts +1 -1
  72. package/src/plugins/about/about.ts +1 -1
  73. package/src/plugins/class-span/class-span.ts +3 -3
  74. package/src/plugins/clipboard/clipboard.ts +28 -24
  75. package/src/plugins/clipboard/paste/helpers.ts +5 -8
  76. package/src/plugins/clipboard/paste/paste.ts +3 -3
  77. package/src/plugins/fix/clean-html.ts +3 -3
  78. package/src/plugins/image/helpers.ts +1 -1
  79. package/src/plugins/inline-popup/inline-popup.ts +1 -1
  80. package/src/plugins/keyboard/delete.ts +1 -1
  81. package/src/plugins/keyboard/enter.ts +26 -26
  82. package/src/plugins/limit.ts +2 -2
  83. package/src/plugins/link/link.ts +3 -3
  84. package/src/plugins/media/video/index.ts +1 -1
  85. package/src/plugins/ordered-list.ts +64 -61
  86. package/src/plugins/redo-undo.ts +3 -3
  87. package/src/plugins/search/search.ts +2 -2
  88. package/src/plugins/select.ts +3 -2
  89. package/src/plugins/size/resize-handler.ts +2 -2
  90. package/src/plugins/size/size.ts +2 -2
  91. package/src/plugins/source/editor/engines/ace.ts +8 -8
  92. package/src/plugins/source/editor/engines/area.ts +15 -13
  93. package/src/plugins/source/source.ts +1 -1
  94. package/src/plugins/symbols/symbols.ts +3 -3
  95. package/src/plugins/table/config.ts +3 -1
  96. package/src/plugins/table/resize-cells.ts +21 -16
  97. package/src/plugins/table/select-cells.ts +44 -6
  98. package/src/plugins/table/table-keyboard-navigation.ts +134 -131
  99. package/src/plugins/table/table.less +24 -17
  100. package/src/types/async.d.ts +2 -0
  101. package/src/types/core.ts +13 -0
  102. package/src/types/index.d.ts +1 -0
  103. package/src/types/jodit.d.ts +2 -0
  104. package/test/tests/acceptance/editorTest.js +37 -0
  105. package/test/tests/acceptance/tableTest.js +23 -14
  106. package/tsconfig.json +1 -0
  107. package/webpack.config.js +1 -0
@@ -78,78 +78,81 @@ export function orderedList(editor: IJodit): void {
78
78
  };
79
79
 
80
80
  editor.e
81
- .on('beforeCommand', (command: string, _, listStyleType: string):
82
- | false
83
- | void => {
84
- if (isOurCommand(command) && listStyleType) {
85
- const ul = getListWrapper();
86
-
87
- if (ul && !listStyleTypeEqual(ul, listStyleType)) {
88
- if (
89
- (Dom.isTag(ul, 'ul') && /unordered/i.test(command)) ||
90
- (Dom.isTag(ul, 'ol') && !/unordered/i.test(command))
91
- ) {
92
- setListStyleType(ul, listStyleType);
93
- return false;
94
- }
95
- }
96
- }
97
- })
98
- .on('afterCommand', (command: string, _, listStyleType: string):
99
- | false
100
- | void => {
101
- if (isOurCommand(command)) {
102
- const ul = getListWrapper();
103
-
104
- if (ul) {
105
- setListStyleType(ul, listStyleType);
106
- editor.createInside.applyCreateAttributes(ul);
107
-
108
- ul.querySelectorAll('li').forEach(li => {
109
- editor.createInside.applyCreateAttributes(li);
110
- });
111
- }
81
+ .on(
82
+ 'beforeCommand',
83
+ (command: string, _, listStyleType: string): false | void => {
84
+ if (isOurCommand(command) && listStyleType) {
85
+ const ul = getListWrapper();
112
86
 
113
- const unwrapList: Node[] = [],
114
- shouldUnwrap = (elm: Nullable<Node>): void => {
87
+ if (ul && !listStyleTypeEqual(ul, listStyleType)) {
115
88
  if (
116
- Dom.isTag(elm, [
117
- 'p',
118
- 'h1',
119
- 'h2',
120
- 'h3',
121
- 'h4',
122
- 'h5',
123
- 'h6'
124
- ])
89
+ (Dom.isTag(ul, 'ul') &&
90
+ /unordered/i.test(command)) ||
91
+ (Dom.isTag(ul, 'ol') && !/unordered/i.test(command))
125
92
  ) {
126
- unwrapList.push(elm);
93
+ setListStyleType(ul, listStyleType);
94
+ return false;
127
95
  }
128
- };
96
+ }
97
+ }
98
+ }
99
+ )
100
+ .on(
101
+ 'afterCommand',
102
+ (command: string, _, listStyleType: string): false | void => {
103
+ if (isOurCommand(command)) {
104
+ const ul = getListWrapper();
105
+
106
+ if (ul) {
107
+ setListStyleType(ul, listStyleType);
108
+ editor.createInside.applyCreateAttributes(ul);
129
109
 
130
- if (ul) {
131
- shouldUnwrap(ul.parentNode);
110
+ ul.querySelectorAll('li').forEach(li => {
111
+ editor.createInside.applyCreateAttributes(li);
112
+ });
113
+ }
132
114
 
133
- ul.querySelectorAll('li').forEach(li =>
134
- shouldUnwrap(li.firstChild)
135
- );
115
+ const unwrapList: Node[] = [],
116
+ shouldUnwrap = (elm: Nullable<Node>): void => {
117
+ if (
118
+ Dom.isTag(elm, [
119
+ 'p',
120
+ 'h1',
121
+ 'h2',
122
+ 'h3',
123
+ 'h4',
124
+ 'h5',
125
+ 'h6'
126
+ ])
127
+ ) {
128
+ unwrapList.push(elm);
129
+ }
130
+ };
136
131
 
137
- if (unwrapList.length) {
138
- editor.s.save();
132
+ if (ul) {
133
+ shouldUnwrap(ul.parentNode);
139
134
 
140
- toArray(ul.childNodes).forEach(li => {
141
- if (Dom.isTag(li.lastChild, 'br')) {
142
- Dom.safeRemove(li.lastChild);
143
- }
144
- });
135
+ ul.querySelectorAll('li').forEach(li =>
136
+ shouldUnwrap(li.firstChild)
137
+ );
138
+
139
+ if (unwrapList.length) {
140
+ editor.s.save();
145
141
 
146
- unwrapList.forEach(elm => Dom.unwrap(elm));
142
+ toArray(ul.childNodes).forEach(li => {
143
+ if (Dom.isTag(li.lastChild, 'br')) {
144
+ Dom.safeRemove(li.lastChild);
145
+ }
146
+ });
147
147
 
148
- editor.s.restore();
148
+ unwrapList.forEach(elm => Dom.unwrap(elm));
149
+
150
+ editor.s.restore();
151
+ }
149
152
  }
150
- }
151
153
 
152
- editor.setEditorValue();
154
+ editor.setEditorValue();
155
+ }
153
156
  }
154
- });
157
+ );
155
158
  }
@@ -26,7 +26,7 @@ Config.prototype.controls.undo = {
26
26
  */
27
27
  export class redoUndo extends Plugin {
28
28
  /** @override */
29
- buttons: IPlugin['buttons'] = [
29
+ override buttons: IPlugin['buttons'] = [
30
30
  {
31
31
  name: 'undo',
32
32
  group: 'history'
@@ -37,11 +37,11 @@ export class redoUndo extends Plugin {
37
37
  }
38
38
  ];
39
39
 
40
- beforeDestruct(): void {
40
+ protected override beforeDestruct(): void {
41
41
  // do nothing
42
42
  }
43
43
 
44
- afterInit(editor: IJodit): void {
44
+ protected override afterInit(editor: IJodit): void {
45
45
  const callback = (command: string): void | false => {
46
46
  editor.observer[command as 'redo' | 'undo']();
47
47
 
@@ -86,7 +86,7 @@ Config.prototype.controls.find = {
86
86
  * ```
87
87
  */
88
88
  export class search extends Plugin {
89
- buttons: IPlugin['buttons'] = [
89
+ override buttons: IPlugin['buttons'] = [
90
90
  {
91
91
  name: 'find',
92
92
  group: 'search'
@@ -520,7 +520,7 @@ export class search extends Plugin {
520
520
  return false;
521
521
  };
522
522
 
523
- open = (searchAndReplace: boolean = false) => {
523
+ open = (searchAndReplace: boolean = false): void => {
524
524
  if (!this.isOpened) {
525
525
  this.searchBox.classList.add('jodit-search_active');
526
526
  this.isOpened = true;
@@ -31,16 +31,17 @@ export class select extends Plugin {
31
31
  'touchend'
32
32
  ];
33
33
 
34
+ /** @override */
34
35
  protected afterInit(jodit: IJodit): void {
35
36
  this.proxyEventsList.forEach(eventName => {
36
- jodit.e.on(eventName + '.inline-popup', this.onStartSelection);
37
+ jodit.e.on(eventName + '.select', this.onStartSelection);
37
38
  });
38
39
  }
39
40
 
40
41
  /** @override */
41
42
  protected beforeDestruct(jodit: IJodit): void {
42
43
  this.proxyEventsList.forEach(eventName => {
43
- jodit.e.on(eventName + '.inline-popup', this.onStartSelection);
44
+ jodit.e.on(eventName + '.select', this.onStartSelection);
44
45
  });
45
46
  }
46
47
 
@@ -15,7 +15,7 @@ export class resizeHandler extends Plugin {
15
15
  static requires: string[] = ['size'];
16
16
 
17
17
  /** @override **/
18
- protected afterInit(editor: IJodit) {
18
+ protected afterInit(editor: IJodit): void {
19
19
  const { height, width, allowResizeX } = editor.o;
20
20
  let { allowResizeY } = editor.o;
21
21
 
@@ -121,7 +121,7 @@ export class resizeHandler extends Plugin {
121
121
  );
122
122
 
123
123
  /** @override **/
124
- protected beforeDestruct(editor: IJodit) {
124
+ protected beforeDestruct(editor: IJodit): void {
125
125
  Dom.safeRemove(this.handle);
126
126
 
127
127
  this.j.e.off(this.j.ow, 'mouseup touchsend', this.onHandleResizeEnd);
@@ -17,7 +17,7 @@ import { autobind } from '../../core/decorators';
17
17
  @autobind
18
18
  export class size extends Plugin {
19
19
  /** @override **/
20
- protected afterInit(editor: IJodit) {
20
+ protected afterInit(editor: IJodit): void {
21
21
  editor.e
22
22
  .on('setHeight.size', this.setHeight)
23
23
  .on('setWidth.size', this.setWidth)
@@ -186,7 +186,7 @@ export class size extends Plugin {
186
186
  );
187
187
 
188
188
  /** @override **/
189
- protected beforeDestruct(jodit: IJodit) {
189
+ protected beforeDestruct(jodit: IJodit): void {
190
190
  this.j.e
191
191
  .off(this.j.ow, 'load.size', this.resizeWorkspaces)
192
192
  .off('.size');
@@ -13,7 +13,7 @@ export class AceEditor
13
13
  extends SourceEditor<AceAjax.Editor>
14
14
  implements ISourceEditor
15
15
  {
16
- className = 'jodit_ace_editor';
16
+ override className = 'jodit_ace_editor';
17
17
 
18
18
  private aceExists() {
19
19
  return (this.j.ow as any).ace !== undefined;
@@ -198,7 +198,7 @@ export class AceEditor
198
198
  this.j?.events?.off('aceInited.source');
199
199
  }
200
200
 
201
- setValue(value: string) {
201
+ setValue(value: string): void {
202
202
  if (!this.j.o.editHTMLDocumentMode && this.j.o.beautifyHTML) {
203
203
  const html = this.j.e.fire('beautifyHTML', value);
204
204
 
@@ -212,7 +212,7 @@ export class AceEditor
212
212
  this.instance.clearSelection();
213
213
  }
214
214
 
215
- getValue() {
215
+ getValue(): string {
216
216
  return this.instance.getValue();
217
217
  }
218
218
 
@@ -220,7 +220,7 @@ export class AceEditor
220
220
  this.instance.setReadOnly(isReadOnly);
221
221
  }
222
222
 
223
- focus() {
223
+ focus(): void {
224
224
  this.instance.focus();
225
225
  }
226
226
 
@@ -236,11 +236,11 @@ export class AceEditor
236
236
  return this.getIndexByRowColumn(range.end.row, range.end.column);
237
237
  }
238
238
 
239
- selectAll() {
239
+ selectAll(): void {
240
240
  this.instance.selection.selectAll();
241
241
  }
242
242
 
243
- insertRaw(html: string) {
243
+ insertRaw(html: string): void {
244
244
  const start = this.instance.selection.getCursor(),
245
245
  end = this.instance.session.insert(start, html);
246
246
 
@@ -253,7 +253,7 @@ export class AceEditor
253
253
  );
254
254
  }
255
255
 
256
- setSelectionRange(start: number, end: number) {
256
+ setSelectionRange(start: number, end: number): void {
257
257
  this.setSelectionRangeIndices(start, end);
258
258
  }
259
259
 
@@ -261,7 +261,7 @@ export class AceEditor
261
261
  // ACE does not support placeholder
262
262
  }
263
263
 
264
- replaceUndoManager() {
264
+ replaceUndoManager(): void {
265
265
  const { observer } = this.jodit;
266
266
 
267
267
  this.instance.commands.addCommand({
@@ -113,20 +113,22 @@ export class TextAreaEditor
113
113
  replaceUndoManager(): void {
114
114
  const { observer } = this.jodit;
115
115
 
116
- this.j.e.on(this.instance, 'keydown', (e: KeyboardEvent):
117
- | false
118
- | void => {
119
- if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
120
- if (e.shiftKey) {
121
- observer.redo();
122
- } else {
123
- observer.undo();
116
+ this.j.e.on(
117
+ this.instance,
118
+ 'keydown',
119
+ (e: KeyboardEvent): false | void => {
120
+ if ((e.ctrlKey || e.metaKey) && e.key === 'z') {
121
+ if (e.shiftKey) {
122
+ observer.redo();
123
+ } else {
124
+ observer.undo();
125
+ }
126
+
127
+ this.setSelectionRange(this.getValue().length);
128
+
129
+ return false;
124
130
  }
125
-
126
- this.setSelectionRange(this.getValue().length);
127
-
128
- return false;
129
131
  }
130
- });
132
+ );
131
133
  }
132
134
  }
@@ -22,7 +22,7 @@ import { autobind, watch } from '../../core/decorators';
22
22
  */
23
23
  export class source extends Plugin {
24
24
  /** @override */
25
- buttons: Plugin['buttons'] = [
25
+ override buttons: Plugin['buttons'] = [
26
26
  {
27
27
  name: 'source',
28
28
  group: 'source'
@@ -24,7 +24,7 @@ import { attr } from '../../core/helpers/utils';
24
24
  */
25
25
  export class symbols extends Plugin {
26
26
  /** @override */
27
- buttons: Plugin['buttons'] = [
27
+ override buttons: Plugin['buttons'] = [
28
28
  {
29
29
  name: 'symbol',
30
30
  group: 'insert'
@@ -34,7 +34,7 @@ export class symbols extends Plugin {
34
34
  private countInRow: number = 17;
35
35
 
36
36
  /** @override */
37
- afterInit(jodit: IJodit): void {
37
+ override afterInit(jodit: IJodit): void {
38
38
  jodit.e.on('generateSpecialCharactersTable.symbols', () => {
39
39
  const container = jodit.c.fromHTML(
40
40
  '<div class="jodit-symbols__container">' +
@@ -170,7 +170,7 @@ export class symbols extends Plugin {
170
170
  }
171
171
 
172
172
  /** @override */
173
- protected beforeDestruct(jodit: IJodit) {
173
+ protected beforeDestruct(jodit: IJodit): void {
174
174
  jodit.e.off('generateSpecialCharactersTable.symbols');
175
175
  }
176
176
  }
@@ -12,7 +12,9 @@ import { $$, scrollIntoViewIfNeeded } from '../../core/helpers';
12
12
 
13
13
  Config.prototype.table = {
14
14
  allowCellSelection: true,
15
- selectionCellStyle: 'border: 1px double #1e88e5 !important;',
15
+ selectionCellStyle:
16
+ 'border: 1px double #1e88e5 !important;' +
17
+ 'background-color: rgba(158, 207, 250, 0.3)!important',
16
18
 
17
19
  allowCellResize: true,
18
20
  useExtraClassesOptions: false
@@ -460,23 +460,28 @@ export class resizeCells extends Plugin {
460
460
  .on(
461
461
  table,
462
462
  'mousemove.resize-cells touchmove.resize-cells',
463
- (event: MouseEvent) => {
464
- if (this.j.isLocked) {
465
- return;
463
+ this.j.async.throttle(
464
+ (event: MouseEvent) => {
465
+ if (this.j.isLocked) {
466
+ return;
467
+ }
468
+
469
+ const cell = Dom.up(
470
+ event.target as HTMLElement,
471
+ elm => Dom.isCell(elm, this.j.ew),
472
+ table
473
+ ) as HTMLTableCellElement;
474
+
475
+ if (!cell) {
476
+ return;
477
+ }
478
+
479
+ this.calcHandlePosition(table, cell, event.offsetX);
480
+ },
481
+ {
482
+ timeout: this.j.defaultTimeout
466
483
  }
467
-
468
- const cell = Dom.up(
469
- event.target as HTMLElement,
470
- elm => Dom.isCell(elm, this.j.ew),
471
- table
472
- ) as HTMLTableCellElement;
473
-
474
- if (!cell) {
475
- return;
476
- }
477
-
478
- this.calcHandlePosition(table, cell, event.offsetX);
479
- }
484
+ )
480
485
  );
481
486
 
482
487
  this.createResizeHandle();
@@ -13,10 +13,11 @@ import { KEY_TAB } from '../../core/constants';
13
13
  import { autobind, watch } from '../../core/decorators';
14
14
 
15
15
  const key = 'table_processor_observer';
16
+ const MOUSE_MOVE_LABEL = 'onMoveTableSelectCell';
16
17
 
17
18
  export class selectCells extends Plugin {
18
19
  /** @override */
19
- requires = ['select'];
20
+ override requires = ['select'];
20
21
 
21
22
  /**
22
23
  * Shortcut for Table module
@@ -41,6 +42,7 @@ export class selectCells extends Plugin {
41
42
  .on('beforeCommand.select-cells', this.onExecCommand)
42
43
  .on('afterCommand.select-cells', this.onAfterCommand)
43
44
 
45
+ // see `plugins/select.ts`
44
46
  .on(
45
47
  [
46
48
  'clickEditor',
@@ -55,8 +57,14 @@ export class selectCells extends Plugin {
55
57
  )
56
58
  // For `clickEditor` correct working. Because `mousedown` on first cell
57
59
  // and mouseup on another cell call `click` only for `TR` element.
58
- .on('clickTr', (): void | false => {
59
- if (this.module.getAllSelectedCells().length) {
60
+ .on('clickTr clickTbody', (): void | false => {
61
+ const cellsCount = this.module.getAllSelectedCells().length;
62
+
63
+ if (cellsCount) {
64
+ if (cellsCount > 1) {
65
+ this.j.s.sel?.removeAllRanges();
66
+ }
67
+
60
68
  return false;
61
69
  }
62
70
  });
@@ -67,6 +75,11 @@ export class selectCells extends Plugin {
67
75
  */
68
76
  private selectedCell: Nullable<HTMLTableCellElement> = null;
69
77
 
78
+ /**
79
+ * User is selecting cells now
80
+ */
81
+ private isSelectionMode: boolean = false;
82
+
70
83
  /**
71
84
  * Mouse click inside the table
72
85
  * @param cell
@@ -97,6 +110,7 @@ export class selectCells extends Plugin {
97
110
  cell.appendChild(this.j.createInside.element('br'));
98
111
  }
99
112
 
113
+ this.isSelectionMode = true;
100
114
  this.selectedCell = cell;
101
115
 
102
116
  this.module.addSelection(cell);
@@ -105,7 +119,11 @@ export class selectCells extends Plugin {
105
119
  .on(
106
120
  table,
107
121
  'mousemove.select-cells touchmove.select-cells',
108
- this.onMove.bind(this, table)
122
+ // Don't use decorator because need clear label on mouseup
123
+ this.j.async.throttle(this.onMove.bind(this, table), {
124
+ label: MOUSE_MOVE_LABEL,
125
+ timeout: this.j.defaultTimeout / 2
126
+ })
109
127
  )
110
128
  .on(
111
129
  table,
@@ -117,8 +135,16 @@ export class selectCells extends Plugin {
117
135
  }
118
136
 
119
137
  @watch(':outsideClick')
120
- protected onOutsideClick(e: MouseEvent): void {
121
- this.unselectCells();
138
+ protected onOutsideClick(): void {
139
+ this.selectedCell = null;
140
+ this.onRemoveSelection();
141
+ }
142
+
143
+ @watch(':change')
144
+ protected onChange(): void {
145
+ if (!this.j.isLocked && !this.isSelectionMode) {
146
+ this.onRemoveSelection();
147
+ }
122
148
  }
123
149
 
124
150
  /**
@@ -163,6 +189,12 @@ export class selectCells extends Plugin {
163
189
  }
164
190
  }
165
191
 
192
+ const cellsCount = this.module.getAllSelectedCells().length;
193
+
194
+ if (cellsCount > 1) {
195
+ this.j.s.sel?.removeAllRanges();
196
+ }
197
+
166
198
  this.j.e.fire('hidePopup');
167
199
  e.stopPropagation();
168
200
 
@@ -196,6 +228,7 @@ export class selectCells extends Plugin {
196
228
  return;
197
229
  }
198
230
 
231
+ this.isSelectionMode = false;
199
232
  this.selectedCell = null;
200
233
  }
201
234
 
@@ -208,6 +241,8 @@ export class selectCells extends Plugin {
208
241
  return;
209
242
  }
210
243
 
244
+ this.isSelectionMode = false;
245
+
211
246
  this.j.unlock();
212
247
 
213
248
  const node = this.j.ed.elementFromPoint(e.clientX, e.clientY);
@@ -223,6 +258,7 @@ export class selectCells extends Plugin {
223
258
  }
224
259
 
225
260
  const ownTable = Dom.closest(cell, 'table', table);
261
+
226
262
  if (ownTable && ownTable !== table) {
227
263
  return; // Nested tables
228
264
  }
@@ -258,6 +294,8 @@ export class selectCells extends Plugin {
258
294
  'mousemove.select-cells touchmove.select-cells mouseup.select-cells touchend.select-cells'
259
295
  );
260
296
  });
297
+
298
+ this.j.async.clearTimeout(MOUSE_MOVE_LABEL);
261
299
  }
262
300
 
263
301
  /**