@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.
Files changed (58) hide show
  1. package/README.md +73 -22
  2. package/dist/rich-editor.esm.js +2 -0
  3. package/dist/rich-editor.esm.js.map +1 -0
  4. package/dist/rich-editor.min.js +2 -0
  5. package/dist/rich-editor.min.js.map +1 -0
  6. package/package.json +12 -7
  7. package/index.js +0 -221
  8. package/lib/core/editor.js +0 -1175
  9. package/lib/core/format.js +0 -542
  10. package/lib/core/module.js +0 -81
  11. package/lib/core/registry.js +0 -152
  12. package/lib/formats/background.js +0 -212
  13. package/lib/formats/bold.js +0 -67
  14. package/lib/formats/capitalization.js +0 -563
  15. package/lib/formats/color.js +0 -165
  16. package/lib/formats/emoji.js +0 -282
  17. package/lib/formats/font-family.js +0 -547
  18. package/lib/formats/heading.js +0 -502
  19. package/lib/formats/image.js +0 -344
  20. package/lib/formats/import.js +0 -385
  21. package/lib/formats/indent.js +0 -297
  22. package/lib/formats/italic.js +0 -27
  23. package/lib/formats/line-height.js +0 -558
  24. package/lib/formats/link.js +0 -251
  25. package/lib/formats/list.js +0 -635
  26. package/lib/formats/strike.js +0 -31
  27. package/lib/formats/subscript.js +0 -36
  28. package/lib/formats/superscript.js +0 -35
  29. package/lib/formats/table.js +0 -288
  30. package/lib/formats/tag.js +0 -304
  31. package/lib/formats/text-align.js +0 -421
  32. package/lib/formats/text-size.js +0 -497
  33. package/lib/formats/underline.js +0 -30
  34. package/lib/formats/video.js +0 -372
  35. package/lib/modules/block-toolbar.js +0 -628
  36. package/lib/modules/code-view.js +0 -434
  37. package/lib/modules/history.js +0 -410
  38. package/lib/modules/resize-handles.js +0 -677
  39. package/lib/modules/table-toolbar.js +0 -618
  40. package/lib/modules/toolbar.js +0 -424
  41. package/lib/styles-loader.js +0 -144
  42. package/lib/styles.css +0 -2123
  43. package/lib/ui/color-picker.js +0 -296
  44. package/lib/ui/customselect.js +0 -319
  45. package/lib/ui/emoji-picker.js +0 -196
  46. package/lib/ui/icons.js +0 -413
  47. package/lib/ui/image-popup.js +0 -444
  48. package/lib/ui/import-popup.js +0 -288
  49. package/lib/ui/link-popup.js +0 -191
  50. package/lib/ui/list-picker.js +0 -307
  51. package/lib/ui/select-button.js +0 -61
  52. package/lib/ui/table-popup.js +0 -171
  53. package/lib/ui/tag-popup.js +0 -249
  54. package/lib/ui/text-align-picker.js +0 -281
  55. package/lib/ui/video-popup.js +0 -422
  56. package/lib/utils/history-helper.js +0 -50
  57. package/lib/utils/popup-helper.js +0 -219
  58. package/lib/utils/popup-positioning.js +0 -231
