@oix1987/yjd 1.0.0 → 1.0.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.
- package/README.md +73 -22
- package/dist/rich-editor.esm.js +2 -0
- package/dist/rich-editor.esm.js.map +1 -0
- package/dist/rich-editor.min.js +2 -0
- package/dist/rich-editor.min.js.map +1 -0
- package/package.json +12 -7
- package/index.js +0 -221
- package/lib/core/editor.js +0 -1175
- package/lib/core/format.js +0 -542
- package/lib/core/module.js +0 -81
- package/lib/core/registry.js +0 -152
- package/lib/formats/background.js +0 -212
- package/lib/formats/bold.js +0 -67
- package/lib/formats/capitalization.js +0 -563
- package/lib/formats/color.js +0 -165
- package/lib/formats/emoji.js +0 -282
- package/lib/formats/font-family.js +0 -547
- package/lib/formats/heading.js +0 -502
- package/lib/formats/image.js +0 -344
- package/lib/formats/import.js +0 -385
- package/lib/formats/indent.js +0 -297
- package/lib/formats/italic.js +0 -27
- package/lib/formats/line-height.js +0 -558
- package/lib/formats/link.js +0 -251
- package/lib/formats/list.js +0 -635
- package/lib/formats/strike.js +0 -31
- package/lib/formats/subscript.js +0 -36
- package/lib/formats/superscript.js +0 -35
- package/lib/formats/table.js +0 -288
- package/lib/formats/tag.js +0 -304
- package/lib/formats/text-align.js +0 -421
- package/lib/formats/text-size.js +0 -497
- package/lib/formats/underline.js +0 -30
- package/lib/formats/video.js +0 -372
- package/lib/modules/block-toolbar.js +0 -628
- package/lib/modules/code-view.js +0 -434
- package/lib/modules/history.js +0 -410
- package/lib/modules/resize-handles.js +0 -677
- package/lib/modules/table-toolbar.js +0 -618
- package/lib/modules/toolbar.js +0 -424
- package/lib/styles-loader.js +0 -144
- package/lib/styles.css +0 -2123
- package/lib/ui/color-picker.js +0 -296
- package/lib/ui/customselect.js +0 -319
- package/lib/ui/emoji-picker.js +0 -196
- package/lib/ui/icons.js +0 -413
- package/lib/ui/image-popup.js +0 -444
- package/lib/ui/import-popup.js +0 -288
- package/lib/ui/link-popup.js +0 -191
- package/lib/ui/list-picker.js +0 -307
- package/lib/ui/select-button.js +0 -61
- package/lib/ui/table-popup.js +0 -171
- package/lib/ui/tag-popup.js +0 -249
- package/lib/ui/text-align-picker.js +0 -281
- package/lib/ui/video-popup.js +0 -422
- package/lib/utils/history-helper.js +0 -50
- package/lib/utils/popup-helper.js +0 -219
- package/lib/utils/popup-positioning.js +0 -231
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
import { InlineFormat } from '../core/format.js';
|
|
2
|
-
import CustomSelect from '../ui/customselect.js';
|
|
3
|
-
import { saveBeforeFormat } from '../utils/history-helper.js';
|
|
4
|
-
import Editor from '../core/editor.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Font Family Format - Handles font family formatting
|
|
8
|
-
* Now supports multiple editor instances with separate popup instances
|
|
9
|
-
*/
|
|
10
|
-
class FontFamily extends InlineFormat {
|
|
11
|
-
static formatName = 'fontFamily';
|
|
12
|
-
static tagName = 'SPAN';
|
|
13
|
-
|
|
14
|
-
constructor() {
|
|
15
|
-
super();
|
|
16
|
-
|
|
17
|
-
// Get current editor instance
|
|
18
|
-
const currentEditor = Editor.getCurrentInstance();
|
|
19
|
-
if (!currentEditor) {
|
|
20
|
-
console.warn('No editor instance found for FontFamily format');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.editorId = currentEditor.instanceId;
|
|
25
|
-
|
|
26
|
-
// Check if this editor already has a font family select instance
|
|
27
|
-
let customSelect = currentEditor.getPopupInstance('font-family');
|
|
28
|
-
|
|
29
|
-
if (!customSelect) {
|
|
30
|
-
// Create new custom select instance for this editor
|
|
31
|
-
const fontMap = FontFamily.getFontMap();
|
|
32
|
-
const items = Object.values(fontMap).map(fontData => ({
|
|
33
|
-
value: fontData.font,
|
|
34
|
-
label: fontData.element,
|
|
35
|
-
title: fontData.title
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
customSelect = new CustomSelect({
|
|
39
|
-
items: items,
|
|
40
|
-
displayProperty: 'label',
|
|
41
|
-
valueProperty: 'value',
|
|
42
|
-
className: 'font-family-select',
|
|
43
|
-
onItemSelect: (value, item) => {
|
|
44
|
-
FontFamily.applyFontFamilyToCurrentSelection(value, this.editorId);
|
|
45
|
-
},
|
|
46
|
-
editor: currentEditor,
|
|
47
|
-
editorId: this.editorId
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Store popup instance in editor
|
|
51
|
-
currentEditor.setPopupInstance('font-family', customSelect);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.customSelect = customSelect;
|
|
55
|
-
|
|
56
|
-
// Set up event listener for selection changes
|
|
57
|
-
this.setupSelectionListener();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Create a new FontFamily format instance for a specific editor
|
|
62
|
-
* @param {string} editorId - Editor instance ID
|
|
63
|
-
* @returns {FontFamily} FontFamily format instance
|
|
64
|
-
*/
|
|
65
|
-
static createForEditor(editorId) {
|
|
66
|
-
const editor = Editor.getInstanceById(editorId);
|
|
67
|
-
if (!editor) {
|
|
68
|
-
console.warn('No editor instance found for ID:', editorId);
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Temporarily set as current instance
|
|
73
|
-
const originalCurrent = Editor.currentInstance;
|
|
74
|
-
Editor.currentInstance = editor;
|
|
75
|
-
|
|
76
|
-
// Create format instance
|
|
77
|
-
const format = new FontFamily();
|
|
78
|
-
|
|
79
|
-
// Restore original current instance
|
|
80
|
-
Editor.currentInstance = originalCurrent;
|
|
81
|
-
|
|
82
|
-
return format;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Set up event listener for selection changes to update button text
|
|
87
|
-
*/
|
|
88
|
-
setupSelectionListener() {
|
|
89
|
-
// Use a debounced function to avoid too many updates
|
|
90
|
-
let updateTimeout;
|
|
91
|
-
const debouncedUpdate = () => {
|
|
92
|
-
clearTimeout(updateTimeout);
|
|
93
|
-
updateTimeout = setTimeout(() => {
|
|
94
|
-
// Only update if selection is in this editor
|
|
95
|
-
const selection = window.getSelection();
|
|
96
|
-
if (selection && selection.rangeCount > 0) {
|
|
97
|
-
const range = selection.getRangeAt(0);
|
|
98
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
99
|
-
if (editor && (editor.editor.contains(range.startContainer) || editor.editor.isSameNode(range.startContainer))) {
|
|
100
|
-
this.updateButtonText();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}, 50); // 50ms delay
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
// Listen for selection changes
|
|
107
|
-
document.addEventListener('selectionchange', debouncedUpdate);
|
|
108
|
-
|
|
109
|
-
// Also listen for mouseup and keyup events for immediate feedback
|
|
110
|
-
document.addEventListener('mouseup', debouncedUpdate);
|
|
111
|
-
document.addEventListener('keyup', debouncedUpdate);
|
|
112
|
-
|
|
113
|
-
// Store the listener for cleanup
|
|
114
|
-
this.selectionListener = debouncedUpdate;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Get font map with different font families
|
|
119
|
-
*/
|
|
120
|
-
static getFontMap() {
|
|
121
|
-
return {
|
|
122
|
-
'Arial': {
|
|
123
|
-
font: 'Arial, sans-serif',
|
|
124
|
-
element: '<span style="font-family: Arial, sans-serif">Arial</span>',
|
|
125
|
-
title: 'Arial'
|
|
126
|
-
},
|
|
127
|
-
'Helvetica': {
|
|
128
|
-
font: 'Helvetica, Arial, sans-serif',
|
|
129
|
-
element: '<span style="font-family: Helvetica, Arial, sans-serif">Helvetica</span>',
|
|
130
|
-
title: 'Helvetica'
|
|
131
|
-
},
|
|
132
|
-
'Times New Roman': {
|
|
133
|
-
font: '"Times New Roman", Times, serif',
|
|
134
|
-
element: '<span style="font-family: \'Times New Roman\', Times, serif">Times New Roman</span>',
|
|
135
|
-
title: 'Times New Roman'
|
|
136
|
-
},
|
|
137
|
-
'Georgia': {
|
|
138
|
-
font: 'Georgia, serif',
|
|
139
|
-
element: '<span style="font-family: Georgia, serif">Georgia</span>',
|
|
140
|
-
title: 'Georgia'
|
|
141
|
-
},
|
|
142
|
-
'Verdana': {
|
|
143
|
-
font: 'Verdana, Geneva, sans-serif',
|
|
144
|
-
element: '<span style="font-family: Verdana, Geneva, sans-serif">Verdana</span>',
|
|
145
|
-
title: 'Verdana'
|
|
146
|
-
},
|
|
147
|
-
'Courier New': {
|
|
148
|
-
font: '"Courier New", Courier, monospace',
|
|
149
|
-
element: '<span style="font-family: \'Courier New\', Courier, monospace">Courier New</span>',
|
|
150
|
-
title: 'Courier New'
|
|
151
|
-
},
|
|
152
|
-
'Trebuchet MS': {
|
|
153
|
-
font: '"Trebuchet MS", Helvetica, sans-serif',
|
|
154
|
-
element: '<span style="font-family: \'Trebuchet MS\', Helvetica, sans-serif">Trebuchet MS</span>',
|
|
155
|
-
title: 'Trebuchet MS'
|
|
156
|
-
},
|
|
157
|
-
'Comic Sans MS': {
|
|
158
|
-
font: '"Comic Sans MS", cursive',
|
|
159
|
-
element: '<span style="font-family: \'Comic Sans MS\', cursive">Comic Sans MS</span>',
|
|
160
|
-
title: 'Comic Sans MS'
|
|
161
|
-
},
|
|
162
|
-
'Impact': {
|
|
163
|
-
font: 'Impact, Charcoal, sans-serif',
|
|
164
|
-
element: '<span style="font-family: Impact, Charcoal, sans-serif">Impact</span>',
|
|
165
|
-
title: 'Impact'
|
|
166
|
-
},
|
|
167
|
-
'Lucida Console': {
|
|
168
|
-
font: '"Lucida Console", Monaco, monospace',
|
|
169
|
-
element: '<span style="font-family: \'Lucida Console\', Monaco, monospace">Lucida Console</span>',
|
|
170
|
-
title: 'Lucida Console'
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Get display name for font
|
|
178
|
-
* @param {string} font - Font family value
|
|
179
|
-
* @returns {string} Display name
|
|
180
|
-
*/
|
|
181
|
-
static getFontDisplayName(font) {
|
|
182
|
-
const fontMap = this.getFontMap();
|
|
183
|
-
// Find by font value
|
|
184
|
-
for (const [key, value] of Object.entries(fontMap)) {
|
|
185
|
-
if (value.font === font || key === font) {
|
|
186
|
-
return value.title;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return 'Arial';
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Update custom button text based on current font
|
|
194
|
-
*/
|
|
195
|
-
updateButtonText() {
|
|
196
|
-
const currentFont = this.getCurrentFont();
|
|
197
|
-
const displayName = FontFamily.getFontDisplayName(currentFont || 'Arial, sans-serif');
|
|
198
|
-
|
|
199
|
-
// Find font-family button in the specific editor's toolbar using editorId
|
|
200
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
201
|
-
if (!editor) return;
|
|
202
|
-
|
|
203
|
-
const toolbar = editor.getModule('toolbar');
|
|
204
|
-
let fontFamilyButton = null;
|
|
205
|
-
|
|
206
|
-
if (toolbar) {
|
|
207
|
-
fontFamilyButton = toolbar.getButton('font-family');
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Fallback: find button by class in the specific editor's toolbar
|
|
211
|
-
if (!fontFamilyButton) {
|
|
212
|
-
const toolbarContainer = toolbar?.getContainer();
|
|
213
|
-
if (toolbarContainer) {
|
|
214
|
-
fontFamilyButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.font-family-btn');
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Final fallback: find any font-family button in the specific editor's wrapper
|
|
219
|
-
if (!fontFamilyButton) {
|
|
220
|
-
fontFamilyButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.font-family-btn');
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (fontFamilyButton && fontFamilyButton.updateText) {
|
|
224
|
-
fontFamilyButton.updateText(displayName);
|
|
225
|
-
} else if (fontFamilyButton) {
|
|
226
|
-
fontFamilyButton.textContent = displayName;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Create element with specific font family
|
|
232
|
-
* @param {string} font - Font family value
|
|
233
|
-
* @returns {HTMLElement}
|
|
234
|
-
*/
|
|
235
|
-
static create(font = 'Arial, sans-serif') {
|
|
236
|
-
const node = document.createElement('span');
|
|
237
|
-
node.style.fontFamily = font;
|
|
238
|
-
return node;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Static method to apply font family to current selection
|
|
243
|
-
* @param {string} font - Font family value
|
|
244
|
-
* @param {string} editorId - Editor instance ID
|
|
245
|
-
*/
|
|
246
|
-
static applyFontFamilyToCurrentSelection(font, editorId = null) {
|
|
247
|
-
// Get the correct editor instance
|
|
248
|
-
let editor = null;
|
|
249
|
-
if (editorId) {
|
|
250
|
-
editor = Editor.getInstanceById(editorId);
|
|
251
|
-
} else {
|
|
252
|
-
editor = Editor.getCurrentInstance();
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
if (!editor) {
|
|
256
|
-
console.warn('No editor instance found for font family application');
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const selection = window.getSelection();
|
|
261
|
-
if (!selection || !selection.rangeCount) return;
|
|
262
|
-
|
|
263
|
-
// Save state before applying format
|
|
264
|
-
saveBeforeFormat();
|
|
265
|
-
|
|
266
|
-
const range = selection.getRangeAt(0);
|
|
267
|
-
const fontFamilyFormat = FontFamily.createForEditor(editorId);
|
|
268
|
-
if (fontFamilyFormat) {
|
|
269
|
-
fontFamilyFormat.apply(font);
|
|
270
|
-
|
|
271
|
-
// Update button text after applying
|
|
272
|
-
fontFamilyFormat.updateButtonText();
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Trigger content change after applying format
|
|
276
|
-
setTimeout(() => {
|
|
277
|
-
if (editor && typeof editor.onContentChange === 'function') {
|
|
278
|
-
editor.onContentChange();
|
|
279
|
-
}
|
|
280
|
-
}, 0);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Apply font family format with specified font
|
|
285
|
-
* @param {string} font - Font family value
|
|
286
|
-
*/
|
|
287
|
-
apply(font = 'Arial, sans-serif') {
|
|
288
|
-
const selection = window.getSelection();
|
|
289
|
-
if (!selection || !selection.rangeCount) return;
|
|
290
|
-
const range = selection.getRangeAt(0);
|
|
291
|
-
|
|
292
|
-
function isCaretInsideFontSpan(selection, font) {
|
|
293
|
-
if (!selection.rangeCount) return false;
|
|
294
|
-
const range = selection.getRangeAt(0);
|
|
295
|
-
let node = range.startContainer;
|
|
296
|
-
|
|
297
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
298
|
-
node = node.parentNode;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const fontNormalized = font.split(',')[0].trim().toLowerCase();
|
|
302
|
-
|
|
303
|
-
while (node && node.nodeType === Node.ELEMENT_NODE) {
|
|
304
|
-
if (node.tagName === 'SPAN') {
|
|
305
|
-
const styleFont = node.style.fontFamily;
|
|
306
|
-
if (styleFont) {
|
|
307
|
-
const styleFontNormalized = styleFont.split(',')[0].trim().toLowerCase();
|
|
308
|
-
if (styleFontNormalized === fontNormalized) {
|
|
309
|
-
if (
|
|
310
|
-
node.childNodes.length === 1 &&
|
|
311
|
-
node.firstChild.nodeType === Node.TEXT_NODE &&
|
|
312
|
-
node.firstChild.textContent === '\u200B'
|
|
313
|
-
) {
|
|
314
|
-
return true; // Đang trong span marker rồi
|
|
315
|
-
}
|
|
316
|
-
return true; // Đang trong span font-family đó
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
node = node.parentNode;
|
|
321
|
-
}
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Hàm đặt caret vào bên trong span mới
|
|
326
|
-
function moveCaretInside(el) {
|
|
327
|
-
const sel = window.getSelection();
|
|
328
|
-
const range = document.createRange();
|
|
329
|
-
const textNode = el.firstChild;
|
|
330
|
-
range.setStart(textNode, textNode.length);
|
|
331
|
-
range.collapse(true);
|
|
332
|
-
sel.removeAllRanges();
|
|
333
|
-
sel.addRange(range);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
if (range.collapsed) {
|
|
337
|
-
if (isCaretInsideFontSpan(selection, font)) {
|
|
338
|
-
// Đã ở trong span font rồi, không cần làm gì thêm
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
let node = range.startContainer;
|
|
343
|
-
let offset = range.startOffset;
|
|
344
|
-
|
|
345
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
346
|
-
node = node.parentNode;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
const currentSpan = node.closest && node.closest('span');
|
|
350
|
-
|
|
351
|
-
// Trường hợp 1: caret trong span rỗng chứa \u200B
|
|
352
|
-
if (currentSpan && currentSpan.textContent === "\u200B") {
|
|
353
|
-
currentSpan.style.fontFamily = font;
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Trường hợp 2: caret trong span có text thật
|
|
358
|
-
if (currentSpan && currentSpan.firstChild && currentSpan.firstChild.nodeType === Node.TEXT_NODE) {
|
|
359
|
-
const textNode = currentSpan.firstChild;
|
|
360
|
-
const caretPos = range.startOffset;
|
|
361
|
-
|
|
362
|
-
const textBefore = textNode.data.slice(0, caretPos);
|
|
363
|
-
const textAfter = textNode.data.slice(caretPos);
|
|
364
|
-
|
|
365
|
-
const parent = currentSpan.parentNode;
|
|
366
|
-
|
|
367
|
-
if (caretPos === 0) {
|
|
368
|
-
// Chèn span mới trước currentSpan
|
|
369
|
-
const newSpan = document.createElement('span');
|
|
370
|
-
newSpan.style.fontFamily = font;
|
|
371
|
-
newSpan.appendChild(document.createTextNode('\u200B'));
|
|
372
|
-
parent.insertBefore(newSpan, currentSpan);
|
|
373
|
-
moveCaretInside(newSpan);
|
|
374
|
-
} else if (caretPos === textNode.data.length) {
|
|
375
|
-
// Chèn span mới sau currentSpan
|
|
376
|
-
const newSpan = document.createElement('span');
|
|
377
|
-
newSpan.style.fontFamily = font;
|
|
378
|
-
newSpan.appendChild(document.createTextNode('\u200B'));
|
|
379
|
-
parent.insertBefore(newSpan, currentSpan.nextSibling);
|
|
380
|
-
moveCaretInside(newSpan);
|
|
381
|
-
} else {
|
|
382
|
-
// Tách thành 3 span
|
|
383
|
-
const span1 = document.createElement('span');
|
|
384
|
-
span1.style.fontFamily = currentSpan.style.fontFamily;
|
|
385
|
-
span1.appendChild(document.createTextNode(textBefore));
|
|
386
|
-
|
|
387
|
-
const span2 = document.createElement('span');
|
|
388
|
-
span2.style.fontFamily = font;
|
|
389
|
-
span2.appendChild(document.createTextNode('\u200B'));
|
|
390
|
-
|
|
391
|
-
const span3 = document.createElement('span');
|
|
392
|
-
span3.style.fontFamily = currentSpan.style.fontFamily;
|
|
393
|
-
span3.appendChild(document.createTextNode(textAfter));
|
|
394
|
-
|
|
395
|
-
parent.insertBefore(span1, currentSpan);
|
|
396
|
-
parent.insertBefore(span2, currentSpan);
|
|
397
|
-
parent.insertBefore(span3, currentSpan);
|
|
398
|
-
parent.removeChild(currentSpan);
|
|
399
|
-
|
|
400
|
-
moveCaretInside(span2);
|
|
401
|
-
}
|
|
402
|
-
return;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Trường hợp 3: không ở trong span nào → tạo mới
|
|
406
|
-
const newSpan = document.createElement('span');
|
|
407
|
-
newSpan.style.fontFamily = font;
|
|
408
|
-
newSpan.appendChild(document.createTextNode('\u200B'));
|
|
409
|
-
range.insertNode(newSpan);
|
|
410
|
-
moveCaretInside(newSpan);
|
|
411
|
-
|
|
412
|
-
} else {
|
|
413
|
-
// Có selection → dùng execCommand áp dụng fontName
|
|
414
|
-
document.execCommand('fontName', false, font);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
/**
|
|
420
|
-
* Toggle font family format - shows/hides font picker
|
|
421
|
-
*/
|
|
422
|
-
async toggle(anchorButton = null) {
|
|
423
|
-
if (this.customSelect.isVisible) {
|
|
424
|
-
this.customSelect.hide();
|
|
425
|
-
} else {
|
|
426
|
-
await this.showFontPicker(anchorButton);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Show custom select positioned relative to font family button on toolbar
|
|
432
|
-
*/
|
|
433
|
-
async showFontPicker(anchorButton = null) {
|
|
434
|
-
// Use provided anchor button or find the default toolbar button
|
|
435
|
-
let fontFamilyButton = anchorButton;
|
|
436
|
-
|
|
437
|
-
if (!fontFamilyButton) {
|
|
438
|
-
// Find font-family button in the current editor's toolbar
|
|
439
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
440
|
-
if (!editor) return;
|
|
441
|
-
|
|
442
|
-
const toolbar = editor.getModule('toolbar');
|
|
443
|
-
|
|
444
|
-
if (toolbar) {
|
|
445
|
-
fontFamilyButton = toolbar.getButton('font-family');
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Fallback: find button by class in the current editor's toolbar
|
|
449
|
-
if (!fontFamilyButton) {
|
|
450
|
-
const toolbarContainer = toolbar?.getContainer();
|
|
451
|
-
if (toolbarContainer) {
|
|
452
|
-
fontFamilyButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.font-family-btn');
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
// Final fallback: find any font-family button in the current editor's wrapper
|
|
457
|
-
if (!fontFamilyButton) {
|
|
458
|
-
fontFamilyButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.font-family-btn');
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
if (!fontFamilyButton) return;
|
|
463
|
-
|
|
464
|
-
// Update current selection before showing
|
|
465
|
-
const currentFont = this.getCurrentFont();
|
|
466
|
-
if (currentFont) {
|
|
467
|
-
this.customSelect.setCurrentValue(currentFont);
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
await this.customSelect.show(fontFamilyButton);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* Check if font family format is active - always return false (no active state)
|
|
475
|
-
* Only update button text to show current font
|
|
476
|
-
* @param {string} font - Optional specific font to check
|
|
477
|
-
* @returns {boolean}
|
|
478
|
-
*/
|
|
479
|
-
isActive(font = null) {
|
|
480
|
-
// Always update button text to show current font
|
|
481
|
-
this.updateButtonText();
|
|
482
|
-
|
|
483
|
-
// Never show active state for font family button
|
|
484
|
-
return false;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Get current font family of the selection
|
|
489
|
-
* @returns {string|null} Current font family or null
|
|
490
|
-
*/
|
|
491
|
-
getCurrentFont() {
|
|
492
|
-
const selection = window.getSelection();
|
|
493
|
-
if (!selection || !selection.rangeCount) return null;
|
|
494
|
-
|
|
495
|
-
const range = selection.getRangeAt(0);
|
|
496
|
-
let currentNode = range.startContainer;
|
|
497
|
-
|
|
498
|
-
// If text node, get parent element
|
|
499
|
-
if (currentNode.nodeType === Node.TEXT_NODE) {
|
|
500
|
-
currentNode = currentNode.parentElement;
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Get the specific editor instance
|
|
504
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
505
|
-
if (!editor) return 'Arial, sans-serif';
|
|
506
|
-
|
|
507
|
-
// Check if the selection is within this editor
|
|
508
|
-
if (!editor.editor.contains(currentNode) && !editor.editor.isSameNode(currentNode)) {
|
|
509
|
-
// Selection is not in this editor, return default
|
|
510
|
-
return 'Arial, sans-serif';
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Find element with font-family style
|
|
514
|
-
while (currentNode && currentNode !== document.body) {
|
|
515
|
-
if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
|
516
|
-
const element = currentNode;
|
|
517
|
-
|
|
518
|
-
// Priority 1: Check if this element has explicit inline font-family
|
|
519
|
-
if (element.style.fontFamily) {
|
|
520
|
-
return element.style.fontFamily;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Priority 2: Check computed font-family
|
|
524
|
-
const computedStyle = window.getComputedStyle(element);
|
|
525
|
-
const fontFamily = computedStyle.fontFamily;
|
|
526
|
-
if (fontFamily && fontFamily !== 'initial' && fontFamily !== 'inherit') {
|
|
527
|
-
return fontFamily;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
currentNode = currentNode.parentElement;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Default fallback
|
|
534
|
-
return 'Arial, sans-serif';
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
* Set current font for future typing
|
|
539
|
-
* @param {string} font - Font family value
|
|
540
|
-
*/
|
|
541
|
-
setCurrentFont(font) {
|
|
542
|
-
// Store for future typing operations
|
|
543
|
-
this.currentFont = font;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
export default FontFamily;
|