@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,558 +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
|
-
* Line Height Format - Handles line height formatting
|
|
8
|
-
* Now supports multiple editor instances with separate popup instances
|
|
9
|
-
*/
|
|
10
|
-
class LineHeight extends InlineFormat {
|
|
11
|
-
static formatName = 'lineHeight';
|
|
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 LineHeight format');
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
this.editorId = currentEditor.instanceId;
|
|
25
|
-
|
|
26
|
-
// Check if this editor already has a line height select instance
|
|
27
|
-
let customSelect = currentEditor.getPopupInstance('line-height');
|
|
28
|
-
|
|
29
|
-
if (!customSelect) {
|
|
30
|
-
// Create new custom select instance for this editor
|
|
31
|
-
const heightMap = LineHeight.getHeightMap();
|
|
32
|
-
const items = Object.values(heightMap).map(heightData => ({
|
|
33
|
-
value: heightData.height,
|
|
34
|
-
label: heightData.element,
|
|
35
|
-
title: heightData.title
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
customSelect = new CustomSelect({
|
|
39
|
-
items: items,
|
|
40
|
-
displayProperty: 'label',
|
|
41
|
-
valueProperty: 'value',
|
|
42
|
-
className: 'line-height-select',
|
|
43
|
-
onItemSelect: (value, item) => {
|
|
44
|
-
LineHeight.applyLineHeightToCurrentSelection(value, this.editorId);
|
|
45
|
-
},
|
|
46
|
-
editor: currentEditor,
|
|
47
|
-
editorId: this.editorId
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
// Store popup instance in editor
|
|
51
|
-
currentEditor.setPopupInstance('line-height', 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 LineHeight format instance for a specific editor
|
|
62
|
-
* @param {string} editorId - Editor instance ID
|
|
63
|
-
* @returns {LineHeight} LineHeight 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 LineHeight();
|
|
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 height map with different line heights
|
|
119
|
-
*/
|
|
120
|
-
static getHeightMap() {
|
|
121
|
-
return {
|
|
122
|
-
'1.0': {
|
|
123
|
-
height: '1',
|
|
124
|
-
element: '<span>1.0</span>',
|
|
125
|
-
title: '1.0'
|
|
126
|
-
},
|
|
127
|
-
'1.2': {
|
|
128
|
-
height: '1.2',
|
|
129
|
-
element: '<span>1.2</span>',
|
|
130
|
-
title: '1.2'
|
|
131
|
-
},
|
|
132
|
-
'1.5': {
|
|
133
|
-
height: '1.5',
|
|
134
|
-
element: '<span>1.5</span>',
|
|
135
|
-
title: '1.5'
|
|
136
|
-
},
|
|
137
|
-
'1.8': {
|
|
138
|
-
height: '1.8',
|
|
139
|
-
element: '<span>1.8</span>',
|
|
140
|
-
title: '1.8'
|
|
141
|
-
},
|
|
142
|
-
'2.0': {
|
|
143
|
-
height: '2',
|
|
144
|
-
element: '<span>2.0</span>',
|
|
145
|
-
title: '2.0'
|
|
146
|
-
},
|
|
147
|
-
'2.5': {
|
|
148
|
-
height: '2.5',
|
|
149
|
-
element: '<span>2.5</span>',
|
|
150
|
-
title: '2.5'
|
|
151
|
-
},
|
|
152
|
-
'3.0': {
|
|
153
|
-
height: '3',
|
|
154
|
-
element: '<span>3.0</span>',
|
|
155
|
-
title: '3.0'
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Get display name for line height
|
|
162
|
-
* @param {string} height - Line height value
|
|
163
|
-
* @returns {string} Display name
|
|
164
|
-
*/
|
|
165
|
-
static getHeightDisplayName(height) {
|
|
166
|
-
const heightMap = this.getHeightMap();
|
|
167
|
-
return heightMap[height]?.title || 'line height';
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Update custom button text based on current line height
|
|
172
|
-
*/
|
|
173
|
-
updateButtonText() {
|
|
174
|
-
const currentHeight = this.getCurrentHeight();
|
|
175
|
-
const displayName = LineHeight.getHeightDisplayName(currentHeight || '1.15');
|
|
176
|
-
|
|
177
|
-
// Find line-height button in the specific editor's toolbar using editorId
|
|
178
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
179
|
-
if (!editor) return;
|
|
180
|
-
|
|
181
|
-
const toolbar = editor.getModule('toolbar');
|
|
182
|
-
let lineHeightButton = null;
|
|
183
|
-
|
|
184
|
-
if (toolbar) {
|
|
185
|
-
lineHeightButton = toolbar.getButton('line-height');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Fallback: find button by class in the specific editor's toolbar
|
|
189
|
-
if (!lineHeightButton) {
|
|
190
|
-
const toolbarContainer = toolbar?.getContainer();
|
|
191
|
-
if (toolbarContainer) {
|
|
192
|
-
lineHeightButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.line-height-btn');
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// Final fallback: find any line-height button in the specific editor's wrapper
|
|
197
|
-
if (!lineHeightButton) {
|
|
198
|
-
lineHeightButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.line-height-btn');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (lineHeightButton && lineHeightButton.updateText) {
|
|
202
|
-
lineHeightButton.updateText(displayName);
|
|
203
|
-
} else if (lineHeightButton) {
|
|
204
|
-
lineHeightButton.textContent = displayName;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Create element with specific line height
|
|
210
|
-
* @param {string} height - Line height value
|
|
211
|
-
* @returns {HTMLElement}
|
|
212
|
-
*/
|
|
213
|
-
static create(height = '1.15') {
|
|
214
|
-
const node = document.createElement('span');
|
|
215
|
-
node.style.lineHeight = height;
|
|
216
|
-
return node;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Static method to apply line height to current selection
|
|
221
|
-
* @param {string} height - Line height value
|
|
222
|
-
* @param {string} editorId - Editor instance ID
|
|
223
|
-
*/
|
|
224
|
-
static applyLineHeightToCurrentSelection(height, editorId = null) {
|
|
225
|
-
// Get the correct editor instance
|
|
226
|
-
let editor = null;
|
|
227
|
-
if (editorId) {
|
|
228
|
-
editor = Editor.getInstanceById(editorId);
|
|
229
|
-
} else {
|
|
230
|
-
editor = Editor.getCurrentInstance();
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (!editor) {
|
|
234
|
-
console.warn('No editor instance found for line height application');
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const selection = window.getSelection();
|
|
239
|
-
if (!selection || !selection.rangeCount) return;
|
|
240
|
-
|
|
241
|
-
// Save state before applying format
|
|
242
|
-
saveBeforeFormat();
|
|
243
|
-
|
|
244
|
-
const range = selection.getRangeAt(0);
|
|
245
|
-
const lineHeightFormat = LineHeight.createForEditor(editorId);
|
|
246
|
-
if (lineHeightFormat) {
|
|
247
|
-
lineHeightFormat.apply(height);
|
|
248
|
-
|
|
249
|
-
// Update button text after applying
|
|
250
|
-
lineHeightFormat.updateButtonText();
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Trigger content change after applying format
|
|
254
|
-
setTimeout(() => {
|
|
255
|
-
if (editor && typeof editor.onContentChange === 'function') {
|
|
256
|
-
editor.onContentChange();
|
|
257
|
-
}
|
|
258
|
-
}, 0);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Apply line height format with specified height
|
|
263
|
-
* @param {string} height - Line height value
|
|
264
|
-
*/
|
|
265
|
-
apply(height = '1.15') {
|
|
266
|
-
const selection = window.getSelection();
|
|
267
|
-
if (!selection || !selection.rangeCount) return;
|
|
268
|
-
// Hàm đặt caret vào bên trong span mới
|
|
269
|
-
function moveCaretInside(el) {
|
|
270
|
-
const sel = window.getSelection();
|
|
271
|
-
const range = document.createRange();
|
|
272
|
-
const textNode = el.firstChild;
|
|
273
|
-
range.setStart(textNode, textNode.length);
|
|
274
|
-
range.collapse(true);
|
|
275
|
-
sel.removeAllRanges();
|
|
276
|
-
sel.addRange(range);
|
|
277
|
-
}
|
|
278
|
-
// Save state before applying format
|
|
279
|
-
saveBeforeFormat();
|
|
280
|
-
|
|
281
|
-
const range = selection.getRangeAt(0);
|
|
282
|
-
|
|
283
|
-
if (range.collapsed) {
|
|
284
|
-
// No selection - set style for future typing
|
|
285
|
-
let node = range.startContainer;
|
|
286
|
-
if (node.nodeType === Node.TEXT_NODE) {
|
|
287
|
-
node = node.parentNode;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// Tìm phần tử block cha gần nhất (div, p, li, ...)
|
|
291
|
-
const blockParent = node.closest('div, p, li, section, article') || node;
|
|
292
|
-
blockParent.style.lineHeight = height;
|
|
293
|
-
moveCaretInside(blockParent);
|
|
294
|
-
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Apply to block elements if possible for better line height effect
|
|
299
|
-
const blockElements = this.getBlockElementsInRange(range);
|
|
300
|
-
|
|
301
|
-
if (blockElements.length > 0) {
|
|
302
|
-
// Apply to block elements
|
|
303
|
-
blockElements.forEach(block => {
|
|
304
|
-
block.style.lineHeight = height;
|
|
305
|
-
});
|
|
306
|
-
} else {
|
|
307
|
-
// Fallback: wrap in span with line-height
|
|
308
|
-
const heightSpan = this.constructor.create(height);
|
|
309
|
-
|
|
310
|
-
try {
|
|
311
|
-
const contents = range.extractContents();
|
|
312
|
-
heightSpan.appendChild(contents);
|
|
313
|
-
range.insertNode(heightSpan);
|
|
314
|
-
|
|
315
|
-
// Select the content in the span
|
|
316
|
-
const newRange = document.createRange();
|
|
317
|
-
newRange.selectNodeContents(heightSpan);
|
|
318
|
-
selection.removeAllRanges();
|
|
319
|
-
selection.addRange(newRange);
|
|
320
|
-
} catch (error) {
|
|
321
|
-
console.warn('Failed to apply line height manually:', error);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* Get block elements within the range
|
|
328
|
-
* @param {Range} range - Selection range
|
|
329
|
-
* @returns {Array} Array of block elements
|
|
330
|
-
*/
|
|
331
|
-
getBlockElementsInRange(range) {
|
|
332
|
-
const blockElements = [];
|
|
333
|
-
const blockTags = ['P', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'BLOCKQUOTE', 'PRE', 'UL', 'OL', 'LI'];
|
|
334
|
-
|
|
335
|
-
// Create a fragment of the selection
|
|
336
|
-
const fragment = range.cloneContents();
|
|
337
|
-
|
|
338
|
-
// Get all potential block elements in the fragment
|
|
339
|
-
const walker = document.createTreeWalker(
|
|
340
|
-
fragment,
|
|
341
|
-
NodeFilter.SHOW_ELEMENT,
|
|
342
|
-
{
|
|
343
|
-
acceptNode: (node) => {
|
|
344
|
-
return blockTags.includes(node.tagName) ?
|
|
345
|
-
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
);
|
|
349
|
-
|
|
350
|
-
// Get corresponding elements from the actual document
|
|
351
|
-
let node = walker.nextNode();
|
|
352
|
-
while (node) {
|
|
353
|
-
// Find the actual element in the document that corresponds to this fragment node
|
|
354
|
-
const actualNode = range.commonAncestorContainer.querySelector(
|
|
355
|
-
`${node.tagName.toLowerCase()}:not([data-processed])`
|
|
356
|
-
);
|
|
357
|
-
if (actualNode && range.intersectsNode(actualNode)) {
|
|
358
|
-
blockElements.push(actualNode);
|
|
359
|
-
// Mark as processed to avoid duplicates
|
|
360
|
-
actualNode.setAttribute('data-processed', 'true');
|
|
361
|
-
}
|
|
362
|
-
node = walker.nextNode();
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Clean up the temporary attribute
|
|
366
|
-
blockElements.forEach(el => el.removeAttribute('data-processed'));
|
|
367
|
-
|
|
368
|
-
// If no block elements found in selection, get the closest parent block element
|
|
369
|
-
if (blockElements.length === 0) {
|
|
370
|
-
let currentNode = range.startContainer;
|
|
371
|
-
|
|
372
|
-
// If text node, get parent element
|
|
373
|
-
if (currentNode.nodeType === Node.TEXT_NODE) {
|
|
374
|
-
currentNode = currentNode.parentElement;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Find parent block element
|
|
378
|
-
while (currentNode && currentNode !== document.body) {
|
|
379
|
-
if (currentNode.nodeType === Node.ELEMENT_NODE &&
|
|
380
|
-
blockTags.includes(currentNode.tagName)) {
|
|
381
|
-
blockElements.push(currentNode);
|
|
382
|
-
break;
|
|
383
|
-
}
|
|
384
|
-
currentNode = currentNode.parentElement;
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
return blockElements;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* Toggle line height format - shows/hides height picker
|
|
393
|
-
*/
|
|
394
|
-
async toggle() {
|
|
395
|
-
if (this.customSelect.isVisible) {
|
|
396
|
-
this.customSelect.hide();
|
|
397
|
-
} else {
|
|
398
|
-
await this.showHeightPicker();
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Show custom select positioned relative to line height button on toolbar
|
|
404
|
-
*/
|
|
405
|
-
async showHeightPicker() {
|
|
406
|
-
// Find line-height button in the current editor's toolbar
|
|
407
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
408
|
-
if (!editor) return;
|
|
409
|
-
|
|
410
|
-
const toolbar = editor.getModule('toolbar');
|
|
411
|
-
let lineHeightButton = null;
|
|
412
|
-
|
|
413
|
-
if (toolbar) {
|
|
414
|
-
lineHeightButton = toolbar.getButton('line-height');
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// Fallback: find button by class in the current editor's toolbar
|
|
418
|
-
if (!lineHeightButton) {
|
|
419
|
-
const toolbarContainer = toolbar?.getContainer();
|
|
420
|
-
if (toolbarContainer) {
|
|
421
|
-
lineHeightButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.line-height-btn');
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Final fallback: find any line-height button in the current editor's wrapper
|
|
426
|
-
if (!lineHeightButton) {
|
|
427
|
-
lineHeightButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.line-height-btn');
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
if (!lineHeightButton) {
|
|
431
|
-
console.warn('Line-height button not found for editor:', this.editorId);
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// Update current selection before showing
|
|
436
|
-
const currentHeight = this.getCurrentHeight();
|
|
437
|
-
if (currentHeight) {
|
|
438
|
-
this.customSelect.setCurrentValue(currentHeight);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
await this.customSelect.show(lineHeightButton);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
/**
|
|
445
|
-
* Check if line height format is active - always return false (no active state)
|
|
446
|
-
* Only update button text to show current height
|
|
447
|
-
* @param {string} height - Optional specific height to check
|
|
448
|
-
* @returns {boolean}
|
|
449
|
-
*/
|
|
450
|
-
isActive(height = null) {
|
|
451
|
-
// Always update button text to show current height
|
|
452
|
-
this.updateButtonText();
|
|
453
|
-
|
|
454
|
-
// Never show active state for line height button
|
|
455
|
-
return false;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
/**
|
|
459
|
-
* Get current line height of the selection
|
|
460
|
-
* @returns {string|null} Current line height or null
|
|
461
|
-
*/
|
|
462
|
-
getCurrentHeight() {
|
|
463
|
-
const selection = window.getSelection();
|
|
464
|
-
if (!selection || !selection.rangeCount) return null;
|
|
465
|
-
|
|
466
|
-
const range = selection.getRangeAt(0);
|
|
467
|
-
let currentNode = range.startContainer;
|
|
468
|
-
|
|
469
|
-
// If text node, get parent element
|
|
470
|
-
if (currentNode.nodeType === Node.TEXT_NODE) {
|
|
471
|
-
currentNode = currentNode.parentElement;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// Get the specific editor instance
|
|
475
|
-
const editor = Editor.getInstanceById(this.editorId);
|
|
476
|
-
if (!editor) return '1.15';
|
|
477
|
-
|
|
478
|
-
// Check if the selection is within this editor
|
|
479
|
-
if (!editor.editor.contains(currentNode) && !editor.editor.isSameNode(currentNode)) {
|
|
480
|
-
// Selection is not in this editor, return default
|
|
481
|
-
return '1.15';
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
// Find element with line-height style
|
|
485
|
-
while (currentNode && currentNode !== document.body) {
|
|
486
|
-
if (currentNode.nodeType === Node.ELEMENT_NODE) {
|
|
487
|
-
const element = currentNode;
|
|
488
|
-
|
|
489
|
-
// Priority 1: Check if this element has explicit inline line-height
|
|
490
|
-
if (element.style.lineHeight) {
|
|
491
|
-
const height = element.style.lineHeight;
|
|
492
|
-
return this.normalizeHeightValue(height);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Priority 2: Check computed line-height
|
|
496
|
-
const computedStyle = window.getComputedStyle(element);
|
|
497
|
-
const lineHeight = computedStyle.lineHeight;
|
|
498
|
-
|
|
499
|
-
if (lineHeight && lineHeight !== 'normal' && lineHeight !== 'initial' && lineHeight !== 'inherit') {
|
|
500
|
-
// Convert pixel values to relative values if possible
|
|
501
|
-
if (lineHeight.endsWith('px')) {
|
|
502
|
-
const fontSize = parseFloat(computedStyle.fontSize);
|
|
503
|
-
const lineHeightPx = parseFloat(lineHeight);
|
|
504
|
-
if (fontSize > 0) {
|
|
505
|
-
const relative = (lineHeightPx / fontSize).toFixed(2);
|
|
506
|
-
return this.normalizeHeightValue(relative);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
return this.normalizeHeightValue(lineHeight);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
currentNode = currentNode.parentElement;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
// Default fallback
|
|
516
|
-
return '1.15';
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Normalize height value to match heightMap keys
|
|
521
|
-
* @param {string} height - Raw height value
|
|
522
|
-
* @returns {string} Normalized height value
|
|
523
|
-
*/
|
|
524
|
-
normalizeHeightValue(height) {
|
|
525
|
-
if (!height) return '1.15';
|
|
526
|
-
|
|
527
|
-
// Convert to number and back to string to normalize
|
|
528
|
-
const numValue = parseFloat(height);
|
|
529
|
-
if (isNaN(numValue)) return '1.15';
|
|
530
|
-
|
|
531
|
-
// Round to 1 decimal place and convert back to string
|
|
532
|
-
const normalized = numValue.toFixed(1);
|
|
533
|
-
|
|
534
|
-
// Check if this normalized value exists in our heightMap
|
|
535
|
-
const heightMap = this.constructor.getHeightMap();
|
|
536
|
-
if (heightMap[normalized]) {
|
|
537
|
-
return normalized;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// If not in map, return the original value
|
|
541
|
-
return height;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* Clean up event listeners to prevent memory leaks
|
|
547
|
-
*/
|
|
548
|
-
destroy() {
|
|
549
|
-
if (this.selectionListener) {
|
|
550
|
-
document.removeEventListener('selectionchange', this.selectionListener);
|
|
551
|
-
document.removeEventListener('mouseup', this.selectionListener);
|
|
552
|
-
document.removeEventListener('keyup', this.selectionListener);
|
|
553
|
-
this.selectionListener = null;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
export default LineHeight;
|