@akilli/editor-src 5.2.7 → 5.4.0
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.
- package/base/Alignment.js +2 -2
- package/base/Base.js +1 -1
- package/base/CommandManager.js +0 -9
- package/base/DialogManager.js +0 -9
- package/base/Dom.js +50 -7
- package/base/EditableListener.js +24 -0
- package/base/Editor.js +2 -18
- package/base/FilterManager.js +0 -9
- package/base/Key.js +9 -9
- package/base/Listener.js +2 -2
- package/base/PluginManager.js +0 -9
- package/base/Sorting.js +2 -2
- package/base/TagGroup.js +2 -2
- package/base/TagManager.js +0 -13
- package/base/TagName.js +2 -2
- package/details/Details.js +2 -0
- package/details/DetailsListener.js +0 -77
- package/details/SummaryListener.js +88 -0
- package/license.md +1 -1
- package/package.json +1 -1
- package/paragraph/Paragraph.js +0 -2
- package/paragraph/ParagraphListener.js +0 -40
package/base/Alignment.js
CHANGED
package/base/Base.js
CHANGED
|
@@ -16,8 +16,8 @@ import SortableListener from './SortableListener.js';
|
|
|
16
16
|
import Sorting from './Sorting.js';
|
|
17
17
|
import TagGroup from './TagGroup.js';
|
|
18
18
|
import TagListener from './TagListener.js';
|
|
19
|
-
import ToolbarListener from './ToolbarListener.js';
|
|
20
19
|
import TagName from './TagName.js';
|
|
20
|
+
import ToolbarListener from './ToolbarListener.js';
|
|
21
21
|
|
|
22
22
|
export default class Base extends Plugin {
|
|
23
23
|
/**
|
package/base/CommandManager.js
CHANGED
|
@@ -48,13 +48,4 @@ export default class CommandManager {
|
|
|
48
48
|
execute(name) {
|
|
49
49
|
this.get(name).execute();
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* @return {void}
|
|
54
|
-
*/
|
|
55
|
-
freeze() {
|
|
56
|
-
this.#items.forEach((command) => Object.freeze(command));
|
|
57
|
-
Object.freeze(this.#items);
|
|
58
|
-
Object.freeze(this);
|
|
59
|
-
}
|
|
60
51
|
}
|
package/base/DialogManager.js
CHANGED
|
@@ -32,13 +32,4 @@ export default class DialogManager {
|
|
|
32
32
|
|
|
33
33
|
this.#items.set(dialog.name, dialog);
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* @return {void}
|
|
38
|
-
*/
|
|
39
|
-
freeze() {
|
|
40
|
-
this.#items.forEach((dialog) => Object.freeze(dialog));
|
|
41
|
-
Object.freeze(this.#items);
|
|
42
|
-
Object.freeze(this);
|
|
43
|
-
}
|
|
44
35
|
}
|
package/base/Dom.js
CHANGED
|
@@ -116,6 +116,13 @@ export default class Dom {
|
|
|
116
116
|
return element;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* @return {DocumentFragment}
|
|
121
|
+
*/
|
|
122
|
+
createFragment() {
|
|
123
|
+
return this.document.createDocumentFragment();
|
|
124
|
+
}
|
|
125
|
+
|
|
119
126
|
/**
|
|
120
127
|
* Inserts element
|
|
121
128
|
*
|
|
@@ -154,14 +161,50 @@ export default class Dom {
|
|
|
154
161
|
*/
|
|
155
162
|
insertText(text) {
|
|
156
163
|
const editable = this.getSelectedEditable();
|
|
164
|
+
const range = this.getRange();
|
|
165
|
+
|
|
166
|
+
if (!editable || !range || !text) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
range.deleteContents();
|
|
171
|
+
range.insertNode(this.createText(text));
|
|
172
|
+
range.collapse();
|
|
173
|
+
editable.normalize();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* @param {string} html
|
|
178
|
+
* @return {void}
|
|
179
|
+
*/
|
|
180
|
+
insertHtml(html) {
|
|
181
|
+
const editable = this.getSelectedEditable();
|
|
182
|
+
const range = this.getRange();
|
|
183
|
+
|
|
184
|
+
if (!editable || !range || !html) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const element = this.createElement(editable.localName, { html });
|
|
189
|
+
this.editor.filters.filter(element);
|
|
190
|
+
const fragment = this.createFragment();
|
|
157
191
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
range.deleteContents();
|
|
161
|
-
range.insertNode(this.createText(text));
|
|
162
|
-
range.collapse();
|
|
163
|
-
editable.normalize();
|
|
192
|
+
while (element.firstChild) {
|
|
193
|
+
fragment.appendChild(element.firstChild);
|
|
164
194
|
}
|
|
195
|
+
|
|
196
|
+
range.deleteContents();
|
|
197
|
+
range.insertNode(fragment);
|
|
198
|
+
|
|
199
|
+
const lastChild = fragment.lastChild;
|
|
200
|
+
|
|
201
|
+
if (lastChild) {
|
|
202
|
+
range.setStartAfter(lastChild);
|
|
203
|
+
range.setEndAfter(lastChild);
|
|
204
|
+
this.setRange(range);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
editable.normalize();
|
|
165
208
|
}
|
|
166
209
|
|
|
167
210
|
/**
|
|
@@ -502,7 +545,7 @@ export default class Dom {
|
|
|
502
545
|
return true;
|
|
503
546
|
}
|
|
504
547
|
|
|
505
|
-
const root = element.closest(
|
|
548
|
+
const root = element.closest(TagName.ROOT);
|
|
506
549
|
|
|
507
550
|
return root && !root.parentElement;
|
|
508
551
|
}
|
package/base/EditableListener.js
CHANGED
|
@@ -9,6 +9,7 @@ export default class EditableListener extends Listener {
|
|
|
9
9
|
constructor(editor) {
|
|
10
10
|
super(editor);
|
|
11
11
|
this.editor.root.addEventListener('insert', this);
|
|
12
|
+
this.editor.root.addEventListener('beforeinput', this);
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
/**
|
|
@@ -79,4 +80,27 @@ export default class EditableListener extends Listener {
|
|
|
79
80
|
this.editor.dom.selectContents(target);
|
|
80
81
|
this.editor.commands.findByTagName(target.localName)?.execute();
|
|
81
82
|
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {InputEvent} event
|
|
86
|
+
* @return {void}
|
|
87
|
+
*/
|
|
88
|
+
beforeinput(event) {
|
|
89
|
+
if (event.inputType !== 'insertFromPaste' || !event.cancelable) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const html = event.dataTransfer?.getData('text/html');
|
|
94
|
+
const text = event.dataTransfer?.getData('text/plain');
|
|
95
|
+
|
|
96
|
+
if (html) {
|
|
97
|
+
event.preventDefault();
|
|
98
|
+
event.stopPropagation();
|
|
99
|
+
this.editor.dom.insertHtml(html);
|
|
100
|
+
} else if (text) {
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
event.stopPropagation();
|
|
103
|
+
this.editor.dom.insertText(text);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
82
106
|
}
|
package/base/Editor.js
CHANGED
|
@@ -323,21 +323,6 @@ export default class Editor {
|
|
|
323
323
|
this.rootDispatcher.dispatch('init');
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
/**
|
|
327
|
-
* Freezes the editor and its configuration
|
|
328
|
-
*
|
|
329
|
-
* @return {void}
|
|
330
|
-
*/
|
|
331
|
-
freeze() {
|
|
332
|
-
Object.freeze(this.config);
|
|
333
|
-
Object.freeze(this.i18n);
|
|
334
|
-
this.tags.freeze();
|
|
335
|
-
this.filters.freeze();
|
|
336
|
-
this.dialogs.freeze();
|
|
337
|
-
this.commands.freeze();
|
|
338
|
-
this.plugins.freeze();
|
|
339
|
-
}
|
|
340
|
-
|
|
341
326
|
/**
|
|
342
327
|
* Loads editor element into DOM
|
|
343
328
|
*
|
|
@@ -373,7 +358,7 @@ export default class Editor {
|
|
|
373
358
|
* @return {string}
|
|
374
359
|
*/
|
|
375
360
|
getHtml() {
|
|
376
|
-
const root = this.dom.createElement(
|
|
361
|
+
const root = this.dom.createElement(TagName.ROOT, { html: this.root.innerHTML });
|
|
377
362
|
this.filters.filter(root);
|
|
378
363
|
this.rootDispatcher.dispatch('gethtml', root);
|
|
379
364
|
|
|
@@ -387,7 +372,7 @@ export default class Editor {
|
|
|
387
372
|
* @return {void}
|
|
388
373
|
*/
|
|
389
374
|
setHtml(html) {
|
|
390
|
-
const root = this.dom.createElement(
|
|
375
|
+
const root = this.dom.createElement(TagName.ROOT, { html });
|
|
391
376
|
this.rootDispatcher.dispatch('sethtml', root);
|
|
392
377
|
this.filters.filter(root);
|
|
393
378
|
this.root.innerHTML = root.innerHTML;
|
|
@@ -440,7 +425,6 @@ export default class Editor {
|
|
|
440
425
|
static create(element, config = {}) {
|
|
441
426
|
const editor = new this(element, config);
|
|
442
427
|
editor.init();
|
|
443
|
-
editor.freeze();
|
|
444
428
|
editor.load();
|
|
445
429
|
|
|
446
430
|
return editor;
|
package/base/FilterManager.js
CHANGED
package/base/Key.js
CHANGED
|
@@ -4,7 +4,7 @@ import Sorting from './Sorting.js';
|
|
|
4
4
|
/**
|
|
5
5
|
* @enum {string}
|
|
6
6
|
*/
|
|
7
|
-
const Key =
|
|
7
|
+
const Key = {
|
|
8
8
|
A: 'a',
|
|
9
9
|
ARROWDOWN: 'ArrowDown',
|
|
10
10
|
ARROWLEFT: 'ArrowLeft',
|
|
@@ -39,44 +39,44 @@ const Key = Object.freeze({
|
|
|
39
39
|
X: 'x',
|
|
40
40
|
Y: 'y',
|
|
41
41
|
Z: 'z',
|
|
42
|
-
}
|
|
42
|
+
};
|
|
43
43
|
|
|
44
44
|
export default Key;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* @type {string[]}
|
|
48
48
|
*/
|
|
49
|
-
export const ArrowKeys =
|
|
49
|
+
export const ArrowKeys = [Key.ARROWLEFT, Key.ARROWRIGHT, Key.ARROWUP, Key.ARROWDOWN];
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* @type {string[]}
|
|
53
53
|
*/
|
|
54
|
-
export const NavKeys =
|
|
54
|
+
export const NavKeys = [Key.HOME, Key.ARROWUP, Key.ARROWDOWN, Key.END];
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* @type {string[]}
|
|
58
58
|
*/
|
|
59
|
-
export const BarNavKeys =
|
|
59
|
+
export const BarNavKeys = [Key.HOME, Key.ARROWLEFT, Key.ARROWRIGHT, Key.END];
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* @type {Object<Key, Alignment>}
|
|
63
63
|
*/
|
|
64
|
-
export const AlignKeyMap =
|
|
64
|
+
export const AlignKeyMap = {
|
|
65
65
|
[Key.ARROWUP]: Alignment.NONE,
|
|
66
66
|
[Key.ARROWLEFT]: Alignment.LEFT,
|
|
67
67
|
[Key.ARROWDOWN]: Alignment.CENTER,
|
|
68
68
|
[Key.ARROWRIGHT]: Alignment.RIGHT,
|
|
69
|
-
}
|
|
69
|
+
};
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* @type {Object<Key, Sorting>}
|
|
73
73
|
*/
|
|
74
|
-
export const SortKeyMap =
|
|
74
|
+
export const SortKeyMap = {
|
|
75
75
|
[Key.HOME]: Sorting.FIRST,
|
|
76
76
|
[Key.ARROWUP]: Sorting.PREV,
|
|
77
77
|
[Key.ARROWDOWN]: Sorting.NEXT,
|
|
78
78
|
[Key.END]: Sorting.LAST,
|
|
79
|
-
}
|
|
79
|
+
};
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Indicates if keyboard event was triggered for given key combination
|
package/base/Listener.js
CHANGED
|
@@ -3,7 +3,8 @@ import { isFunction } from './util.js';
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @abstract
|
|
6
|
-
* @implements EventListener
|
|
6
|
+
* @implements {EventListener}
|
|
7
|
+
* @borrows EventListener#handleEvent as handleEvent
|
|
7
8
|
*/
|
|
8
9
|
export default class Listener {
|
|
9
10
|
/**
|
|
@@ -19,7 +20,6 @@ export default class Listener {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* @borrows handleEvent
|
|
23
23
|
* @param {Editor} editor
|
|
24
24
|
*/
|
|
25
25
|
constructor(editor) {
|
package/base/PluginManager.js
CHANGED
|
@@ -39,13 +39,4 @@ export default class PluginManager {
|
|
|
39
39
|
init() {
|
|
40
40
|
this.#items.forEach((plugin) => plugin.init());
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @return {void}
|
|
45
|
-
*/
|
|
46
|
-
freeze() {
|
|
47
|
-
this.#items.forEach((plugin) => Object.freeze(plugin));
|
|
48
|
-
Object.freeze(this.#items);
|
|
49
|
-
Object.freeze(this);
|
|
50
|
-
}
|
|
51
42
|
}
|
package/base/Sorting.js
CHANGED
package/base/TagGroup.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @enum {string}
|
|
3
3
|
*/
|
|
4
|
-
export default
|
|
4
|
+
export default {
|
|
5
5
|
AUDIO: 'audio',
|
|
6
6
|
BLOCK: 'block',
|
|
7
7
|
BREAK: 'break',
|
|
@@ -28,4 +28,4 @@ export default Object.freeze({
|
|
|
28
28
|
TABLEROW: 'tablerow',
|
|
29
29
|
TABLESECTION: 'tablesection',
|
|
30
30
|
VIDEO: 'video',
|
|
31
|
-
}
|
|
31
|
+
};
|
package/base/TagManager.js
CHANGED
|
@@ -45,17 +45,4 @@ export default class TagManager {
|
|
|
45
45
|
const group = isGroup ? childKey : this.get(childKey)?.group;
|
|
46
46
|
return !!this.get(key)?.children.includes(group);
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* @return {void}
|
|
51
|
-
*/
|
|
52
|
-
freeze() {
|
|
53
|
-
this.#items.forEach((tag) => {
|
|
54
|
-
Object.freeze(tag.children);
|
|
55
|
-
Object.freeze(tag.attributes);
|
|
56
|
-
Object.freeze(tag);
|
|
57
|
-
});
|
|
58
|
-
Object.freeze(this.#items);
|
|
59
|
-
Object.freeze(this);
|
|
60
|
-
}
|
|
61
48
|
}
|
package/base/TagName.js
CHANGED
package/details/Details.js
CHANGED
|
@@ -3,6 +3,7 @@ import DetailsDialog from './DetailsDialog.js';
|
|
|
3
3
|
import DetailsFilter from './DetailsFilter.js';
|
|
4
4
|
import DetailsListener from './DetailsListener.js';
|
|
5
5
|
import Plugin from '../base/Plugin.js';
|
|
6
|
+
import SummaryListener from './SummaryListener.js';
|
|
6
7
|
import TagGroup from '../base/TagGroup.js';
|
|
7
8
|
import TagName from '../base/TagName.js';
|
|
8
9
|
|
|
@@ -58,6 +59,7 @@ export default class Details extends Plugin {
|
|
|
58
59
|
enter: TagName.P,
|
|
59
60
|
});
|
|
60
61
|
new DetailsListener(this.editor);
|
|
62
|
+
new SummaryListener(this.editor);
|
|
61
63
|
this.editor.dialogs.set(new DetailsDialog(this.editor, this.constructor.name));
|
|
62
64
|
this._command(TagName.DETAILS);
|
|
63
65
|
this._toolbar('Details');
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Key, { isKey } from '../base/Key.js';
|
|
2
1
|
import Listener from '../base/Listener.js';
|
|
3
2
|
import TagName from '../base/TagName.js';
|
|
4
3
|
|
|
@@ -9,7 +8,6 @@ export default class DetailsListener extends Listener {
|
|
|
9
8
|
constructor(editor) {
|
|
10
9
|
super(editor);
|
|
11
10
|
this.editor.root.addEventListener('insertdetails', this);
|
|
12
|
-
this.editor.root.addEventListener('insertsummary', this);
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
/**
|
|
@@ -24,79 +22,4 @@ export default class DetailsListener extends Listener {
|
|
|
24
22
|
this.editor.dom.insertFirstChild(this.editor.dom.createElement(TagName.SUMMARY), element);
|
|
25
23
|
}
|
|
26
24
|
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* @param {CustomEvent} event
|
|
30
|
-
* @param {HTMLElement} event.detail.element
|
|
31
|
-
* @return {void}
|
|
32
|
-
*/
|
|
33
|
-
insertsummary({ detail: { element } }) {
|
|
34
|
-
this.#empty(element);
|
|
35
|
-
element.addEventListener('click', this);
|
|
36
|
-
element.addEventListener('blur', this);
|
|
37
|
-
element.addEventListener('keydown', this);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Prevents toggling details open state
|
|
42
|
-
*
|
|
43
|
-
* @param {MouseEvent} event
|
|
44
|
-
* @param {HTMLElement} event.target
|
|
45
|
-
* @return {void}
|
|
46
|
-
*/
|
|
47
|
-
click(event) {
|
|
48
|
-
event.preventDefault();
|
|
49
|
-
event.stopPropagation();
|
|
50
|
-
event.target.parentElement.open = true;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Sets default summary text content if it's empty
|
|
55
|
-
*
|
|
56
|
-
* @param {FocusEvent} event
|
|
57
|
-
* @param {HTMLElement} event.target
|
|
58
|
-
* @return {void}
|
|
59
|
-
*/
|
|
60
|
-
blur({ target }) {
|
|
61
|
-
this.#empty(target);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Fixes space and enter key handling for editable summary elements
|
|
66
|
-
*
|
|
67
|
-
* @param {KeyboardEvent} event
|
|
68
|
-
* @param {HTMLElement} event.target
|
|
69
|
-
* @return {void}
|
|
70
|
-
*/
|
|
71
|
-
keydown(event) {
|
|
72
|
-
if (isKey(event, Key.SPACE)) {
|
|
73
|
-
event.preventDefault();
|
|
74
|
-
event.stopPropagation();
|
|
75
|
-
this.editor.dom.insertText(' ');
|
|
76
|
-
} else if (isKey(event, Key.ENTER)) {
|
|
77
|
-
event.preventDefault();
|
|
78
|
-
event.stopPropagation();
|
|
79
|
-
event.target.parentElement.open = true;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Ensures summary element is not empty to avoid strange browser behaviour
|
|
85
|
-
*
|
|
86
|
-
* @param {HTMLElement} element
|
|
87
|
-
* @return {void}
|
|
88
|
-
*/
|
|
89
|
-
#empty(element) {
|
|
90
|
-
if (!element.textContent.trim()) {
|
|
91
|
-
element.textContent = this._('Details');
|
|
92
|
-
} else {
|
|
93
|
-
element
|
|
94
|
-
.querySelectorAll(TagName.BR + ':not(:last-child)')
|
|
95
|
-
.forEach((item) => item.parentElement.removeChild(item));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!(element.lastElementChild instanceof HTMLBRElement)) {
|
|
99
|
-
this.editor.dom.insertLastChild(this.editor.dom.createElement(TagName.BR), element);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
25
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import Key, { isKey } from '../base/Key.js';
|
|
2
|
+
import Listener from '../base/Listener.js';
|
|
3
|
+
import TagName from '../base/TagName.js';
|
|
4
|
+
|
|
5
|
+
export default class SummaryListener extends Listener {
|
|
6
|
+
/**
|
|
7
|
+
* @param {Editor} editor
|
|
8
|
+
*/
|
|
9
|
+
constructor(editor) {
|
|
10
|
+
super(editor);
|
|
11
|
+
this.editor.root.addEventListener('insertsummary', this);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {CustomEvent} event
|
|
16
|
+
* @param {HTMLElement} event.detail.element
|
|
17
|
+
* @return {void}
|
|
18
|
+
*/
|
|
19
|
+
insertsummary({ detail: { element } }) {
|
|
20
|
+
this.#empty(element);
|
|
21
|
+
element.addEventListener('click', this);
|
|
22
|
+
element.addEventListener('blur', this);
|
|
23
|
+
element.addEventListener('keydown', this);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Prevents toggling details open state
|
|
28
|
+
*
|
|
29
|
+
* @param {MouseEvent} event
|
|
30
|
+
* @param {HTMLElement} event.target
|
|
31
|
+
* @return {void}
|
|
32
|
+
*/
|
|
33
|
+
click(event) {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
event.stopPropagation();
|
|
36
|
+
event.target.parentElement.open = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Sets default summary text content if it's empty
|
|
41
|
+
*
|
|
42
|
+
* @param {FocusEvent} event
|
|
43
|
+
* @param {HTMLElement} event.target
|
|
44
|
+
* @return {void}
|
|
45
|
+
*/
|
|
46
|
+
blur({ target }) {
|
|
47
|
+
this.#empty(target);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Fixes space and enter key handling for editable summary elements
|
|
52
|
+
*
|
|
53
|
+
* @param {KeyboardEvent} event
|
|
54
|
+
* @param {HTMLElement} event.target
|
|
55
|
+
* @return {void}
|
|
56
|
+
*/
|
|
57
|
+
keydown(event) {
|
|
58
|
+
if (isKey(event, Key.SPACE)) {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
event.stopPropagation();
|
|
61
|
+
this.editor.dom.insertText(' ');
|
|
62
|
+
} else if (isKey(event, Key.ENTER)) {
|
|
63
|
+
event.preventDefault();
|
|
64
|
+
event.stopPropagation();
|
|
65
|
+
event.target.parentElement.open = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Ensures summary element is not empty to avoid strange browser behaviour
|
|
71
|
+
*
|
|
72
|
+
* @param {HTMLElement} element
|
|
73
|
+
* @return {void}
|
|
74
|
+
*/
|
|
75
|
+
#empty(element) {
|
|
76
|
+
if (!element.textContent.trim()) {
|
|
77
|
+
element.textContent = this._('Details');
|
|
78
|
+
} else {
|
|
79
|
+
element
|
|
80
|
+
.querySelectorAll(TagName.BR + ':not(:last-child)')
|
|
81
|
+
.forEach((item) => item.parentElement.removeChild(item));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!(element.lastElementChild instanceof HTMLBRElement)) {
|
|
85
|
+
this.editor.dom.insertLastChild(this.editor.dom.createElement(TagName.BR), element);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
package/license.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c)
|
|
1
|
+
Copyright (c) 2026 Ayhan Akilli
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
4
4
|
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
package/package.json
CHANGED
package/paragraph/Paragraph.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import Base from '../base/Base.js';
|
|
2
2
|
import Break from '../break/Break.js';
|
|
3
|
-
import ParagraphListener from './ParagraphListener.js';
|
|
4
3
|
import Plugin from '../base/Plugin.js';
|
|
5
4
|
import TagGroup from '../base/TagGroup.js';
|
|
6
5
|
import TagName from '../base/TagName.js';
|
|
@@ -35,7 +34,6 @@ export default class Paragraph extends Plugin {
|
|
|
35
34
|
sortable: true,
|
|
36
35
|
enter: TagName.P,
|
|
37
36
|
});
|
|
38
|
-
new ParagraphListener(this.editor);
|
|
39
37
|
this._command(TagName.P);
|
|
40
38
|
this._toolbar('Paragraph');
|
|
41
39
|
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import Listener from '../base/Listener.js';
|
|
2
|
-
import TagName from '../base/TagName.js';
|
|
3
|
-
|
|
4
|
-
export default class ParagraphListener extends Listener {
|
|
5
|
-
/**
|
|
6
|
-
* @param {Editor} editor
|
|
7
|
-
*/
|
|
8
|
-
constructor(editor) {
|
|
9
|
-
super(editor);
|
|
10
|
-
this.editor.root.addEventListener('sethtml', this);
|
|
11
|
-
this.editor.root.addEventListener('insertp', this);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param {CustomEvent} event
|
|
16
|
-
* @param {HTMLElement} event.detail.element
|
|
17
|
-
* @return {void}
|
|
18
|
-
*/
|
|
19
|
-
sethtml({ detail: { element } }) {
|
|
20
|
-
Array.from(element.getElementsByTagName(TagName.P)).forEach((item) => item.addEventListener('paste', this));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @param {CustomEvent} event
|
|
25
|
-
* @param {HTMLParagraphElement} event.detail.element
|
|
26
|
-
* @return {void}
|
|
27
|
-
*/
|
|
28
|
-
insertp({ detail: { element } }) {
|
|
29
|
-
element.addEventListener('paste', this);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* @param {ClipboardEvent} event
|
|
34
|
-
* @param {HTMLParagraphElement} event.target
|
|
35
|
-
* @return {void}
|
|
36
|
-
*/
|
|
37
|
-
paste({ target }) {
|
|
38
|
-
this.editor.dom.window.setTimeout(() => this.editor.filters.filter(target));
|
|
39
|
-
}
|
|
40
|
-
}
|