overtype 1.2.7 → 2.0.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/README.md +222 -34
- package/dist/overtype-webcomponent.esm.js +4763 -0
- package/dist/overtype-webcomponent.esm.js.map +7 -0
- package/dist/overtype-webcomponent.js +4785 -0
- package/dist/overtype-webcomponent.js.map +7 -0
- package/dist/overtype-webcomponent.min.js +991 -0
- package/dist/overtype.cjs +682 -389
- package/dist/overtype.cjs.map +4 -4
- package/dist/overtype.d.ts +57 -14
- package/dist/overtype.esm.js +679 -388
- package/dist/overtype.esm.js.map +4 -4
- package/dist/overtype.js +679 -388
- package/dist/overtype.js.map +4 -4
- package/dist/overtype.min.js +157 -125
- package/package.json +18 -4
- package/src/link-tooltip.js +48 -73
- package/src/overtype-webcomponent.js +676 -0
- package/src/overtype.d.ts +57 -14
- package/src/overtype.js +186 -59
- package/src/parser.js +120 -17
- package/src/styles.js +92 -30
- package/src/toolbar-buttons.js +163 -0
- package/src/toolbar.js +194 -249
- package/diagram.png +0 -0
package/src/toolbar.js
CHANGED
|
@@ -1,351 +1,286 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Toolbar component for OverType editor
|
|
3
|
-
* Provides markdown formatting buttons with
|
|
3
|
+
* Provides markdown formatting buttons with support for custom buttons
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import * as icons from './icons.js';
|
|
7
6
|
import * as markdownActions from 'markdown-actions';
|
|
8
7
|
|
|
9
8
|
export class Toolbar {
|
|
10
|
-
constructor(editor,
|
|
9
|
+
constructor(editor, options = {}) {
|
|
11
10
|
this.editor = editor;
|
|
12
11
|
this.container = null;
|
|
13
12
|
this.buttons = {};
|
|
14
|
-
this.buttonConfig = buttonConfig;
|
|
15
|
-
}
|
|
16
13
|
|
|
14
|
+
// Get toolbar buttons array
|
|
15
|
+
this.toolbarButtons = options.toolbarButtons || [];
|
|
16
|
+
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Create and
|
|
19
|
+
* Create and render toolbar
|
|
20
20
|
*/
|
|
21
21
|
create() {
|
|
22
|
-
// Create toolbar container
|
|
23
22
|
this.container = document.createElement('div');
|
|
24
23
|
this.container.className = 'overtype-toolbar';
|
|
25
24
|
this.container.setAttribute('role', 'toolbar');
|
|
26
|
-
this.container.setAttribute('aria-label', '
|
|
27
|
-
|
|
28
|
-
// Define toolbar buttons
|
|
29
|
-
const buttonConfig = this.buttonConfig ?? [
|
|
30
|
-
{ name: 'bold', icon: icons.boldIcon, title: 'Bold (Ctrl+B)', action: 'toggleBold' },
|
|
31
|
-
{ name: 'italic', icon: icons.italicIcon, title: 'Italic (Ctrl+I)', action: 'toggleItalic' },
|
|
32
|
-
{ separator: true },
|
|
33
|
-
{ name: 'h1', icon: icons.h1Icon, title: 'Heading 1', action: 'insertH1' },
|
|
34
|
-
{ name: 'h2', icon: icons.h2Icon, title: 'Heading 2', action: 'insertH2' },
|
|
35
|
-
{ name: 'h3', icon: icons.h3Icon, title: 'Heading 3', action: 'insertH3' },
|
|
36
|
-
{ separator: true },
|
|
37
|
-
{ name: 'link', icon: icons.linkIcon, title: 'Insert Link (Ctrl+K)', action: 'insertLink' },
|
|
38
|
-
{ name: 'code', icon: icons.codeIcon, title: 'Code (Ctrl+`)', action: 'toggleCode' },
|
|
39
|
-
{ separator: true },
|
|
40
|
-
{ name: 'quote', icon: icons.quoteIcon, title: 'Quote', action: 'toggleQuote' },
|
|
41
|
-
{ separator: true },
|
|
42
|
-
{ name: 'bulletList', icon: icons.bulletListIcon, title: 'Bullet List', action: 'toggleBulletList' },
|
|
43
|
-
{ name: 'orderedList', icon: icons.orderedListIcon, title: 'Numbered List', action: 'toggleNumberedList' },
|
|
44
|
-
{ name: 'taskList', icon: icons.taskListIcon, title: 'Task List', action: 'toggleTaskList' },
|
|
45
|
-
{ separator: true },
|
|
46
|
-
{ name: 'viewMode', icon: icons.eyeIcon, title: 'View mode', action: 'toggle-view-menu', hasDropdown: true }
|
|
47
|
-
];
|
|
25
|
+
this.container.setAttribute('aria-label', 'Formatting toolbar');
|
|
48
26
|
|
|
49
|
-
// Create buttons
|
|
50
|
-
|
|
51
|
-
if (
|
|
52
|
-
const separator =
|
|
53
|
-
separator.className = 'overtype-toolbar-separator';
|
|
54
|
-
separator.setAttribute('role', 'separator');
|
|
27
|
+
// Create buttons from toolbarButtons array
|
|
28
|
+
this.toolbarButtons.forEach(buttonConfig => {
|
|
29
|
+
if (buttonConfig.name === 'separator') {
|
|
30
|
+
const separator = this.createSeparator();
|
|
55
31
|
this.container.appendChild(separator);
|
|
56
32
|
} else {
|
|
57
|
-
const button = this.createButton(
|
|
58
|
-
this.buttons[
|
|
33
|
+
const button = this.createButton(buttonConfig);
|
|
34
|
+
this.buttons[buttonConfig.name] = button;
|
|
59
35
|
this.container.appendChild(button);
|
|
60
36
|
}
|
|
61
37
|
});
|
|
62
38
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const wrapper = this.editor.element.querySelector('.overtype-wrapper');
|
|
66
|
-
if (container && wrapper) {
|
|
67
|
-
container.insertBefore(this.container, wrapper);
|
|
68
|
-
}
|
|
39
|
+
this.editor.wrapper.insertBefore(this.container, this.editor.wrapper.firstChild);
|
|
40
|
+
}
|
|
69
41
|
|
|
70
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Create a toolbar separator
|
|
44
|
+
*/
|
|
45
|
+
createSeparator() {
|
|
46
|
+
const separator = document.createElement('div');
|
|
47
|
+
separator.className = 'overtype-toolbar-separator';
|
|
48
|
+
separator.setAttribute('role', 'separator');
|
|
49
|
+
return separator;
|
|
71
50
|
}
|
|
72
51
|
|
|
73
52
|
/**
|
|
74
|
-
* Create
|
|
53
|
+
* Create a toolbar button
|
|
75
54
|
*/
|
|
76
|
-
createButton(
|
|
55
|
+
createButton(buttonConfig) {
|
|
77
56
|
const button = document.createElement('button');
|
|
78
57
|
button.className = 'overtype-toolbar-button';
|
|
79
58
|
button.type = 'button';
|
|
80
|
-
button.
|
|
81
|
-
button.
|
|
82
|
-
button.setAttribute('
|
|
83
|
-
button.innerHTML =
|
|
59
|
+
button.setAttribute('data-button', buttonConfig.name);
|
|
60
|
+
button.title = buttonConfig.title || '';
|
|
61
|
+
button.setAttribute('aria-label', buttonConfig.title || buttonConfig.name);
|
|
62
|
+
button.innerHTML = this.sanitizeSVG(buttonConfig.icon || '');
|
|
84
63
|
|
|
85
|
-
//
|
|
86
|
-
if (
|
|
64
|
+
// Special handling for viewMode dropdown
|
|
65
|
+
if (buttonConfig.name === 'viewMode') {
|
|
87
66
|
button.classList.add('has-dropdown');
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
67
|
+
button.dataset.dropdown = 'true';
|
|
68
|
+
button.addEventListener('click', (e) => {
|
|
69
|
+
e.preventDefault();
|
|
70
|
+
this.toggleViewModeDropdown(button);
|
|
71
|
+
});
|
|
72
|
+
return button;
|
|
92
73
|
}
|
|
93
74
|
|
|
94
|
-
//
|
|
95
|
-
button.
|
|
75
|
+
// Standard button click handler
|
|
76
|
+
button._clickHandler = async (e) => {
|
|
96
77
|
e.preventDefault();
|
|
97
|
-
this.handleAction(config.action, button);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
return button;
|
|
101
|
-
}
|
|
102
78
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
*/
|
|
106
|
-
async handleAction(action, button) {
|
|
107
|
-
const textarea = this.editor.textarea;
|
|
108
|
-
if (!textarea) return;
|
|
79
|
+
// Focus textarea before action
|
|
80
|
+
this.editor.textarea.focus();
|
|
109
81
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
82
|
+
try {
|
|
83
|
+
if (buttonConfig.action) {
|
|
84
|
+
// Call action with consistent context object
|
|
85
|
+
await buttonConfig.action({
|
|
86
|
+
editor: this.editor,
|
|
87
|
+
getValue: () => this.editor.getValue(),
|
|
88
|
+
setValue: (value) => this.editor.setValue(value),
|
|
89
|
+
event: e
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error(`Button "${buttonConfig.name}" error:`, error);
|
|
115
94
|
|
|
116
|
-
|
|
117
|
-
|
|
95
|
+
// Dispatch error event
|
|
96
|
+
this.editor.wrapper.dispatchEvent(new CustomEvent('button-error', {
|
|
97
|
+
detail: { buttonName: buttonConfig.name, error }
|
|
98
|
+
}));
|
|
118
99
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
markdownActions.toggleItalic(textarea);
|
|
127
|
-
break;
|
|
128
|
-
case 'insertH1':
|
|
129
|
-
markdownActions.toggleH1(textarea);
|
|
130
|
-
break;
|
|
131
|
-
case 'insertH2':
|
|
132
|
-
markdownActions.toggleH2(textarea);
|
|
133
|
-
break;
|
|
134
|
-
case 'insertH3':
|
|
135
|
-
markdownActions.toggleH3(textarea);
|
|
136
|
-
break;
|
|
137
|
-
case 'insertLink':
|
|
138
|
-
markdownActions.insertLink(textarea);
|
|
139
|
-
break;
|
|
140
|
-
case 'toggleCode':
|
|
141
|
-
markdownActions.toggleCode(textarea);
|
|
142
|
-
break;
|
|
143
|
-
case 'toggleBulletList':
|
|
144
|
-
markdownActions.toggleBulletList(textarea);
|
|
145
|
-
break;
|
|
146
|
-
case 'toggleNumberedList':
|
|
147
|
-
markdownActions.toggleNumberedList(textarea);
|
|
148
|
-
break;
|
|
149
|
-
case 'toggleQuote':
|
|
150
|
-
markdownActions.toggleQuote(textarea);
|
|
151
|
-
break;
|
|
152
|
-
case 'toggleTaskList':
|
|
153
|
-
markdownActions.toggleTaskList(textarea);
|
|
154
|
-
break;
|
|
155
|
-
case 'toggle-plain':
|
|
156
|
-
// Toggle between plain textarea and overlay mode
|
|
157
|
-
const isPlain = this.editor.container.classList.contains('plain-mode');
|
|
158
|
-
this.editor.showPlainTextarea(!isPlain);
|
|
159
|
-
break;
|
|
100
|
+
// Visual feedback
|
|
101
|
+
button.classList.add('button-error');
|
|
102
|
+
button.style.animation = 'buttonError 0.3s';
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
button.classList.remove('button-error');
|
|
105
|
+
button.style.animation = '';
|
|
106
|
+
}, 300);
|
|
160
107
|
}
|
|
108
|
+
};
|
|
161
109
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error('Error loading markdown-actions:', error);
|
|
166
|
-
}
|
|
110
|
+
button.addEventListener('click', button._clickHandler);
|
|
111
|
+
return button;
|
|
167
112
|
}
|
|
168
113
|
|
|
169
114
|
/**
|
|
170
|
-
*
|
|
115
|
+
* Sanitize SVG to prevent XSS
|
|
171
116
|
*/
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (!textarea) return;
|
|
117
|
+
sanitizeSVG(svg) {
|
|
118
|
+
if (typeof svg !== 'string') return '';
|
|
175
119
|
|
|
176
|
-
|
|
177
|
-
|
|
120
|
+
// Remove script tags and on* event handlers
|
|
121
|
+
const cleaned = svg
|
|
122
|
+
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
|
|
123
|
+
.replace(/\son\w+\s*=\s*["'][^"']*["']/gi, '')
|
|
124
|
+
.replace(/\son\w+\s*=\s*[^\s>]*/gi, '');
|
|
178
125
|
|
|
179
|
-
|
|
180
|
-
Object.entries(this.buttons).forEach(([name, button]) => {
|
|
181
|
-
let isActive = false;
|
|
182
|
-
|
|
183
|
-
switch (name) {
|
|
184
|
-
case 'bold':
|
|
185
|
-
isActive = activeFormats.includes('bold');
|
|
186
|
-
break;
|
|
187
|
-
case 'italic':
|
|
188
|
-
isActive = activeFormats.includes('italic');
|
|
189
|
-
break;
|
|
190
|
-
case 'code':
|
|
191
|
-
// Disabled: code detection is unreliable in code blocks
|
|
192
|
-
// isActive = activeFormats.includes('code');
|
|
193
|
-
isActive = false;
|
|
194
|
-
break;
|
|
195
|
-
case 'bulletList':
|
|
196
|
-
isActive = activeFormats.includes('bullet-list');
|
|
197
|
-
break;
|
|
198
|
-
case 'orderedList':
|
|
199
|
-
isActive = activeFormats.includes('numbered-list');
|
|
200
|
-
break;
|
|
201
|
-
case 'quote':
|
|
202
|
-
isActive = activeFormats.includes('quote');
|
|
203
|
-
break;
|
|
204
|
-
case 'taskList':
|
|
205
|
-
isActive = activeFormats.includes('task-list');
|
|
206
|
-
break;
|
|
207
|
-
case 'h1':
|
|
208
|
-
isActive = activeFormats.includes('header');
|
|
209
|
-
break;
|
|
210
|
-
case 'h2':
|
|
211
|
-
isActive = activeFormats.includes('header-2');
|
|
212
|
-
break;
|
|
213
|
-
case 'h3':
|
|
214
|
-
isActive = activeFormats.includes('header-3');
|
|
215
|
-
break;
|
|
216
|
-
case 'togglePlain':
|
|
217
|
-
// Button is active when in overlay mode (not plain mode)
|
|
218
|
-
isActive = !this.editor.container.classList.contains('plain-mode');
|
|
219
|
-
break;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
button.classList.toggle('active', isActive);
|
|
223
|
-
button.setAttribute('aria-pressed', isActive.toString());
|
|
224
|
-
});
|
|
225
|
-
} catch (error) {
|
|
226
|
-
// Silently fail if markdown-actions not available
|
|
227
|
-
}
|
|
126
|
+
return cleaned;
|
|
228
127
|
}
|
|
229
128
|
|
|
230
129
|
/**
|
|
231
|
-
* Toggle view mode dropdown
|
|
130
|
+
* Toggle view mode dropdown (internal implementation)
|
|
131
|
+
* Not exposed to users - viewMode button behavior is fixed
|
|
232
132
|
*/
|
|
233
|
-
|
|
133
|
+
toggleViewModeDropdown(button) {
|
|
234
134
|
// Close any existing dropdown
|
|
235
135
|
const existingDropdown = document.querySelector('.overtype-dropdown-menu');
|
|
236
136
|
if (existingDropdown) {
|
|
237
137
|
existingDropdown.remove();
|
|
238
138
|
button.classList.remove('dropdown-active');
|
|
239
|
-
document.removeEventListener('click', this.handleDocumentClick);
|
|
240
139
|
return;
|
|
241
140
|
}
|
|
242
141
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
142
|
+
button.classList.add('dropdown-active');
|
|
143
|
+
|
|
144
|
+
const dropdown = this.createViewModeDropdown(button);
|
|
145
|
+
|
|
246
146
|
// Position dropdown relative to button
|
|
247
147
|
const rect = button.getBoundingClientRect();
|
|
248
|
-
dropdown.style.
|
|
148
|
+
dropdown.style.position = 'absolute';
|
|
149
|
+
dropdown.style.top = `${rect.bottom + 5}px`;
|
|
249
150
|
dropdown.style.left = `${rect.left}px`;
|
|
250
|
-
|
|
251
|
-
// Append to body instead of button
|
|
151
|
+
|
|
252
152
|
document.body.appendChild(dropdown);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// Store reference for document click handler
|
|
153
|
+
|
|
154
|
+
// Click outside to close
|
|
256
155
|
this.handleDocumentClick = (e) => {
|
|
257
|
-
if (!
|
|
156
|
+
if (!dropdown.contains(e.target) && !button.contains(e.target)) {
|
|
258
157
|
dropdown.remove();
|
|
259
158
|
button.classList.remove('dropdown-active');
|
|
260
159
|
document.removeEventListener('click', this.handleDocumentClick);
|
|
261
160
|
}
|
|
262
161
|
};
|
|
263
|
-
|
|
264
|
-
// Close on click outside
|
|
162
|
+
|
|
265
163
|
setTimeout(() => {
|
|
266
164
|
document.addEventListener('click', this.handleDocumentClick);
|
|
267
165
|
}, 0);
|
|
268
166
|
}
|
|
269
167
|
|
|
270
168
|
/**
|
|
271
|
-
* Create view mode dropdown menu
|
|
169
|
+
* Create view mode dropdown menu (internal implementation)
|
|
272
170
|
*/
|
|
273
|
-
|
|
171
|
+
createViewModeDropdown(button) {
|
|
274
172
|
const dropdown = document.createElement('div');
|
|
275
173
|
dropdown.className = 'overtype-dropdown-menu';
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const isPlain = this.editor.container.classList.contains('plain-mode');
|
|
279
|
-
const isPreview = this.editor.container.classList.contains('preview-mode');
|
|
280
|
-
const currentMode = isPreview ? 'preview' : (isPlain ? 'plain' : 'normal');
|
|
281
|
-
|
|
282
|
-
// Create menu items
|
|
283
|
-
const modes = [
|
|
174
|
+
|
|
175
|
+
const items = [
|
|
284
176
|
{ id: 'normal', label: 'Normal Edit', icon: '✓' },
|
|
285
177
|
{ id: 'plain', label: 'Plain Textarea', icon: '✓' },
|
|
286
178
|
{ id: 'preview', label: 'Preview Mode', icon: '✓' }
|
|
287
179
|
];
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (currentMode === mode.id) {
|
|
305
|
-
item.classList.add('active');
|
|
180
|
+
|
|
181
|
+
const currentMode = this.editor.container.dataset.mode || 'normal';
|
|
182
|
+
|
|
183
|
+
items.forEach(item => {
|
|
184
|
+
const menuItem = document.createElement('button');
|
|
185
|
+
menuItem.className = 'overtype-dropdown-item';
|
|
186
|
+
menuItem.type = 'button';
|
|
187
|
+
menuItem.textContent = item.label;
|
|
188
|
+
|
|
189
|
+
if (item.id === currentMode) {
|
|
190
|
+
menuItem.classList.add('active');
|
|
191
|
+
menuItem.setAttribute('aria-current', 'true');
|
|
192
|
+
const checkmark = document.createElement('span');
|
|
193
|
+
checkmark.className = 'overtype-dropdown-icon';
|
|
194
|
+
checkmark.textContent = item.icon;
|
|
195
|
+
menuItem.prepend(checkmark);
|
|
306
196
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
e.
|
|
310
|
-
|
|
197
|
+
|
|
198
|
+
menuItem.addEventListener('click', (e) => {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
|
|
201
|
+
// Handle view mode changes
|
|
202
|
+
switch(item.id) {
|
|
203
|
+
case 'plain':
|
|
204
|
+
this.editor.showPlainTextarea();
|
|
205
|
+
break;
|
|
206
|
+
case 'preview':
|
|
207
|
+
this.editor.showPreviewMode();
|
|
208
|
+
break;
|
|
209
|
+
case 'normal':
|
|
210
|
+
default:
|
|
211
|
+
this.editor.showNormalEditMode();
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
|
|
311
215
|
dropdown.remove();
|
|
312
|
-
|
|
216
|
+
button.classList.remove('dropdown-active');
|
|
313
217
|
document.removeEventListener('click', this.handleDocumentClick);
|
|
314
218
|
});
|
|
315
|
-
|
|
316
|
-
dropdown.appendChild(
|
|
219
|
+
|
|
220
|
+
dropdown.appendChild(menuItem);
|
|
317
221
|
});
|
|
318
|
-
|
|
222
|
+
|
|
319
223
|
return dropdown;
|
|
320
224
|
}
|
|
321
225
|
|
|
322
226
|
/**
|
|
323
|
-
*
|
|
227
|
+
* Update active states of toolbar buttons
|
|
324
228
|
*/
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
229
|
+
updateButtonStates() {
|
|
230
|
+
try {
|
|
231
|
+
const activeFormats = markdownActions.getActiveFormats?.(
|
|
232
|
+
this.editor.textarea,
|
|
233
|
+
this.editor.textarea.selectionStart
|
|
234
|
+
) || [];
|
|
235
|
+
|
|
236
|
+
Object.entries(this.buttons).forEach(([name, button]) => {
|
|
237
|
+
if (name === 'viewMode') return; // Skip dropdown button
|
|
238
|
+
|
|
239
|
+
let isActive = false;
|
|
240
|
+
|
|
241
|
+
switch(name) {
|
|
242
|
+
case 'bold':
|
|
243
|
+
isActive = activeFormats.includes('bold');
|
|
244
|
+
break;
|
|
245
|
+
case 'italic':
|
|
246
|
+
isActive = activeFormats.includes('italic');
|
|
247
|
+
break;
|
|
248
|
+
case 'code':
|
|
249
|
+
isActive = false; // Disabled: unreliable in code blocks
|
|
250
|
+
break;
|
|
251
|
+
case 'bulletList':
|
|
252
|
+
isActive = activeFormats.includes('bullet-list');
|
|
253
|
+
break;
|
|
254
|
+
case 'orderedList':
|
|
255
|
+
isActive = activeFormats.includes('numbered-list');
|
|
256
|
+
break;
|
|
257
|
+
case 'taskList':
|
|
258
|
+
isActive = activeFormats.includes('task-list');
|
|
259
|
+
break;
|
|
260
|
+
case 'quote':
|
|
261
|
+
isActive = activeFormats.includes('quote');
|
|
262
|
+
break;
|
|
263
|
+
case 'h1':
|
|
264
|
+
isActive = activeFormats.includes('header');
|
|
265
|
+
break;
|
|
266
|
+
case 'h2':
|
|
267
|
+
isActive = activeFormats.includes('header-2');
|
|
268
|
+
break;
|
|
269
|
+
case 'h3':
|
|
270
|
+
isActive = activeFormats.includes('header-3');
|
|
271
|
+
break;
|
|
342
272
|
}
|
|
343
|
-
|
|
273
|
+
|
|
274
|
+
button.classList.toggle('active', isActive);
|
|
275
|
+
button.setAttribute('aria-pressed', isActive.toString());
|
|
276
|
+
});
|
|
277
|
+
} catch (error) {
|
|
278
|
+
// Silently fail if markdown-actions not available
|
|
344
279
|
}
|
|
345
280
|
}
|
|
346
281
|
|
|
347
282
|
/**
|
|
348
|
-
* Destroy toolbar
|
|
283
|
+
* Destroy toolbar and cleanup
|
|
349
284
|
*/
|
|
350
285
|
destroy() {
|
|
351
286
|
if (this.container) {
|
|
@@ -353,9 +288,19 @@ export class Toolbar {
|
|
|
353
288
|
if (this.handleDocumentClick) {
|
|
354
289
|
document.removeEventListener('click', this.handleDocumentClick);
|
|
355
290
|
}
|
|
291
|
+
|
|
292
|
+
// Clean up button listeners
|
|
293
|
+
Object.values(this.buttons).forEach(button => {
|
|
294
|
+
if (button._clickHandler) {
|
|
295
|
+
button.removeEventListener('click', button._clickHandler);
|
|
296
|
+
delete button._clickHandler;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Remove container
|
|
356
301
|
this.container.remove();
|
|
357
302
|
this.container = null;
|
|
358
303
|
this.buttons = {};
|
|
359
304
|
}
|
|
360
305
|
}
|
|
361
|
-
}
|
|
306
|
+
}
|
package/diagram.png
DELETED
|
Binary file
|