@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,344 +0,0 @@
1
- import { InlineFormat } from '../core/format.js';
2
- import ImagePopup from '../ui/image-popup.js';
3
- import Editor from '../core/editor.js';
4
-
5
- /**
6
- * Image Format - Handles image insertion
7
- * Now supports multiple editor instances with separate popup instances
8
- */
9
- class Image extends InlineFormat {
10
- static formatName = 'image';
11
- static tagName = 'IMG';
12
- static className = 'inserted-image';
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 Image format');
21
- return;
22
- }
23
-
24
- this.editorId = currentEditor.instanceId;
25
-
26
- // Check if this editor already has an image popup instance
27
- let imagePopup = currentEditor.getPopupInstance('image');
28
-
29
- if (!imagePopup) {
30
- // Create new image popup instance for this editor
31
- imagePopup = new ImagePopup({
32
- onImageInsert: (src, alt) => {
33
- Image.insertImageAtCurrentPosition(src, alt, this.editorId);
34
- },
35
- editor: currentEditor,
36
- editorId: this.editorId
37
- });
38
-
39
- // Store popup instance in editor
40
- currentEditor.setPopupInstance('image', imagePopup);
41
- }
42
-
43
- this.imagePopup = imagePopup;
44
- }
45
-
46
- /**
47
- * Create a new Image format instance for a specific editor
48
- * @param {string} editorId - Editor instance ID
49
- * @returns {Image} Image 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 Image();
64
-
65
- // Restore original current instance
66
- Editor.currentInstance = originalCurrent;
67
-
68
- return format;
69
- }
70
-
71
- /**
72
- * Create image element
73
- * @param {string} src - Image source URL
74
- * @param {string} alt - Alt text
75
- * @returns {HTMLElement}
76
- */
77
- static create(src, alt = '') {
78
- const img = document.createElement('IMG');
79
- img.src = src;
80
- img.alt = alt || 'Inserted image';
81
- img.className = 'inserted-image';
82
- img.style.maxWidth = '100%';
83
- img.style.height = 'auto';
84
- img.setAttribute('contenteditable', 'false');
85
- return img;
86
- }
87
-
88
- /**
89
- * Insert image at current cursor position
90
- * @param {string} src - Image source URL
91
- * @param {string} alt - Alt text
92
- * @param {string} editorId - Editor instance ID
93
- */
94
- static insertImageAtCurrentPosition(src, alt = '', editorId = null) {
95
- // Get the correct editor instance
96
- let editor = null;
97
- if (editorId) {
98
- editor = Editor.getInstanceById(editorId);
99
- } else {
100
- editor = Editor.getCurrentInstance();
101
- }
102
-
103
- if (!editor) {
104
- console.warn('No editor instance found for image insertion');
105
- return;
106
- }
107
-
108
- const selection = window.getSelection();
109
- if (!selection || !selection.rangeCount) {
110
- return;
111
- }
112
-
113
- try {
114
- const range = selection.getRangeAt(0);
115
- // Create image element
116
- const imageElement = Image.create(src, alt);
117
- // Insert image at cursor position
118
- range.deleteContents();
119
- range.insertNode(imageElement);
120
- // Add a space after the image for easier editing
121
- const spaceNode = document.createTextNode(' ');
122
- range.setStartAfter(imageElement);
123
- range.insertNode(spaceNode);
124
- // Position cursor after the space
125
- range.setStartAfter(spaceNode);
126
- range.collapse(true);
127
- selection.removeAllRanges();
128
- selection.addRange(range);
129
-
130
- // Trigger content change event
131
- if (editor && typeof editor.onContentChange === 'function') {
132
- editor.onContentChange();
133
- }
134
- } catch (error) {
135
- console.error('Error inserting image:', error);
136
- }
137
-
138
- // Trigger content change after applying format
139
- setTimeout(() => {
140
- const currentEditor = Editor.getCurrentInstance();
141
- if (currentEditor && typeof currentEditor.onContentChange === 'function') {
142
- currentEditor.onContentChange();
143
- }
144
- }, 0);
145
- }
146
-
147
- /**
148
- * Apply image formatting - shows image popup
149
- */
150
- apply(src, alt) {
151
- if (src) {
152
- Image.insertImageAtCurrentPosition(src, alt, this.editorId);
153
- } else {
154
- this.showImagePopup();
155
- }
156
- }
157
-
158
- /**
159
- * Remove image formatting
160
- */
161
- remove() {
162
- const selection = window.getSelection();
163
- if (!selection || !selection.rangeCount) return;
164
-
165
- const range = selection.getRangeAt(0);
166
- const imageElement = this.getImageElement(range);
167
-
168
- if (imageElement) {
169
- imageElement.remove();
170
- }
171
- }
172
-
173
- /**
174
- * Toggle image formatting - shows image popup
175
- */
176
- toggle() {
177
- if (this.imagePopup.isVisible) {
178
- this.imagePopup.hide();
179
- } else {
180
- this.showImagePopup();
181
- }
182
- }
183
-
184
- /**
185
- * Show image popup
186
- */
187
- showImagePopup() {
188
- // Find image button in the current editor's toolbar
189
- const editor = Editor.getInstanceById(this.editorId);
190
- if (!editor) return;
191
-
192
- const toolbar = editor.getModule('toolbar');
193
- let imageButton = null;
194
-
195
- if (toolbar) {
196
- imageButton = toolbar.getButton('image');
197
- }
198
-
199
- // Fallback: find button by class in the current editor's toolbar
200
- if (!imageButton) {
201
- const toolbarContainer = toolbar?.getContainer();
202
- if (toolbarContainer) {
203
- imageButton = toolbarContainer.querySelector('.rich-editor-toolbar-btn.image-btn');
204
- }
205
- }
206
-
207
- // Final fallback: find any image button in the current editor's wrapper
208
- if (!imageButton) {
209
- imageButton = editor.wrapper.querySelector('.rich-editor-toolbar-btn.image-btn');
210
- }
211
-
212
- if (!imageButton) {
213
- console.warn('Image button not found for editor:', this.editorId);
214
- return;
215
- }
216
-
217
- this.imagePopup.show(imageButton);
218
- }
219
-
220
- /**
221
- * Check if image formatting is active
222
- */
223
- isActive() {
224
- const selection = window.getSelection();
225
- if (!selection || !selection.rangeCount) return false;
226
-
227
- const range = selection.getRangeAt(0);
228
- const imageElement = this.getImageElement(range);
229
-
230
- return imageElement !== null;
231
- }
232
-
233
- /**
234
- * Get image element from selection
235
- * @param {Range} range - Selection range
236
- * @returns {HTMLElement|null}
237
- */
238
- getImageElement(range) {
239
- let node = range.commonAncestorContainer;
240
-
241
- // If it's a text node, get its parent
242
- if (node.nodeType === Node.TEXT_NODE) {
243
- node = node.parentNode;
244
- }
245
-
246
- // Check if current node is an image
247
- if (node.tagName === 'IMG' && node.classList && node.classList.contains('inserted-image')) {
248
- return node;
249
- }
250
-
251
- // Check if selection contains an image
252
- const imageInSelection = range.cloneContents().querySelector('.inserted-image');
253
- if (imageInSelection) {
254
- return imageInSelection;
255
- }
256
-
257
- return null;
258
- }
259
-
260
- /**
261
- * Handle file upload
262
- * @param {File} file - Image file
263
- * @returns {Promise<string>} - Promise that resolves to image URL
264
- */
265
- static async handleFileUpload(file) {
266
- return new Promise((resolve, reject) => {
267
- if (!file || !file.type.startsWith('image/')) {
268
- reject(new Error('Please select a valid image file'));
269
- return;
270
- }
271
-
272
- const reader = new FileReader();
273
- reader.onload = (e) => {
274
- resolve(e.target.result);
275
- };
276
- reader.onerror = () => {
277
- reject(new Error('Failed to read file'));
278
- };
279
- reader.readAsDataURL(file);
280
- });
281
- }
282
-
283
- /**
284
- * Validate image URL
285
- * @param {string} url - Image URL
286
- * @returns {Promise<boolean>} - Promise that resolves to validation result
287
- */
288
- static validateImageUrl(url) {
289
- return new Promise((resolve) => {
290
- // Check if it's a valid image URL format
291
- const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
292
- const hasValidExtension = imageExtensions.some(ext =>
293
- url.toLowerCase().includes(`.${ext}`)
294
- );
295
-
296
- // Check if it's a data URL
297
- if (url.startsWith('data:image/')) {
298
- resolve(true);
299
- return;
300
- }
301
-
302
- // Check if it's a valid HTTP(S) URL
303
- if (!/^https?:\/\//.test(url)) {
304
- resolve(false);
305
- return;
306
- }
307
-
308
- // If it has a valid extension, assume it's valid
309
- if (hasValidExtension) {
310
- resolve(true);
311
- return;
312
- }
313
-
314
- // Try to load the image (fallback)
315
- const img = new Image();
316
- img.onload = () => {
317
- resolve(true);
318
- };
319
- img.onerror = () => {
320
- // If loading fails, but URL looks like an image, still allow it
321
- // This handles cases where CORS blocks loading but the URL is valid
322
- if (url.includes('imgur.com') || url.includes('drive.google.com') || hasValidExtension) {
323
- resolve(true);
324
- } else {
325
- resolve(false);
326
- }
327
- };
328
-
329
- // Set timeout to avoid hanging
330
- setTimeout(() => {
331
- // If no response after 5 seconds, still allow if URL looks valid
332
- if (hasValidExtension || url.includes('imgur.com') || url.includes('drive.google.com')) {
333
- resolve(true);
334
- } else {
335
- resolve(false);
336
- }
337
- }, 5000);
338
-
339
- img.src = url;
340
- });
341
- }
342
- }
343
-
344
- export default Image;