@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,563 +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
|
-
* Capitalization Format - Handles text capitalization
|
|
8
|
-
* Now supports multiple editor instances with separate popup instances
|
|
9
|
-
*/
|
|
10
|
-
class Capitalization extends InlineFormat {
|
|
11
|
-
static formatName = 'capitalization';
|
|
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 Capitalization format');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.editorId = currentEditor.instanceId;
|
|
25
|
-
|
|
26
|
-
// Check if this editor already has a capitalization select instance
|
|
27
|
-
let customSelect = currentEditor.getPopupInstance('capitalization');
|
|
28
|
-
|
|
29
|
-
if (!customSelect) {
|
|
30
|
-
// Create new custom select instance for this editor
|
|
31
|
-
const capMap = Capitalization.getCapitalizationMap();
|
|
32
|
-
const items = Object.values(capMap).map(capData => ({
|
|
33
|
-
value: capData.style,
|
|
34
|
-
label: capData.element,
|
|
35
|
-
title: capData.title
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
customSelect = new CustomSelect({
|
|
39
|
-
items: items,
|
|
40
|
-
displayProperty: 'label',
|
|
41
|
-
valueProperty: 'value',
|
|
42
|
-
className: 'capitalization-select',
|
|
43
|
-
onItemSelect: (value, item) => {
|
|
44
|
-
Capitalization.applyCapitalizationToCurrentSelection(value, this.editorId);
|
|
45
|
-
},
|
|
46
|
-
editor: currentEditor,
|
|
47
|
-
editorId: this.editorId
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Store popup instance in editor
|
|
51
|
-
currentEditor.setPopupInstance('capitalization', customSelect);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
this.customSelect = customSelect;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Create a new Capitalization format instance for a specific editor
|
|
59
|
-
* @param {string} editorId - Editor instance ID
|
|
60
|
-
* @returns {Capitalization} Capitalization format instance
|
|
61
|
-
*/
|
|
62
|
-
static createForEditor(editorId) {
|
|
63
|
-
const editor = Editor.getInstanceById(editorId);
|
|
64
|
-
if (!editor) {
|
|
65
|
-
console.warn('No editor instance found for ID:', editorId);
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Temporarily set as current instance
|
|
70
|
-
const originalCurrent = Editor.currentInstance;
|
|
71
|
-
Editor.currentInstance = editor;
|
|
72
|
-
|
|
73
|
-
// Create format instance
|
|
74
|
-
const format = new Capitalization();
|
|
75
|
-
|
|
76
|
-
// Restore original current instance
|
|
77
|
-
Editor.currentInstance = originalCurrent;
|
|
78
|
-
|
|
79
|
-
return format;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Get capitalization map with different text transformations
|
|
84
|
-
*/
|
|
85
|
-
static getCapitalizationMap() {
|
|
86
|
-
return {
|
|
87
|
-
'capitalize': {
|
|
88
|
-
style: 'capitalize',
|
|
89
|
-
element: '<span>Capitalize</span>',
|
|
90
|
-
title: 'Capitalize'
|
|
91
|
-
},
|
|
92
|
-
'uppercase': {
|
|
93
|
-
style: 'uppercase',
|
|
94
|
-
element: '<span>UPPERCASE</span>',
|
|
95
|
-
title: 'UPPERCASE'
|
|
96
|
-
},
|
|
97
|
-
'lowercase': {
|
|
98
|
-
style: 'lowercase',
|
|
99
|
-
element: '<span>lowercase</span>',
|
|
100
|
-
title: 'lowercase'
|
|
101
|
-
},
|
|
102
|
-
'small-caps': {
|
|
103
|
-
style: 'small-caps',
|
|
104
|
-
element: '<span>Small Caps</span>',
|
|
105
|
-
title: 'Small Caps'
|
|
106
|
-
}
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get display name for capitalization
|
|
112
|
-
* @param {string} style - Text transform value
|
|
113
|
-
* @returns {string} Display name
|
|
114
|
-
*/
|
|
115
|
-
static getCapitalizationDisplayName(style) {
|
|
116
|
-
const capMap = this.getCapitalizationMap();
|
|
117
|
-
return capMap[style]?.title || 'Capitalization';
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Update custom button text based on current capitalization
|
|
122
|
-
*/
|
|
123
|
-
updateButtonText() {
|
|
124
|
-
const currentCap = this.getCurrentCapitalization();
|
|
125
|
-
const displayName = Capitalization.getCapitalizationDisplayName(currentCap || 'none');
|
|
126
|
-
|
|
127
|
-
// Find capitalization button in the current editor's toolbar
|
|
128
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
129
|
-
if (!editor) return;
|
|
130
|
-
|
|
131
|
-
const toolbar = editor.getModule('toolbar');
|
|
132
|
-
let capitalizationButton = null;
|
|
133
|
-
|
|
134
|
-
if (toolbar) {
|
|
135
|
-
capitalizationButton = toolbar.getButton('capitalization');
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Fallback: find button by class in the current editor's toolbar
|
|
139
|
-
if (!capitalizationButton) {
|
|
140
|
-
const toolbarContainer = toolbar?.getContainer();
|
|
141
|
-
if (toolbarContainer) {
|
|
142
|
-
capitalizationButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.capitalization-btn');
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Final fallback: find any capitalization button in the current editor's wrapper
|
|
147
|
-
if (!capitalizationButton) {
|
|
148
|
-
capitalizationButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.capitalization-btn');
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (capitalizationButton && capitalizationButton.updateText) {
|
|
152
|
-
capitalizationButton.updateText(displayName);
|
|
153
|
-
} else if (capitalizationButton) {
|
|
154
|
-
capitalizationButton.textContent = displayName;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Create element with specific text transformation
|
|
160
|
-
* @param {string} style - Text transform value
|
|
161
|
-
* @returns {HTMLElement}
|
|
162
|
-
*/
|
|
163
|
-
static create(style = 'none') {
|
|
164
|
-
const node = document.createElement('span');
|
|
165
|
-
if (style === 'small-caps') {
|
|
166
|
-
node.style.fontVariant = 'small-caps';
|
|
167
|
-
} else {
|
|
168
|
-
node.style.textTransform = style;
|
|
169
|
-
}
|
|
170
|
-
return node;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Static method to apply capitalization to current selection
|
|
175
|
-
* @param {string} style - Text transform value
|
|
176
|
-
* @param {string} editorId - Editor instance ID
|
|
177
|
-
*/
|
|
178
|
-
static applyCapitalizationToCurrentSelection(style, editorId = null) {
|
|
179
|
-
// Get the correct editor instance
|
|
180
|
-
let editor = null;
|
|
181
|
-
if (editorId) {
|
|
182
|
-
editor = Editor.getInstanceById(editorId);
|
|
183
|
-
} else {
|
|
184
|
-
editor = Editor.getCurrentInstance();
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (!editor) {
|
|
188
|
-
console.warn('No editor instance found for capitalization application');
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const selection = window.getSelection();
|
|
193
|
-
if (!selection || !selection.rangeCount) return;
|
|
194
|
-
|
|
195
|
-
// Save state before applying format
|
|
196
|
-
saveBeforeFormat();
|
|
197
|
-
|
|
198
|
-
const range = selection.getRangeAt(0);
|
|
199
|
-
const capFormat = Capitalization.createForEditor(editorId);
|
|
200
|
-
if (capFormat) {
|
|
201
|
-
capFormat.apply(style);
|
|
202
|
-
|
|
203
|
-
// Update button text after applying
|
|
204
|
-
capFormat.updateButtonText();
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Trigger content change after applying format
|
|
208
|
-
setTimeout(() => {
|
|
209
|
-
if (editor && typeof editor.onContentChange === 'function') {
|
|
210
|
-
editor.onContentChange();
|
|
211
|
-
}
|
|
212
|
-
}, 0);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Check if an element has capitalization-related inline or computed styles
|
|
217
|
-
* @param {Element} element
|
|
218
|
-
* @returns {boolean}
|
|
219
|
-
*/
|
|
220
|
-
hasCapitalizationStyling(element) {
|
|
221
|
-
if (!element || element.nodeType !== Node.ELEMENT_NODE) return false;
|
|
222
|
-
if (element.style.fontVariant === 'small-caps') return true;
|
|
223
|
-
if (element.style.textTransform && element.style.textTransform !== 'none') return true;
|
|
224
|
-
const computed = window.getComputedStyle(element);
|
|
225
|
-
if (computed.fontVariant === 'small-caps') return true;
|
|
226
|
-
if (computed.textTransform && computed.textTransform !== 'none') return true;
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Determine whether an element is our capitalization wrapper (inline styles only)
|
|
232
|
-
* @param {Element} element
|
|
233
|
-
* @returns {boolean}
|
|
234
|
-
*/
|
|
235
|
-
isCapitalizationElement(element) {
|
|
236
|
-
if (!element || element.nodeType !== Node.ELEMENT_NODE) return false;
|
|
237
|
-
if (element.style.fontVariant === 'small-caps') return true;
|
|
238
|
-
if (element.style.textTransform && element.style.textTransform !== 'none') return true;
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* Find nearest ancestor element that is a capitalization wrapper
|
|
244
|
-
* @param {Node} node
|
|
245
|
-
* @returns {Element|null}
|
|
246
|
-
*/
|
|
247
|
-
findAncestorCapitalizationElement(node) {
|
|
248
|
-
let current = node;
|
|
249
|
-
if (!current) return null;
|
|
250
|
-
if (current.nodeType === Node.TEXT_NODE) current = current.parentElement;
|
|
251
|
-
while (current && current !== document.body) {
|
|
252
|
-
if (this.isCapitalizationElement(current)) return current;
|
|
253
|
-
current = current.parentElement;
|
|
254
|
-
}
|
|
255
|
-
return null;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Apply capitalization style directly to an element
|
|
260
|
-
* @param {Element} element
|
|
261
|
-
* @param {string} style
|
|
262
|
-
*/
|
|
263
|
-
setElementCapitalizationStyle(element, style) {
|
|
264
|
-
if (!element) return;
|
|
265
|
-
if (style === 'small-caps') {
|
|
266
|
-
element.style.fontVariant = 'small-caps';
|
|
267
|
-
element.style.textTransform = '';
|
|
268
|
-
} else if (style === 'none') {
|
|
269
|
-
element.style.fontVariant = '';
|
|
270
|
-
element.style.textTransform = '';
|
|
271
|
-
} else {
|
|
272
|
-
element.style.fontVariant = '';
|
|
273
|
-
element.style.textTransform = style;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Apply capitalization format with specified style
|
|
279
|
-
* @param {string} style - Text transform value
|
|
280
|
-
*/
|
|
281
|
-
apply(style = 'none') {
|
|
282
|
-
const selection = window.getSelection();
|
|
283
|
-
if (!selection || !selection.rangeCount) return;
|
|
284
|
-
|
|
285
|
-
// Lưu trạng thái trước khi format
|
|
286
|
-
saveBeforeFormat();
|
|
287
|
-
|
|
288
|
-
const range = selection.getRangeAt(0);
|
|
289
|
-
|
|
290
|
-
// Hàm đổi chữ theo style
|
|
291
|
-
function transformText(text, style) {
|
|
292
|
-
switch (style) {
|
|
293
|
-
case 'uppercase':
|
|
294
|
-
return text.toUpperCase();
|
|
295
|
-
case 'lowercase':
|
|
296
|
-
return text.toLowerCase();
|
|
297
|
-
case 'capitalize':
|
|
298
|
-
text =text.toLowerCase();
|
|
299
|
-
return text.replace(/\b\w/g, char => char.toUpperCase());
|
|
300
|
-
default:
|
|
301
|
-
return text; // 'none' hoặc không đổi
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
function removeEmptyElements(node) {
|
|
305
|
-
if (!node) return;
|
|
306
|
-
|
|
307
|
-
// Duyệt cây DOM từ node này xuống
|
|
308
|
-
const walker = document.createTreeWalker(node, NodeFilter.SHOW_ELEMENT, null);
|
|
309
|
-
const toRemove = [];
|
|
310
|
-
|
|
311
|
-
while (walker.nextNode()) {
|
|
312
|
-
const el = walker.currentNode;
|
|
313
|
-
// Nếu không có text hoặc chỉ toàn khoảng trắng & không có element con
|
|
314
|
-
if (!el.textContent.trim() && el.childElementCount === 0) {
|
|
315
|
-
toRemove.push(el);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
toRemove.forEach(el => el.remove());
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Nếu có selection: đổi text bên trong
|
|
323
|
-
const contents = range.extractContents();
|
|
324
|
-
const walker = document.createTreeWalker(contents, NodeFilter.SHOW_TEXT, null);
|
|
325
|
-
|
|
326
|
-
while (walker.nextNode()) {
|
|
327
|
-
const textNode = walker.currentNode;
|
|
328
|
-
textNode.textContent = transformText(textNode.textContent, style);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
range.deleteContents();
|
|
332
|
-
range.insertNode(contents);
|
|
333
|
-
removeEmptyElements(range.commonAncestorContainer);
|
|
334
|
-
|
|
335
|
-
// Giữ nguyên selection
|
|
336
|
-
selection.removeAllRanges();
|
|
337
|
-
selection.addRange(range);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* Remove existing capitalization formatting from range
|
|
342
|
-
* @param {Range} range - Selection range
|
|
343
|
-
*/
|
|
344
|
-
removeExistingCapitalization(range) {
|
|
345
|
-
const root = range.commonAncestorContainer;
|
|
346
|
-
const elementsToProcess = new Set();
|
|
347
|
-
|
|
348
|
-
// Helper to maybe add element
|
|
349
|
-
const maybeAdd = (el) => {
|
|
350
|
-
if (el && el.nodeType === Node.ELEMENT_NODE && this.hasCapitalizationStyling(el) && range.intersectsNode(el)) {
|
|
351
|
-
elementsToProcess.add(el);
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
// Include the root if applicable
|
|
356
|
-
if (root && root.nodeType === Node.ELEMENT_NODE) {
|
|
357
|
-
maybeAdd(root);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Walk descendants
|
|
361
|
-
const walker = document.createTreeWalker(
|
|
362
|
-
root,
|
|
363
|
-
NodeFilter.SHOW_ELEMENT,
|
|
364
|
-
{
|
|
365
|
-
acceptNode: (node) => (range.intersectsNode(node) && this.hasCapitalizationStyling(node)) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP
|
|
366
|
-
}
|
|
367
|
-
);
|
|
368
|
-
let node;
|
|
369
|
-
while ((node = walker.nextNode())) {
|
|
370
|
-
elementsToProcess.add(node);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Include ancestors from start and end containers
|
|
374
|
-
const addAncestors = (startNode) => {
|
|
375
|
-
let current = startNode.nodeType === Node.TEXT_NODE ? startNode.parentElement : startNode;
|
|
376
|
-
while (current && current !== document.body) {
|
|
377
|
-
maybeAdd(current);
|
|
378
|
-
current = current.parentElement;
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
addAncestors(range.startContainer);
|
|
382
|
-
addAncestors(range.endContainer);
|
|
383
|
-
|
|
384
|
-
Array.from(elementsToProcess).forEach(element => {
|
|
385
|
-
// Clear text transform and font variant styles
|
|
386
|
-
element.style.textTransform = '';
|
|
387
|
-
element.style.fontVariant = '';
|
|
388
|
-
|
|
389
|
-
// If element has no other styles, unwrap it
|
|
390
|
-
if (!element.style.cssText.trim() && !element.className) {
|
|
391
|
-
this.unwrapElement(element);
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Unwrap an element, moving its children to its parent
|
|
398
|
-
* @param {Element} element - Element to unwrap
|
|
399
|
-
*/
|
|
400
|
-
unwrapElement(element) {
|
|
401
|
-
const parent = element.parentNode;
|
|
402
|
-
if (!parent) return;
|
|
403
|
-
|
|
404
|
-
while (element.firstChild) {
|
|
405
|
-
parent.insertBefore(element.firstChild, element);
|
|
406
|
-
}
|
|
407
|
-
parent.removeChild(element);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Toggle capitalization format - shows/hides capitalization picker
|
|
412
|
-
*/
|
|
413
|
-
async toggle() {
|
|
414
|
-
if (this.customSelect.isVisible) {
|
|
415
|
-
this.customSelect.hide();
|
|
416
|
-
} else {
|
|
417
|
-
await this.showCapitalizationPicker();
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Show custom select positioned relative to capitalization button on toolbar
|
|
423
|
-
*/
|
|
424
|
-
async showCapitalizationPicker() {
|
|
425
|
-
// Find capitalization button in the current editor's toolbar
|
|
426
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
427
|
-
if (!editor) return;
|
|
428
|
-
|
|
429
|
-
const toolbar = editor.getModule('toolbar');
|
|
430
|
-
let capitalizationButton = null;
|
|
431
|
-
|
|
432
|
-
if (toolbar) {
|
|
433
|
-
capitalizationButton = toolbar.getButton('capitalization');
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Fallback: find button by class in the current editor's toolbar
|
|
437
|
-
if (!capitalizationButton) {
|
|
438
|
-
const toolbarContainer = toolbar?.getContainer();
|
|
439
|
-
if (toolbarContainer) {
|
|
440
|
-
capitalizationButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.capitalization-btn');
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// Final fallback: find any capitalization button in the current editor's wrapper
|
|
445
|
-
if (!capitalizationButton) {
|
|
446
|
-
capitalizationButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.capitalization-btn');
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (!capitalizationButton) {
|
|
450
|
-
console.warn('Capitalization button not found for editor:', this.editorId);
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Update current selection before showing
|
|
455
|
-
const currentCap = this.getCurrentCapitalization();
|
|
456
|
-
if (currentCap) {
|
|
457
|
-
this.customSelect.setCurrentValue(currentCap);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
await this.customSelect.show(capitalizationButton);
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Check if capitalization format is active - always return false (no active state)
|
|
465
|
-
* Only update button text to show current capitalization
|
|
466
|
-
* @param {string} style - Optional specific style to check
|
|
467
|
-
* @returns {boolean}
|
|
468
|
-
*/
|
|
469
|
-
isActive(style = null) {
|
|
470
|
-
// Always update button text to show current capitalization
|
|
471
|
-
this.updateButtonText();
|
|
472
|
-
|
|
473
|
-
// Never show active state for capitalization button
|
|
474
|
-
return false;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
* Get current capitalization of the selection
|
|
479
|
-
* @returns {string|null} Current text transform or null
|
|
480
|
-
*/
|
|
481
|
-
getCurrentCapitalization() {
|
|
482
|
-
const selection = window.getSelection();
|
|
483
|
-
if (!selection || !selection.rangeCount) return null;
|
|
484
|
-
|
|
485
|
-
const range = selection.getRangeAt(0);
|
|
486
|
-
let currentNode = range.startContainer;
|
|
487
|
-
|
|
488
|
-
// If text node, get parent element
|
|
489
|
-
if (currentNode.nodeType === Node.TEXT_NODE) {
|
|
490
|
-
currentNode = currentNode.parentElement;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// Find element with text-transform or font-variant style
|
|
494
|
-
while (currentNode && currentNode !== document.body) {
|
|
495
|
-
if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
|
496
|
-
const element = currentNode;
|
|
497
|
-
|
|
498
|
-
// Priority 1: Check if this element has explicit inline styles
|
|
499
|
-
if (element.style.fontVariant === 'small-caps') {
|
|
500
|
-
return 'small-caps';
|
|
501
|
-
}
|
|
502
|
-
if (element.style.textTransform && element.style.textTransform !== 'none') {
|
|
503
|
-
return element.style.textTransform;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// Priority 2: Check computed styles
|
|
507
|
-
const computedStyle = window.getComputedStyle(element);
|
|
508
|
-
if (computedStyle.fontVariant === 'small-caps') {
|
|
509
|
-
return 'small-caps';
|
|
510
|
-
}
|
|
511
|
-
if (computedStyle.textTransform && computedStyle.textTransform !== 'none') {
|
|
512
|
-
return computedStyle.textTransform;
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
currentNode = currentNode.parentElement;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
// Default fallback
|
|
519
|
-
return 'none';
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
/**
|
|
523
|
-
* Set current capitalization for future typing
|
|
524
|
-
* @param {string} style - Text transform value
|
|
525
|
-
*/
|
|
526
|
-
setCurrentCapitalization(style) {
|
|
527
|
-
// Store for future typing operations
|
|
528
|
-
this.currentCapitalization = style;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Quick toggle methods for common capitalizations
|
|
533
|
-
*/
|
|
534
|
-
static toggleUppercase() {
|
|
535
|
-
const cap = new Capitalization();
|
|
536
|
-
const current = cap.getCurrentCapitalization();
|
|
537
|
-
const newStyle = current === 'uppercase' ? 'none' : 'uppercase';
|
|
538
|
-
Capitalization.applyCapitalizationToCurrentSelection(newStyle);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
static toggleLowercase() {
|
|
542
|
-
const cap = new Capitalization();
|
|
543
|
-
const current = cap.getCurrentCapitalization();
|
|
544
|
-
const newStyle = current === 'lowercase' ? 'none' : 'lowercase';
|
|
545
|
-
Capitalization.applyCapitalizationToCurrentSelection(newStyle);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
static toggleCapitalize() {
|
|
549
|
-
const cap = new Capitalization();
|
|
550
|
-
const current = cap.getCurrentCapitalization();
|
|
551
|
-
const newStyle = current === 'capitalize' ? 'none' : 'capitalize';
|
|
552
|
-
Capitalization.applyCapitalizationToCurrentSelection(newStyle);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
static toggleSmallCaps() {
|
|
556
|
-
const cap = new Capitalization();
|
|
557
|
-
const current = cap.getCurrentCapitalization();
|
|
558
|
-
const newStyle = current === 'small-caps' ? 'none' : 'small-caps';
|
|
559
|
-
Capitalization.applyCapitalizationToCurrentSelection(newStyle);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
export default Capitalization;
|