@@ -1,165 +0,0 @@
1
- import { InlineFormat } from '../core/format.js';
2
- import ColorPicker from '../ui/color-picker.js';
3
- import { saveBeforeFormat } from '../utils/history-helper.js';
4
- import Editor from '../core/editor.js';
5
-
6
- /**
7
- * Color Format - Handles text color formatting
8
- */
9
- class Color extends InlineFormat {
10
- static formatName = 'color';
11
- static tagName = 'SPAN';
12
- static attribute = 'color';
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 Color format');
21
- return;
22
- }
23
-
24
- this.editorId = currentEditor.instanceId;
25
-
26
- // Check if this editor already has a color picker instance
27
- let colorPicker = currentEditor.getPopupInstance('color');
28
-
29
- if (!colorPicker) {
30
- // Create new color picker instance for this editor
31
- colorPicker = new ColorPicker({
32
- onColorSelect: (color) => {
33
- Color.applyColorToCurrentSelection(color);
34
- },
35
- editor: Editor.getCurrentInstance()
36
- });
37
-
38
- // Store popup instance in editor
39
- currentEditor.setPopupInstance('color', colorPicker);
40
- }
41
-
42
- this.colorPicker = colorPicker;
43
- }
44
-
45
- /**
46
- * Static method to apply color to current selection
47
- */
48
- static applyColorToCurrentSelection(color) {
49
- const selection = window.getSelection();
50
- if (!selection || !selection.rangeCount || selection.isCollapsed) return;
51
-
52
- // Save state before applying format
53
- saveBeforeFormat();
54
- const colorbutton = document.querySelector('.rich-editor-toolbar-btn.color-btn');
55
-
56
- try {
57
-
58
- document.execCommand('styleWithCSS', false, true);
59
- if(color === 'transparent') {
60
- document.execCommand('foreColor', false, '#2c3e50');
61
- colorbutton.classList.remove('active');
62
-
63
- } else {
64
- document.execCommand('foreColor', false, color);
65
- colorbutton.classList.add('active');
66
-
67
- }
68
- } catch (error) {
69
- console.error('Error applying color format:', error);
70
- }
71
-
72
- // Trigger content change after applying format
73
- setTimeout(() => {
74
- const currentEditor = Editor.getCurrentInstance();
75
- if (currentEditor && typeof currentEditor.onContentChange === 'function') {
76
- currentEditor.onContentChange();
77
- }
78
- }, 0);
79
- }
80
-
81
- /**
82
- * Toggle color formatting - shows/hides color picker
83
- */
84
- toggle() {
85
- if (this.colorPicker.isVisible) {
86
- this.colorPicker.hide();
87
- } else {
88
- this.showColorPicker();
89
- }
90
- }
91
-
92
- /**
93
- * Show color picker positioned relative to color button on toolbar
94
- */
95
- showColorPicker() {
96
- // Find color button in the current editor's toolbar
97
- const editor = Editor.getInstanceById(this.editorId);
98
- if (!editor) return;
99
-
100
- const toolbar = editor.getModule('toolbar');
101
- let colorButton = null;
102
-
103
- if (toolbar) {
104
- colorButton = toolbar.getButton('color');
105
- }
106
-
107
- // Fallback: find button by class in the current editor's toolbar
108
- if (!colorButton) {
109
- const toolbarContainer = toolbar?.getContainer();
110
- if (toolbarContainer) {
111
- colorButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.color-btn');
112
- }
113
- }
114
-
115
- // Final fallback: find any color button in the current editor's wrapper
116
- if (!colorButton) {
117
- colorButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.color-btn');
118
- }
119
-
120
- if (!colorButton) {
121
- console.warn('Color button not found for editor:', this.editorId);
122
- return;
123
- }
124
-
125
- this.colorPicker.show(colorButton);
126
- }
127
-
128
- /**
129
- * Check if color formatting is active in current selection
130
- */
131
- isActive() {
132
- const selection = window.getSelection();
133
- if (!selection || !selection.rangeCount) return false;
134
-
135
- const range = selection.getRangeAt(0);
136
- let node = range.startContainer;
137
-
138
- // Nếu là text node thì lấy parent element
139
- if (node.nodeType === Node.TEXT_NODE) {
140
- node = node.parentNode;
141
- }
142
-
143
- while (node && node !== document.body) {
144
- if (node.nodeType === Node.ELEMENT_NODE) {
145
- const color = window.getComputedStyle(node).color;
146
-
147
- // Nếu gặp đúng màu mặc định -> dừng lại, coi như chưa đổi màu
148
- if (color === 'rgb(44, 62, 80)') {
149
- return false;
150
- }
151
-
152
- // Nếu gặp màu khác mặc định (và không phải transparent)
153
- if (color && color !== 'rgba(0, 0, 0, 0)') {
154
- return true;
155
- }
156
- }
157
-
158
- node = node.parentNode;
159
- }
160
-
161
- return false;
162
- }
163
- }
164
-
165
- export default Color;
@@ -1,282 +0,0 @@
1
- import { InlineFormat } from '../core/format.js';
2
- import EmojiPicker from '../ui/emoji-picker.js';
3
- import Editor from '../core/editor.js';
4
-
5
- /**
6
- * Emoji Format - Handles emoji insertion
7
- * Now supports multiple editor instances with separate popup instances
8
- */
9
- class Emoji extends InlineFormat {
10
- static formatName = 'emoji';
11
- static tagName = 'SPAN';
12
- static className = 'emoji';
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 Emoji format');
21
- return;
22
- }
23
-
24
- this.editorId = currentEditor.instanceId;
25
-
26
- // Check if this editor already has an emoji picker instance
27
- let emojiPicker = currentEditor.getPopupInstance('emoji');
28
-
29
- if (!emojiPicker) {
30
- // Create new emoji picker instance for this editor
31
- emojiPicker = new EmojiPicker({
32
- onEmojiSelect: (emoji) => {
33
- Emoji.insertEmojiAtCurrentPosition(emoji, this.editorId);
34
- },
35
- editor: currentEditor,
36
- editorId: this.editorId
37
- });
38
-
39
- // Store popup instance in editor
40
- currentEditor.setPopupInstance('emoji', emojiPicker);
41
- }
42
-
43
- this.emojiPicker = emojiPicker;
44
- }
45
-
46
- /**
47
- * Create a new Emoji format instance for a specific editor
48
- * @param {string} editorId - Editor instance ID
49
- * @returns {Emoji} Emoji format instance
50
- */
51
- static createForEditor(editorId) {
52
- const editor = Editor.getInstanceById(editorId);
53
- if (!editor) {
54
- console.warn('No editor instance found for ID:', editorId);
55
- return null;
56
- }
57
-
58
- // Temporarily set as current instance
59
- const originalCurrent = Editor.currentInstance;
60
- Editor.currentInstance = editor;
61
-
62
- // Create format instance
63
- const format = new Emoji();
64
-
65
- // Restore original current instance
66
- Editor.currentInstance = originalCurrent;
67
-
68
- return format;
69
- }
70
-
71
- /**
72
- * Create emoji element
73
- * @param {string} value - Emoji character
74
- * @returns {HTMLElement}
75
- */
76
- static create(value) {
77
- const span = document.createElement('SPAN');
78
- span.className = 'emoji';
79
- span.textContent = value;
80
- span.setAttribute('data-emoji', value);
81
- return span;
82
- }
83
-
84
- /**
85
- * Insert emoji at current cursor position
86
- * @param {string} emoji - Emoji character to insert
87
- * @param {string} editorId - Editor instance ID
88
- */
89
- static insertEmojiAtCurrentPosition(emoji, editorId = null) {
90
- // Get the correct editor instance
91
- let editor = null;
92
- if (editorId) {
93
- editor = Editor.getInstanceById(editorId);
94
- } else {
95
- editor = Editor.getCurrentInstance();
96
- }
97
-
98
- if (!editor) {
99
- console.warn('No editor instance found for emoji insertion');
100
- return;
101
- }
102
-
103
- const selection = window.getSelection();
104
- if (!selection || !selection.rangeCount) return;
105
-
106
- try {
107
- const range = selection.getRangeAt(0);
108
-
109
- // Check if cursor is inside an existing emoji span
110
- let currentNode = range.startContainer;
111
- let emojiParent = null;
112
-
113
- // If cursor is in a text node, check its parent
114
- if (currentNode.nodeType === Node.TEXT_NODE) {
115
- currentNode = currentNode.parentNode;
116
- }
117
-
118
- // Find if we're inside an emoji span
119
- while (currentNode && currentNode !== editor.element) {
120
- if (currentNode.classList && currentNode.classList.contains('emoji')) {
121
- emojiParent = currentNode;
122
- break;
123
- }
124
- currentNode = currentNode.parentNode;
125
- }
126
-
127
- // If cursor is inside an emoji span, move it outside
128
- if (emojiParent) {
129
- // Move cursor after the emoji span
130
- range.setStartAfter(emojiParent);
131
- range.collapse(true);
132
- selection.removeAllRanges();
133
- selection.addRange(range);
134
- }
135
-
136
- // Create emoji element
137
- const emojiElement = Emoji.create(emoji);
138
-
139
- // Insert emoji at cursor position
140
- range.deleteContents();
141
- range.insertNode(emojiElement);
142
-
143
- // Create a zero-width space character after the emoji
144
- const zeroWidthSpace = document.createTextNode('\u200B'); // Zero-width space
145
-
146
- // Insert zero-width space after emoji
147
- range.setStartAfter(emojiElement);
148
- range.insertNode(zeroWidthSpace);
149
-
150
- // Position cursor after the zero-width space
151
- range.setStartAfter(zeroWidthSpace);
152
- range.collapse(true);
153
- selection.removeAllRanges();
154
- selection.addRange(range);
155
-
156
- // Trigger content change event
157
- if (editor && typeof editor.onContentChange === 'function') {
158
- editor.onContentChange();
159
- }
160
-
161
- } catch (error) {
162
- console.error('Error inserting emoji:', error);
163
- }
164
- }
165
-
166
- /**
167
- * Apply emoji formatting - shows emoji picker
168
- */
169
- apply(value) {
170
- if (value) {
171
- Emoji.insertEmojiAtCurrentPosition(value, this.editorId);
172
- } else {
173
- this.showEmojiPicker();
174
- }
175
- }
176
-
177
- /**
178
- * Remove emoji formatting
179
- */
180
- remove() {
181
- const selection = window.getSelection();
182
- if (!selection || !selection.rangeCount) return;
183
-
184
- const range = selection.getRangeAt(0);
185
- const emojiElement = this.getEmojiElement(range);
186
-
187
- if (emojiElement) {
188
- // Replace emoji element with its text content
189
- const textNode = document.createTextNode(emojiElement.textContent);
190
- emojiElement.parentNode.replaceChild(textNode, emojiElement);
191
- }
192
- }
193
-
194
- /**
195
- * Toggle emoji formatting - shows emoji picker
196
- */
197
- toggle() {
198
- if (this.emojiPicker.isVisible) {
199
- this.emojiPicker.hide();
200
- } else {
201
- this.showEmojiPicker();
202
- }
203
- }
204
-
205
- /**
206
- * Show emoji picker popup
207
- */
208
- showEmojiPicker() {
209
- // Find emoji button in the current editor's toolbar
210
- const editor = Editor.getInstanceById(this.editorId);
211
- if (!editor) return;
212
-
213
- const toolbar = editor.getModule('toolbar');
214
- let emojiButton = null;
215
-
216
- if (toolbar) {
217
- emojiButton = toolbar.getButton('emoji');
218
- }
219
-
220
- // Fallback: find button by class in the current editor's toolbar
221
- if (!emojiButton) {
222
- const toolbarContainer = toolbar?.getContainer();
223
- if (toolbarContainer) {
224
- emojiButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.emoji-btn');
225
- }
226
- }
227
-
228
- // Final fallback: find any emoji button in the current editor's wrapper
229
- if (!emojiButton) {
230
- emojiButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.emoji-btn');
231
- }
232
-
233
- if (!emojiButton) {
234
- console.warn('Emoji button not found for editor:', this.editorId);
235
- return;
236
- }
237
-
238
- this.emojiPicker.show(emojiButton);
239
- }
240
-
241
- /**
242
- * Check if emoji formatting is active
243
- */
244
- isActive() {
245
- const selection = window.getSelection();
246
- if (!selection || !selection.rangeCount) return false;
247
-
248
- const range = selection.getRangeAt(0);
249
- const emojiElement = this.getEmojiElement(range);
250
-
251
- return emojiElement !== null;
252
- }
253
-
254
- /**
255
- * Get emoji element from selection
256
- * @param {Range} range - Selection range
257
- * @returns {HTMLElement|null}
258
- */
259
- getEmojiElement(range) {
260
- let node = range.commonAncestorContainer;
261
-
262
- // If it's a text node, get its parent
263
- if (node.nodeType === Node.TEXT_NODE) {
264
- node = node.parentNode;
265
- }
266
-
267
- // Check if current node is an emoji
268
- if (node.classList && node.classList.contains('emoji')) {
269
- return node;
270
- }
271
-
272
- // Check if selection contains an emoji
273
- const emojiInSelection = range.cloneContents().querySelector('.emoji');
274
- if (emojiInSelection) {
275
- return emojiInSelection;
276
- }
277
-
278
- return null;
279
- }
280
- }
281
-
282
- export default Emoji;