autumnnote 1.6.1 → 1.6.3
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/dist/autumnnote.es.js +147 -348
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +147 -348
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/Context.js +5 -2
- package/src/js/index.js +1 -1
- package/src/js/module/BaseDialog.js +126 -0
- package/src/js/module/EmojiDialog.js +7 -44
- package/src/js/module/FindReplace.js +4 -18
- package/src/js/module/IconDialog.js +9 -41
- package/src/js/module/ImageDialog.js +12 -82
- package/src/js/module/LinkDialog.js +12 -78
- package/src/js/module/ShortcutsDialog.js +20 -49
- package/src/js/module/VideoDialog.js +10 -80
|
@@ -3,41 +3,12 @@
|
|
|
3
3
|
* Inspired by Summernote's LinkDialog — rewritten without jQuery
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createElement, on
|
|
7
|
-
import {
|
|
6
|
+
import { createElement, on } from '../core/dom.js';
|
|
7
|
+
import { BaseDialog } from './BaseDialog.js';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* @param {import('../Context.js').Context} context
|
|
12
|
-
*/
|
|
13
|
-
constructor(context) {
|
|
14
|
-
this.context = context;
|
|
15
|
-
this.options = context.options;
|
|
16
|
-
/** @type {HTMLElement|null} */
|
|
17
|
-
this._dialog = null;
|
|
18
|
-
this._disposers = [];
|
|
19
|
-
this._savedRange = null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// ---------------------------------------------------------------------------
|
|
23
|
-
// Lifecycle
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
|
|
26
|
-
initialize() {
|
|
27
|
-
this._dialog = this._buildDialog();
|
|
28
|
-
document.body.appendChild(this._dialog);
|
|
29
|
-
return this;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
destroy() {
|
|
33
|
-
this._disposers.forEach((d) => d());
|
|
34
|
-
this._disposers = [];
|
|
35
|
-
if (this._dialog && this._dialog.parentNode) {
|
|
36
|
-
this._dialog.remove();
|
|
37
|
-
}
|
|
38
|
-
this._dialog = null;
|
|
39
|
-
}
|
|
9
|
+
const ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>`;
|
|
40
10
|
|
|
11
|
+
export class LinkDialog extends BaseDialog {
|
|
41
12
|
// ---------------------------------------------------------------------------
|
|
42
13
|
// Public API
|
|
43
14
|
// ---------------------------------------------------------------------------
|
|
@@ -47,9 +18,7 @@ export class LinkDialog {
|
|
|
47
18
|
* Pre-fills with the currently selected link if present.
|
|
48
19
|
*/
|
|
49
20
|
show() {
|
|
50
|
-
|
|
51
|
-
this._savedRange = range;
|
|
52
|
-
});
|
|
21
|
+
this._saveRange();
|
|
53
22
|
this._prefill();
|
|
54
23
|
this._open();
|
|
55
24
|
}
|
|
@@ -60,16 +29,7 @@ export class LinkDialog {
|
|
|
60
29
|
|
|
61
30
|
_buildDialog() {
|
|
62
31
|
const L = this.context.locale.linkDialog;
|
|
63
|
-
const
|
|
64
|
-
const box = createElement('div', { class: 'an-dialog-box' });
|
|
65
|
-
|
|
66
|
-
const header = createElement('div', { class: 'an-dialog-header' });
|
|
67
|
-
const iconEl = createElement('span', { class: 'an-dialog-icon' });
|
|
68
|
-
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>`;
|
|
69
|
-
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
70
|
-
title.textContent = L.title;
|
|
71
|
-
header.appendChild(iconEl);
|
|
72
|
-
header.appendChild(title);
|
|
32
|
+
const { overlay, box } = this._buildDialogShell(L.ariaLabel, ICON_SVG, L.title);
|
|
73
33
|
|
|
74
34
|
// URL field
|
|
75
35
|
const urlLabel = createElement('label', { class: 'an-label' });
|
|
@@ -83,6 +43,7 @@ export class LinkDialog {
|
|
|
83
43
|
autocomplete: 'off',
|
|
84
44
|
}));
|
|
85
45
|
this._urlInput = urlInput;
|
|
46
|
+
this._firstInput = urlInput;
|
|
86
47
|
|
|
87
48
|
// Text field
|
|
88
49
|
const textLabel = createElement('label', { class: 'an-label' });
|
|
@@ -108,26 +69,13 @@ export class LinkDialog {
|
|
|
108
69
|
tabLabel.appendChild(tabCheckbox);
|
|
109
70
|
tabLabel.appendChild(document.createTextNode(' ' + L.openInNewTab));
|
|
110
71
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
116
|
-
cancelBtn.textContent = L.cancelBtn;
|
|
117
|
-
btnRow.appendChild(insertBtn);
|
|
118
|
-
btnRow.appendChild(cancelBtn);
|
|
119
|
-
|
|
120
|
-
box.append(header, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
|
|
121
|
-
overlay.appendChild(box);
|
|
122
|
-
makeDraggable(header, box);
|
|
123
|
-
|
|
124
|
-
// Events
|
|
125
|
-
const d1 = on(insertBtn, 'click', () => this._onInsert());
|
|
126
|
-
const d2 = on(cancelBtn, 'click', () => this._close());
|
|
127
|
-
const d3 = on(overlay, 'click', (e) => { if (e.target === overlay) this._close(); });
|
|
72
|
+
const btnRow = this._buildButtonRow(L.insertBtn, L.cancelBtn, () => this._onInsert());
|
|
73
|
+
|
|
74
|
+
box.append(urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
|
|
75
|
+
|
|
128
76
|
const d4 = on(urlInput, 'keydown', (e) => { if (/** @type {KeyboardEvent} */ (e).key === 'Enter') { e.preventDefault(); this._onInsert(); } });
|
|
129
77
|
const d5 = on(textInput, 'keydown', (e) => { if (/** @type {KeyboardEvent} */ (e).key === 'Enter') { e.preventDefault(); this._onInsert(); } });
|
|
130
|
-
this._disposers.push(
|
|
78
|
+
this._disposers.push(d4, d5);
|
|
131
79
|
|
|
132
80
|
return overlay;
|
|
133
81
|
}
|
|
@@ -194,18 +142,4 @@ export class LinkDialog {
|
|
|
194
142
|
this.context.invoke('editor.insertLink', url, text, newTab);
|
|
195
143
|
this._close();
|
|
196
144
|
}
|
|
197
|
-
|
|
198
|
-
_open() {
|
|
199
|
-
if (this._dialog) {
|
|
200
|
-
this._dialog.style.display = 'flex';
|
|
201
|
-
this._removeTrap = trapFocus(this._dialog, () => this._close());
|
|
202
|
-
setTimeout(() => this._urlInput && this._urlInput.focus(), 50);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
_close() {
|
|
207
|
-
if (this._dialog) this._dialog.style.display = 'none';
|
|
208
|
-
if (this._removeTrap) { this._removeTrap(); this._removeTrap = null; }
|
|
209
|
-
this._savedRange = null;
|
|
210
|
-
}
|
|
211
145
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Opened via the toolbar '?' button or Shift+? inside the editor.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createElement, on
|
|
6
|
+
import { createElement, on } from '../core/dom.js';
|
|
7
|
+
import { BaseDialog } from './BaseDialog.js';
|
|
7
8
|
|
|
8
9
|
const SHORTCUTS = [
|
|
9
10
|
{
|
|
@@ -51,64 +52,37 @@ const SHORTCUTS = [
|
|
|
51
52
|
},
|
|
52
53
|
];
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
/** @param {import('../Context.js').Context} context */
|
|
56
|
-
constructor(context) {
|
|
57
|
-
this.context = context;
|
|
58
|
-
this._dialog = null;
|
|
59
|
-
this._closeBtn = null;
|
|
60
|
-
this._disposers = [];
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
initialize() {
|
|
64
|
-
this._dialog = this._buildDialog();
|
|
65
|
-
document.body.appendChild(this._dialog);
|
|
66
|
-
return this;
|
|
67
|
-
}
|
|
55
|
+
const ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="20" height="14" rx="2"/><path d="M6 11h.01M10 11h.01M14 11h.01M18 11h.01M6 15h.01M18 15h.01M10 15h4"/><path d="M7 3l5 4 5-4"/></svg>`;
|
|
68
56
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
this._dialog = null;
|
|
74
|
-
}
|
|
57
|
+
export class ShortcutsDialog extends BaseDialog {
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// Public API
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
75
61
|
|
|
76
62
|
show() {
|
|
77
|
-
|
|
78
|
-
this._dialog.style.display = 'flex';
|
|
79
|
-
this._removeTrap = trapFocus(this._dialog, () => this._close());
|
|
80
|
-
setTimeout(() => this._closeBtn?.focus(), 50);
|
|
81
|
-
}
|
|
63
|
+
this._open();
|
|
82
64
|
}
|
|
83
65
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Build dialog
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
88
69
|
|
|
89
70
|
_buildDialog() {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
'aria-modal': 'true',
|
|
94
|
-
'aria-label': this.context.locale.shortcutsDialog.ariaLabel,
|
|
95
|
-
});
|
|
71
|
+
const L = this.context.locale.shortcutsDialog;
|
|
72
|
+
const { overlay, box } = this._buildDialogShell(L.ariaLabel, ICON_SVG, L.title);
|
|
73
|
+
box.classList.add('an-shortcuts-box');
|
|
96
74
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Title row with close button
|
|
100
|
-
const titleRow = createElement('div', { class: 'an-icon-title-row' });
|
|
101
|
-
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
102
|
-
title.textContent = this.context.locale.shortcutsDialog.title;
|
|
75
|
+
// Close button pinned to the right of the header row
|
|
103
76
|
const closeBtn = createElement('button', {
|
|
104
77
|
type: 'button',
|
|
105
78
|
class: 'an-icon-close',
|
|
106
|
-
'aria-label':
|
|
79
|
+
'aria-label': L.close,
|
|
80
|
+
style: 'margin-left:auto',
|
|
107
81
|
});
|
|
108
82
|
closeBtn.textContent = '×';
|
|
109
83
|
this._closeBtn = closeBtn;
|
|
110
|
-
|
|
111
|
-
box.appendChild(
|
|
84
|
+
this._firstInput = closeBtn;
|
|
85
|
+
box.querySelector('.an-dialog-header').appendChild(closeBtn);
|
|
112
86
|
|
|
113
87
|
const shortcuts = this.context.locale.shortcutsDialog.shortcuts || SHORTCUTS;
|
|
114
88
|
shortcuts.forEach(({ category, items }) => {
|
|
@@ -129,11 +103,8 @@ export class ShortcutsDialog {
|
|
|
129
103
|
box.appendChild(table);
|
|
130
104
|
});
|
|
131
105
|
|
|
132
|
-
overlay.appendChild(box);
|
|
133
|
-
|
|
134
106
|
const d1 = on(closeBtn, 'click', () => this._close());
|
|
135
|
-
|
|
136
|
-
this._disposers.push(d1, d2);
|
|
107
|
+
this._disposers.push(d1);
|
|
137
108
|
|
|
138
109
|
return overlay;
|
|
139
110
|
}
|
|
@@ -7,47 +7,18 @@
|
|
|
7
7
|
* • Direct video URLs → <video> element (.mp4 / .webm / .ogg)
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { createElement, on
|
|
11
|
-
import {
|
|
10
|
+
import { createElement, on } from '../core/dom.js';
|
|
11
|
+
import { BaseDialog } from './BaseDialog.js';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
/** @param {import('../Context.js').Context} context */
|
|
15
|
-
constructor(context) {
|
|
16
|
-
this.context = context;
|
|
17
|
-
this.options = context.options;
|
|
18
|
-
/** @type {HTMLElement|null} */
|
|
19
|
-
this._dialog = null;
|
|
20
|
-
this._disposers = [];
|
|
21
|
-
this._savedRange = null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Lifecycle
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
initialize() {
|
|
29
|
-
this._dialog = this._buildDialog();
|
|
30
|
-
document.body.appendChild(this._dialog);
|
|
31
|
-
return this;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
destroy() {
|
|
35
|
-
this._disposers.forEach((d) => d());
|
|
36
|
-
this._disposers = [];
|
|
37
|
-
if (this._dialog && this._dialog.parentNode) {
|
|
38
|
-
this._dialog.remove();
|
|
39
|
-
}
|
|
40
|
-
this._dialog = null;
|
|
41
|
-
}
|
|
13
|
+
const ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`;
|
|
42
14
|
|
|
15
|
+
export class VideoDialog extends BaseDialog {
|
|
43
16
|
// ---------------------------------------------------------------------------
|
|
44
17
|
// Public API
|
|
45
18
|
// ---------------------------------------------------------------------------
|
|
46
19
|
|
|
47
20
|
show() {
|
|
48
|
-
|
|
49
|
-
this._savedRange = range;
|
|
50
|
-
});
|
|
21
|
+
this._saveRange();
|
|
51
22
|
this._urlInput.value = '';
|
|
52
23
|
this._widthInput.value = '560';
|
|
53
24
|
this._hintEl.textContent = '';
|
|
@@ -60,21 +31,7 @@ export class VideoDialog {
|
|
|
60
31
|
|
|
61
32
|
_buildDialog() {
|
|
62
33
|
const L = this.context.locale.videoDialog;
|
|
63
|
-
const overlay =
|
|
64
|
-
class: 'an-dialog-overlay',
|
|
65
|
-
role: 'dialog',
|
|
66
|
-
'aria-modal': 'true',
|
|
67
|
-
'aria-label': L.ariaLabel,
|
|
68
|
-
});
|
|
69
|
-
const box = createElement('div', { class: 'an-dialog-box' });
|
|
70
|
-
|
|
71
|
-
const header = createElement('div', { class: 'an-dialog-header' });
|
|
72
|
-
const iconEl = createElement('span', { class: 'an-dialog-icon' });
|
|
73
|
-
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`;
|
|
74
|
-
const title = createElement('h3', { class: 'an-dialog-title' });
|
|
75
|
-
title.textContent = L.title;
|
|
76
|
-
header.appendChild(iconEl);
|
|
77
|
-
header.appendChild(title);
|
|
34
|
+
const { overlay, box } = this._buildDialogShell(L.ariaLabel, ICON_SVG, L.title);
|
|
78
35
|
|
|
79
36
|
// URL input
|
|
80
37
|
const urlLabel = createElement('label', { class: 'an-label' });
|
|
@@ -86,6 +43,7 @@ export class VideoDialog {
|
|
|
86
43
|
autocomplete: 'off',
|
|
87
44
|
}));
|
|
88
45
|
this._urlInput = urlInput;
|
|
46
|
+
this._firstInput = urlInput;
|
|
89
47
|
|
|
90
48
|
// Hint (detected source)
|
|
91
49
|
const hintEl = createElement('p', { class: 'an-dialog-hint' });
|
|
@@ -104,30 +62,16 @@ export class VideoDialog {
|
|
|
104
62
|
}));
|
|
105
63
|
this._widthInput = widthInput;
|
|
106
64
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const insertBtn = createElement('button', { type: 'button', class: 'an-btn an-btn-primary' });
|
|
110
|
-
insertBtn.textContent = L.insertBtn;
|
|
111
|
-
const cancelBtn = createElement('button', { type: 'button', class: 'an-btn' });
|
|
112
|
-
cancelBtn.textContent = L.cancelBtn;
|
|
113
|
-
btnRow.appendChild(insertBtn);
|
|
114
|
-
btnRow.appendChild(cancelBtn);
|
|
115
|
-
|
|
116
|
-
box.append(header, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
|
|
117
|
-
overlay.appendChild(box);
|
|
118
|
-
makeDraggable(header, box);
|
|
65
|
+
const btnRow = this._buildButtonRow(L.insertBtn, L.cancelBtn, () => this._onInsert());
|
|
66
|
+
box.append(urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
|
|
119
67
|
|
|
120
68
|
// Live URL hint
|
|
121
69
|
const d0 = on(urlInput, 'input', () => {
|
|
122
70
|
const info = this._parseVideoUrl(urlInput.value.trim());
|
|
123
71
|
hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : (urlInput.value ? this.context.locale.videoDialog.unknownFormat : '');
|
|
124
72
|
});
|
|
125
|
-
|
|
126
|
-
const d1 = on(insertBtn, 'click', () => this._onInsert());
|
|
127
|
-
const d2 = on(cancelBtn, 'click', () => this._close());
|
|
128
|
-
const d3 = on(overlay, 'click', (e) => { if (e.target === overlay) this._close(); });
|
|
129
73
|
const d4 = on(urlInput, 'keydown', (e) => { if (/** @type {KeyboardEvent} */ (e).key === 'Enter') { e.preventDefault(); this._onInsert(); } });
|
|
130
|
-
this._disposers.push(d0,
|
|
74
|
+
this._disposers.push(d0, d4);
|
|
131
75
|
|
|
132
76
|
return overlay;
|
|
133
77
|
}
|
|
@@ -157,20 +101,6 @@ export class VideoDialog {
|
|
|
157
101
|
this._close();
|
|
158
102
|
}
|
|
159
103
|
|
|
160
|
-
_open() {
|
|
161
|
-
if (this._dialog) {
|
|
162
|
-
this._dialog.style.display = 'flex';
|
|
163
|
-
this._removeTrap = trapFocus(this._dialog, () => this._close());
|
|
164
|
-
setTimeout(() => this._urlInput && this._urlInput.focus(), 50);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
_close() {
|
|
169
|
-
if (this._dialog) this._dialog.style.display = 'none';
|
|
170
|
-
if (this._removeTrap) { this._removeTrap(); this._removeTrap = null; }
|
|
171
|
-
this._savedRange = null;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
104
|
// ---------------------------------------------------------------------------
|
|
175
105
|
// URL parsing & HTML building
|
|
176
106
|
// ---------------------------------------------------------------------------
|